mirror of
https://github.com/thestk/stk
synced 2026-01-11 20:11:52 +00:00
121 lines
3.4 KiB
C++
121 lines
3.4 KiB
C++
/***************************************************/
|
|
/*! \class JCRev
|
|
\brief John Chowning's reverberator class.
|
|
|
|
This class takes a monophonic input signal and produces a stereo
|
|
output signal. It 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.
|
|
|
|
by Perry R. Cook and Gary P. Scavone, 1995 - 2009.
|
|
*/
|
|
/***************************************************/
|
|
|
|
#include "JCRev.h"
|
|
#include <cmath>
|
|
|
|
namespace stk {
|
|
|
|
JCRev :: JCRev( StkFloat T60 )
|
|
{
|
|
lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output
|
|
|
|
// Delay lengths for 44100 Hz sample rate.
|
|
int lengths[9] = {1777, 1847, 1993, 2137, 389, 127, 43, 211, 179};
|
|
double scaler = Stk::sampleRate() / 44100.0;
|
|
|
|
int delay, i;
|
|
if ( scaler != 1.0 ) {
|
|
for ( i=0; i<9; i++ ) {
|
|
delay = (int) floor( scaler * lengths[i] );
|
|
if ( (delay & 1) == 0) delay++;
|
|
while ( !this->isPrime(delay) ) delay += 2;
|
|
lengths[i] = delay;
|
|
}
|
|
}
|
|
|
|
for ( i=0; i<3; i++ ) {
|
|
allpassDelays_[i].setMaximumDelay( lengths[i+4] );
|
|
allpassDelays_[i].setDelay( lengths[i+4] );
|
|
}
|
|
|
|
for ( i=0; i<4; i++ ) {
|
|
combDelays_[i].setMaximumDelay( lengths[i] );
|
|
combDelays_[i].setDelay( lengths[i] );
|
|
}
|
|
|
|
this->setT60( T60 );
|
|
outLeftDelay_.setMaximumDelay( lengths[7] );
|
|
outLeftDelay_.setDelay( lengths[7] );
|
|
outRightDelay_.setMaximumDelay( lengths[8] );
|
|
outRightDelay_.setDelay( lengths[8] );
|
|
allpassCoefficient_ = 0.7;
|
|
effectMix_ = 0.3;
|
|
this->clear();
|
|
}
|
|
|
|
void JCRev :: clear()
|
|
{
|
|
allpassDelays_[0].clear();
|
|
allpassDelays_[1].clear();
|
|
allpassDelays_[2].clear();
|
|
combDelays_[0].clear();
|
|
combDelays_[1].clear();
|
|
combDelays_[2].clear();
|
|
combDelays_[3].clear();
|
|
outRightDelay_.clear();
|
|
outLeftDelay_.clear();
|
|
lastFrame_[0] = 0.0;
|
|
lastFrame_[1] = 0.0;
|
|
}
|
|
|
|
void JCRev :: setT60( StkFloat T60 )
|
|
{
|
|
for ( int i=0; i<4; i++ )
|
|
combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate())));
|
|
}
|
|
|
|
StkFrames& JCRev :: tick( StkFrames& frames, unsigned int channel )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( channel >= frames.channels() - 1 ) {
|
|
errorString_ << "JCRev::tick(): channel and StkFrames arguments are incompatible!";
|
|
handleError( StkError::FUNCTION_ARGUMENT );
|
|
}
|
|
#endif
|
|
|
|
StkFloat *samples = &frames[channel];
|
|
unsigned int hop = frames.channels();
|
|
for ( unsigned int i=0; i<frames.frames(); i++, samples += hop ) {
|
|
*samples = tick( *samples );
|
|
*samples++;
|
|
*samples = lastFrame_[1];
|
|
}
|
|
|
|
return frames;
|
|
}
|
|
|
|
StkFrames& JCRev :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) {
|
|
errorString_ << "JCRev::tick(): channel and StkFrames arguments are incompatible!";
|
|
handleError( StkError::FUNCTION_ARGUMENT );
|
|
}
|
|
#endif
|
|
|
|
StkFloat *iSamples = &iFrames[iChannel];
|
|
StkFloat *oSamples = &oFrames[oChannel];
|
|
unsigned int iHop = iFrames.channels(), oHop = oFrames.channels();
|
|
for ( unsigned int i=0; i<iFrames.frames(); i++, iSamples += iHop, oSamples += oHop ) {
|
|
*oSamples++ = tick( *iSamples );
|
|
*oSamples = lastFrame_[1];
|
|
}
|
|
|
|
return iFrames;
|
|
}
|
|
|
|
} // stk namespace
|