|
- #ifndef STK_GUITAR_H
- #define STK_GUITAR_H
-
- #include "Stk.h"
- #include "Twang.h"
- #include "OnePole.h"
- #include "OneZero.h"
-
- namespace stk {
-
- /***************************************************/
- /*! \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.
- */
- /***************************************************/
-
- class Guitar : public Stk
- {
- public:
- //! Class constructor, specifying an arbitrary number of strings (default = 6).
- Guitar( unsigned int nStrings = 6, std::string bodyfile = "" );
-
- //! Reset and clear all internal state.
- void clear( void );
-
- //! Set the string excitation, using either a soundfile or computed noise.
- /*!
- If no argument is provided, the std::string is empty, or an error
- occurs reading the file data, an enveloped noise signal will be
- generated for use as the pluck excitation.
- */
- void setBodyFile( std::string bodyfile = "" );
-
- //! Set the pluck position for one or all strings.
- /*!
- If the \c string argument is < 0, the pluck position is set
- for all strings.
- */
- void setPluckPosition( StkFloat position, int string = -1 );
-
- //! Set the loop gain for one or all strings.
- /*!
- If the \c string argument is < 0, the loop gain is set for all
- strings.
- */
- void setLoopGain( StkFloat gain, int string = -1 );
-
- //! Set instrument parameters for a particular frequency.
- void setFrequency( StkFloat frequency, unsigned int string = 0 );
-
- //! Start a note with the given frequency and amplitude.
- /*!
- If the \c amplitude parameter is less than 0.2, the string will
- be undamped but it will not be "plucked."
- */
- void noteOn( StkFloat frequency, StkFloat amplitude, unsigned int string = 0 );
-
- //! Stop a note with the given amplitude (speed of decay).
- void noteOff( StkFloat amplitude, unsigned int string = 0 );
-
- //! Perform the control change specified by \e number and \e value (0.0 - 128.0).
- /*!
- If the \c string argument is < 0, then the control change is
- applied to all strings (if appropriate).
- */
- void controlChange( int number, StkFloat value, int string = -1 );
-
- //! Return the last computed output value.
- StkFloat lastOut( void ) { return lastFrame_[0]; };
-
- //! Take an optional input sample and compute one output sample.
- StkFloat tick( StkFloat input = 0.0 );
-
- //! Take a channel of the \c iFrames object as inputs to the class and write outputs to the \c oFrames object.
- /*!
- The \c iFrames object reference is returned. Each channel
- argument must be less than the number of channels in the
- corresponding 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 a channel of the \c iFrames object as inputs to the effect and write outputs to the \c oFrames object.
- /*!
- The \c iFrames object reference is returned. Each channel
- argument must be less than the number of channels in the
- corresponding 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& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 );
-
- protected:
-
- std::vector< stk::Twang > strings_;
- std::vector< int > stringState_; // 0 = off, 1 = decaying, 2 = on
- std::vector< unsigned int > decayCounter_;
- std::vector< unsigned int > filePointer_;
- std::vector< StkFloat > pluckGains_;
-
- OnePole pickFilter_;
- OnePole couplingFilter_;
- StkFloat couplingGain_;
- StkFrames excitation_;
- StkFrames lastFrame_;
- };
-
- inline StkFloat Guitar :: tick( StkFloat input )
- {
- StkFloat temp, output = 0.0;
- lastFrame_[0] /= strings_.size(); // evenly spread coupling across strings
- for ( unsigned int i=0; i<strings_.size(); i++ ) {
- if ( stringState_[i] ) {
- temp = input;
- // If pluckGain < 0.2, let string ring but don't pluck it.
- if ( filePointer_[i] < excitation_.frames() && pluckGains_[i] > 0.2 )
- temp += pluckGains_[i] * excitation_[filePointer_[i]++];
- temp += couplingGain_ * couplingFilter_.tick( lastFrame_[0] ); // bridge coupling
- output += strings_[i].tick( temp );
- // Check if string energy has decayed sufficiently to turn it off.
- if ( stringState_[i] == 1 ) {
- if ( fabs( strings_[i].lastOut() ) < 0.001 ) decayCounter_[i]++;
- else decayCounter_[i] = 0;
- if ( decayCounter_[i] > (unsigned int) floor( 0.1 * Stk::sampleRate() ) ) {
- stringState_[i] = 0;
- decayCounter_[i] = 0;
- }
- }
- }
- }
-
- return lastFrame_[0] = output;
- }
-
- inline StkFrames& Guitar :: tick( StkFrames& frames, unsigned int channel )
- {
- #if defined(_STK_DEBUG_)
- if ( channel >= frames.channels() ) {
- oStream_ << "Guitar::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<frames.frames(); i++, samples += hop )
- *samples = tick( *samples );
-
- return frames;
- }
-
- inline StkFrames& Guitar :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel )
- {
- #if defined(_STK_DEBUG_)
- if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) {
- oStream_ << "Guitar::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(), oHop = oFrames.channels();
- for ( unsigned int i=0; i<iFrames.frames(); i++, iSamples += iHop, oSamples += oHop )
- *oSamples = tick( *iSamples );
-
- return iFrames;
- }
-
- } // stk namespace
-
- #endif
|