Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move reverb code out of utils #3403

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Jamulus.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions src/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "socket.h"
#include "channel.h"
#include "util.h"
#include "plugins/audioreverb.h"
#include "buffer.h"
#include "signalhandler.h"

Expand Down
207 changes: 207 additions & 0 deletions src/plugins/audioreverb.cpp
Original file line number Diff line number Diff line change
@@ -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<float> ( iSampleRate ) / 44100.0f;

if ( scaler != 1.0f )
{
for ( int i = 0; i < 9; i++ )
{
int delay = static_cast<int> ( 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<int> ( sqrtf ( static_cast<float> ( 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<float> ( -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<int16_t>& 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() );
}
}
}
57 changes: 57 additions & 0 deletions src/plugins/audioreverb.h
Original file line number Diff line number Diff line change
@@ -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<int16_t>& 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<float> allpassDelays[3];
CFIFO<float> combDelays[4];
COnePole combFilters[4];
CFIFO<float> outLeftDelay;
CFIFO<float> outRightDelay;
float allpassCoefficient;
float combCoefficient[4];
};
Loading
Loading