mirror of
https://github.com/thestk/stk
synced 2026-01-12 04:21:52 +00:00
120 lines
3.5 KiB
C++
120 lines
3.5 KiB
C++
/***************************************************/
|
|
/*! \class NRev
|
|
\brief CCRMA's NRev reverberator class.
|
|
|
|
This class takes a monophonic input signal and produces a stereo
|
|
output signal. It is derived from the CLM NRev function, which is
|
|
based on the use of networks of simple allpass and comb delay
|
|
filters. This particular arrangement consists of 6 comb filters
|
|
in parallel, followed by 3 allpass filters, a lowpass filter, and
|
|
another allpass in series, followed by two allpass filters in
|
|
parallel with corresponding right and left outputs.
|
|
|
|
by Perry R. Cook and Gary P. Scavone, 1995--2019.
|
|
*/
|
|
/***************************************************/
|
|
|
|
#include "NRev.h"
|
|
#include <cmath>
|
|
|
|
namespace stk {
|
|
|
|
NRev :: NRev( StkFloat T60 )
|
|
{
|
|
if ( T60 <= 0.0 ) {
|
|
oStream_ << "NRev::NRev: argument (" << T60 << ") must be positive!";
|
|
handleError( StkError::FUNCTION_ARGUMENT );
|
|
}
|
|
|
|
lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output
|
|
|
|
int lengths[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19};
|
|
double scaler = Stk::sampleRate() / 25641.0;
|
|
|
|
int delay, i;
|
|
for ( i=0; i<15; 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<6; i++ ) {
|
|
combDelays_[i].setMaximumDelay( lengths[i] );
|
|
combDelays_[i].setDelay( lengths[i] );
|
|
combCoefficient_[i] = pow(10.0, (-3 * lengths[i] / (T60 * Stk::sampleRate())));
|
|
}
|
|
|
|
for ( i=0; i<8; i++ ) {
|
|
allpassDelays_[i].setMaximumDelay( lengths[i+6] );
|
|
allpassDelays_[i].setDelay( lengths[i+6] );
|
|
}
|
|
|
|
this->setT60( T60 );
|
|
allpassCoefficient_ = 0.7;
|
|
effectMix_ = 0.3;
|
|
this->clear();
|
|
}
|
|
|
|
void NRev :: clear()
|
|
{
|
|
int i;
|
|
for (i=0; i<6; i++) combDelays_[i].clear();
|
|
for (i=0; i<8; i++) allpassDelays_[i].clear();
|
|
lastFrame_[0] = 0.0;
|
|
lastFrame_[1] = 0.0;
|
|
lowpassState_ = 0.0;
|
|
}
|
|
|
|
void NRev :: setT60( StkFloat T60 )
|
|
{
|
|
if ( T60 <= 0.0 ) {
|
|
oStream_ << "NRev::setT60: argument (" << T60 << ") must be positive!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
for ( int i=0; i<6; i++ )
|
|
combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate())));
|
|
}
|
|
|
|
StkFrames& NRev :: tick( StkFrames& frames, unsigned int channel )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( channel >= frames.channels() - 1 ) {
|
|
oStream_ << "NRev::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+1) = lastFrame_[1];
|
|
}
|
|
|
|
return frames;
|
|
}
|
|
|
|
StkFrames& NRev :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) {
|
|
oStream_ << "NRev::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+1) = lastFrame_[1];
|
|
}
|
|
|
|
return iFrames;
|
|
}
|
|
|
|
} // stk namespace
|