f@0: #pragma once f@0: f@0: namespace collidoscope { f@0: f@2: f@2: /* f@2: * An ASR envelope with linear shape. It is modeled after the STK envelope classes. f@2: * The tick() method advances the computation of the envelope one sample and returns the computed sample f@2: * The class is templated for the type of the samples that each tick of the envelope produces. f@2: * f@2: * Client classes can set/get the current state of the envelope with the f@2: * respective getter/setter methods f@2: * f@2: */ f@0: template f@0: class EnvASR f@0: { f@0: public: f@0: f@0: enum class State { f@0: eAttack, f@0: eSustain, f@0: eRelease, f@0: eIdle // before attack after release f@0: }; f@0: f@0: EnvASR( T sustainLevel, T attackTime, T releaseTime, std::size_t sampleRate ) : f@0: mSustainLevel( sustainLevel ), f@0: mState( State::eIdle ), f@0: mValue( 0 ) f@0: f@0: { f@0: if ( attackTime <= 0 ) f@0: attackTime = T( 0.001 ); f@0: f@0: if ( releaseTime <= 0 ) f@0: releaseTime = T( 0.001 ); f@0: f@0: mAttackRate = T( 1.0 ) / (attackTime * sampleRate); f@0: mReleaseRate = T( 1.0 ) / (releaseTime * sampleRate); f@0: } f@0: f@0: T tick() f@0: { f@0: f@0: switch ( mState ) f@0: { f@0: f@0: case State::eIdle: { f@0: mValue = 0; f@0: }; f@0: break; f@0: f@0: case State::eAttack: { f@0: mValue += mAttackRate; f@0: if ( mValue >= mSustainLevel ){ f@0: mValue = mSustainLevel; f@0: mState = State::eSustain; f@0: } f@0: }; f@0: break; f@0: f@0: case State::eRelease: f@0: mValue -= mReleaseRate; f@0: if ( mValue <= 0 ){ f@0: mValue = 0; f@0: mState = State::eIdle; f@0: } f@0: break; f@0: default: f@0: break; f@0: } f@0: f@0: return mValue; f@0: f@0: } f@0: f@0: State getState() const f@0: { f@0: return mState; f@0: } f@0: f@0: void setState( State state ) f@0: { f@0: mState = state; f@0: } f@0: f@0: private: f@0: T mSustainLevel; f@0: T mAttackRate; f@0: T mReleaseRate; f@0: f@0: // output f@0: T mValue; f@0: f@0: State mState; f@0: f@0: }; f@0: f@0: f@2: }