f@0: #ifndef STK_ADSR_H f@0: #define STK_ADSR_H f@0: f@0: #include "Generator.h" f@0: f@0: namespace stk { f@0: f@0: /***************************************************/ f@0: /*! \class ADSR f@0: \brief STK ADSR envelope class. f@0: f@0: This class implements a traditional ADSR (Attack, Decay, Sustain, f@0: Release) envelope. It responds to simple keyOn and keyOff f@0: messages, keeping track of its state. The \e state = ADSR::IDLE f@0: before being triggered and after the envelope value reaches 0.0 in f@0: the ADSR::RELEASE state. All rate, target and level settings must f@0: be non-negative. All time settings are in seconds and must be f@0: positive. f@0: f@0: by Perry R. Cook and Gary P. Scavone, 1995--2014. f@0: */ f@0: /***************************************************/ f@0: f@0: class ADSR : public Generator f@0: { f@0: public: f@0: f@0: //! ADSR envelope states. f@0: enum { f@0: ATTACK, /*!< Attack */ f@0: DECAY, /*!< Decay */ f@0: SUSTAIN, /*!< Sustain */ f@0: RELEASE, /*!< Release */ f@0: IDLE /*!< Before attack / after release */ f@0: }; f@0: f@0: //! Default constructor. f@0: ADSR( void ); f@0: f@0: //! Class destructor. f@0: ~ADSR( void ); f@0: f@0: //! Set target = 1, state = \e ADSR::ATTACK. f@0: void keyOn( void ); f@0: f@0: //! Set target = 0, state = \e ADSR::RELEASE. f@0: void keyOff( void ); f@0: f@0: //! Set the attack rate (gain / sample). f@0: void setAttackRate( StkFloat rate ); f@0: f@0: //! Set the target value for the attack (default = 1.0). f@0: void setAttackTarget( StkFloat target ); f@0: f@0: //! Set the decay rate (gain / sample). f@0: void setDecayRate( StkFloat rate ); f@0: f@0: //! Set the sustain level. f@0: void setSustainLevel( StkFloat level ); f@0: f@0: //! Set the release rate (gain / sample). f@0: void setReleaseRate( StkFloat rate ); f@0: f@0: //! Set the attack rate based on a time duration (seconds). f@0: void setAttackTime( StkFloat time ); f@0: f@0: //! Set the decay rate based on a time duration (seconds). f@0: void setDecayTime( StkFloat time ); f@0: f@0: //! Set the release rate based on a time duration (seconds). f@0: void setReleaseTime( StkFloat time ); f@0: f@0: //! Set sustain level and attack, decay, and release time durations (seconds). f@0: void setAllTimes( StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime ); f@0: f@0: //! Set a sustain target value and attack or decay from current value to target. f@0: void setTarget( StkFloat target ); f@0: f@0: //! Return the current envelope \e state (ATTACK, DECAY, SUSTAIN, RELEASE, IDLE). f@0: int getState( void ) const { return state_; }; f@0: f@0: //! Set to state = ADSR::SUSTAIN with current and target values of \e value. f@0: void setValue( StkFloat value ); f@0: f@0: //! Return the last computed output value. f@0: StkFloat lastOut( void ) const { return lastFrame_[0]; }; f@0: f@0: //! Compute and return one output sample. f@0: StkFloat tick( void ); f@0: f@0: //! Fill a channel of the StkFrames object with computed outputs. f@0: /*! f@0: The \c channel argument must be less than the number of f@0: channels in the StkFrames argument (the first channel is specified f@0: by 0). However, range checking is only performed if _STK_DEBUG_ f@0: is defined during compilation, in which case an out-of-range value f@0: will trigger an StkError exception. f@0: */ f@0: StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); f@0: f@0: protected: f@0: f@0: void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); f@0: f@0: int state_; f@0: StkFloat value_; f@0: StkFloat target_; f@0: StkFloat attackRate_; f@0: StkFloat decayRate_; f@0: StkFloat releaseRate_; f@0: StkFloat releaseTime_; f@0: StkFloat sustainLevel_; f@0: }; f@0: f@0: inline StkFloat ADSR :: tick( void ) f@0: { f@0: switch ( state_ ) { f@0: f@0: case ATTACK: f@0: value_ += attackRate_; f@0: if ( value_ >= target_ ) { f@0: value_ = target_; f@0: target_ = sustainLevel_; f@0: state_ = DECAY; f@0: } f@0: lastFrame_[0] = value_; f@0: break; f@0: f@0: case DECAY: f@0: if ( value_ > sustainLevel_ ) { f@0: value_ -= decayRate_; f@0: if ( value_ <= sustainLevel_ ) { f@0: value_ = sustainLevel_; f@0: state_ = SUSTAIN; f@0: } f@0: } f@0: else { f@0: value_ += decayRate_; // attack target < sustain level f@0: if ( value_ >= sustainLevel_ ) { f@0: value_ = sustainLevel_; f@0: state_ = SUSTAIN; f@0: } f@0: } f@0: lastFrame_[0] = value_; f@0: break; f@0: f@0: case RELEASE: f@0: value_ -= releaseRate_; f@0: if ( value_ <= 0.0 ) { f@0: value_ = 0.0; f@0: state_ = IDLE; f@0: } f@0: lastFrame_[0] = value_; f@0: f@0: } f@0: f@0: return value_; f@0: } f@0: f@0: inline StkFrames& ADSR :: tick( StkFrames& frames, unsigned int channel ) f@0: { f@0: #if defined(_STK_DEBUG_) f@0: if ( channel >= frames.channels() ) { f@0: oStream_ << "ADSR::tick(): channel and StkFrames arguments are incompatible!"; f@0: handleError( StkError::FUNCTION_ARGUMENT ); f@0: } f@0: #endif f@0: f@0: StkFloat *samples = &frames[channel]; f@0: unsigned int hop = frames.channels(); f@0: for ( unsigned int i=0; i