/* ZynAddSubFX - a software synthesizer PADnote.C - The "pad" synthesizer Copyright (C) 2002-2005 Nasca Octavian Paul Author: Nasca Octavian Paul This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (version 2) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "PADnote.h" #include "../Misc/Config.h" PADnote::PADnote(PADnoteParameters *parameters, Controller *ctl_,REALTYPE freq, REALTYPE velocity, int portamento_, int midinote){ ready=0; pars=parameters; portamento=portamento_; ctl=ctl_; this->velocity=velocity; finished_=false; if (pars->Pfixedfreq==0) basefreq=freq; else { basefreq=440.0; int fixedfreqET=pars->PfixedfreqET; if (fixedfreqET!=0) {//if the frequency varies according the keyboard note REALTYPE tmp=(midinote-69.0)/12.0*(pow(2.0,(fixedfreqET-1)/63.0)-1.0); if (fixedfreqET<=64) basefreq*=pow(2.0,tmp); else basefreq*=pow(3.0,tmp); }; }; firsttime=true; released=false; realfreq=basefreq; NoteGlobalPar.Detune=getdetune(pars->PDetuneType ,pars->PCoarseDetune,pars->PDetune); //find out the closest note REALTYPE logfreq=log(basefreq*pow(2.0,NoteGlobalPar.Detune/1200.0)); REALTYPE mindist=fabs(logfreq-log(pars->sample[0].basefreq+0.0001)); nsample=0; for (int i=1;isample[i].smp==NULL) break; REALTYPE dist=fabs(logfreq-log(pars->sample[i].basefreq+0.0001)); // printf("(mindist=%g) %i %g %g\n",mindist,i,dist,pars->sample[i].basefreq); if (distsample[nsample].size; if (size==0) size=1; poshi_l=(int)(RND*(size-1)); if (pars->PStereo!=0) poshi_r=(poshi_l+size/2)%size; else poshi_r=poshi_l; poslo=0.0; tmpwave=new REALTYPE [SOUND_BUFFER_SIZE]; if (pars->PPanning==0) NoteGlobalPar.Panning=RND; else NoteGlobalPar.Panning=pars->PPanning/128.0; NoteGlobalPar.FilterCenterPitch=pars->GlobalFilter->getfreq()+//center freq pars->PFilterVelocityScale/127.0*6.0* //velocity sensing (VelF(velocity,pars->PFilterVelocityScaleFunction)-1); if (pars->PPunchStrength!=0) { NoteGlobalPar.Punch.Enabled=1; NoteGlobalPar.Punch.t=1.0;//start from 1.0 and to 0.0 NoteGlobalPar.Punch.initialvalue=( (pow(10,1.5*pars->PPunchStrength/127.0)-1.0) *VelF(velocity,pars->PPunchVelocitySensing) ); REALTYPE time=pow(10,3.0*pars->PPunchTime/127.0)/10000.0;//0.1 .. 100 ms REALTYPE stretch=pow(440.0/freq,pars->PPunchStretch/64.0); NoteGlobalPar.Punch.dt=1.0/(time*SAMPLE_RATE*stretch); } else NoteGlobalPar.Punch.Enabled=0; NoteGlobalPar.FreqEnvelope=new Envelope(pars->FreqEnvelope,basefreq); NoteGlobalPar.FreqLfo=new LFO(pars->FreqLfo,basefreq); NoteGlobalPar.AmpEnvelope=new Envelope(pars->AmpEnvelope,basefreq); NoteGlobalPar.AmpLfo=new LFO(pars->AmpLfo,basefreq); NoteGlobalPar.Volume=4.0*pow(0.1,3.0*(1.0-pars->PVolume/96.0))//-60 dB .. 0 dB *VelF(velocity,pars->PAmpVelocityScaleFunction);//velocity sensing NoteGlobalPar.AmpEnvelope->envout_dB();//discard the first envelope output globaloldamplitude=globalnewamplitude=NoteGlobalPar.Volume*NoteGlobalPar.AmpEnvelope->envout_dB()*NoteGlobalPar.AmpLfo->amplfoout(); NoteGlobalPar.GlobalFilterL=new Filter(pars->GlobalFilter); NoteGlobalPar.GlobalFilterR=new Filter(pars->GlobalFilter); NoteGlobalPar.FilterEnvelope=new Envelope(pars->FilterEnvelope,basefreq); NoteGlobalPar.FilterLfo=new LFO(pars->FilterLfo,basefreq); NoteGlobalPar.FilterQ=pars->GlobalFilter->getq(); NoteGlobalPar.FilterFreqTracking=pars->GlobalFilter->getfreqtracking(basefreq); ready=1;///sa il pun pe asta doar cand e chiar gata if (parameters->sample[nsample].smp==NULL){ finished_=true; return; }; }; PADnote::~PADnote(){ delete (NoteGlobalPar.FreqEnvelope); delete (NoteGlobalPar.FreqLfo); delete (NoteGlobalPar.AmpEnvelope); delete (NoteGlobalPar.AmpLfo); delete (NoteGlobalPar.GlobalFilterL); delete (NoteGlobalPar.GlobalFilterR); delete (NoteGlobalPar.FilterEnvelope); delete (NoteGlobalPar.FilterLfo); delete (tmpwave); }; inline void PADnote::fadein(REALTYPE *smps){ int zerocrossings=0; for (int i=1;i0.0)) zerocrossings++;//this is only the possitive crossings REALTYPE tmp=(SOUND_BUFFER_SIZE-1.0)/(zerocrossings+1)/3.0; if (tmp<8.0) tmp=8.0; int n; F2I(tmp,n);//how many samples is the fade-in if (n>SOUND_BUFFER_SIZE) n=SOUND_BUFFER_SIZE; for (int i=0;ienvout()+ NoteGlobalPar.FreqLfo->lfoout()*ctl->modwheel.relmod+NoteGlobalPar.Detune); globaloldamplitude=globalnewamplitude; globalnewamplitude=NoteGlobalPar.Volume*NoteGlobalPar.AmpEnvelope->envout_dB()*NoteGlobalPar.AmpLfo->amplfoout(); globalfilterpitch=NoteGlobalPar.FilterEnvelope->envout()+NoteGlobalPar.FilterLfo->lfoout() +NoteGlobalPar.FilterCenterPitch; REALTYPE tmpfilterfreq=globalfilterpitch+ctl->filtercutoff.relfreq +NoteGlobalPar.FilterFreqTracking; tmpfilterfreq=NoteGlobalPar.GlobalFilterL->getrealfreq(tmpfilterfreq); REALTYPE globalfilterq=NoteGlobalPar.FilterQ*ctl->filterq.relq; NoteGlobalPar.GlobalFilterL->setfreq_and_q(tmpfilterfreq,globalfilterq); NoteGlobalPar.GlobalFilterR->setfreq_and_q(tmpfilterfreq,globalfilterq); //compute the portamento, if it is used by this note REALTYPE portamentofreqrap=1.0; if (portamento!=0){//this voice use portamento portamentofreqrap=ctl->portamento.freqrap; if (ctl->portamento.used==0){//the portamento has finished portamento=0;//this note is no longer "portamented" }; }; realfreq=basefreq*portamentofreqrap*pow(2.0,globalpitch/12.0)*ctl->pitchwheel.relfreq; }; int PADnote::Compute_Linear(REALTYPE *outl,REALTYPE *outr,int freqhi,REALTYPE freqlo){ REALTYPE *smps=pars->sample[nsample].smp; if (smps==NULL){ finished_=true; return(1); }; int size=pars->sample[nsample].size; for (int i=0;i=1.0){ poshi_l+=1; poshi_r+=1; poslo-=1.0; }; if (poshi_l>=size) poshi_l%=size; if (poshi_r>=size) poshi_r%=size; outl[i]=smps[poshi_l]*(1.0-poslo)+smps[poshi_l+1]*poslo; outr[i]=smps[poshi_r]*(1.0-poslo)+smps[poshi_r+1]*poslo; }; return(1); }; int PADnote::Compute_Cubic(REALTYPE *outl,REALTYPE *outr,int freqhi,REALTYPE freqlo){ REALTYPE *smps=pars->sample[nsample].smp; if (smps==NULL){ finished_=true; return(1); }; int size=pars->sample[nsample].size; REALTYPE xm1,x0,x1,x2,a,b,c; for (int i=0;i=1.0){ poshi_l+=1; poshi_r+=1; poslo-=1.0; }; if (poshi_l>=size) poshi_l%=size; if (poshi_r>=size) poshi_r%=size; //left xm1=smps[poshi_l]; x0=smps[poshi_l + 1]; x1=smps[poshi_l + 2]; x2=smps[poshi_l + 3]; a = (3.0 * (x0-x1) - xm1 + x2)*0.5; b = 2.0*x1 + xm1 - (5.0*x0 + x2)*0.5; c = (x1 - xm1)*0.5; outl[i] = (((a * poslo) + b) * poslo + c) * poslo + x0; //right xm1=smps[poshi_r]; x0=smps[poshi_r + 1]; x1=smps[poshi_r + 2]; x2=smps[poshi_r + 3]; a = (3.0 * (x0-x1) - xm1 + x2)*0.5; b = 2.0*x1 + xm1 - (5.0*x0 + x2)*0.5; c = (x1 - xm1)*0.5; outr[i] = (((a * poslo) + b) * poslo + c) * poslo + x0; }; return(1); }; int PADnote::noteout(REALTYPE *outl,REALTYPE *outr){ computecurrentparameters(); REALTYPE *smps=pars->sample[nsample].smp; if (smps==NULL){ for (int i=0;isample[nsample].basefreq; REALTYPE freqrap=realfreq/smpfreq; int freqhi=(int) (floor(freqrap)); REALTYPE freqlo=freqrap-floor(freqrap); if (config.cfg.Interpolation) Compute_Cubic(outl,outr,freqhi,freqlo); else Compute_Linear(outl,outr,freqhi,freqlo); if (firsttime){ fadein(outl); fadein(outr); firsttime=false; }; NoteGlobalPar.GlobalFilterL->filterout(outl); NoteGlobalPar.GlobalFilterR->filterout(outr); //Apply the punch if (NoteGlobalPar.Punch.Enabled!=0){ for (int i=0;ifinished()!=0) { for (int i=0;irelasekey(); NoteGlobalPar.FilterEnvelope->relasekey(); NoteGlobalPar.AmpEnvelope->relasekey(); };