/***********************************************************************/ /*! \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 #include 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= 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= 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