clean up the reverberation and panning code in the client

This commit is contained in:
Volker Fischer 2020-06-12 18:26:34 +02:00
parent 0d77ccdcc4
commit 4c94633c1a
4 changed files with 117 additions and 176 deletions

View file

@ -889,8 +889,9 @@ void CClient::Init()
iNumAudioChannels ); iNumAudioChannels );
// init reverberation // init reverberation
AudioReverbL.Init ( SYSTEM_SAMPLE_RATE_HZ ); AudioReverb.Init ( eAudioChannelConf,
AudioReverbR.Init ( SYSTEM_SAMPLE_RATE_HZ ); iStereoBlockSizeSam,
SYSTEM_SAMPLE_RATE_HZ );
// init the sound card conversion buffers // init the sound card conversion buffers
if ( bSndCrdConversionBufferRequired ) if ( bSndCrdConversionBufferRequired )
@ -973,118 +974,41 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
// add reverberation effect if activated // add reverberation effect if activated
if ( iReverbLevel != 0 ) if ( iReverbLevel != 0 )
{ {
// calculate attenuation amplification factor AudioReverb.Process ( vecsStereoSndCrd,
const double dRevLev = static_cast<double> ( iReverbLevel ) / AUD_REVERB_MAX / 4; bReverbOnLeftChan,
static_cast<double> ( 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<double> ( iAudioInFader ) / AUD_FADER_IN_MAX;
if ( eAudioChannelConf == CC_STEREO ) if ( eAudioChannelConf == CC_STEREO )
{ {
// for stereo always apply reverberation effect on both channels // for stereo only apply pan attenuation on one channel (same as pan in the server)
for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) const double dGainL = std::min ( 0.5, 1 - dPan ) * 2;
{ const double dGainR = std::min ( 0.5, dPan ) * 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 );
}
}
}
}
// 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 ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
{ {
// for the sum make sure we have more bits available (cast to // note that the gain is always <= 1, therefore a simple cast is
// int32), after the normalization by 2, the result will fit // ok since we never can get an overload
// into the old size so that cast to int16 is safe vecsStereoSndCrd[j + 1] = static_cast<int16_t> ( dGainR * vecsStereoSndCrd[j + 1] );
vecsStereoSndCrd[i] = static_cast<int16_t> ( vecsStereoSndCrd[j] = static_cast<int16_t> ( dGainL * vecsStereoSndCrd[j] );
( static_cast<int32_t> ( vecsStereoSndCrd[j] ) + vecsStereoSndCrd[j + 1] ) / 2 );
}
}
}
else
{
if ( eAudioChannelConf == CC_STEREO )
{
// stereo
const double dAttFactStereo = static_cast<double> (
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] );
}
} }
} }
else else
{ {
// mono and mono-in/stereo out mode // for mono implement a cross-fade between channels and mix them
// make sure that in the middle position the two channels are const double dGainL = 1 - dPan;
// amplified by 1/2, if the pan is set to one channel, this const double dGainR = dPan;
// channel should have an amplification of 1
const double dAttFactMono = static_cast<double> (
AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / AUD_FADER_IN_MIDDLE / 2;
const double dAmplFactMono = 0.5 + static_cast<double> ( for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
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 ) vecsStereoSndCrd[i] = Double2Short (
{ dGainL * vecsStereoSndCrd[j] + dGainR * vecsStereoSndCrd[j + 1] );
// 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] );
}
} }
} }
} }
@ -1175,20 +1099,6 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& 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 // for muted stream we have to add our local data here
if ( bMuteOutStream ) if ( bMuteOutStream )
{ {

View file

@ -151,8 +151,7 @@ public:
void SetReverbOnLeftChan ( const bool bIL ) void SetReverbOnLeftChan ( const bool bIL )
{ {
bReverbOnLeftChan = bIL; bReverbOnLeftChan = bIL;
AudioReverbL.Clear(); AudioReverb.Clear();
AudioReverbR.Clear();
} }
void SetDoAutoSockBufSize ( const bool bValue ); void SetDoAutoSockBufSize ( const bool bValue );
@ -354,8 +353,7 @@ protected:
int iAudioInFader; int iAudioInFader;
bool bReverbOnLeftChan; bool bReverbOnLeftChan;
int iReverbLevel; int iReverbLevel;
CAudioReverb AudioReverbL; CAudioReverb AudioReverb;
CAudioReverb AudioReverbR;
int iSndCrdPrefFrameSizeFactor; int iSndCrdPrefFrameSizeFactor;
int iSndCrdFrameSizeFactor; int iSndCrdFrameSizeFactor;

View file

@ -165,20 +165,24 @@ uint32_t CCRC::GetCRC()
three series allpass units, followed by four parallel comb filters, and two three series allpass units, followed by four parallel comb filters, and two
decorrelation delay lines in parallel at the output. decorrelation delay lines in parallel at the output.
*/ */
void CAudioReverb::Init ( const int iSampleRate, void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf,
const double rT60 ) 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 // delay lengths for 44100 Hz sample rate
int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 };
const double scaler = static_cast<double> ( iSampleRate ) / 44100.0; const double scaler = static_cast<double> ( iSampleRate ) / 44100.0;
if ( scaler != 1.0 ) if ( scaler != 1.0 )
{ {
for ( i = 0; i < 9; i++ ) for ( int i = 0; i < 9; i++ )
{ {
delay = static_cast<int> ( floor ( scaler * lengths[i] ) ); int delay = static_cast<int> ( floor ( scaler * lengths[i] ) );
if ( ( delay & 1 ) == 0 ) 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] ); allpassDelays[i].Init ( lengths[i + 4] );
} }
for ( i = 0; i < 4; i++ ) for ( int i = 0; i < 4; i++ )
{ {
combDelays[i].Init ( lengths[i] ); combDelays[i].Init ( lengths[i] );
combFilters[i].setPole ( 0.2 ); combFilters[i].setPole ( 0.2 );
@ -285,58 +289,81 @@ double CAudioReverb::COnePole::Calc ( const double dIn )
return dLastSample; return dLastSample;
} }
void CAudioReverb::ProcessSample ( int16_t& iInputOutputLeft, void CAudioReverb::Process ( CVector<int16_t>& vecsStereoInOut,
int16_t& iInputOutputRight, const bool bReverbOnLeftChan,
const double dAttenuation ) const double dAttenuation )
{ {
// compute one output sample double dMixedInput, temp, temp0, temp1, temp2;
double temp, temp0, temp1, temp2;
// we sum up the stereo input channels (in case mono input is used, a zero for ( int i = 0; i < iStereoBlockSizeSam; i += 2 )
// shall be input for the right channel) {
const double dMixedInput = 0.5 * ( iInputOutputLeft + iInputOutputRight ); // 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(); temp = allpassDelays[0].Get();
temp0 = allpassCoefficient * temp; temp0 = allpassCoefficient * temp;
temp0 += dMixedInput; temp0 += dMixedInput;
allpassDelays[0].Add ( temp0 ); allpassDelays[0].Add ( temp0 );
temp0 = - ( allpassCoefficient * temp0 ) + temp; temp0 = - ( allpassCoefficient * temp0 ) + temp;
temp = allpassDelays[1].Get(); temp = allpassDelays[1].Get();
temp1 = allpassCoefficient * temp; temp1 = allpassCoefficient * temp;
temp1 += temp0; temp1 += temp0;
allpassDelays[1].Add ( temp1 ); allpassDelays[1].Add ( temp1 );
temp1 = - ( allpassCoefficient * temp1 ) + temp; temp1 = - ( allpassCoefficient * temp1 ) + temp;
temp = allpassDelays[2].Get(); temp = allpassDelays[2].Get();
temp2 = allpassCoefficient * temp; temp2 = allpassCoefficient * temp;
temp2 += temp1; temp2 += temp1;
allpassDelays[2].Add ( temp2 ); allpassDelays[2].Add ( temp2 );
temp2 = - ( allpassCoefficient * temp2 ) + temp; temp2 = - ( allpassCoefficient * temp2 ) + temp;
const double temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].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 temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() );
const double temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].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 temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() );
combDelays[0].Add ( temp3 ); combDelays[0].Add ( temp3 );
combDelays[1].Add ( temp4 ); combDelays[1].Add ( temp4 );
combDelays[2].Add ( temp5 ); combDelays[2].Add ( temp5 );
combDelays[3].Add ( temp6 ); combDelays[3].Add ( temp6 );
const double filtout = temp3 + temp4 + temp5 + temp6; const double filtout = temp3 + temp4 + temp5 + temp6;
outLeftDelay.Add ( filtout ); outLeftDelay.Add ( filtout );
outRightDelay.Add ( filtout ); outRightDelay.Add ( filtout );
// inplace apply the attenuated reverb signal // inplace apply the attenuated reverb signal (for stereo always apply
iInputOutputLeft = Double2Short ( // reverberation effect on both channels)
( 1.0 - dAttenuation ) * iInputOutputLeft + if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan )
0.5 * dAttenuation * outLeftDelay.Get() ); {
vecsStereoInOut[i] = Double2Short (
( 1.0 - dAttenuation ) * vecsStereoInOut[i] +
0.5 * dAttenuation * outLeftDelay.Get() );
}
iInputOutputRight = Double2Short ( if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan )
( 1.0 - dAttenuation ) * iInputOutputRight + {
0.5 * dAttenuation * outRightDelay.Get() ); vecsStereoInOut[i + 1] = Double2Short (
( 1.0 - dAttenuation ) * vecsStereoInOut[i + 1] +
0.5 * dAttenuation * outRightDelay.Get() );
}
}
} }

View file

@ -694,8 +694,8 @@ public:
} }
protected: protected:
double UpdateCurLevel ( double dCurLevel, double UpdateCurLevel ( double dCurLevel,
const short& sMax ); const short& sMax );
double dCurLevelL; double dCurLevelL;
double dCurLevelR; double dCurLevelR;
@ -1098,11 +1098,15 @@ class CAudioReverb
public: public:
CAudioReverb() {} 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 Clear();
void ProcessSample ( int16_t& iInputOutputLeft, void Process ( CVector<int16_t>& vecsStereoInOut,
int16_t& iInputOutputRight, const bool bReverbOnLeftChan,
const double dAttenuation ); const double dAttenuation );
protected: protected:
void setT60 ( const double rT60, const int iSampleRate ); void setT60 ( const double rT60, const int iSampleRate );
@ -1122,6 +1126,8 @@ protected:
double dLastSample; double dLastSample;
}; };
EAudChanConf eAudioChannelConf;
int iStereoBlockSizeSam;
CFIFO<double> allpassDelays[3]; CFIFO<double> allpassDelays[3];
CFIFO<double> combDelays[4]; CFIFO<double> combDelays[4];
COnePole combFilters[4]; COnePole combFilters[4];