mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-18 11:40:33 +02:00
255 lines
8.2 KiB
C++
255 lines
8.2 KiB
C++
#ifndef STK_FREEVERB_H
|
|
#define STK_FREEVERB_H
|
|
|
|
#include "Effect.h"
|
|
#include "Delay.h"
|
|
#include "OnePole.h"
|
|
|
|
namespace stk {
|
|
|
|
/***********************************************************************/
|
|
/*! \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.
|
|
*/
|
|
/***********************************************************************/
|
|
|
|
class FreeVerb : public Effect
|
|
{
|
|
public:
|
|
//! FreeVerb Constructor
|
|
/*!
|
|
Initializes the effect with default parameters. Note that these defaults
|
|
are slightly different than those in the original implementation of
|
|
FreeVerb [Effect Mix: 0.75; Room Size: 0.75; Damping: 0.25; Width: 1.0;
|
|
Mode: freeze mode off].
|
|
*/
|
|
FreeVerb();
|
|
|
|
//! Destructor
|
|
~FreeVerb();
|
|
|
|
//! Set the effect mix [0 = mostly dry, 1 = mostly wet].
|
|
void setEffectMix( StkFloat mix );
|
|
|
|
//! Set the room size (comb filter feedback gain) parameter [0,1].
|
|
void setRoomSize( StkFloat value );
|
|
|
|
//! Get the room size (comb filter feedback gain) parameter.
|
|
StkFloat getRoomSize( void );
|
|
|
|
//! Set the damping parameter [0=low damping, 1=higher damping].
|
|
void setDamping( StkFloat value );
|
|
|
|
//! Get the damping parameter.
|
|
StkFloat getDamping( void );
|
|
|
|
//! Set the width (left-right mixing) parameter [0,1].
|
|
void setWidth( StkFloat value );
|
|
|
|
//! Get the width (left-right mixing) parameter.
|
|
StkFloat getWidth( void );
|
|
|
|
//! Set the mode [frozen = 1, unfrozen = 0].
|
|
void setMode( bool isFrozen );
|
|
|
|
//! Get the current freeze mode [frozen = 1, unfrozen = 0].
|
|
StkFloat getMode( void );
|
|
|
|
//! Clears delay lines, etc.
|
|
void clear( void );
|
|
|
|
//! Return the specified channel value of the last computed stereo frame.
|
|
/*!
|
|
Use the lastFrame() function to get both values of the last
|
|
computed stereo frame. The \c channel argument must be 0 or 1
|
|
(the first channel is specified by 0). However, range checking is
|
|
only performed if _STK_DEBUG_ is defined during compilation, in
|
|
which case an out-of-range value will trigger an StkError
|
|
exception.
|
|
*/
|
|
StkFloat lastOut( unsigned int channel = 0 );
|
|
|
|
//! Input one or two samples to the effect and return the specified \c channel value of the computed stereo frame.
|
|
/*!
|
|
Use the lastFrame() function to get both values of the computed
|
|
stereo output frame. The \c channel argument must be 0 or 1 (the
|
|
first channel is specified by 0). However, range checking is only
|
|
performed if _STK_DEBUG_ is defined during compilation, in which
|
|
case an out-of-range value will trigger an StkError exception.
|
|
*/
|
|
StkFloat tick( StkFloat inputL, StkFloat inputR = 0.0, unsigned int channel = 0 );
|
|
|
|
//! Take two channels of the StkFrames object as inputs to the effect and replace with stereo outputs.
|
|
/*!
|
|
The StkFrames argument reference is returned. The stereo
|
|
inputs are taken from (and written back to) the StkFrames argument
|
|
starting at the specified \c channel. Therefore, the \c channel
|
|
argument must be less than ( channels() - 1 ) of the StkFrames
|
|
argument (the first channel is specified by 0). However, range
|
|
checking is only performed if _STK_DEBUG_ is defined during
|
|
compilation, in which case an out-of-range value will trigger an
|
|
StkError exception.
|
|
*/
|
|
StkFrames& tick( StkFrames& frames, unsigned int channel = 0 );
|
|
|
|
//! Take one or two channels of the \c iFrames object as inputs to the effect and write stereo outputs to the \c oFrames object.
|
|
/*!
|
|
The \c iFrames object reference is returned. The \c iChannel
|
|
argument must be less than the number of channels in the \c
|
|
iFrames argument (the first channel is specified by 0). If more
|
|
than one channel of data exists in \c iFrames starting from \c
|
|
iChannel, stereo data is input to the effect. The \c oChannel
|
|
argument must be less than ( channels() - 1 ) of the \c oFrames
|
|
argument. However, range checking is only performed if
|
|
_STK_DEBUG_ is defined during compilation, in which case an
|
|
out-of-range value will trigger an StkError exception.
|
|
*/
|
|
StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 );
|
|
|
|
protected:
|
|
//! Update interdependent parameters.
|
|
void update( void );
|
|
|
|
// Clamp very small floats to zero, version from
|
|
// http://music.columbia.edu/pipermail/linux-audio-user/2004-July/013489.html .
|
|
// However, this is for 32-bit floats only.
|
|
//static inline StkFloat undenormalize( volatile StkFloat s ) {
|
|
// s += 9.8607615E-32f;
|
|
// return s - 9.8607615E-32f;
|
|
//}
|
|
|
|
static const int nCombs = 8;
|
|
static const int nAllpasses = 4;
|
|
static const int stereoSpread = 23;
|
|
static const StkFloat fixedGain;
|
|
static const StkFloat scaleWet;
|
|
static const StkFloat scaleDry;
|
|
static const StkFloat scaleDamp;
|
|
static const StkFloat scaleRoom;
|
|
static const StkFloat offsetRoom;
|
|
|
|
// Delay line lengths for 44100Hz sampling rate.
|
|
static int cDelayLengths[nCombs];
|
|
static int aDelayLengths[nAllpasses];
|
|
|
|
StkFloat g_; // allpass coefficient
|
|
StkFloat gain_;
|
|
StkFloat roomSizeMem_, roomSize_;
|
|
StkFloat dampMem_, damp_;
|
|
StkFloat wet1_, wet2_;
|
|
StkFloat dry_;
|
|
StkFloat width_;
|
|
bool frozenMode_;
|
|
|
|
// LBFC: Lowpass Feedback Comb Filters
|
|
Delay combDelayL_[nCombs];
|
|
Delay combDelayR_[nCombs];
|
|
OnePole combLPL_[nCombs];
|
|
OnePole combLPR_[nCombs];
|
|
|
|
// AP: Allpass Filters
|
|
Delay allPassDelayL_[nAllpasses];
|
|
Delay allPassDelayR_[nAllpasses];
|
|
};
|
|
|
|
inline StkFloat FreeVerb :: lastOut( unsigned int channel )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( channel > 1 ) {
|
|
oStream_ << "FreeVerb::lastOut(): channel argument must be less than 2!";
|
|
handleError( StkError::FUNCTION_ARGUMENT );
|
|
}
|
|
#endif
|
|
|
|
return lastFrame_[channel];
|
|
}
|
|
|
|
inline StkFloat FreeVerb::tick( StkFloat inputL, StkFloat inputR, unsigned int channel )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( channel > 1 ) {
|
|
oStream_ << "FreeVerb::tick(): channel argument must be less than 2!";
|
|
handleError(StkError::FUNCTION_ARGUMENT);
|
|
}
|
|
#endif
|
|
|
|
if ( !inputR ) {
|
|
inputR = inputL;
|
|
}
|
|
|
|
StkFloat fInput = (inputL + inputR) * gain_;
|
|
StkFloat outL = 0.0;
|
|
StkFloat outR = 0.0;
|
|
|
|
// Parallel LBCF filters
|
|
for ( int i = 0; i < nCombs; i++ ) {
|
|
// Left channel
|
|
//StkFloat yn = fInput + (roomSize_ * FreeVerb::undenormalize(combLPL_[i].tick(FreeVerb::undenormalize(combDelayL_[i].nextOut()))));
|
|
StkFloat yn = fInput + (roomSize_ * combLPL_[i].tick( combDelayL_[i].nextOut() ) );
|
|
combDelayL_[i].tick(yn);
|
|
outL += yn;
|
|
|
|
// Right channel
|
|
//yn = fInput + (roomSize_ * FreeVerb::undenormalize(combLPR_[i].tick(FreeVerb::undenormalize(combDelayR_[i].nextOut()))));
|
|
yn = fInput + (roomSize_ * combLPR_[i].tick( combDelayR_[i].nextOut() ) );
|
|
combDelayR_[i].tick(yn);
|
|
outR += yn;
|
|
}
|
|
|
|
// Series allpass filters
|
|
for ( int i = 0; i < nAllpasses; i++ ) {
|
|
// Left channel
|
|
//StkFloat vn_m = FreeVerb::undenormalize(allPassDelayL_[i].nextOut());
|
|
StkFloat vn_m = allPassDelayL_[i].nextOut();
|
|
StkFloat vn = outL + (g_ * vn_m);
|
|
allPassDelayL_[i].tick(vn);
|
|
|
|
// calculate output
|
|
outL = -vn + (1.0 + g_)*vn_m;
|
|
|
|
// Right channel
|
|
//vn_m = FreeVerb::undenormalize(allPassDelayR_[i].nextOut());
|
|
vn_m = allPassDelayR_[i].nextOut();
|
|
vn = outR + (g_ * vn_m);
|
|
allPassDelayR_[i].tick(vn);
|
|
|
|
// calculate output
|
|
outR = -vn + (1.0 + g_)*vn_m;
|
|
}
|
|
|
|
// Mix output
|
|
lastFrame_[0] = outL*wet1_ + outR*wet2_ + inputL*dry_;
|
|
lastFrame_[1] = outR*wet1_ + outL*wet2_ + inputR*dry_;
|
|
|
|
/*
|
|
// Hard limiter ... there's not much else we can do at this point
|
|
if ( lastFrame_[0] >= 1.0 ) {
|
|
lastFrame_[0] = 0.9999;
|
|
}
|
|
if ( lastFrame_[0] <= -1.0 ) {
|
|
lastFrame_[0] = -0.9999;
|
|
}
|
|
if ( lastFrame_[1] >= 1.0 ) {
|
|
lastFrame_[1] = 0.9999;
|
|
}
|
|
if ( lastFrame_[1] <= -1.0 ) {
|
|
lastFrame_[1] = -0.9999;
|
|
}
|
|
*/
|
|
|
|
return lastFrame_[channel];
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|