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