1 module stk.adsr;
2 
3 import stk.stk;
4 import stk.generator;
5 
6 /***************************************************/
7 /*! \class ADSR
8 \brief STK ADSR envelope class.
9 
10 This class implements a traditional ADSR (Attack, Decay, Sustain,
11 Release) envelope.  It responds to simple keyOn and keyOff
12 messages, keeping track of its state.  The \e state = ADSR::IDLE
13 before being triggered and after the envelope value reaches 0.0 in
14 the ADSR::RELEASE state.  All rate, target and level settings must
15 be non-negative.  All time settings are in seconds and must be
16 positive.
17 
18 by Perry R. Cook and Gary P. Scavone, 1995--2016.
19 */
20 /***************************************************/
21 
22 class ADSR : Generator
23 {
24 public:
25 
26     //! ADSR envelope states.
27     enum : int {
28         ATTACK,   /*!< Attack */
29         DECAY,    /*!< Decay */
30         SUSTAIN,  /*!< Sustain */
31         RELEASE,  /*!< Release */
32         IDLE      /*!< Before attack / after release */
33     }
34 
35     //! Default constructor.
36     this () {
37         target_ = 0.0;
38         value_ = 0.0;
39         attackRate_ = 0.001;
40         decayRate_ = 0.001;
41         releaseRate_ = 0.005;
42         releaseTime_ = -1.0;
43         sustainLevel_ = 0.5;
44         state_ = IDLE;
45         Stk.addSampleRateAlert(this);
46     }
47 
48     //! Class destructor.
49     ~this() {
50         Stk.removeSampleRateAlert(this);
51     }
52 
53     //! Set target = 1, state = \e ADSR::ATTACK.
54     void keyOn() {
55         if ( target_ <= 0.0 ) 
56             target_ = 1.0;
57         state_ = ATTACK;
58     }
59 
60     //! Set target = 0, state = \e ADSR::RELEASE.
61     void keyOff() {
62         target_ = 0.0;
63         state_ = RELEASE;
64 
65         // FIXED October 2010 - Nick Donaldson
66         // Need to make release rate relative to current value!!
67         // Only update if we have set a TIME rather than a RATE,
68         // in which case releaseTime_ will be -1
69         if ( releaseTime_ > 0.0 )
70             releaseRate_ = value_ / ( releaseTime_ * Stk.sampleRate() );
71     }
72 
73     //! Set the attack rate (gain / sample).
74     void setAttackRate( StkFloat rate ) {
75         assert(rate >= 0);
76         attackRate_ = rate;
77     }
78 
79     //! Set the target value for the attack (default = 1.0).
80     void setAttackTarget( StkFloat target ) {
81         assert(target >= 0);
82         target_ = target;
83     }
84 
85     //! Set the decay rate (gain / sample).
86     void setDecayRate( StkFloat rate ) {
87         decayRate_ = rate;
88     }
89 
90     //! Set the sustain level.
91     void setSustainLevel( StkFloat level ) {
92         sustainLevel_ = level;
93     }
94 
95     //! Set the release rate (gain / sample).
96     void setReleaseRate( StkFloat rate ) {
97         releaseRate_ = rate;
98 
99         // Set to negative value so we don't update the release rate on keyOff()
100         releaseTime_ = -1.0;
101     }
102 
103     //! Set the attack rate based on a time duration (seconds).
104     void setAttackTime( StkFloat time ) {
105         attackRate_ = 1.0 / ( time * Stk.sampleRate() );
106     }
107 
108     //! Set the decay rate based on a time duration (seconds).
109     void setDecayTime( StkFloat time ) {
110         decayRate_ = (1.0 - sustainLevel_) / ( time * Stk.sampleRate() );
111     }
112 
113     //! Set the release rate based on a time duration (seconds).
114     void setReleaseTime( StkFloat time ) {
115         releaseRate_ = sustainLevel_ / ( time * Stk.sampleRate() );
116         releaseTime_ = time;
117     }
118 
119     //! Set sustain level and attack, decay, and release time durations (seconds).
120     void setAllTimes( StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime ) {
121         this.setAttackTime( aTime );
122         this.setSustainLevel( sLevel );
123         this.setDecayTime( dTime );
124         this.setReleaseTime( rTime );
125     }
126 
127     //! Set a sustain target value and attack or decay from current value to target.
128     void setTarget( StkFloat target ) {
129         target_ = target;
130         this.setSustainLevel( target_ );
131         if ( value_ < target_ ) state_ = ATTACK;
132         if ( value_ > target_ ) state_ = DECAY;
133     }
134 
135     //! Return the current envelope \e state (ATTACK, DECAY, SUSTAIN, RELEASE, IDLE).
136     int getState() const {
137         return state_;
138     }
139 
140     //! Set to state = ADSR::SUSTAIN with current and target values of \e value.
141     void setValue( StkFloat value ) {
142         state_ = SUSTAIN;
143         target_ = value;
144         value_ = value;
145         this.setSustainLevel( value );
146         lastFrame_[0] = value;
147     }
148 
149     //! Return the last computed output value.
150     StkFloat lastOut() const { return lastFrame_[0]; };
151 
152     //! Compute and return one output sample.
153     StkFloat tick() {
154         switch ( state_ ) {
155 
156             case ATTACK:
157                 value_ += attackRate_;
158                 if ( value_ >= target_ ) {
159                     value_ = target_;
160                     target_ = sustainLevel_;
161                     state_ = DECAY;
162                 }
163                 lastFrame_[0] = value_;
164                 break;
165 
166             case DECAY:
167                 if ( value_ > sustainLevel_ ) {
168                     value_ -= decayRate_;
169                     if ( value_ <= sustainLevel_ ) {
170                         value_ = sustainLevel_;
171                         state_ = SUSTAIN;
172                     }
173                 }
174                 else {
175                     value_ += decayRate_; // attack target < sustain level
176                     if ( value_ >= sustainLevel_ ) {
177                         value_ = sustainLevel_;
178                         state_ = SUSTAIN;
179                     }
180                 }
181                 lastFrame_[0] = value_;
182                 break;
183 
184             case RELEASE:
185                 value_ -= releaseRate_;
186                 if ( value_ <= 0.0 ) {
187                     value_ = 0.0;
188                     state_ = IDLE;
189                 }
190                 lastFrame_[0] = value_;
191                 break;
192             default:
193                 break;
194 
195         }
196 
197         return value_;
198     }
199 
200     //! Fill a channel of the StkFrames object with computed outputs.
201     /*!
202     The \c channel argument must be less than the number of
203     channels in the StkFrames argument (the first channel is specified
204     by 0).  However, range checking is only performed if _STK_DEBUG_
205     is defined during compilation, in which case an out-of-range value
206     will trigger an StkError exception.
207     */
208     ref StkFrames tick( ref StkFrames frames, uint channel = 0 ) {
209         StkFloat *samples = &frames[channel];
210         uint hop = frames.channels();
211         for ( uint i=0; i < frames.frames(); i++, samples += hop )
212             *samples = tick();
213         return frames;
214     }
215 
216 protected:
217 
218     override void sampleRateChanged( StkFloat newRate, StkFloat oldRate ) {
219         if ( !ignoreSampleRateChange_ ) {
220             attackRate_ = oldRate * attackRate_ / newRate;
221             decayRate_ = oldRate * decayRate_ / newRate;
222             releaseRate_ = oldRate * releaseRate_ / newRate;
223         }
224     }
225 
226     int state_;
227     StkFloat value_;
228     StkFloat target_;
229     StkFloat attackRate_;
230     StkFloat decayRate_;
231     StkFloat releaseRate_;
232     StkFloat releaseTime_;
233     StkFloat sustainLevel_;
234 }