mirror of
https://github.com/thestk/stk
synced 2026-01-15 22:11:52 +00:00
182 lines
5.0 KiB
C++
182 lines
5.0 KiB
C++
/***************************************************/
|
|
/*! \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--2014.
|
|
*/
|
|
/***************************************************/
|
|
|
|
#include "StifKarp.h"
|
|
#include "SKINI.msg"
|
|
#include <cmath>
|
|
|
|
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; i<length_; i++ ) {
|
|
// Fill delay with noise additively with current contents.
|
|
delayLine_.tick( (delayLine_.lastOut() * 0.6) + 0.4 * noise_.tick() * pluckAmplitude_ );
|
|
//delayLine_.tick( combDelay_.tick((delayLine_.lastOut() * 0.6) + 0.4 * noise->tick() * 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
|