+ Wavetable: one more wavetable (experimental, will be changed)
[calf.git] / src / calf / envelope.h
blob8a8fce13ef8bbd3ee33cd75eee95dfcb851f3f70
1 /* Calf DSP Library
2 * ADSR envelope class (and other envelopes in future)
4 * Copyright (C) 2007-2008 Krzysztof Foltman
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.
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.
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
21 #ifndef __CALF_ENVELOPE_H
22 #define __CALF_ENVELOPE_H
24 #include "primitives.h"
26 namespace dsp {
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
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
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;
61 adsr()
63 attack = decay = sustain = release = thisrelease = thiss = 0.f;
64 reset();
66 /// Stop (reset) the envelope
67 inline void reset()
69 old_value = value = 0.0;
70 thiss = 0.0;
71 state = STOP;
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)
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;
98 /// @retval true if envelope is in released state (forced decay, release or stopped)
99 inline bool released() const
101 return state == LOCKDECAY || state == RELEASE || state == STOP;
103 /// @retval true if envelope is stopped (has not been started or has run till its end)
104 inline bool stopped() const
106 return state == STOP;
108 /// Start the envelope
109 inline void note_on()
111 state = ATTACK;
112 thiss = sustain;
114 /// Release the envelope
115 inline void note_off()
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;
136 /// Calculate next envelope value
137 inline void advance()
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)
143 case ATTACK:
144 value += attack;
145 if (value >= 1.0) {
146 value = 1.0;
147 state = DECAY;
149 break;
150 case DECAY:
151 value -= decay;
152 if (value < sustain)
154 value = sustain;
155 state = SUSTAIN;
157 break;
158 case LOCKDECAY:
159 value -= decay;
160 if (value < sustain)
162 if (value < 0.f)
163 value = 0.f;
164 state = RELEASE;
165 thisrelease = release;
167 break;
168 case SUSTAIN:
169 if (fade != 0.f)
171 value -= fade;
172 if (value > 1.f)
173 value = 1.f;
175 else
176 value = sustain;
177 if (value < 0.00001f) {
178 value = 0;
179 state = STOP;
181 break;
182 case RELEASE:
183 value -= thisrelease;
184 if (value <= 0.f) {
185 value = 0.f;
186 state = STOP;
188 break;
189 case STOP:
190 value = 0.f;
191 break;
194 /// Return a value between old_value (previous step) and value (current step)
195 /// @param pos between 0 and 1
196 inline double interpolate(double pos)
198 return old_value + (value - old_value) * pos;
204 #endif