From 4c94633c1a8808553fa3ba0a7723f00f2bd60aae Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Fri, 12 Jun 2020 18:26:34 +0200 Subject: [PATCH] clean up the reverberation and panning code in the client --- src/client.cpp | 142 +++++++++---------------------------------------- src/client.h | 6 +-- src/util.cpp | 127 ++++++++++++++++++++++++++----------------- src/util.h | 18 ++++--- 4 files changed, 117 insertions(+), 176 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 10d3afac..13b42cf2 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -889,8 +889,9 @@ void CClient::Init() iNumAudioChannels ); // init reverberation - AudioReverbL.Init ( SYSTEM_SAMPLE_RATE_HZ ); - AudioReverbR.Init ( SYSTEM_SAMPLE_RATE_HZ ); + AudioReverb.Init ( eAudioChannelConf, + iStereoBlockSizeSam, + SYSTEM_SAMPLE_RATE_HZ ); // init the sound card conversion buffers if ( bSndCrdConversionBufferRequired ) @@ -973,118 +974,41 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) // add reverberation effect if activated if ( iReverbLevel != 0 ) { - // calculate attenuation amplification factor - const double dRevLev = static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4; + AudioReverb.Process ( vecsStereoSndCrd, + bReverbOnLeftChan, + static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4 ); + } + + // apply pan (audio fader) and mix mono signals + if ( !( ( iAudioInFader == AUD_FADER_IN_MIDDLE ) && ( eAudioChannelConf == CC_STEREO ) ) ) + { + // calculate pan gain in the range 0 to 1, where 0.5 is the middle position + const double dPan = static_cast ( iAudioInFader ) / AUD_FADER_IN_MAX; if ( eAudioChannelConf == CC_STEREO ) { - // for stereo always apply reverberation effect on both channels - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // both channels (stereo) - AudioReverbL.ProcessSample ( vecsStereoSndCrd[i], vecsStereoSndCrd[i + 1], dRevLev ); - } - } - else - { - // mono and mono-in/stereo out mode - if ( bReverbOnLeftChan ) - { - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // left channel - int16_t sRightDummy = 0; // has to be 0 for mono reverb - AudioReverbL.ProcessSample ( vecsStereoSndCrd[i], sRightDummy, dRevLev ); - } - } - else - { - for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) - { - // right channel - int16_t sRightDummy = 0; // has to be 0 for mono reverb - AudioReverbR.ProcessSample ( vecsStereoSndCrd[i], sRightDummy, dRevLev ); - } - } - } - } + // for stereo only apply pan attenuation on one channel (same as pan in the server) + const double dGainL = std::min ( 0.5, 1 - dPan ) * 2; + const double dGainR = std::min ( 0.5, dPan ) * 2; - // mix both signals depending on the fading setting, convert - // from double to short - if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) - { - // no action require if fader is in the middle and stereo is used - if ( eAudioChannelConf != CC_STEREO ) - { - // mix channels together (store result in first half of the vector) for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - // for the sum make sure we have more bits available (cast to - // int32), after the normalization by 2, the result will fit - // into the old size so that cast to int16 is safe - vecsStereoSndCrd[i] = static_cast ( - ( static_cast ( vecsStereoSndCrd[j] ) + vecsStereoSndCrd[j + 1] ) / 2 ); - } - } - } - else - { - if ( eAudioChannelConf == CC_STEREO ) - { - // stereo - const double dAttFactStereo = static_cast ( - AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / AUD_FADER_IN_MIDDLE; - - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel - vecsStereoSndCrd[j + 1] = Double2Short ( dAttFactStereo * vecsStereoSndCrd[j + 1] ); - } - } - else - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel - vecsStereoSndCrd[j] = Double2Short ( dAttFactStereo * vecsStereoSndCrd[j] ); - } + // note that the gain is always <= 1, therefore a simple cast is + // ok since we never can get an overload + vecsStereoSndCrd[j + 1] = static_cast ( dGainR * vecsStereoSndCrd[j + 1] ); + vecsStereoSndCrd[j] = static_cast ( dGainL * vecsStereoSndCrd[j] ); } } else { - // mono and mono-in/stereo out mode - // make sure that in the middle position the two channels are - // amplified by 1/2, if the pan is set to one channel, this - // channel should have an amplification of 1 - const double dAttFactMono = static_cast ( - AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / AUD_FADER_IN_MIDDLE / 2; + // for mono implement a cross-fade between channels and mix them + const double dGainL = 1 - dPan; + const double dGainR = dPan; - const double dAmplFactMono = 0.5 + static_cast ( - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / AUD_FADER_IN_MIDDLE / 2; - - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel (store result in first half - // of the vector) - vecsStereoSndCrd[i] = Double2Short ( - dAmplFactMono * vecsStereoSndCrd[j] + - dAttFactMono * vecsStereoSndCrd[j + 1] ); - } - } - else - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel (store result in first half - // of the vector) - vecsStereoSndCrd[i] = Double2Short ( - dAmplFactMono * vecsStereoSndCrd[j + 1] + - dAttFactMono * vecsStereoSndCrd[j] ); - } + vecsStereoSndCrd[i] = Double2Short ( + dGainL * vecsStereoSndCrd[j] + dGainR * vecsStereoSndCrd[j + 1] ); } } } @@ -1175,20 +1099,6 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) } } -/* -// TEST -// fid=fopen('c:\\temp\test2.dat','r');x=fread(fid,'int16');fclose(fid); -static FILE* pFileDelay = fopen("c:\\temp\\test2.dat", "wb"); -short sData[2]; -for (i = 0; i < iMonoBlockSizeSam; i++) -{ - sData[0] = (short) vecsStereoSndCrd[i]; - fwrite(&sData, size_t(2), size_t(1), pFileDelay); -} -fflush(pFileDelay); -*/ - - // for muted stream we have to add our local data here if ( bMuteOutStream ) { diff --git a/src/client.h b/src/client.h index d56435c6..b8497bbf 100755 --- a/src/client.h +++ b/src/client.h @@ -151,8 +151,7 @@ public: void SetReverbOnLeftChan ( const bool bIL ) { bReverbOnLeftChan = bIL; - AudioReverbL.Clear(); - AudioReverbR.Clear(); + AudioReverb.Clear(); } void SetDoAutoSockBufSize ( const bool bValue ); @@ -354,8 +353,7 @@ protected: int iAudioInFader; bool bReverbOnLeftChan; int iReverbLevel; - CAudioReverb AudioReverbL; - CAudioReverb AudioReverbR; + CAudioReverb AudioReverb; int iSndCrdPrefFrameSizeFactor; int iSndCrdFrameSizeFactor; diff --git a/src/util.cpp b/src/util.cpp index 98051287..13808bbc 100755 --- a/src/util.cpp +++ b/src/util.cpp @@ -165,20 +165,24 @@ uint32_t CCRC::GetCRC() three series allpass units, followed by four parallel comb filters, and two decorrelation delay lines in parallel at the output. */ -void CAudioReverb::Init ( const int iSampleRate, - const double rT60 ) +void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf, + const int iNStereoBlockSizeSam, + const int iSampleRate, + const double rT60 ) { - int delay, i; + // store paramters + eAudioChannelConf = eNAudioChannelConf; + iStereoBlockSizeSam = iNStereoBlockSizeSam; // delay lengths for 44100 Hz sample rate - int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; - const double scaler = static_cast ( iSampleRate ) / 44100.0; + int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; + const double scaler = static_cast ( iSampleRate ) / 44100.0; if ( scaler != 1.0 ) { - for ( i = 0; i < 9; i++ ) + for ( int i = 0; i < 9; i++ ) { - delay = static_cast ( floor ( scaler * lengths[i] ) ); + int delay = static_cast ( floor ( scaler * lengths[i] ) ); if ( ( delay & 1 ) == 0 ) { @@ -194,12 +198,12 @@ void CAudioReverb::Init ( const int iSampleRate, } } - for ( i = 0; i < 3; i++ ) + for ( int i = 0; i < 3; i++ ) { allpassDelays[i].Init ( lengths[i + 4] ); } - for ( i = 0; i < 4; i++ ) + for ( int i = 0; i < 4; i++ ) { combDelays[i].Init ( lengths[i] ); combFilters[i].setPole ( 0.2 ); @@ -285,58 +289,81 @@ double CAudioReverb::COnePole::Calc ( const double dIn ) return dLastSample; } -void CAudioReverb::ProcessSample ( int16_t& iInputOutputLeft, - int16_t& iInputOutputRight, - const double dAttenuation ) +void CAudioReverb::Process ( CVector& vecsStereoInOut, + const bool bReverbOnLeftChan, + const double dAttenuation ) { - // compute one output sample - double temp, temp0, temp1, temp2; + double dMixedInput, temp, temp0, temp1, temp2; - // we sum up the stereo input channels (in case mono input is used, a zero - // shall be input for the right channel) - const double dMixedInput = 0.5 * ( iInputOutputLeft + iInputOutputRight ); + 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 ) + { + dMixedInput = 0.5 * ( vecsStereoInOut[i] + vecsStereoInOut[i + 1] ); + } + else + { + if ( bReverbOnLeftChan ) + { + dMixedInput = vecsStereoInOut[i]; + } + else + { + dMixedInput = vecsStereoInOut[i + 1]; + } + } - temp = allpassDelays[0].Get(); - temp0 = allpassCoefficient * temp; - temp0 += dMixedInput; - allpassDelays[0].Add ( temp0 ); - temp0 = - ( allpassCoefficient * temp0 ) + temp; + temp = allpassDelays[0].Get(); + temp0 = allpassCoefficient * temp; + temp0 += dMixedInput; + 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[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; + temp = allpassDelays[2].Get(); + temp2 = allpassCoefficient * temp; + temp2 += temp1; + allpassDelays[2].Add ( temp2 ); + temp2 = - ( allpassCoefficient * temp2 ) + temp; - const double temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() ); - const double temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() ); - const double temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() ); - const double temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() ); + const double temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() ); + const double temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() ); + const double temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() ); + const double 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 ); + combDelays[0].Add ( temp3 ); + combDelays[1].Add ( temp4 ); + combDelays[2].Add ( temp5 ); + combDelays[3].Add ( temp6 ); - const double filtout = temp3 + temp4 + temp5 + temp6; + const double filtout = temp3 + temp4 + temp5 + temp6; - outLeftDelay.Add ( filtout ); - outRightDelay.Add ( filtout ); + outLeftDelay.Add ( filtout ); + outRightDelay.Add ( filtout ); - // inplace apply the attenuated reverb signal - iInputOutputLeft = Double2Short ( - ( 1.0 - dAttenuation ) * iInputOutputLeft + - 0.5 * dAttenuation * outLeftDelay.Get() ); + // inplace apply the attenuated reverb signal (for stereo always apply + // reverberation effect on both channels) + if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan ) + { + vecsStereoInOut[i] = Double2Short ( + ( 1.0 - dAttenuation ) * vecsStereoInOut[i] + + 0.5 * dAttenuation * outLeftDelay.Get() ); + } - iInputOutputRight = Double2Short ( - ( 1.0 - dAttenuation ) * iInputOutputRight + - 0.5 * dAttenuation * outRightDelay.Get() ); + if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan ) + { + vecsStereoInOut[i + 1] = Double2Short ( + ( 1.0 - dAttenuation ) * vecsStereoInOut[i + 1] + + 0.5 * dAttenuation * outRightDelay.Get() ); + } + } } diff --git a/src/util.h b/src/util.h index c119dcc6..935ebd02 100755 --- a/src/util.h +++ b/src/util.h @@ -694,8 +694,8 @@ public: } protected: - double UpdateCurLevel ( double dCurLevel, - const short& sMax ); + double UpdateCurLevel ( double dCurLevel, + const short& sMax ); double dCurLevelL; double dCurLevelR; @@ -1098,11 +1098,15 @@ class CAudioReverb public: CAudioReverb() {} - void Init ( const int iSampleRate, const double rT60 = 1.1 ); + void Init ( const EAudChanConf eNAudioChannelConf, + const int iNStereoBlockSizeSam, + const int iSampleRate, + const double rT60 = 1.1 ); + void Clear(); - void ProcessSample ( int16_t& iInputOutputLeft, - int16_t& iInputOutputRight, - const double dAttenuation ); + void Process ( CVector& vecsStereoInOut, + const bool bReverbOnLeftChan, + const double dAttenuation ); protected: void setT60 ( const double rT60, const int iSampleRate ); @@ -1122,6 +1126,8 @@ protected: double dLastSample; }; + EAudChanConf eAudioChannelConf; + int iStereoBlockSizeSam; CFIFO allpassDelays[3]; CFIFO combDelays[4]; COnePole combFilters[4];