// Copyright (c) 2010 Martin Eastwood // This code is distributed under the terms of the GNU General Public License // MVerb is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // at your option) any later version. // // MVerb 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 for more details. // // You should have received a copy of the GNU General Public License // along with this MVerb. If not, see . #ifndef EMVERB_H #define EMVERB_H #include #include //forward declaration template class Allpass; template class StaticAllpassFourTap; template class StaticDelayLine; template class StaticDelayLineFourTap; template class StaticDelayLineEightTap; template class StateVariable; template class MVerb { private: Allpass allpass[4]; StaticAllpassFourTap allpassFourTap[4]; StateVariable bandwidthFilter[2]; StateVariable damping[2]; StaticDelayLine predelay; StaticDelayLineFourTap staticDelayLine[4]; StaticDelayLineEightTap earlyReflectionsDelayLine[2]; T SampleRate, DampingFreq, Density1, Density2, BandwidthFreq, PreDelayTime, Decay, Gain, Mix, EarlyMix, Size; T MixSmooth, EarlyLateSmooth, BandwidthSmooth, DampingSmooth, PredelaySmooth, SizeSmooth, DensitySmooth, DecaySmooth; T PreviousLeftTank, PreviousRightTank; int ControlRate, ControlRateCounter; public: enum { DAMPINGFREQ=0, DENSITY, BANDWIDTHFREQ, DECAY, PREDELAY, SIZE, GAIN, MIX, EARLYMIX, NUM_PARAMS }; MVerb(){ DampingFreq = 18000.; BandwidthFreq = 18000.; SampleRate = 44100.; Decay = 0.5; Gain = 1.; Mix = 1.; Size = 1.; EarlyMix = 1.; PreviousLeftTank = 0.; PreviousRightTank = 0.; PreDelayTime = 100 * (SampleRate / 1000); MixSmooth = EarlyLateSmooth = BandwidthSmooth = DampingSmooth = PredelaySmooth = SizeSmooth = DecaySmooth = DensitySmooth = 0.; ControlRate = SampleRate / 1000; ControlRateCounter = 0; reset(); } ~MVerb(){ //nowt to do here } void process(const T **inputs, T **outputs, int sampleFrames){ T OneOverSampleFrames = 1. / sampleFrames; T MixDelta = (Mix - MixSmooth) * OneOverSampleFrames; T EarlyLateDelta = (EarlyMix - EarlyLateSmooth) * OneOverSampleFrames; T BandwidthDelta = (((BandwidthFreq * 18400.) + 100.) - BandwidthSmooth) * OneOverSampleFrames; T DampingDelta = (((DampingFreq * 18400.) + 100.) - DampingSmooth) * OneOverSampleFrames; T PredelayDelta = ((PreDelayTime * 200 * (SampleRate / 1000)) - PredelaySmooth) * OneOverSampleFrames; T SizeDelta = (Size - SizeSmooth) * OneOverSampleFrames; T DecayDelta = (((0.7995f * Decay) + 0.005) - DecaySmooth) * OneOverSampleFrames; T DensityDelta = (((0.7995f * Density1) + 0.005) - DensitySmooth) * OneOverSampleFrames; for(int i=0;i= ControlRate){ ControlRateCounter = 0; bandwidthFilter[0].Frequency(BandwidthSmooth); bandwidthFilter[1].Frequency(BandwidthSmooth); damping[0].Frequency(DampingSmooth); damping[1].Frequency(DampingSmooth); } ++ControlRateCounter; predelay.SetLength(PredelaySmooth); Density2 = DecaySmooth + 0.15; if (Density2 > 0.5) Density2 = 0.5; if (Density2 < 0.25) Density2 = 0.25; allpassFourTap[1].SetFeedback(Density2); allpassFourTap[3].SetFeedback(Density2); allpassFourTap[0].SetFeedback(Density1); allpassFourTap[2].SetFeedback(Density1); T bandwidthLeft = bandwidthFilter[0](left) ; T bandwidthRight = bandwidthFilter[1](right) ; T earlyReflectionsL = earlyReflectionsDelayLine[0] ( bandwidthLeft * 0.5 + bandwidthRight * 0.3 ) + earlyReflectionsDelayLine[0].GetIndex(2) * 0.6 + earlyReflectionsDelayLine[0].GetIndex(3) * 0.4 + earlyReflectionsDelayLine[0].GetIndex(4) * 0.3 + earlyReflectionsDelayLine[0].GetIndex(5) * 0.3 + earlyReflectionsDelayLine[0].GetIndex(6) * 0.1 + earlyReflectionsDelayLine[0].GetIndex(7) * 0.1 + ( bandwidthLeft * 0.4 + bandwidthRight * 0.2 ) * 0.5 ; T earlyReflectionsR = earlyReflectionsDelayLine[1] ( bandwidthLeft * 0.3 + bandwidthRight * 0.5 ) + earlyReflectionsDelayLine[1].GetIndex(2) * 0.6 + earlyReflectionsDelayLine[1].GetIndex(3) * 0.4 + earlyReflectionsDelayLine[1].GetIndex(4) * 0.3 + earlyReflectionsDelayLine[1].GetIndex(5) * 0.3 + earlyReflectionsDelayLine[1].GetIndex(6) * 0.1 + earlyReflectionsDelayLine[1].GetIndex(7) * 0.1 + ( bandwidthLeft * 0.2 + bandwidthRight * 0.4 ) * 0.5 ; T predelayMonoInput = predelay(( bandwidthRight + bandwidthLeft ) * 0.5f); T smearedInput = predelayMonoInput; for(int j=0;j<4;j++) smearedInput = allpass[j] ( smearedInput ); T leftTank = allpassFourTap[0] ( smearedInput + PreviousRightTank ) ; leftTank = staticDelayLine[0] (leftTank); leftTank = damping[0](leftTank); leftTank = allpassFourTap[1](leftTank); leftTank = staticDelayLine[1](leftTank); T rightTank = allpassFourTap[2] (smearedInput + PreviousLeftTank) ; rightTank = staticDelayLine[2](rightTank); rightTank = damping[1] (rightTank); rightTank = allpassFourTap[3](rightTank); rightTank = staticDelayLine[3](rightTank); PreviousLeftTank = leftTank * DecaySmooth; PreviousRightTank = rightTank * DecaySmooth; T accumulatorL = (0.6*staticDelayLine[2].GetIndex(1)) +(0.6*staticDelayLine[2].GetIndex(2)) -(0.6*allpassFourTap[3].GetIndex(1)) +(0.6*staticDelayLine[3].GetIndex(1)) -(0.6*staticDelayLine[0].GetIndex(1)) -(0.6*allpassFourTap[1].GetIndex(1)) -(0.6*staticDelayLine[1].GetIndex(1)); T accumulatorR = (0.6*staticDelayLine[0].GetIndex(2)) +(0.6*staticDelayLine[0].GetIndex(3)) -(0.6*allpassFourTap[1].GetIndex(2)) +(0.6*staticDelayLine[1].GetIndex(2)) -(0.6*staticDelayLine[2].GetIndex(3)) -(0.6*allpassFourTap[3].GetIndex(2)) -(0.6*staticDelayLine[3].GetIndex(2)); accumulatorL = ((accumulatorL * EarlyMix) + ((1 - EarlyMix) * earlyReflectionsL)); accumulatorR = ((accumulatorR * EarlyMix) + ((1 - EarlyMix) * earlyReflectionsR)); left = ( left + MixSmooth * ( accumulatorL - left ) ) * Gain; right = ( right + MixSmooth * ( accumulatorR - right ) ) * Gain; outputs[0][i] = left; outputs[1][i] = right; } } void reset(){ ControlRateCounter = 0; bandwidthFilter[0].SetSampleRate (SampleRate ); bandwidthFilter[1].SetSampleRate (SampleRate ); bandwidthFilter[0].Reset(); bandwidthFilter[1].Reset(); damping[0].SetSampleRate (SampleRate ); damping[1].SetSampleRate (SampleRate ); damping[0].Reset(); damping[1].Reset(); predelay.Clear(); predelay.SetLength(PreDelayTime); allpass[0].Clear(); allpass[1].Clear(); allpass[2].Clear(); allpass[3].Clear(); allpass[0].SetLength (0.0048 * SampleRate); allpass[1].SetLength (0.0036 * SampleRate); allpass[2].SetLength (0.0127 * SampleRate); allpass[3].SetLength (0.0093 * SampleRate); allpass[0].SetFeedback (0.75); allpass[1].SetFeedback (0.75); allpass[2].SetFeedback (0.625); allpass[3].SetFeedback (0.625); allpassFourTap[0].Clear(); allpassFourTap[1].Clear(); allpassFourTap[2].Clear(); allpassFourTap[3].Clear(); allpassFourTap[0].SetLength(0.020 * SampleRate * Size); allpassFourTap[1].SetLength(0.060 * SampleRate * Size); allpassFourTap[2].SetLength(0.030 * SampleRate * Size); allpassFourTap[3].SetLength(0.089 * SampleRate * Size); allpassFourTap[0].SetFeedback(Density1); allpassFourTap[1].SetFeedback(Density2); allpassFourTap[2].SetFeedback(Density1); allpassFourTap[3].SetFeedback(Density2); allpassFourTap[0].SetIndex(0,0,0,0); allpassFourTap[1].SetIndex(0,0.006 * SampleRate * Size, 0.041 * SampleRate * Size, 0); allpassFourTap[2].SetIndex(0,0,0,0); allpassFourTap[3].SetIndex(0,0.031 * SampleRate * Size, 0.011 * SampleRate * Size, 0); staticDelayLine[0].Clear(); staticDelayLine[1].Clear(); staticDelayLine[2].Clear(); staticDelayLine[3].Clear(); staticDelayLine[0].SetLength(0.15 * SampleRate * Size); staticDelayLine[1].SetLength(0.12 * SampleRate * Size); staticDelayLine[2].SetLength(0.14 * SampleRate * Size); staticDelayLine[3].SetLength(0.11 * SampleRate * Size); staticDelayLine[0].SetIndex(0, 0.067 * SampleRate * Size, 0.011 * SampleRate * Size , 0.121 * SampleRate * Size); staticDelayLine[1].SetIndex(0, 0.036 * SampleRate * Size, 0.089 * SampleRate * Size , 0); staticDelayLine[2].SetIndex(0, 0.0089 * SampleRate * Size, 0.099 * SampleRate * Size , 0); staticDelayLine[3].SetIndex(0, 0.067 * SampleRate * Size, 0.0041 * SampleRate * Size , 0); earlyReflectionsDelayLine[0].Clear(); earlyReflectionsDelayLine[1].Clear(); earlyReflectionsDelayLine[0].SetLength(0.089 * SampleRate); earlyReflectionsDelayLine[0].SetIndex (0, 0.0199*SampleRate, 0.0219*SampleRate, 0.0354*SampleRate,0.0389*SampleRate, 0.0414*SampleRate, 0.0692*SampleRate, 0); earlyReflectionsDelayLine[1].SetLength(0.069 * SampleRate); earlyReflectionsDelayLine[1].SetIndex (0, 0.0099*SampleRate, 0.011*SampleRate, 0.0182*SampleRate,0.0189*SampleRate, 0.0213*SampleRate, 0.0431*SampleRate, 0); } void setParameter(int index, T value){ switch(index){ case DAMPINGFREQ: DampingFreq = /* 1. - */ value; // FIXME? break; case DENSITY: Density1 = value; break; case BANDWIDTHFREQ: BandwidthFreq = value; break; case PREDELAY: PreDelayTime = value; break; case SIZE: Size = value; allpassFourTap[0].Clear(); allpassFourTap[1].Clear(); allpassFourTap[2].Clear(); allpassFourTap[3].Clear(); allpassFourTap[0].SetLength(0.020 * SampleRate * Size); allpassFourTap[1].SetLength(0.060 * SampleRate * Size); allpassFourTap[2].SetLength(0.030 * SampleRate * Size); allpassFourTap[3].SetLength(0.089 * SampleRate * Size); allpassFourTap[1].SetIndex(0,0.006 * SampleRate * Size, 0.041 * SampleRate * Size, 0); allpassFourTap[3].SetIndex(0,0.031 * SampleRate * Size, 0.011 * SampleRate * Size, 0); staticDelayLine[0].Clear(); staticDelayLine[1].Clear(); staticDelayLine[2].Clear(); staticDelayLine[3].Clear(); staticDelayLine[0].SetLength(0.15 * SampleRate * Size); staticDelayLine[1].SetLength(0.12 * SampleRate * Size); staticDelayLine[2].SetLength(0.14 * SampleRate * Size); staticDelayLine[3].SetLength(0.11 * SampleRate * Size); staticDelayLine[0].SetIndex(0, 0.067 * SampleRate * Size, 0.011 * SampleRate * Size , 0.121 * SampleRate * Size); staticDelayLine[1].SetIndex(0, 0.036 * SampleRate * Size, 0.089 * SampleRate * Size , 0); staticDelayLine[2].SetIndex(0, 0.0089 * SampleRate * Size, 0.099 * SampleRate * Size , 0); staticDelayLine[3].SetIndex(0, 0.067 * SampleRate * Size, 0.0041 * SampleRate * Size , 0); break; case DECAY: Decay = value; break; case GAIN: Gain = value; break; case MIX: Mix = value; break; case EARLYMIX: EarlyMix = value; break; } } float getParameter(int index) const{ switch(index){ case DAMPINGFREQ: return DampingFreq; break; case DENSITY: return Density1; break; case BANDWIDTHFREQ: return BandwidthFreq; break; case PREDELAY: return PreDelayTime; break; case SIZE: return Size; break; case DECAY: return Decay; break; case GAIN: return Gain; break; case MIX: return Mix; break; case EARLYMIX: return EarlyMix; break; default: return 0.f; break; } } void setSampleRate(T sr){ SampleRate = sr; ControlRate = SampleRate / 1000; reset(); } }; template class Allpass { private: T buffer[maxLength]; int index; int Length; T Feedback; public: Allpass() { SetLength ( maxLength - 1 ); Clear(); Feedback = 0.5; } T operator()(T input) { T output; T bufout; bufout = buffer[index]; T temp = input * -Feedback; output = bufout + temp; buffer[index] = input + ((bufout+temp)*Feedback); if(++index>=Length) index = 0; return output; } void SetLength (int Length) { if( Length >= maxLength ) Length = maxLength; if( Length < 0 ) Length = 0; this->Length = Length; } void SetFeedback(T feedback) { Feedback = feedback; } void Clear() { std::memset(buffer, 0, sizeof(buffer)); index = 0; } int GetLength() const { return Length; } }; template class StaticAllpassFourTap { private: T buffer[maxLength]; int index1, index2, index3, index4; int Length; T Feedback; public: StaticAllpassFourTap() { SetLength ( maxLength - 1 ); Clear(); Feedback = 0.5; } T operator()(T input) { T output; T bufout; bufout = buffer[index1]; T temp = input * -Feedback; output = bufout + temp; buffer[index1] = input + ((bufout+temp)*Feedback); if(++index1>=Length) index1 = 0; if(++index2 >= Length) index2 = 0; if(++index3 >= Length) index3 = 0; if(++index4 >= Length) index4 = 0; return output; } void SetIndex (int Index1, int Index2, int Index3, int Index4) { index1 = Index1; index2 = Index2; index3 = Index3; index4 = Index4; } T GetIndex (int Index) { switch (Index) { case 0: return buffer[index1]; break; case 1: return buffer[index2]; break; case 2: return buffer[index3]; break; case 3: return buffer[index4]; break; default: return buffer[index1]; break; } } void SetLength (int Length) { if( Length >= maxLength ) Length = maxLength; if( Length < 0 ) Length = 0; this->Length = Length; } void Clear() { std::memset(buffer, 0, sizeof(buffer)); index1 = index2 = index3 = index4 = 0; } void SetFeedback(T feedback) { Feedback = feedback; } int GetLength() const { return Length; } }; template class StaticDelayLine { private: T buffer[maxLength]; int index; int Length; T Feedback; public: StaticDelayLine() { SetLength ( maxLength - 1 ); Clear(); } T operator()(T input) { T output = buffer[index]; buffer[index++] = input; if(index >= Length) index = 0; return output; } void SetLength (int Length) { if( Length >= maxLength ) Length = maxLength; if( Length < 0 ) Length = 0; this->Length = Length; } void Clear() { std::memset(buffer, 0, sizeof(buffer)); index = 0; } int GetLength() const { return Length; } }; template class StaticDelayLineFourTap { private: T buffer[maxLength]; int index1, index2, index3, index4; int Length; T Feedback; public: StaticDelayLineFourTap() { SetLength ( maxLength - 1 ); Clear(); } //get ouput and iterate T operator()(T input) { T output = buffer[index1]; buffer[index1++] = input; if(index1 >= Length) index1 = 0; if(++index2 >= Length) index2 = 0; if(++index3 >= Length) index3 = 0; if(++index4 >= Length) index4 = 0; return output; } void SetIndex (int Index1, int Index2, int Index3, int Index4) { index1 = Index1; index2 = Index2; index3 = Index3; index4 = Index4; } T GetIndex (int Index) { switch (Index) { case 0: return buffer[index1]; break; case 1: return buffer[index2]; break; case 2: return buffer[index3]; break; case 3: return buffer[index4]; break; default: return buffer[index1]; break; } } void SetLength (int Length) { if( Length >= maxLength ) Length = maxLength; if( Length < 0 ) Length = 0; this->Length = Length; } void Clear() { std::memset(buffer, 0, sizeof(buffer)); index1 = index2 = index3 = index4 = 0; } int GetLength() const { return Length; } }; template class StaticDelayLineEightTap { private: T buffer[maxLength]; int index1, index2, index3, index4, index5, index6, index7, index8; int Length; T Feedback; public: StaticDelayLineEightTap() { SetLength ( maxLength - 1 ); Clear(); } //get ouput and iterate T operator()(T input) { T output = buffer[index1]; buffer[index1++] = input; if(index1 >= Length) index1 = 0; if(++index2 >= Length) index2 = 0; if(++index3 >= Length) index3 = 0; if(++index4 >= Length) index4 = 0; if(++index5 >= Length) index5 = 0; if(++index6 >= Length) index6 = 0; if(++index7 >= Length) index7 = 0; if(++index8 >= Length) index8 = 0; return output; } void SetIndex (int Index1, int Index2, int Index3, int Index4, int Index5, int Index6, int Index7, int Index8) { index1 = Index1; index2 = Index2; index3 = Index3; index4 = Index4; index5 = Index5; index6 = Index6; index7 = Index7; index8 = Index8; } T GetIndex (int Index) { switch (Index) { case 0: return buffer[index1]; break; case 1: return buffer[index2]; break; case 2: return buffer[index3]; break; case 3: return buffer[index4]; break; case 4: return buffer[index5]; break; case 5: return buffer[index6]; break; case 6: return buffer[index7]; break; case 7: return buffer[index8]; break; default: return buffer[index1]; break; } } void SetLength (int Length) { if( Length >= maxLength ) Length = maxLength; if( Length < 0 ) Length = 0; this->Length = Length; } void Clear() { std::memset(buffer, 0, sizeof(buffer)); index1 = index2 = index3 = index4 = index5 = index6 = index7 = index8 = 0; } int GetLength() const { return Length; } }; template class StateVariable { public: enum FilterType { LOWPASS, HIGHPASS, BANDPASS, NOTCH, FilterTypeCount }; private: T sampleRate; T frequency; T q; T f; T low; T high; T band; T notch; T *out; public: StateVariable() { SetSampleRate(44100.); Frequency(1000.); Resonance(0); Type(LOWPASS); Reset(); } T operator()(T input) { for(unsigned int i = 0; i < OverSampleCount; i++) { low += f * band + 1e-25; high = input - low - q * band; band += f * high; notch = low + high; } return *out; } void Reset() { low = high = band = notch = 0; } void SetSampleRate(T sampleRate) { this->sampleRate = sampleRate * OverSampleCount; UpdateCoefficient(); } void Frequency(T frequency) { this->frequency = frequency; UpdateCoefficient(); } void Resonance(T resonance) { this->q = 2 - 2 * resonance; } void Type(int type) { switch(type) { case LOWPASS: out = &low; break; case HIGHPASS: out = &high; break; case BANDPASS: out = &band; break; case NOTCH: out = ¬ch; break; default: out = &low; break; } } private: void UpdateCoefficient() { f = 2. * std::sin(M_PI * frequency / sampleRate); } }; #endif