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., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
21 #ifndef __CALF_ENVELOPE_H
22 #define __CALF_ENVELOPE_H
24 #include "primitives.h"
28 /// Rate-based ADSR 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.
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)
40 RELEASE
, ///< release - fall from sustain (or pre-sustain) level to 0
41 LOCKDECAY
///< locked decay
44 /// Current envelope stage
46 /// @note these are *rates*, not times
47 double attack
, decay
, sustain
, release
;
48 /// Requested release time (not the rate!) in frames, used for recalculating the rate if sustain is changed
50 /// Current envelope (output) level
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)
55 /// Sustain level used for the current note (used to calculate release rate if sustain changed during release stage
56 /// of the current note)
61 attack
= decay
= sustain
= release
= thisrelease
= thiss
= 0.f
;
64 /// Stop (reset) the envelope
71 /// Set the envelope parameters (updates rate member variables based on values passed)
72 /// @param a attack time
73 /// @param d decay time
74 /// @param s sustain level
75 /// @param r release time
76 /// @param er Envelope (update) rate
77 inline void set(float a
, float d
, float s
, float r
, float er
)
79 attack
= 1.0 / (a
* er
);
80 decay
= (1 - s
) / (d
* er
);
82 release_time
= r
* er
;
83 release
= s
/ release_time
;
85 // lock thiss setting (start of release for current note) and unlock thisrelease setting (current note's release rate)
89 thisrelease
= thiss
/ release_time
;
91 /// @retval true if envelope is in released state (forced decay, release or stopped)
92 inline bool released() const
94 return state
== LOCKDECAY
|| state
== RELEASE
|| state
== STOP
;
96 /// @retval true if envelope is stopped (has not been started or has run till its end)
97 inline bool stopped() const
101 /// Start the envelope
102 inline void note_on()
107 /// Release the envelope
108 inline void note_off()
110 // Do nothing if envelope is already stopped
113 // XXXKF what if envelope is already released? (doesn't happen in any current synth, but who knows?)
114 // Raise sustain value if it has been changed... I'm not sure if it's needed
115 thiss
= std::max(sustain
, value
);
116 // Calculate release rate from sustain level
117 thisrelease
= thiss
/ release_time
;
118 // we're in attack or decay, and if decay is faster than release
119 if (value
> sustain
&& decay
> thisrelease
) {
120 // use standard release time later (because we'll be switching at sustain point)
121 thisrelease
= release
;
124 // in attack/decay, but use fixed release time
125 // in case value fell below sustain, assume it didn't (for the purpose of calculating release rate only)
129 /// Calculate next envelope value
130 inline void advance()
132 // XXXKF This may use a state array instead of a switch some day (at least for phases other than attack and possibly sustain)
157 thisrelease
= release
;
162 if (value
< 0.00001f
) {
168 value
-= thisrelease
;