/* ZynAddSubFX - a software synthesizer SUBnote.C - The "subtractive" 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 #include #include "../globals.h" #include "SUBnote.h" #include "../Misc/Util.h" SUBnote::SUBnote(SUBnoteParameters *parameters,Controller *ctl_,REALTYPE freq,REALTYPE velocity,int portamento_,int midinote){ ready=0; tmpsmp=new REALTYPE[SOUND_BUFFER_SIZE]; tmprnd=new REALTYPE[SOUND_BUFFER_SIZE]; pars=parameters; ctl=ctl_; portamento=portamento_; NoteEnabled=ON; volume=pow(0.1,3.0*(1.0-pars->PVolume/96.0));//-60 dB .. 0 dB volume*=VelF(velocity,pars->PAmpVelocityScaleFunction); if (pars->PPanning!=0) panning=pars->PPanning/127.0; else panning=RND; numstages=pars->Pnumstages; stereo=pars->Pstereo; start=pars->Pstart; firsttick=1; int pos[MAX_SUB_HARMONICS]; 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); }; }; REALTYPE detune=getdetune(pars->PDetuneType,pars->PCoarseDetune,pars->PDetune); basefreq*=pow(2.0,detune/1200.0);//detune // basefreq*=ctl->pitchwheel.relfreq;//pitch wheel //global filter GlobalFilterCenterPitch=pars->GlobalFilter->getfreq()+//center freq (pars->PGlobalFilterVelocityScale/127.0*6.0)* //velocity sensing (VelF(velocity,pars->PGlobalFilterVelocityScaleFunction)-1); GlobalFilterL=NULL;GlobalFilterR=NULL; GlobalFilterEnvelope=NULL; //select only harmonics that desire to compute numharmonics=0; for (int n=0;nPhmag[n]==0)continue; if (n*basefreq>SAMPLE_RATE/2.0) break;//remove the freqs above the Nyquist freq pos[numharmonics++]=n; }; if (numharmonics==0) { NoteEnabled=OFF; return; }; lfilter=new bpfilter[numstages*numharmonics]; if (stereo!=0) rfilter=new bpfilter[numstages*numharmonics]; //how much the amplitude is normalised (because the harmonics) REALTYPE reduceamp=0.0; for (int n=0;nPbandwidth-127.0)/127.0*4)*numstages; //Bandwidth Scale bw*=pow(1000/freq,(pars->Pbwscale-64.0)/64.0*3.0); //Relative BandWidth bw*=pow(100,(pars->Phrelbw[pos[n]]-64.0)/64.0); if (bw>25.0) bw=25.0; //try to keep same amplitude on all freqs and bw. (empirically) REALTYPE gain=sqrt(1500.0/(bw*freq)); REALTYPE hmagnew=1.0-pars->Phmag[pos[n]]/127.0; REALTYPE hgain; switch(pars->Phmagtype){ case 1:hgain=exp(hmagnew*log(0.01)); break; case 2:hgain=exp(hmagnew*log(0.001));break; case 3:hgain=exp(hmagnew*log(0.0001));break; case 4:hgain=exp(hmagnew*log(0.00001));break; default:hgain=1.0-hmagnew; }; gain*=hgain; reduceamp+=hgain; for (int nph=0;nphPfixedfreq==0) initparameters(basefreq); else initparameters(basefreq/440.0*freq); oldamplitude=newamplitude; ready=1; }; SUBnote::~SUBnote(){ if (NoteEnabled!=OFF) KillNote(); delete [] tmpsmp; delete [] tmprnd; }; /* * Kill the note */ void SUBnote::KillNote(){ if (NoteEnabled!=OFF){ delete [] lfilter; lfilter=NULL; if (stereo!=0) delete [] rfilter; rfilter=NULL; delete(AmpEnvelope); if (FreqEnvelope!=NULL) delete(FreqEnvelope); if (BandWidthEnvelope!=NULL) delete(BandWidthEnvelope); NoteEnabled=OFF; }; }; /* * Compute the filters coefficients */ void SUBnote::computefiltercoefs(bpfilter &filter,REALTYPE freq,REALTYPE bw,REALTYPE gain){ if (freq>SAMPLE_RATE/2.0-200.0) { freq=SAMPLE_RATE/2.0-200.0; }; REALTYPE omega=2.0*PI*freq/SAMPLE_RATE; REALTYPE sn=sin(omega);REALTYPE cs=cos(omega); REALTYPE alpha=sn*sinh(LOG_2/2.0*bw*omega/sn); if (alpha>1) alpha=1; if (alpha>bw) alpha=bw; filter.b0=alpha/(1.0+alpha)*filter.amp*gain; filter.b2=-alpha/(1.0+alpha)*filter.amp*gain; filter.a1=-2.0*cs/(1.0+alpha); filter.a2=(1.0-alpha)/(1.0+alpha); }; /* * Initialise the filters */ void SUBnote::initfilter(bpfilter &filter,REALTYPE freq,REALTYPE bw,REALTYPE amp,REALTYPE mag){ filter.xn1=0.0;filter.xn2=0.0; if (start==0) { filter.yn1=0.0; filter.yn2=0.0; } else { REALTYPE a=0.1*mag;//empirically REALTYPE p=RND*2.0*PI; if (start==1) a*=RND; filter.yn1=a*cos(p); filter.yn2=a*cos(p+freq*2.0*PI/SAMPLE_RATE); //correct the error of computation the start amplitude //at very high frequencies if (freq>SAMPLE_RATE*0.96) { filter.yn1=0.0; filter.yn2=0.0; }; }; filter.amp=amp; filter.freq=freq; filter.bw=bw; computefiltercoefs(filter,freq,bw,1.0); }; /* * Do the filtering */ void SUBnote::filter(bpfilter &filter,REALTYPE *smps){ int i; REALTYPE out; for (i=0;iAmpEnvelope,freq); if (pars->PFreqEnvelopeEnabled!=0) FreqEnvelope=new Envelope(pars->FreqEnvelope,freq); else FreqEnvelope=NULL; if (pars->PBandWidthEnvelopeEnabled!=0) BandWidthEnvelope=new Envelope(pars->BandWidthEnvelope,freq); else BandWidthEnvelope=NULL; if (pars->PGlobalFilterEnabled!=0){ globalfiltercenterq=pars->GlobalFilter->getq(); GlobalFilterL=new Filter(pars->GlobalFilter); if (stereo!=0) GlobalFilterR=new Filter(pars->GlobalFilter); GlobalFilterEnvelope=new Envelope(pars->GlobalFilterEnvelope,freq); GlobalFilterFreqTracking=pars->GlobalFilter->getfreqtracking(basefreq); }; computecurrentparameters(); }; /* * Compute Parameters of SUBnote for each tick */ void SUBnote::computecurrentparameters(){ if ((FreqEnvelope!=NULL)||(BandWidthEnvelope!=NULL)|| (oldpitchwheel!=ctl->pitchwheel.data)|| (oldbandwidth!=ctl->bandwidth.data)|| (portamento!=0)){ REALTYPE envfreq=1.0; REALTYPE envbw=1.0; REALTYPE gain=1.0; if (FreqEnvelope!=NULL) { envfreq=FreqEnvelope->envout()/1200; envfreq=pow(2.0,envfreq); }; envfreq*=ctl->pitchwheel.relfreq;//pitch wheel if (portamento!=0) {//portamento is used envfreq*=ctl->portamento.freqrap; if (ctl->portamento.used==0){//the portamento has finished portamento=0;//this note is no longer "portamented" }; }; if (BandWidthEnvelope!=NULL) { envbw=BandWidthEnvelope->envout(); envbw=pow(2,envbw); }; envbw*=ctl->bandwidth.relbw;//bandwidth controller REALTYPE tmpgain=1.0/sqrt(envbw*envfreq); for (int n=0;nbandwidth.data; oldpitchwheel=ctl->pitchwheel.data; }; newamplitude=volume*AmpEnvelope->envout_dB()*2.0; //Filter if (GlobalFilterL!=NULL){ REALTYPE globalfilterpitch=GlobalFilterCenterPitch+GlobalFilterEnvelope->envout(); REALTYPE filterfreq=globalfilterpitch+ctl->filtercutoff.relfreq+GlobalFilterFreqTracking; filterfreq=GlobalFilterL->getrealfreq(filterfreq); GlobalFilterL->setfreq_and_q(filterfreq,globalfiltercenterq*ctl->filterq.relq); if (GlobalFilterR!=NULL) GlobalFilterR->setfreq_and_q(filterfreq,globalfiltercenterq*ctl->filterq.relq); }; }; /* * Note Output */ int SUBnote::noteout(REALTYPE *outl,REALTYPE *outr){ int i; for (i=0;ifilterout(&outl[0]); //right channel if (stereo!=0){ for (i=0;ifilterout(&outr[0]); } else for (i=0;iSOUND_BUFFER_SIZE) n=SOUND_BUFFER_SIZE; for (i=0;ifinished()!=0){ for (i=0;irelasekey(); if (FreqEnvelope!=NULL) FreqEnvelope->relasekey(); if (BandWidthEnvelope!=NULL) BandWidthEnvelope->relasekey(); if (GlobalFilterEnvelope!=NULL) GlobalFilterEnvelope->relasekey(); }; /* * Check if the note is finished */ int SUBnote::finished(){ if (NoteEnabled==OFF) return(1); else return(0); };