/***************************************************/ /*! \class StifKarp \brief STK plucked stiff string instrument. This class implements a simple plucked string algorithm (Karplus Strong) with enhancements (Jaffe-Smith, Smith, and others), including string stiffness and pluck position controls. The stiffness is modeled with allpass filters. This is a digital waveguide model, making its use possibly subject to patents held by Stanford University, Yamaha, and others. Control Change Numbers: - Pickup Position = 4 - String Sustain = 11 - String Stretch = 1 by Perry R. Cook and Gary P. Scavone, 1995--2017. */ /***************************************************/ #include "StifKarp.h" #include "SKINImsg.h" #include namespace stk { StifKarp :: StifKarp( StkFloat lowestFrequency ) { if ( lowestFrequency <= 0.0 ) { oStream_ << "StifKarp::StifKarp: argument is less than or equal to zero!"; handleError( StkError::FUNCTION_ARGUMENT ); } unsigned long nDelays = (unsigned long) ( Stk::sampleRate() / lowestFrequency ); delayLine_.setMaximumDelay( nDelays + 1 ); combDelay_.setMaximumDelay( nDelays + 1 ); pluckAmplitude_ = 0.3; pickupPosition_ = 0.4; stretching_ = 0.9999; baseLoopGain_ = 0.995; loopGain_ = 0.999; this->clear(); this->setFrequency( 220.0 ); } StifKarp :: ~StifKarp( void ) { } void StifKarp :: clear( void ) { delayLine_.clear(); combDelay_.clear(); filter_.clear(); } void StifKarp :: setFrequency( StkFloat frequency ) { #if defined(_STK_DEBUG_) if ( frequency <= 0.0 ) { oStream_ << "StifKarp::setFrequency: parameter is less than or equal to zero!"; handleError( StkError::WARNING ); return; } #endif lastFrequency_ = frequency; lastLength_ = Stk::sampleRate() / lastFrequency_; StkFloat delay = lastLength_ - 0.5; delayLine_.setDelay( delay ); loopGain_ = baseLoopGain_ + (frequency * 0.000005); if (loopGain_ >= 1.0) loopGain_ = 0.99999; setStretch(stretching_); combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); } void StifKarp :: setStretch( StkFloat stretch ) { stretching_ = stretch; StkFloat coefficient; StkFloat freq = lastFrequency_ * 2.0; StkFloat dFreq = ( (0.5 * Stk::sampleRate()) - freq ) * 0.25; StkFloat temp = 0.5 + (stretch * 0.5); if ( temp > 0.9999 ) temp = 0.9999; for ( int i=0; i<4; i++ ) { coefficient = temp * temp; biquad_[i].setA2( coefficient ); biquad_[i].setB0( coefficient ); biquad_[i].setB2( 1.0 ); coefficient = -2.0 * temp * cos(TWO_PI * freq / Stk::sampleRate()); biquad_[i].setA1( coefficient ); biquad_[i].setB1( coefficient ); freq += dFreq; } } void StifKarp :: setPickupPosition( StkFloat position ) { if ( position < 0.0 || position > 1.0 ) { oStream_ << "StifKarp::setPickupPosition: parameter is out of range!"; handleError( StkError::WARNING ); return; } // Set the pick position, which puts zeroes at position * length. pickupPosition_ = position; combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); } void StifKarp :: setBaseLoopGain( StkFloat aGain ) { baseLoopGain_ = aGain; loopGain_ = baseLoopGain_ + (lastFrequency_ * 0.000005); if ( loopGain_ > 0.99999 ) loopGain_ = (StkFloat) 0.99999; } void StifKarp :: pluck( StkFloat amplitude ) { if ( amplitude < 0.0 || amplitude > 1.0 ) { oStream_ << "StifKarp::pluck: amplitude is out of range!"; handleError( StkError::WARNING ); return; } pluckAmplitude_ = amplitude; for ( unsigned long i=0; itick() * pluckAmplitude_) ); } } void StifKarp :: noteOn( StkFloat frequency, StkFloat amplitude ) { this->setFrequency( frequency ); this->pluck( amplitude ); } void StifKarp :: noteOff( StkFloat amplitude ) { if ( amplitude < 0.0 || amplitude > 1.0 ) { oStream_ << "StifKarp::noteOff: amplitude is out of range!"; handleError( StkError::WARNING ); return; } loopGain_ = (1.0 - amplitude) * 0.5; } void StifKarp :: controlChange( int number, StkFloat value ) { #if defined(_STK_DEBUG_) if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { oStream_ << "Clarinet::controlChange: value (" << value << ") is out of range!"; handleError( StkError::WARNING ); return; } #endif StkFloat normalizedValue = value * ONE_OVER_128; if (number == __SK_PickPosition_) // 4 this->setPickupPosition( normalizedValue ); else if (number == __SK_StringDamping_) // 11 this->setBaseLoopGain( 0.97 + (normalizedValue * 0.03) ); else if (number == __SK_StringDetune_) // 1 this->setStretch( 0.9 + (0.1 * (1.0 - normalizedValue)) ); #if defined(_STK_DEBUG_) else { oStream_ << "StifKarp::controlChange: undefined control number (" << number << ")!"; handleError( StkError::WARNING ); } #endif } } // stk namespace