mirror of
https://github.com/thestk/stk
synced 2026-01-12 04:21:52 +00:00
228 lines
6.1 KiB
C++
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;
|
|
}
|