mirror of
https://github.com/thestk/stk
synced 2026-01-12 04:21:52 +00:00
245 lines
7.4 KiB
C++
245 lines
7.4 KiB
C++
/***************************************************/
|
|
/*! \class Guitar
|
|
\brief STK guitar model class.
|
|
|
|
This class implements a guitar model with an arbitrary number of
|
|
strings (specified during instantiation). Each string is
|
|
represented by an stk::Twang object. The model supports commuted
|
|
synthesis, as discussed by Smith and Karjalainen. It also includes
|
|
a basic body coupling model and supports feedback.
|
|
|
|
This class does not attempt voice management. Rather, most
|
|
functions support a parameter to specify a particular string
|
|
number and string (voice) management is assumed to occur
|
|
externally. Note that this class does not inherit from
|
|
stk::Instrmnt because of API inconsistencies.
|
|
|
|
This is a digital waveguide model, making its use possibly subject
|
|
to patents held by Stanford University, Yamaha, and others.
|
|
|
|
Control Change Numbers:
|
|
- Bridge Coupling Gain = 2
|
|
- Pluck Position = 4
|
|
- Loop Gain = 11
|
|
- Coupling Filter Pole = 1
|
|
- Pick Filter Pole = 128
|
|
|
|
by Gary P. Scavone, 2012.
|
|
*/
|
|
/***************************************************/
|
|
|
|
#include "Guitar.h"
|
|
#include "FileWvIn.h"
|
|
#include "Noise.h"
|
|
#include "SKINImsg.h"
|
|
#include <cmath>
|
|
|
|
namespace stk {
|
|
|
|
#define BASE_COUPLING_GAIN 0.01
|
|
|
|
Guitar :: Guitar( unsigned int nStrings, std::string bodyfile )
|
|
{
|
|
strings_.resize( nStrings );
|
|
stringState_.resize( nStrings, 0 );
|
|
decayCounter_.resize( nStrings, 0 );
|
|
filePointer_.resize( nStrings, 0 );
|
|
pluckGains_.resize( nStrings, 0 );
|
|
|
|
setBodyFile( bodyfile );
|
|
|
|
couplingGain_ = BASE_COUPLING_GAIN;
|
|
couplingFilter_.setPole( 0.9 );
|
|
pickFilter_.setPole( 0.95 );
|
|
lastFrame_.resize(1, 1, 0.0);
|
|
}
|
|
|
|
void Guitar :: clear( void )
|
|
{
|
|
for ( unsigned int i=0; i<strings_.size(); i++ ) {
|
|
strings_[i].clear();
|
|
stringState_[i] = 0;
|
|
filePointer_[i] = 0;
|
|
}
|
|
}
|
|
|
|
void Guitar :: setBodyFile( std::string bodyfile )
|
|
{
|
|
bool fileLoaded = false;
|
|
if ( bodyfile != "" ) {
|
|
try {
|
|
FileWvIn file( bodyfile );
|
|
|
|
// Fill the StkFrames variable with the (possibly interpolated)
|
|
// file data.
|
|
excitation_.resize( (unsigned long) ( 0.5 + ( file.getSize() * Stk::sampleRate() / file.getFileRate() ) ) );
|
|
file.tick( excitation_ );
|
|
fileLoaded = true;
|
|
}
|
|
catch ( StkError &error ) {
|
|
oStream_ << "Guitar::setBodyFile: file error (" << error.getMessage() << ") ... using noise excitation.";
|
|
handleError( StkError::WARNING );
|
|
}
|
|
}
|
|
|
|
if ( !fileLoaded ) {
|
|
unsigned int M = 200; // arbitrary value
|
|
excitation_.resize( M );
|
|
Noise noise;
|
|
noise.tick( excitation_ );
|
|
// Smooth the start and end of the noise.
|
|
unsigned int N = (unsigned int) M * 0.2; // arbitrary value
|
|
for ( unsigned int n=0; n<N; n++ ) {
|
|
StkFloat weight = 0.5 * ( 1.0 - cos( n * PI / (N-1) ) );
|
|
excitation_[n] *= weight;
|
|
excitation_[M-n-1] *= weight;
|
|
}
|
|
}
|
|
|
|
// Filter the excitation to simulate pick hardness
|
|
pickFilter_.tick( excitation_ );
|
|
|
|
// Compute file mean and remove (to avoid DC bias).
|
|
StkFloat mean = 0.0;
|
|
for ( unsigned int i=0; i<excitation_.frames(); i++ )
|
|
mean += excitation_[i];
|
|
mean /= excitation_.frames();
|
|
|
|
for ( unsigned int i=0; i<excitation_.frames(); i++ )
|
|
excitation_[i] -= mean;
|
|
|
|
// Reset all the file pointers.
|
|
for ( unsigned int i=0; i<strings_.size(); i++ )
|
|
filePointer_[i] = 0;
|
|
}
|
|
|
|
|
|
void Guitar :: setPluckPosition( StkFloat position, int string )
|
|
{
|
|
if ( position < 0.0 || position > 1.0 ) {
|
|
std::cerr << "Guitar::setPluckPosition: position parameter out of range!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( string >= (int) strings_.size() ) {
|
|
oStream_ << "Guitar::setPluckPosition: string parameter is greater than number of strings!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( string < 0 ) // set all strings
|
|
for ( unsigned int i=0; i<strings_.size(); i++ )
|
|
strings_[i].setPluckPosition( position );
|
|
else
|
|
strings_[string].setPluckPosition( position );
|
|
}
|
|
|
|
void Guitar :: setLoopGain( StkFloat gain, int string )
|
|
{
|
|
if ( gain < 0.0 || gain > 1.0 ) {
|
|
std::cerr << "Guitar::setLoopGain: gain parameter out of range!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( string >= (int) strings_.size() ) {
|
|
oStream_ << "Guitar::setLoopGain: string parameter is greater than number of strings!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( string < 0 ) // set all strings
|
|
for ( unsigned int i=0; i<strings_.size(); i++ )
|
|
strings_[i].setLoopGain( gain );
|
|
else
|
|
strings_[string].setLoopGain( gain );
|
|
}
|
|
|
|
void Guitar :: setFrequency( StkFloat frequency, unsigned int string )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( frequency <= 0.0 ) {
|
|
oStream_ << "Guitar::setFrequency: frequency parameter is less than or equal to zero!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( string >= strings_.size() ) {
|
|
oStream_ << "Guitar::setFrequency: string parameter is greater than number of strings!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
#endif
|
|
|
|
strings_[string].setFrequency( frequency );
|
|
}
|
|
|
|
void Guitar :: noteOn( StkFloat frequency, StkFloat amplitude, unsigned int string )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( string >= strings_.size() ) {
|
|
oStream_ << "Guitar::noteOn: string parameter is greater than number of strings!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( Stk::inRange( amplitude, 0.0, 1.0 ) == false ) {
|
|
oStream_ << "Guitar::noteOn: amplitude parameter is outside range 0.0 - 1.0!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
#endif
|
|
|
|
this->setFrequency( frequency, string );
|
|
stringState_[string] = 2;
|
|
filePointer_[string] = 0;
|
|
strings_[string].setLoopGain( 0.995 );
|
|
pluckGains_[string] = amplitude;
|
|
}
|
|
|
|
void Guitar :: noteOff( StkFloat amplitude, unsigned int string )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( string >= strings_.size() ) {
|
|
oStream_ << "Guitar::noteOff: string parameter is greater than number of strings!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( Stk::inRange( amplitude, 0.0, 1.0 ) == false ) {
|
|
oStream_ << "Guitar::noteOff: amplitude parameter is outside range 0.0 - 1.0!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
#endif
|
|
|
|
strings_[string].setLoopGain( (1.0 - amplitude) * 0.9 );
|
|
stringState_[string] = 1;
|
|
}
|
|
|
|
void Guitar :: controlChange( int number, StkFloat value, int string )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( Stk::inRange( value, 0.0, 128.0 ) == false ) {
|
|
oStream_ << "Guitar::controlChange: value (" << value << ") is out of range!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( string > 0 && string >= (int) strings_.size() ) {
|
|
oStream_ << "Guitar::controlChange: string parameter is greater than number of strings!";
|
|
handleError( StkError::WARNING ); return;
|
|
}
|
|
#endif
|
|
|
|
StkFloat normalizedValue = value * ONE_OVER_128;
|
|
if ( number == 2 )
|
|
couplingGain_ = 1.5 * BASE_COUPLING_GAIN * normalizedValue;
|
|
else if ( number == __SK_PickPosition_ ) // 4
|
|
this->setPluckPosition( normalizedValue, string );
|
|
else if ( number == __SK_StringDamping_ ) // 11
|
|
this->setLoopGain( 0.97 + (normalizedValue * 0.03), string );
|
|
else if ( number == __SK_ModWheel_ ) // 1
|
|
couplingFilter_.setPole( 0.98 * normalizedValue );
|
|
else if (number == __SK_AfterTouch_Cont_) // 128
|
|
pickFilter_.setPole( 0.95 * normalizedValue );
|
|
#if defined(_STK_DEBUG_)
|
|
else {
|
|
oStream_ << "Guitar::controlChange: undefined control number (" << number << ")!";
|
|
handleError( StkError::WARNING );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} // stk namespace
|