1 /* Calf DSP Library
2  * ADSR envelope class (and other envelopes in future)
3  *
4  * Copyright (C) 2007-2008 Krzysztof Foltman
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  */
21 #ifndef __CALF_ENVELOPE_H
22 #define __CALF_ENVELOPE_H
23 
24 #include "primitives.h"
25 
26 namespace dsp {
27 
28 /// Rate-based ADSFR envelope class. Note that if release rate is slower than decay
29 /// rate, this envelope won't use release rate until output level falls below sustain level
30 /// it's different to what certain hardware synth companies did, but it prevents the very
31 /// un-musical (IMHO) behaviour known from (for example) SoundFont 2.
32 class adsr
33 {
34 public:
35     enum env_state {
36         STOP, ///< envelope is stopped
37         ATTACK, ///< attack - rise from 0 to 1
38         DECAY, ///< decay - fall from 1 to sustain level
39         SUSTAIN, ///< sustain - remain at sustain level (unless sustain is 0 - then it gets stopped); with fade != 0 it goes towards 0% (positive fade) or 100% (negative fade)
40         RELEASE, ///< release - fall from sustain (or pre-sustain) level to 0
41         LOCKDECAY,  ///< locked decay
42     };
43 
44     /// Current envelope stage
45     env_state state;
46     /// @note these are *rates*, not times
47     double attack, decay, sustain, release, fade;
48     /// Requested release time (not the rate!) in frames, used for recalculating the rate if sustain is changed
49     double release_time;
50     /// Current envelope (output) level
51     double value;
52     /// Release rate used for the current note (calculated from this note's sustain level, and not the current sustain level,
53     /// which may have changed after note has been released)
54     double thisrelease;
55     /// Sustain level used for the current note (used to calculate release rate if sustain changed during release stage
56     /// of the current note)
57     double thiss;
58     /// Value from the time before advance() was called last time
59     double old_value;
60 
adsr()61     adsr()
62     {
63         attack = decay = sustain = release = thisrelease = thiss = 0.f;
64         reset();
65     }
66     /// Stop (reset) the envelope
reset()67     inline void reset()
68     {
69         old_value = value = 0.0;
70         thiss = 0.0;
71         state = STOP;
72     }
73     /// Set the envelope parameters (updates rate member variables based on values passed)
74     /// @param a attack time
75     /// @param d decay time
76     /// @param s sustain level
77     /// @param r release time
78     /// @param er Envelope (update) rate
79     /// @param f fade time (if applicable)
80     inline void set(float a, float d, float s, float r, float er, float f = 0.f)
81     {
82         attack = 1.0 / (a * er);
83         decay = (1 - s) / (d * er);
84         sustain = s;
85         release_time = r * er;
86         release = s / release_time;
87         if (fabs(f) > small_value<float>())
88             fade = 1.0 / (f * er);
89         else
90             fade = 0.0;
91         // in release:
92         // lock thiss setting (start of release for current note) and unlock thisrelease setting (current note's release rate)
93         if (state != RELEASE)
94             thiss = s;
95         else
96             thisrelease = thiss / release_time;
97     }
98     /// @retval true if envelope is in released state (forced decay, release or stopped)
released()99     inline bool released() const
100     {
101         return state == LOCKDECAY || state == RELEASE || state == STOP;
102     }
103     /// @retval true if envelope is stopped (has not been started or has run till its end)
stopped()104     inline bool stopped() const
105     {
106         return state == STOP;
107     }
108     /// Start the envelope
note_on()109     inline void note_on()
110     {
111         state = ATTACK;
112         thiss = sustain;
113     }
114     /// Release the envelope
note_off()115     inline void note_off()
116     {
117         // Do nothing if envelope is already stopped
118         if (state == STOP)
119             return;
120         // XXXKF what if envelope is already released? (doesn't happen in any current synth, but who knows?)
121         // Raise sustain value if it has been changed... I'm not sure if it's needed
122         thiss = std::max(sustain, value);
123         // Calculate release rate from sustain level
124         thisrelease = thiss / release_time;
125         // we're in attack or decay, and if decay is faster than release
126         if (value > sustain && decay > thisrelease) {
127             // use standard release time later (because we'll be switching at sustain point)
128             thisrelease = release;
129             state = LOCKDECAY;
130         } else {
131             // in attack/decay, but use fixed release time
132             // in case value fell below sustain, assume it didn't (for the purpose of calculating release rate only)
133             state = RELEASE;
134         }
135     }
136     /// Calculate next envelope value
advance()137     inline void advance()
138     {
139         old_value = value;
140         // XXXKF This may use a state array instead of a switch some day (at least for phases other than attack and possibly sustain)
141         switch(state)
142         {
143         case ATTACK:
144             value += attack;
145             if (value >= 1.0) {
146                 value = 1.0;
147                 state = DECAY;
148             }
149             break;
150         case DECAY:
151             value -= decay;
152             if (value < sustain)
153             {
154                 value = sustain;
155                 state = SUSTAIN;
156             }
157             break;
158         case LOCKDECAY:
159             value -= decay;
160             if (value < sustain)
161             {
162                 if (value < 0.f)
163                     value = 0.f;
164                 state = RELEASE;
165                 thisrelease = release;
166             }
167             break;
168         case SUSTAIN:
169             if (fade != 0.f)
170             {
171                 value -= fade;
172                 if (value > 1.f)
173                     value = 1.f;
174             }
175             else
176                 value = sustain;
177             if (value < 0.00001f) {
178                 value = 0;
179                 state = STOP;
180             }
181             break;
182         case RELEASE:
183             value -= thisrelease;
184             if (value <= 0.f) {
185                 value = 0.f;
186                 state = STOP;
187             }
188             break;
189         case STOP:
190             value = 0.f;
191             break;
192         }
193     }
194     /// Return a value between old_value (previous step) and value (current step)
195     /// @param pos between 0 and 1
interpolate(double pos)196     inline double interpolate(double pos)
197     {
198         return old_value + (value - old_value) * pos;
199     }
get_amp_value()200     inline float get_amp_value()
201     {
202         if (state == RELEASE && sustain > 0 && value < sustain)
203         {
204             return value * value * value / (sustain * sustain);
205         }
206         return value;
207     }
208 };
209 
210 /// Simple linear fade out for note tails
211 struct fadeout
212 {
213     float value;
214     float step, step_orig;
215     bool done, undoing;
216 
217     fadeout(int steps = 256)
218     {
219         step_orig = (float)(1.f / steps);
220         value = 1.f;
221         reset();
222     }
223 
224     /// Prepare fade out
resetfadeout225     void reset()
226     {
227         value = 1.f;
228         step = -step_orig;
229         done = false;
230         undoing = false;
231     }
232 
233     /// Fade back in with double speed (to prevent click on note restart)
undofadeout234     void undo()
235     {
236         step = step_orig;
237         done = false;
238         undoing = true;
239     }
240 
241     /// Reset if fully faded out; fade back in if in the middle of fading out
reset_softfadeout242     void reset_soft()
243     {
244         if (value <= 0.f || value >= 1.f)
245             reset();
246         else
247             undo();
248     }
249 
processfadeout250     void process(float *buffer, int len)
251     {
252         int i = 0;
253         if (!done)
254         {
255             for (; value > 0 && value <= 1.0 && i < len; i++)
256             {
257                 buffer[i] *= value;
258                 value += step;
259             }
260             if (value <= 0 || value > 1)
261                 done = true;
262         }
263         if (done && value <= 0)
264         {
265             while (i < len)
266                 buffer[i++] = 0.f;
267         }
268         if (done && undoing && value >= 1)
269         {
270             undoing = false;
271             done = false;
272             // prepare for the next fade-out
273             value = 1.f;
274         }
275     }
276 };
277 
278 };
279 
280 #endif
281 
282 
283