/* ZynAddSubFX - a software synthesizer Reverb.C - Reverberation effect 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 <stdio.h> #include <math.h> #include <stdlib.h> #include "Reverb.h" /*TODO: EarlyReflections,Prdelay,Perbalance */ Reverb::Reverb(int insertion_,REALTYPE *efxoutl_,REALTYPE *efxoutr_){ efxoutl=efxoutl_; efxoutr=efxoutr_; inputbuf=new REALTYPE[SOUND_BUFFER_SIZE]; filterpars=NULL; insertion=insertion_; //defaults Ppreset=0; Pvolume=48; Ppan=64; Ptime=64; Pidelay=40; Pidelayfb=0; Prdelay=0; Plpf=127; Phpf=0; Perbalance=64; Plohidamp=80; Ptype=1; Proomsize=64;roomsize=1.0;rs=1.0; for (int i=0;i<REV_COMBS*2;i++) { comblen[i]=800+(int)(RND*1400); combk[i]=0; lpcomb[i]=0; combfb[i]=-0.97; comb[i]=NULL; }; for (int i=0;i<REV_APS*2;i++) { aplen[i]=500+(int)(RND*500); apk[i]=0; ap[i]=NULL; }; lpf=NULL;hpf=NULL;//no filter idelay=NULL; setpreset(Ppreset); cleanup();//do not call this before the comb initialisation }; Reverb::~Reverb(){ int i; if (idelay!=NULL) delete idelay; if (hpf!=NULL) delete hpf; if (lpf!=NULL) delete lpf; for (i=0;i<REV_APS*2;i++) delete ap[i]; for (i=0;i<REV_COMBS*2;i++) delete comb[i]; delete [] inputbuf; }; /* * Cleanup the effect */ void Reverb::cleanup(){ int i,j; for (i=0;i<REV_COMBS*2;i++){ lpcomb[i]=0.0; for (j=0;j<comblen[i];j++) comb[i][j]=0.0; }; for (i=0;i<REV_APS*2;i++) for (j=0;j<aplen[i];j++) ap[i][j]=0.0; if (idelay!=NULL) for (i=0;i<idelaylen;i++) idelay[i]=0.0; if (hpf!=NULL) hpf->cleanup(); if (lpf!=NULL) lpf->cleanup(); }; /* * Process one channel; 0=left,1=right */ void Reverb::processmono(int ch,REALTYPE *output){ int i,j; REALTYPE fbout,tmp; //TODO: implement the high part from lohidamp for (j=REV_COMBS*ch;j<REV_COMBS*(ch+1);j++){ int ck=combk[j]; int comblength=comblen[j]; REALTYPE lpcombj=lpcomb[j]; for (i=0;i<SOUND_BUFFER_SIZE;i++){ fbout=comb[j][ck]*combfb[j]; fbout=fbout*(1.0-lohifb)+lpcombj*lohifb; lpcombj=fbout; comb[j][ck]=inputbuf[i]+fbout; output[i]+=fbout; if ((++ck)>=comblength) ck=0; }; combk[j]=ck; lpcomb[j]=lpcombj; }; for (j=REV_APS*ch;j<REV_APS*(1+ch);j++){ int ak=apk[j]; int aplength=aplen[j]; for (i=0;i<SOUND_BUFFER_SIZE;i++){ tmp=ap[j][ak]; ap[j][ak]=0.7*tmp+output[i]; output[i]=tmp-0.7*ap[j][ak]; if ((++ak)>=aplength) ak=0; }; apk[j]=ak; }; }; /* * Effect output */ void Reverb::out(REALTYPE *smps_l, REALTYPE *smps_r){ int i; if ((Pvolume==0)&&(insertion!=0)) return; for (i=0;i<SOUND_BUFFER_SIZE;i++) { inputbuf[i]=(smps_l[i]+smps_r[i])/2.0; //Initial delay r if (idelay!=NULL){ REALTYPE tmp=inputbuf[i]+idelay[idelayk]*idelayfb; inputbuf[i]=idelay[idelayk]; idelay[idelayk]=tmp; idelayk++;if (idelayk>=idelaylen) idelayk=0; }; }; if (lpf!=NULL) lpf->filterout(inputbuf); if (hpf!=NULL) hpf->filterout(inputbuf); processmono(0,efxoutl);//left processmono(1,efxoutr);//right REALTYPE lvol=rs/REV_COMBS*pan; REALTYPE rvol=rs/REV_COMBS*(1.0-pan); if (insertion!=0){ lvol*=2;rvol*=2; }; for (int i=0;i<SOUND_BUFFER_SIZE;i++){ efxoutl[i]*=lvol; efxoutr[i]*=rvol; }; }; /* * Parameter control */ void Reverb::setvolume(unsigned char Pvolume){ this->Pvolume=Pvolume; if (insertion==0) { outvolume=pow(0.01,(1.0-Pvolume/127.0))*4.0; volume=1.0; } else { volume=outvolume=Pvolume/127.0; if (Pvolume==0) cleanup(); }; }; void Reverb::setpan(unsigned char Ppan){ this->Ppan=Ppan; pan=(REALTYPE)Ppan/127.0; }; void Reverb::settime(unsigned char Ptime){ int i; REALTYPE t; this->Ptime=Ptime; t=pow(60.0,(REALTYPE)Ptime/127.0)-0.97; for (i=0;i<REV_COMBS*2;i++){ combfb[i]=-exp((REALTYPE)comblen[i]/(REALTYPE)SAMPLE_RATE*log(0.001)/t); //the feedback is negative because it removes the DC }; }; void Reverb::setlohidamp(unsigned char Plohidamp){ REALTYPE x; if (Plohidamp<64) Plohidamp=64;//remove this when the high part from lohidamp will be added this->Plohidamp=Plohidamp; if (Plohidamp==64) { lohidamptype=0; lohifb=0.0; } else { if (Plohidamp<64) lohidamptype=1; if (Plohidamp>64) lohidamptype=2; x=fabs((REALTYPE)(Plohidamp-64)/64.1); lohifb=x*x; }; }; void Reverb::setidelay(unsigned char Pidelay){ REALTYPE delay; this->Pidelay=Pidelay; delay=pow(50*Pidelay/127.0,2)-1.0; if (idelay!=NULL) delete (idelay); idelay=NULL; idelaylen=(int) (SAMPLE_RATE*delay/1000); if (idelaylen>1) { idelayk=0; idelay=new REALTYPE[idelaylen]; for (int i=0;i<idelaylen;i++) idelay[i]=0.0; }; }; void Reverb::setidelayfb(unsigned char Pidelayfb){ this->Pidelayfb=Pidelayfb; idelayfb=Pidelayfb/128.0; }; void Reverb::sethpf(unsigned char Phpf){ this->Phpf=Phpf; if (Phpf==0) {//No HighPass if (hpf!=NULL) delete(hpf); hpf=NULL; } else{ REALTYPE fr=exp(pow(Phpf/127.0,0.5)*log(10000.0))+20.0; if (hpf==NULL) hpf=new AnalogFilter(3,fr,1,0); else hpf->setfreq(fr); }; }; void Reverb::setlpf(unsigned char Plpf){ this->Plpf=Plpf; if (Plpf==127) {//No LowPass if (lpf!=NULL) delete(lpf); lpf=NULL; } else{ REALTYPE fr=exp(pow(Plpf/127.0,0.5)*log(25000.0))+40; if (lpf==NULL) lpf=new AnalogFilter(2,fr,1,0); else lpf->setfreq(fr); }; }; void Reverb::settype(unsigned char Ptype){ const int NUM_TYPES=2; int combtunings[NUM_TYPES][REV_COMBS]={ //this is unused (for random) {0,0,0,0,0,0,0,0}, //Freeverb by Jezar at Dreampoint {1116,1188,1277,1356,1422,1491,1557,1617} }; int aptunings[NUM_TYPES][REV_APS]={ //this is unused (for random) {0,0,0,0}, //Freeverb by Jezar at Dreampoint {225,341,441,556} }; if (Ptype>=NUM_TYPES) Ptype=NUM_TYPES-1; this->Ptype=Ptype; REALTYPE tmp; for (int i=0;i<REV_COMBS*2;i++) { if (Ptype==0) tmp=800.0+(int)(RND*1400.0); else tmp=combtunings[Ptype][i%REV_COMBS]; tmp*=roomsize; if (i>REV_COMBS) tmp+=23.0; tmp*=SAMPLE_RATE/44100.0;//adjust the combs according to the samplerate if (tmp<10) tmp=10; comblen[i]=(int) tmp; combk[i]=0; lpcomb[i]=0; if (comb[i]!=NULL) delete comb[i]; comb[i]=new REALTYPE[comblen[i]]; }; for (int i=0;i<REV_APS*2;i++) { if (Ptype==0) tmp=500+(int)(RND*500); else tmp=aptunings[Ptype][i%REV_APS]; tmp*=roomsize; if (i>REV_APS) tmp+=23.0; tmp*=SAMPLE_RATE/44100.0;//adjust the combs according to the samplerate if (tmp<10) tmp=10; aplen[i]=(int) tmp; apk[i]=0; if (ap[i]!=NULL) delete ap[i]; ap[i]=new REALTYPE[aplen[i]]; }; settime(Ptime); cleanup(); }; void Reverb::setroomsize(unsigned char Proomsize){ if (Proomsize==0) Proomsize=64;//this is because the older versions consider roomsize=0 this->Proomsize=Proomsize; roomsize=(Proomsize-64.0)/64.0; if (roomsize>0.0) roomsize*=2.0; roomsize=pow(10.0,roomsize); rs=sqrt(roomsize); settype(Ptype); }; void Reverb::setpreset(unsigned char npreset){ const int PRESET_SIZE=12; const int NUM_PRESETS=13; unsigned char presets[NUM_PRESETS][PRESET_SIZE]={ //Cathedral1 {80,64,63,24,0,0,0,85,5,83,1,64}, //Cathedral2 {80,64,69,35,0,0,0,127,0,71,0,64}, //Cathedral3 {80,64,69,24,0,0,0,127,75,78,1,85}, //Hall1 {90,64,51,10,0,0,0,127,21,78,1,64}, //Hall2 {90,64,53,20,0,0,0,127,75,71,1,64}, //Room1 {100,64,33,0,0,0,0,127,0,106,0,30}, //Room2 {100,64,21,26,0,0,0,62,0,77,1,45}, //Basement {110,64,14,0,0,0,0,127,5,71,0,25}, //Tunnel {85,80,84,20,42,0,0,51,0,78,1,105}, //Echoed1 {95,64,26,60,71,0,0,114,0,64,1,64}, //Echoed2 {90,64,40,88,71,0,0,114,0,88,1,64}, //VeryLong1 {90,64,93,15,0,0,0,114,0,77,0,95}, //VeryLong2 {90,64,111,30,0,0,0,114,90,74,1,80}}; if (npreset>=NUM_PRESETS) npreset=NUM_PRESETS-1; for (int n=0;n<PRESET_SIZE;n++) changepar(n,presets[npreset][n]); if (insertion!=0) changepar(0,presets[npreset][0]/2);//lower the volume if reverb is insertion effect Ppreset=npreset; }; void Reverb::changepar(int npar,unsigned char value){ switch (npar){ case 0: setvolume(value); break; case 1: setpan(value); break; case 2: settime(value); break; case 3: setidelay(value); break; case 4: setidelayfb(value); break; // case 5: setrdelay(value); // break; // case 6: seterbalance(value); // break; case 7: setlpf(value); break; case 8: sethpf(value); break; case 9: setlohidamp(value); break; case 10:settype(value); break; case 11:setroomsize(value); break; }; }; unsigned char Reverb::getpar(int npar){ switch (npar){ case 0: return(Pvolume); break; case 1: return(Ppan); break; case 2: return(Ptime); break; case 3: return(Pidelay); break; case 4: return(Pidelayfb); break; // case 5: return(Prdelay); // break; // case 6: return(Perbalance); // break; case 7: return(Plpf); break; case 8: return(Phpf); break; case 9: return(Plohidamp); break; case 10:return(Ptype); break; case 11:return(Proomsize); break; }; return(0);//in case of bogus "parameter" };