Files
stk/src/FreeVerb.cpp
Gary Scavone fc877b87bf Version 4.4.4
2013-09-29 23:22:28 +02:00

228 lines
6.1 KiB
C++

/***********************************************************************/
/*! \class FreeVerb
\brief Jezar at Dreampoint's FreeVerb, implemented in STK.
Freeverb is a free and open-source Schroeder reverberator
originally implemented in C++. The parameters of the reverberation
model are exceptionally well tuned. FreeVerb uses 8
lowpass-feedback-comb-filters in parallel, followed by 4 Schroeder
allpass filters in series. The input signal can be either mono or
stereo, and the output signal is stereo. The delay lengths are
optimized for a sample rate of 44100 Hz.
Ported to STK by Gregory Burlet, 2012.
*/
/***********************************************************************/
#include "FreeVerb.h"
#include <cmath>
#include <iostream>
using namespace stk;
// Set static delay line lengths
const StkFloat FreeVerb::fixedGain = 0.015;
const StkFloat FreeVerb::scaleWet = 3;
const StkFloat FreeVerb::scaleDry = 2;
const StkFloat FreeVerb::scaleDamp = 0.4;
const StkFloat FreeVerb::scaleRoom = 0.28;
const StkFloat FreeVerb::offsetRoom = 0.7;
int FreeVerb::cDelayLengths[] = {1617, 1557, 1491, 1422, 1356, 1277, 1188, 1116};
int FreeVerb::aDelayLengths[] = {225, 556, 441, 341};
FreeVerb::FreeVerb( void )
{
// Resize lastFrame_ for stereo output
lastFrame_.resize( 1, 2, 0.0 );
// Initialize parameters
Effect::setEffectMix( 0.75 ); // set initially to 3/4 wet 1/4 dry signal (different than original freeverb)
roomSizeMem_ = (0.75 * scaleRoom) + offsetRoom; // feedback attenuation in LBFC
dampMem_ = 0.25 * scaleDamp; // pole of lowpass filters in the LBFC
width_ = 1.0;
frozenMode_ = false;
update();
gain_ = fixedGain; // input gain before sending to filters
g_ = 0.5; // allpass coefficient, immutable in FreeVerb
// Scale delay line lengths according to the current sampling rate
double fsScale = Stk::sampleRate() / 44100.0;
if ( fsScale != 1.0 ) {
// scale comb filter delay lines
for ( int i = 0; i < nCombs; i++ ) {
cDelayLengths[i] = (int) floor(fsScale * cDelayLengths[i]);
}
// Scale allpass filter delay lines
for ( int i = 0; i < nAllpasses; i++ ) {
aDelayLengths[i] = (int) floor(fsScale * aDelayLengths[i]);
}
}
// Initialize delay lines for the LBFC filters
for ( int i = 0; i < nCombs; i++ ) {
combDelayL_[i].setMaximumDelay( cDelayLengths[i] );
combDelayL_[i].setDelay( cDelayLengths[i] );
combDelayR_[i].setMaximumDelay( cDelayLengths[i] + stereoSpread );
combDelayR_[i].setDelay( cDelayLengths[i] + stereoSpread );
}
// initialize delay lines for the allpass filters
for (int i = 0; i < nAllpasses; i++) {
allPassDelayL_[i].setMaximumDelay( aDelayLengths[i] );
allPassDelayL_[i].setDelay( aDelayLengths[i] );
allPassDelayR_[i].setMaximumDelay( aDelayLengths[i] + stereoSpread );
allPassDelayR_[i].setDelay( aDelayLengths[i] + stereoSpread );
}
}
FreeVerb::~FreeVerb()
{
}
void FreeVerb::setEffectMix( StkFloat mix )
{
Effect::setEffectMix( mix );
update();
}
void FreeVerb::setRoomSize( StkFloat roomSize )
{
roomSizeMem_ = (roomSize * scaleRoom) + offsetRoom;
update();
}
StkFloat FreeVerb::getRoomSize()
{
return (roomSizeMem_ - offsetRoom) / scaleRoom;
}
void FreeVerb::setDamping( StkFloat damping )
{
dampMem_ = damping * scaleDamp;
update();
}
StkFloat FreeVerb::getDamping()
{
return dampMem_ / scaleDamp;
}
void FreeVerb::setWidth( StkFloat width )
{
width_ = width;
update();
}
StkFloat FreeVerb::getWidth()
{
return width_;
}
void FreeVerb::setMode( bool isFrozen )
{
frozenMode_ = isFrozen;
update();
}
StkFloat FreeVerb::getMode()
{
return frozenMode_;
}
void FreeVerb::update()
{
StkFloat wet = scaleWet * effectMix_;
dry_ = scaleDry * (1.0-effectMix_);
// Use the L1 norm so the output gain will sum to one while still
// preserving the ratio of scalings in original FreeVerb
wet /= (wet + dry_);
dry_ /= (wet + dry_);
wet1_ = wet * (width_/2.0 + 0.5);
wet2_ = wet * (1.0 - width_)/2.0;
if ( frozenMode_ ) {
// put into freeze mode
roomSize_ = 1.0;
damp_ = 0.0;
gain_ = 0.0;
}
else {
roomSize_ = roomSizeMem_;
damp_ = dampMem_;
gain_ = fixedGain;
}
for ( int i=0; i<nCombs; i++ ) {
// set low pass filter for delay output
combLPL_[i].setCoefficients(1.0 - damp_, -damp_);
combLPR_[i].setCoefficients(1.0 - damp_, -damp_);
}
}
void FreeVerb::clear()
{
// Clear LBFC delay lines
for ( int i = 0; i < nCombs; i++ ) {
combDelayL_[i].clear();
combDelayR_[i].clear();
}
// Clear allpass delay lines
for ( int i = 0; i < nAllpasses; i++ ) {
allPassDelayL_[i].clear();
allPassDelayR_[i].clear();
}
lastFrame_[0] = 0.0;
lastFrame_[1] = 0.0;
}
StkFrames& FreeVerb::tick( StkFrames& frames, unsigned int channel )
{
#if defined(_STK_DEBUG_)
if ( channel >= frames.channels() - 1 ) {
oStream_ << "FreeVerb::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) );
*(samples+1) = lastFrame_[1];
}
return frames;
}
StkFrames& FreeVerb::tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel, unsigned int oChannel )
{
#if defined(_STK_DEBUG_)
if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) {
oStream_ << "FreeVerb::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();
unsigned int oHop = oFrames.channels();
bool stereoInput = ( iFrames.channels() > iChannel+1 ) ? true : false;
for ( unsigned int i=0; i<iFrames.frames(); i++, iSamples += iHop, oSamples += oHop) {
if ( stereoInput )
*oSamples = tick( *iSamples, *(iSamples+1) );
else
*oSamples = tick( *iSamples );
*(oSamples+1) = lastFrame_[1];
}
return oFrames;
}