/* ZynAddSubFX - a software synthesizer Reverb.cpp - 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 or later) 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 "Reverb.h" #include "../Misc/Util.h" #include "../Misc/Allocator.h" #include "../DSP/AnalogFilter.h" #include "../DSP/Unison.h" #include Reverb::Reverb(EffectParams pars) :Effect(pars), // defaults Pvolume(48), Ptime(64), Pidelay(40), Pidelayfb(0), Plpf(127), Phpf(0), Plohidamp(80), Ptype(1), Proomsize(64), Pbandwidth(30), idelaylen(0), roomsize(1.0f), rs(1.0f), bandwidth(NULL), idelay(NULL), lpf(NULL), hpf(NULL) // no filter { for(int i = 0; i < REV_COMBS * 2; ++i) { comblen[i] = 800 + (int)(RND * 1400.0f); combk[i] = 0; lpcomb[i] = 0; combfb[i] = -0.97f; comb[i] = NULL; } for(int i = 0; i < REV_APS * 2; ++i) { aplen[i] = 500 + (int)(RND * 500.0f); apk[i] = 0; ap[i] = NULL; } setpreset(Ppreset); cleanup(); //do not call this before the comb initialisation } Reverb::~Reverb() { memory.devalloc(idelay); memory.dealloc(hpf); memory.dealloc(lpf); for(int i = 0; i < REV_APS * 2; ++i) memory.devalloc(ap[i]); for(int i = 0; i < REV_COMBS * 2; ++i) memory.devalloc(comb[i]); memory.dealloc(bandwidth); } //Cleanup the effect void Reverb::cleanup(void) { for(int i = 0; i < REV_COMBS * 2; ++i) { lpcomb[i] = 0.0f; for(int j = 0; j < comblen[i]; ++j) comb[i][j] = 0.0f; } for(int i = 0; i < REV_APS * 2; ++i) for(int j = 0; j < aplen[i]; ++j) ap[i][j] = 0.0f; if(idelay) for(int i = 0; i < idelaylen; ++i) idelay[i] = 0.0f; if(hpf) hpf->cleanup(); if(lpf) lpf->cleanup(); } //Process one channel; 0=left, 1=right void Reverb::processmono(int ch, float *output, float *inputbuf) { //todo: implement the high part from lohidamp for(int j = REV_COMBS * ch; j < REV_COMBS * (ch + 1); ++j) { int &ck = combk[j]; const int comblength = comblen[j]; float &lpcombj = lpcomb[j]; for(int i = 0; i < buffersize; ++i) { float fbout = comb[j][ck] * combfb[j]; fbout = fbout * (1.0f - lohifb) + lpcombj * lohifb; lpcombj = fbout; comb[j][ck] = inputbuf[i] + fbout; output[i] += fbout; if((++ck) >= comblength) ck = 0; } } for(int j = REV_APS * ch; j < REV_APS * (1 + ch); ++j) { int &ak = apk[j]; const int aplength = aplen[j]; for(int i = 0; i < buffersize; ++i) { float tmp = ap[j][ak]; ap[j][ak] = 0.7f * tmp + output[i]; output[i] = tmp - 0.7f * ap[j][ak]; if((++ak) >= aplength) ak = 0; } } } //Effect output void Reverb::out(const Stereo &smp) { if(!Pvolume && insertion) return; float inputbuf[buffersize]; for(int i = 0; i < buffersize; ++i) inputbuf[i] = (smp.l[i] + smp.r[i]) / 2.0f; if(idelay) for(int i = 0; i < buffersize; ++i) { //Initial delay r float tmp = inputbuf[i] + idelay[idelayk] * idelayfb; inputbuf[i] = idelay[idelayk]; idelay[idelayk] = tmp; idelayk++; if(idelayk >= idelaylen) idelayk = 0; } if(bandwidth) bandwidth->process(buffersize, inputbuf); if(lpf) lpf->filterout(inputbuf); if(hpf) hpf->filterout(inputbuf); processmono(0, efxoutl, inputbuf); //left processmono(1, efxoutr, inputbuf); //right float lvol = rs / REV_COMBS * pangainL; float rvol = rs / REV_COMBS * pangainR; if(insertion != 0) { lvol *= 2.0f; rvol *= 2.0f; } for(int i = 0; i < buffersize; ++i) { efxoutl[i] *= lvol; efxoutr[i] *= rvol; } } //Parameter control void Reverb::setvolume(unsigned char _Pvolume) { Pvolume = _Pvolume; if(!insertion) { outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f; volume = 1.0f; } else { volume = outvolume = Pvolume / 127.0f; if(Pvolume == 0) cleanup(); } } void Reverb::settime(unsigned char _Ptime) { Ptime = _Ptime; float t = powf(60.0f, Ptime / 127.0f) - 0.97f; for(int i = 0; i < REV_COMBS * 2; ++i) combfb[i] = -expf((float)comblen[i] / samplerate_f * logf(0.001f) / t); //the feedback is negative because it removes the DC } void Reverb::setlohidamp(unsigned char _Plohidamp) { Plohidamp = (_Plohidamp < 64) ? 64 : _Plohidamp; //remove this when the high part from lohidamp is added if(Plohidamp == 64) { lohidamptype = 0; lohifb = 0.0f; } else { if(Plohidamp < 64) lohidamptype = 1; if(Plohidamp > 64) lohidamptype = 2; float x = fabsf((float)(Plohidamp - 64) / 64.1f); lohifb = x * x; } } void Reverb::setidelay(unsigned char _Pidelay) { Pidelay = _Pidelay; float delay = powf(50.0f * Pidelay / 127.0f, 2.0f) - 1.0f; int newDelayLen = (int) (samplerate_f * delay / 1000); if(newDelayLen == idelaylen) return; memory.devalloc(idelay); idelaylen = newDelayLen; if(idelaylen > 1) { idelayk = 0; idelay = memory.valloc(idelaylen); memset(idelay, 0, idelaylen * sizeof(float)); } } void Reverb::setidelayfb(unsigned char _Pidelayfb) { Pidelayfb = _Pidelayfb; idelayfb = Pidelayfb / 128.0f; } void Reverb::sethpf(unsigned char _Phpf) { Phpf = _Phpf; if(Phpf == 0) { //No HighPass memory.dealloc(hpf); } else { float fr = expf(sqrtf(Phpf / 127.0f) * logf(10000.0f)) + 20.0f; if(hpf == NULL) hpf = memory.alloc(3, fr, 1, 0, samplerate, buffersize); else hpf->setfreq(fr); } } void Reverb::setlpf(unsigned char _Plpf) { Plpf = _Plpf; if(Plpf == 127) { //No LowPass memory.dealloc(lpf); } else { float fr = expf(sqrtf(Plpf / 127.0f) * logf(25000.0f)) + 40.0f; if(!lpf) lpf = memory.alloc(2, fr, 1, 0, samplerate, buffersize); else lpf->setfreq(fr); } } void Reverb::settype(unsigned char _Ptype) { Ptype = _Ptype; const int NUM_TYPES = 3; const 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 }, //duplicate of Freeverb by Jezar at Dreampoint {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 } }; const int aptunings[NUM_TYPES][REV_APS] = { //this is unused (for random) {0, 0, 0, 0 }, //Freeverb by Jezar at Dreampoint {225, 341, 441, 556 }, //duplicate of Freeverb by Jezar at Dreampoint {225, 341, 441, 556 } }; if(Ptype >= NUM_TYPES) Ptype = NUM_TYPES - 1; // adjust the combs according to the samplerate float samplerate_adjust = samplerate_f / 44100.0f; float tmp; for(int i = 0; i < REV_COMBS * 2; ++i) { if(Ptype == 0) tmp = 800.0f + (int)(RND * 1400.0f); else tmp = combtunings[Ptype][i % REV_COMBS]; tmp *= roomsize; if(i > REV_COMBS) tmp += 23.0f; tmp *= samplerate_adjust; //adjust the combs according to the samplerate if(tmp < 10.0f) tmp = 10.0f; combk[i] = 0; lpcomb[i] = 0; if(comblen[i] != (int)tmp || comb[i] == NULL) { comblen[i] = (int) tmp; memory.devalloc(comb[i]); comb[i] = memory.valloc(comblen[i]); } } for(int i = 0; i < REV_APS * 2; ++i) { if(Ptype == 0) tmp = 500 + (int)(RND * 500.0f); else tmp = aptunings[Ptype][i % REV_APS]; tmp *= roomsize; if(i > REV_APS) tmp += 23.0f; tmp *= samplerate_adjust; //adjust the combs according to the samplerate if(tmp < 10) tmp = 10; apk[i] = 0; if(aplen[i] != (int)tmp || ap[i] == NULL) { aplen[i] = (int) tmp; memory.devalloc(ap[i]); ap[i] = memory.valloc(aplen[i]); } } memory.dealloc(bandwidth); if(Ptype == 2) { //bandwidth //TODO the size of the unison buffer may be too small, though this has //not been verified yet. //As this cannot be resized in a RT context, a good upper bound should //be found bandwidth = memory.alloc(&memory, buffersize / 4 + 1, 2.0f, samplerate_f); bandwidth->setSize(50); bandwidth->setBaseFrequency(1.0f); } settime(Ptime); cleanup(); } void Reverb::setroomsize(unsigned char _Proomsize) { Proomsize = _Proomsize; if(!Proomsize) this->Proomsize = 64; //this is because the older versions consider roomsize=0 roomsize = (this->Proomsize - 64.0f) / 64.0f; if(roomsize > 0.0f) roomsize *= 2.0f; roomsize = powf(10.0f, roomsize); rs = sqrtf(roomsize); settype(Ptype); } void Reverb::setbandwidth(unsigned char _Pbandwidth) { Pbandwidth = _Pbandwidth; float v = Pbandwidth / 127.0f; if(bandwidth) bandwidth->setBandwidth(powf(v, 2.0f) * 200.0f); } void Reverb::setpreset(unsigned char npreset) { const int PRESET_SIZE = 13; 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, 20}, //Cathedral2 {80, 64, 69, 35, 0, 0, 0, 127, 0, 71, 0, 64, 20}, //Cathedral3 {80, 64, 69, 24, 0, 0, 0, 127, 75, 78, 1, 85, 20}, //Hall1 {90, 64, 51, 10, 0, 0, 0, 127, 21, 78, 1, 64, 20}, //Hall2 {90, 64, 53, 20, 0, 0, 0, 127, 75, 71, 1, 64, 20}, //Room1 {100, 64, 33, 0, 0, 0, 0, 127, 0, 106, 0, 30, 20}, //Room2 {100, 64, 21, 26, 0, 0, 0, 62, 0, 77, 1, 45, 20}, //Basement {110, 64, 14, 0, 0, 0, 0, 127, 5, 71, 0, 25, 20}, //Tunnel {85, 80, 84, 20, 42, 0, 0, 51, 0, 78, 1, 105, 20}, //Echoed1 {95, 64, 26, 60, 71, 0, 0, 114, 0, 64, 1, 64, 20}, //Echoed2 {90, 64, 40, 88, 71, 0, 0, 114, 0, 88, 1, 64, 20}, //VeryLong1 {90, 64, 93, 15, 0, 0, 0, 114, 0, 77, 0, 95, 20}, //VeryLong2 {90, 64, 111, 30, 0, 0, 0, 114, 90, 74, 1, 80, 20} }; if(npreset >= NUM_PRESETS) npreset = NUM_PRESETS - 1; for(int n = 0; n < PRESET_SIZE; ++n) changepar(n, presets[npreset][n]); if(insertion) 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: setpanning(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; case 12: setbandwidth(value); break; } } unsigned char Reverb::getpar(int npar) const { switch(npar) { case 0: return Pvolume; case 1: return Ppanning; case 2: return Ptime; case 3: return Pidelay; case 4: return Pidelayfb; case 7: return Plpf; case 8: return Phpf; case 9: return Plohidamp; case 10: return Ptype; case 11: return Proomsize; case 12: return Pbandwidth; default: return 0; } }