// Allpass filter implementation // // Written by Jezar at Dreampoint, June 2000 // http://www.dreampoint.co.uk // This code is public domain #include "freeverb.h" allpass::allpass() { bufidx = 0; } void allpass::setbuffer(float *buf, int size) { buffer = buf; bufsize = size; } void allpass::mute() { for (int i=0; i<bufsize; i++) buffer[i]=0; } void allpass::setfeedback(float val) { feedback = val; } float allpass::getfeedback() { return feedback; } void allpass::deletebuffer() { delete[] buffer; buffer = 0; } // Comb filter implementation // // Written by Jezar at Dreampoint, June 2000 // http://www.dreampoint.co.uk // This code is public domain comb::comb() { filterstore = 0; bufidx = 0; } void comb::setbuffer(float *buf, int size) { buffer = buf; bufsize = size; } void comb::mute() { for (int i=0; i<bufsize; i++) buffer[i]=0; } void comb::setdamp(float val) { damp1 = val; damp2 = 1-val; } float comb::getdamp() { return damp1; } void comb::setfeedback(float val) { feedback = val; } float comb::getfeedback() { return feedback; } void comb::deletebuffer() { delete[] buffer; buffer = 0; } // Reverb model implementation // // Written by Jezar at Dreampoint, June 2000 // Modifications by Jerome Fisher, 2009, 2011 // http://www.dreampoint.co.uk // This code is public domain revmodel::revmodel(float scaletuning) { int i; int bufsize; // Allocate buffers for the components for (i = 0; i < numcombs; i++) { bufsize = int(scaletuning * combtuning[i]); combL[i].setbuffer(new float[bufsize], bufsize); bufsize += int(scaletuning * stereospread); combR[i].setbuffer(new float[bufsize], bufsize); } for (i = 0; i < numallpasses; i++) { bufsize = int(scaletuning * allpasstuning[i]); allpassL[i].setbuffer(new float[bufsize], bufsize); allpassL[i].setfeedback(0.5f); bufsize += int(scaletuning * stereospread); allpassR[i].setbuffer(new float[bufsize], bufsize); allpassR[i].setfeedback(0.5f); } // Set default values dry = initialdry; wet = initialwet*scalewet; damp = initialdamp*scaledamp; roomsize = (initialroom*scaleroom) + offsetroom; width = initialwidth; mode = initialmode; update(); // Buffer will be full of rubbish - so we MUST mute them mute(); } revmodel::~revmodel() { int i; for (i = 0; i < numcombs; i++) { combL[i].deletebuffer(); combR[i].deletebuffer(); } for (i = 0; i < numallpasses; i++) { allpassL[i].deletebuffer(); allpassR[i].deletebuffer(); } } void revmodel::mute() { int i; if (getmode() >= freezemode) return; for (i=0;i<numcombs;i++) { combL[i].mute(); combR[i].mute(); } for (i=0;i<numallpasses;i++) { allpassL[i].mute(); allpassR[i].mute(); } // Init LPF history filtprev1 = 0; filtprev2 = 0; } void revmodel::process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples) { float outL,outR,input; while (numsamples-- > 0) { int i; outL = outR = 0; input = (*inputL + *inputR) * gain; // Implementation of 2-stage IIR single-pole low-pass filter // found at the entrance of reverb processing on real devices filtprev1 += (input - filtprev1) * filtval; filtprev2 += (filtprev1 - filtprev2) * filtval; input = filtprev2; int s = -1; // Accumulate comb filters in parallel for (i=0; i<numcombs; i++) { outL += s * combL[i].process(input); outR += s * combR[i].process(input); s = -s; } // Feed through allpasses in series for (i=0; i<numallpasses; i++) { outL = allpassL[i].process(outL); outR = allpassR[i].process(outR); } // Calculate output REPLACING anything already there *outputL = outL*wet1 + outR*wet2; *outputR = outR*wet1 + outL*wet2; inputL++; inputR++; outputL++; outputR++; } } void revmodel::update() { // Recalculate internal values after parameter change int i; wet1 = wet*(width/2 + 0.5f); wet2 = wet*((1-width)/2); if (mode >= freezemode) { roomsize1 = 1; damp1 = 0; gain = muted; } else { roomsize1 = roomsize; damp1 = damp; gain = fixedgain; } for (i=0; i<numcombs; i++) { combL[i].setfeedback(roomsize1); combR[i].setfeedback(roomsize1); } for (i=0; i<numcombs; i++) { combL[i].setdamp(damp1); combR[i].setdamp(damp1); } } // The following get/set functions are not inlined, because // speed is never an issue when calling them, and also // because as you develop the reverb model, you may // wish to take dynamic action when they are called. void revmodel::setroomsize(float value) { roomsize = (value*scaleroom) + offsetroom; update(); } float revmodel::getroomsize() { return (roomsize-offsetroom)/scaleroom; } void revmodel::setdamp(float value) { damp = value*scaledamp; update(); } float revmodel::getdamp() { return damp/scaledamp; } void revmodel::setwet(float value) { wet = value*scalewet; update(); } float revmodel::getwet() { return wet/scalewet; } void revmodel::setdry(float value) { dry = value*scaledry; } float revmodel::getdry() { return dry/scaledry; } void revmodel::setwidth(float value) { width = value; update(); } float revmodel::getwidth() { return width; } void revmodel::setmode(float value) { mode = value; update(); } float revmodel::getmode() { if (mode >= freezemode) return 1; else return 0; } void revmodel::setfiltval(float value) { filtval = value; }