From 8f3a95547c3e2c256cfaf296955eeabfc6f12893 Mon Sep 17 00:00:00 2001 From: ann0see <20726856+ann0see@users.noreply.github.com> Date: Tue, 8 Oct 2024 22:03:26 +0200 Subject: [PATCH] Move reverb code out of utils --- Jamulus.pro | 6 +- src/client.h | 1 + src/plugins/audioreverb.cpp | 207 ++++++++++++++++++++++++++++++++++++ src/plugins/audioreverb.h | 57 ++++++++++ src/util.cpp | 205 ----------------------------------- src/util.h | 40 ------- 6 files changed, 269 insertions(+), 247 deletions(-) create mode 100644 src/plugins/audioreverb.cpp create mode 100644 src/plugins/audioreverb.h diff --git a/Jamulus.pro b/Jamulus.pro index b9228b018c..87560ab548 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -378,7 +378,8 @@ FORMS_GUI = src/aboutdlgbase.ui \ src/connectdlgbase.ui } -HEADERS += src/buffer.h \ +HEADERS += src/plugins/audioreverb.h \ + src/buffer.h \ src/channel.h \ src/global.h \ src/protocol.h \ @@ -486,7 +487,8 @@ HEADERS_OPUS_X86 = libs/opus/celt/x86/celt_lpc_sse.h \ libs/opus/celt/x86/x86cpu.h \ $$files(libs/opus/silk/x86/*.h) -SOURCES += src/buffer.cpp \ +SOURCES += src/plugins/audioreverb.cpp \ + src/buffer.cpp \ src/channel.cpp \ src/main.cpp \ src/protocol.cpp \ diff --git a/src/client.h b/src/client.h index 1fc33d8430..3f9e30be6d 100644 --- a/src/client.h +++ b/src/client.h @@ -38,6 +38,7 @@ #include "socket.h" #include "channel.h" #include "util.h" +#include "plugins/audioreverb.h" #include "buffer.h" #include "signalhandler.h" diff --git a/src/plugins/audioreverb.cpp b/src/plugins/audioreverb.cpp new file mode 100644 index 0000000000..a10c415bb8 --- /dev/null +++ b/src/plugins/audioreverb.cpp @@ -0,0 +1,207 @@ +/******************************************************************************\ +* Audio Reverberation * +\******************************************************************************/ +/* + The following code is based on "JCRev: John Chowning's reverberator class" + by Perry R. Cook and Gary P. Scavone, 1995 - 2004 + which is in "The Synthesis ToolKit in C++ (STK)" + http://ccrma.stanford.edu/software/stk + + Original description: + This class is derived from the CLM JCRev function, which is based on the use + of networks of simple allpass and comb delay filters. This class implements + three series allpass units, followed by four parallel comb filters, and two + decorrelation delay lines in parallel at the output. +*/ + +#include "audioreverb.h" + +void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 ) +{ + // store parameters + eAudioChannelConf = eNAudioChannelConf; + iStereoBlockSizeSam = iNStereoBlockSizeSam; + + // delay lengths for 44100 Hz sample rate + int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; + const float scaler = static_cast ( iSampleRate ) / 44100.0f; + + if ( scaler != 1.0f ) + { + for ( int i = 0; i < 9; i++ ) + { + int delay = static_cast ( floorf ( scaler * lengths[i] ) ); + + if ( ( delay & 1 ) == 0 ) + { + delay++; + } + + while ( !isPrime ( delay ) ) + { + delay += 2; + } + + lengths[i] = delay; + } + } + + for ( int i = 0; i < 3; i++ ) + { + allpassDelays[i].Init ( lengths[i + 4] ); + } + + for ( int i = 0; i < 4; i++ ) + { + combDelays[i].Init ( lengths[i] ); + combFilters[i].setPole ( 0.2f ); + } + + setT60 ( fT60, iSampleRate ); + outLeftDelay.Init ( lengths[7] ); + outRightDelay.Init ( lengths[8] ); + allpassCoefficient = 0.7f; + Clear(); +} + +bool CAudioReverb::isPrime ( const int number ) +{ + /* + Returns true if argument value is prime. Taken from "class Effect" in + "STK abstract effects parent class". + */ + if ( number == 2 ) + { + return true; + } + + if ( number & 1 ) + { + for ( int i = 3; i < static_cast ( sqrtf ( static_cast ( number ) ) ) + 1; i += 2 ) + { + if ( ( number % i ) == 0 ) + { + return false; + } + } + + return true; // prime + } + else + { + return false; // even + } +} + +void CAudioReverb::Clear() +{ + // reset and clear all internal state + allpassDelays[0].Reset ( 0 ); + allpassDelays[1].Reset ( 0 ); + allpassDelays[2].Reset ( 0 ); + combDelays[0].Reset ( 0 ); + combDelays[1].Reset ( 0 ); + combDelays[2].Reset ( 0 ); + combDelays[3].Reset ( 0 ); + combFilters[0].Reset(); + combFilters[1].Reset(); + combFilters[2].Reset(); + combFilters[3].Reset(); + outRightDelay.Reset ( 0 ); + outLeftDelay.Reset ( 0 ); +} + +void CAudioReverb::setT60 ( const float fT60, const int iSampleRate ) +{ + // set the reverberation T60 decay time + for ( int i = 0; i < 4; i++ ) + { + combCoefficient[i] = powf ( 10.0f, static_cast ( -3.0f * combDelays[i].Size() / ( fT60 * iSampleRate ) ) ); + } +} + +void CAudioReverb::COnePole::setPole ( const float fPole ) +{ + // calculate IIR filter coefficients based on the pole value + fA = -fPole; + fB = 1.0f - fPole; +} + +float CAudioReverb::COnePole::Calc ( const float fIn ) +{ + // calculate IIR filter + fLastSample = fB * fIn - fA * fLastSample; + + return fLastSample; +} + +void CAudioReverb::Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation ) +{ + float fMixedInput, temp, temp0, temp1, temp2; + + for ( int i = 0; i < iStereoBlockSizeSam; i += 2 ) + { + // we sum up the stereo input channels (in case mono input is used, a zero + // shall be input for the right channel) + if ( eAudioChannelConf == CC_STEREO ) + { + fMixedInput = 0.5f * ( vecsStereoInOut[i] + vecsStereoInOut[i + 1] ); + } + else + { + if ( bReverbOnLeftChan ) + { + fMixedInput = vecsStereoInOut[i]; + } + else + { + fMixedInput = vecsStereoInOut[i + 1]; + } + } + + temp = allpassDelays[0].Get(); + temp0 = allpassCoefficient * temp; + temp0 += fMixedInput; + allpassDelays[0].Add ( temp0 ); + temp0 = -( allpassCoefficient * temp0 ) + temp; + + temp = allpassDelays[1].Get(); + temp1 = allpassCoefficient * temp; + temp1 += temp0; + allpassDelays[1].Add ( temp1 ); + temp1 = -( allpassCoefficient * temp1 ) + temp; + + temp = allpassDelays[2].Get(); + temp2 = allpassCoefficient * temp; + temp2 += temp1; + allpassDelays[2].Add ( temp2 ); + temp2 = -( allpassCoefficient * temp2 ) + temp; + + const float temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() ); + const float temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() ); + const float temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() ); + const float temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() ); + + combDelays[0].Add ( temp3 ); + combDelays[1].Add ( temp4 ); + combDelays[2].Add ( temp5 ); + combDelays[3].Add ( temp6 ); + + const float filtout = temp3 + temp4 + temp5 + temp6; + + outLeftDelay.Add ( filtout ); + outRightDelay.Add ( filtout ); + + // inplace apply the attenuated reverb signal (for stereo always apply + // reverberation effect on both channels) + if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan ) + { + vecsStereoInOut[i] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i] + 0.5f * fAttenuation * outLeftDelay.Get() ); + } + + if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan ) + { + vecsStereoInOut[i + 1] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i + 1] + 0.5f * fAttenuation * outRightDelay.Get() ); + } + } +} \ No newline at end of file diff --git a/src/plugins/audioreverb.h b/src/plugins/audioreverb.h new file mode 100644 index 0000000000..a5da6fac61 --- /dev/null +++ b/src/plugins/audioreverb.h @@ -0,0 +1,57 @@ +/******************************************************************************\ +* Audio Reverberation * +\******************************************************************************/ +/* + The following code is based on "JCRev: John Chowning's reverberator class" + by Perry R. Cook and Gary P. Scavone, 1995 - 2004 + which is in "The Synthesis ToolKit in C++ (STK)" + http://ccrma.stanford.edu/software/stk + + Original description: + This class is derived from the CLM JCRev function, which is based on the use + of networks of simple allpass and comb delay filters. This class implements + three series allpass units, followed by four parallel comb filters, and two + decorrelation delay lines in parallel at the output. +*/ + +#pragma once +#include "util.h" + +class CAudioReverb +{ +public: + CAudioReverb() {} + + void Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 = 1.1f ); + + void Clear(); + void Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation ); + +protected: + void setT60 ( const float fT60, const int iSampleRate ); + bool isPrime ( const int number ); + + class COnePole + { + public: + COnePole() : fA ( 0 ), fB ( 0 ) { Reset(); } + void setPole ( const float fPole ); + float Calc ( const float fIn ); + void Reset() { fLastSample = 0; } + + protected: + float fA; + float fB; + float fLastSample; + }; + + EAudChanConf eAudioChannelConf; + int iStereoBlockSizeSam; + CFIFO allpassDelays[3]; + CFIFO combDelays[4]; + COnePole combFilters[4]; + CFIFO outLeftDelay; + CFIFO outRightDelay; + float allpassCoefficient; + float combCoefficient[4]; +}; diff --git a/src/util.cpp b/src/util.cpp index 8dd4621db2..9bd56db361 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -169,211 +169,6 @@ uint32_t CCRC::GetCRC() return iStateShiftReg & ( iBitOutMask - 1 ); } -/******************************************************************************\ -* Audio Reverberation * -\******************************************************************************/ -/* - The following code is based on "JCRev: John Chowning's reverberator class" - by Perry R. Cook and Gary P. Scavone, 1995 - 2004 - which is in "The Synthesis ToolKit in C++ (STK)" - http://ccrma.stanford.edu/software/stk - - Original description: - This class is derived from the CLM JCRev function, which is based on the use - of networks of simple allpass and comb delay filters. This class implements - three series allpass units, followed by four parallel comb filters, and two - decorrelation delay lines in parallel at the output. -*/ -void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 ) -{ - // store parameters - eAudioChannelConf = eNAudioChannelConf; - iStereoBlockSizeSam = iNStereoBlockSizeSam; - - // delay lengths for 44100 Hz sample rate - int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; - const float scaler = static_cast ( iSampleRate ) / 44100.0f; - - if ( scaler != 1.0f ) - { - for ( int i = 0; i < 9; i++ ) - { - int delay = static_cast ( floorf ( scaler * lengths[i] ) ); - - if ( ( delay & 1 ) == 0 ) - { - delay++; - } - - while ( !isPrime ( delay ) ) - { - delay += 2; - } - - lengths[i] = delay; - } - } - - for ( int i = 0; i < 3; i++ ) - { - allpassDelays[i].Init ( lengths[i + 4] ); - } - - for ( int i = 0; i < 4; i++ ) - { - combDelays[i].Init ( lengths[i] ); - combFilters[i].setPole ( 0.2f ); - } - - setT60 ( fT60, iSampleRate ); - outLeftDelay.Init ( lengths[7] ); - outRightDelay.Init ( lengths[8] ); - allpassCoefficient = 0.7f; - Clear(); -} - -bool CAudioReverb::isPrime ( const int number ) -{ - /* - Returns true if argument value is prime. Taken from "class Effect" in - "STK abstract effects parent class". - */ - if ( number == 2 ) - { - return true; - } - - if ( number & 1 ) - { - for ( int i = 3; i < static_cast ( sqrtf ( static_cast ( number ) ) ) + 1; i += 2 ) - { - if ( ( number % i ) == 0 ) - { - return false; - } - } - - return true; // prime - } - else - { - return false; // even - } -} - -void CAudioReverb::Clear() -{ - // reset and clear all internal state - allpassDelays[0].Reset ( 0 ); - allpassDelays[1].Reset ( 0 ); - allpassDelays[2].Reset ( 0 ); - combDelays[0].Reset ( 0 ); - combDelays[1].Reset ( 0 ); - combDelays[2].Reset ( 0 ); - combDelays[3].Reset ( 0 ); - combFilters[0].Reset(); - combFilters[1].Reset(); - combFilters[2].Reset(); - combFilters[3].Reset(); - outRightDelay.Reset ( 0 ); - outLeftDelay.Reset ( 0 ); -} - -void CAudioReverb::setT60 ( const float fT60, const int iSampleRate ) -{ - // set the reverberation T60 decay time - for ( int i = 0; i < 4; i++ ) - { - combCoefficient[i] = powf ( 10.0f, static_cast ( -3.0f * combDelays[i].Size() / ( fT60 * iSampleRate ) ) ); - } -} - -void CAudioReverb::COnePole::setPole ( const float fPole ) -{ - // calculate IIR filter coefficients based on the pole value - fA = -fPole; - fB = 1.0f - fPole; -} - -float CAudioReverb::COnePole::Calc ( const float fIn ) -{ - // calculate IIR filter - fLastSample = fB * fIn - fA * fLastSample; - - return fLastSample; -} - -void CAudioReverb::Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation ) -{ - float fMixedInput, temp, temp0, temp1, temp2; - - for ( int i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // we sum up the stereo input channels (in case mono input is used, a zero - // shall be input for the right channel) - if ( eAudioChannelConf == CC_STEREO ) - { - fMixedInput = 0.5f * ( vecsStereoInOut[i] + vecsStereoInOut[i + 1] ); - } - else - { - if ( bReverbOnLeftChan ) - { - fMixedInput = vecsStereoInOut[i]; - } - else - { - fMixedInput = vecsStereoInOut[i + 1]; - } - } - - temp = allpassDelays[0].Get(); - temp0 = allpassCoefficient * temp; - temp0 += fMixedInput; - allpassDelays[0].Add ( temp0 ); - temp0 = -( allpassCoefficient * temp0 ) + temp; - - temp = allpassDelays[1].Get(); - temp1 = allpassCoefficient * temp; - temp1 += temp0; - allpassDelays[1].Add ( temp1 ); - temp1 = -( allpassCoefficient * temp1 ) + temp; - - temp = allpassDelays[2].Get(); - temp2 = allpassCoefficient * temp; - temp2 += temp1; - allpassDelays[2].Add ( temp2 ); - temp2 = -( allpassCoefficient * temp2 ) + temp; - - const float temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() ); - const float temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() ); - const float temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() ); - const float temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() ); - - combDelays[0].Add ( temp3 ); - combDelays[1].Add ( temp4 ); - combDelays[2].Add ( temp5 ); - combDelays[3].Add ( temp6 ); - - const float filtout = temp3 + temp4 + temp5 + temp6; - - outLeftDelay.Add ( filtout ); - outRightDelay.Add ( filtout ); - - // inplace apply the attenuated reverb signal (for stereo always apply - // reverberation effect on both channels) - if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan ) - { - vecsStereoInOut[i] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i] + 0.5f * fAttenuation * outLeftDelay.Get() ); - } - - if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan ) - { - vecsStereoInOut[i + 1] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i + 1] + 0.5f * fAttenuation * outRightDelay.Get() ); - } - } -} - // CHighPrecisionTimer implementation ****************************************** #ifdef _WIN32 CHighPrecisionTimer::CHighPrecisionTimer ( const bool bNewUseDoubleSystemFrameSize ) : bUseDoubleSystemFrameSize ( bNewUseDoubleSystemFrameSize ) diff --git a/src/util.h b/src/util.h index 9c9d3a3861..5e863b930a 100644 --- a/src/util.h +++ b/src/util.h @@ -1110,46 +1110,6 @@ class COSUtil } }; -// Audio reverbration ---------------------------------------------------------- -class CAudioReverb -{ -public: - CAudioReverb() {} - - void Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 = 1.1f ); - - void Clear(); - void Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation ); - -protected: - void setT60 ( const float fT60, const int iSampleRate ); - bool isPrime ( const int number ); - - class COnePole - { - public: - COnePole() : fA ( 0 ), fB ( 0 ) { Reset(); } - void setPole ( const float fPole ); - float Calc ( const float fIn ); - void Reset() { fLastSample = 0; } - - protected: - float fA; - float fB; - float fLastSample; - }; - - EAudChanConf eAudioChannelConf; - int iStereoBlockSizeSam; - CFIFO allpassDelays[3]; - CFIFO combDelays[4]; - COnePole combFilters[4]; - CFIFO outLeftDelay; - CFIFO outRightDelay; - float allpassCoefficient; - float combCoefficient[4]; -}; - // CRC ------------------------------------------------------------------------- class CCRC {