synth_utils/
adsr.rs

1//! # Attack, Decay, Sustain, Release generator
2//!
3//! ## Acronyms used:
4//!
5//! - `ADSR`: Attack Decay Sustain Release generator
6//! - `LUT`:  Look Up Table
7//! - `DDS`:  Direct Digital Synthesis
8//!
9//! ADSRs are a standard component of most analog synthesizers, used to
10//! dynamically modulate various parameters of synthesizers, most commonly
11//! loudness, timbre, or pitch.
12//!
13//! This ADSR simulates the RC curves typically found in analog ADSRs, where
14//! the attack curve is a truncated up-going convex RC curve, and the decay and
15//! release curves are down-going concave RC curves.
16//!
17//! This ADSR has four variable input parameters:
18//!
19//! - Attack time
20//! - Decay time
21//! - Sustain level
22//! - Release time
23//!
24//! This ADSR responds to two types of time based events:
25//!     
26//! - Gate On events initiate an attack phase
27//! - Gate Off events initiate a release phase
28//!
29//! This ADSR has a single output:
30//!
31//! - The current sample of the ADSR waveform in the range [0.0, 1.0].
32//!
33//! A Phase-Accumulator and Look-Up-Table (LUT) approach is used.
34//! This is known as "Direct Digital Synthesis", or DDS.
35//!
36//! LUTs are used to store the Attack and Decay curves for the ADSRs. These
37//! curves simulate the typical resistor/capacitor time constant curves used in
38//! analog ADSRs.
39
40use crate::{lookup_tables, phase_accumulator::PhaseAccumulator, utils::*};
41
42#[derive(Debug, Clone, Copy)]
43/// An ADSR envelope generator is represented here
44pub struct Adsr {
45    attack_time: TimePeriod,
46    decay_time: TimePeriod,
47    sustain_level: SustainLevel,
48    release_time: TimePeriod,
49
50    phase_accumulator: PhaseAccumulator<TOT_NUM_ACCUM_BITS, NUM_LUT_INDEX_BITS>,
51
52    state: State,
53
54    value_when_gate_on_received: f32,
55    value_when_gate_off_received: f32,
56    value: f32,
57}
58
59impl Adsr {
60    /// `Adrs::new(sr)` is a new ADSR with sample rate `sr`
61    pub fn new(sample_rate_hz: f32) -> Self {
62        Self {
63            // set defaults for very fast times and 100% on sustain
64            attack_time: MIN_TIME_PERIOD_SEC.into(),
65            decay_time: MIN_TIME_PERIOD_SEC.into(),
66            sustain_level: 1.0_f32.into(),
67            release_time: MIN_TIME_PERIOD_SEC.into(),
68
69            phase_accumulator: PhaseAccumulator::new(sample_rate_hz),
70            state: State::AtRest,
71            value_when_gate_on_received: 0.0_f32,
72            value_when_gate_off_received: 0.0_f32,
73            value: 0.0f32,
74        }
75    }
76
77    /// `adsr.tick()` advances the ADSR by 1 tick, must be called at the sample rate
78    pub fn tick(&mut self) {
79        // only calculate frequency and tick the accumulator for tick-able states
80        if self.state == State::Attack || self.state == State::Decay || self.state == State::Release
81        {
82            let period_of_this_phase = match self.state {
83                State::Attack => self.attack_time.0,
84                State::Decay => self.decay_time.0,
85                State::Release => self.release_time.0,
86                // SUSTAIN and AT-REST have no period, these can never happen here. But don't use wildcards, we want the
87                // compiler to complain if anyone adds more stages to make more complex envelopes (hold time, whatever)
88                State::Sustain => MIN_TIME_PERIOD_SEC,
89                State::AtRest => MIN_TIME_PERIOD_SEC,
90            };
91
92            self.phase_accumulator.set_period(period_of_this_phase);
93
94            self.phase_accumulator.tick();
95
96            if self.phase_accumulator.rolled_over() {
97                self.phase_accumulator.reset();
98
99                self.state = match self.state {
100                    State::Attack => State::Decay,
101                    State::Decay => State::Sustain,
102                    State::Release => State::AtRest,
103                    // SUSTAIN and AT-REST can't happen here, but explicitly match all arms
104                    State::Sustain => State::Sustain,
105                    State::AtRest => State::AtRest,
106                };
107            }
108        }
109
110        // calculate the output no matter which state
111        self.value = self.calc_value();
112    }
113
114    /// `adsr.gate_on()` sends a gate-on message to the ADSR, triggering an ATTACK phase if it's not already in ATTACK
115    ///
116    /// Attack phases may be re-triggered by sending a new gate-on message during any phase.
117    pub fn gate_on(&mut self) {
118        match self.state {
119            State::AtRest | State::Decay | State::Sustain | State::Release => {
120                self.value_when_gate_on_received = self.value;
121                self.phase_accumulator.reset();
122                self.state = State::Attack;
123            }
124            State::Attack => (), // ignore the message, we're already in an attack phase
125        }
126    }
127
128    /// `adsr.gate_off()` sends a gate-off message to the ADSR, triggering a RELEASE phase unless it's already RELEASED
129    pub fn gate_off(&mut self) {
130        match self.state {
131            State::Attack | State::Decay | State::Sustain => {
132                self.value_when_gate_off_received = self.value;
133                self.phase_accumulator.reset();
134                self.state = State::Release;
135            }
136            State::Release | State::AtRest => (), // ignore the message, we're already in a release or at-rest phase
137        }
138    }
139
140    /// `adsr.value()` is the current value of the ADSR in `[0.0, 1.0]`
141    pub fn value(&self) -> f32 {
142        self.value
143    }
144
145    /// `adsr.set_input(i)` sets the given ADSR input
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// # use synth_utils::adsr;
151    /// # let mut adsr = adsr::Adsr::new(1_000.0_f32);
152    ///
153    /// // set attack time to 30 milliseconds
154    /// adsr.set_input(adsr::Input::Attack(0.03_f32.into()));
155    ///
156    /// // set decay time to 100 milliseconds
157    /// adsr.set_input(adsr::Input::Decay(0.1_f32.into()));
158    ///
159    /// // set sustain level to 3/4 way up
160    /// adsr.set_input(adsr::Input::Sustain(0.75_f32.into()));
161    ///
162    /// // set release time to 150 milliseconds
163    /// adsr.set_input(adsr::Input::Release(0.15_f32.into()));
164    /// ```
165    pub fn set_input(&mut self, input: Input) {
166        match input {
167            Input::Attack(a) => self.attack_time = a,
168            Input::Decay(d) => self.decay_time = d,
169            Input::Sustain(s) => self.sustain_level = s,
170            Input::Release(r) => self.release_time = r,
171        }
172    }
173
174    /// `adsr.calc_value()` is a private helper function to calculate the current ADSR value
175    fn calc_value(&self) -> f32 {
176        // The coefficient for the sample is between 0 and 1.0. This is used to
177        // "squish" the attack, decay, and release curves as needed.
178
179        // Example: the decay curve starts at full scale, and ramps down to the sustain
180        // level. The range of the decay curve from top to bottom is full-scale at the
181        // top to sustain level at the bottom. The decay curve must be compressed to
182        // fit in this reduced range. The coefficient variable helps accomplish this.
183        let coefficient: f32;
184
185        // The value of the current sample. This will come from the attack LUT if the
186        // current state is attack, from the decay LUT if the current state is decay
187        // or release, and from the sustain level input if the current state is
188        // sustain. If the current state is at-rest, the value of the sample will be zero
189        let sample: f32;
190
191        // The offset for the current sample. This is only non-zero when an attack
192        // phase begins while the ADSR is not at rest, or a decay phase begins while
193        // the sustain level is non-zero. Basically this is how much to "push up" the
194        // ADSR curve, so that it fits between the starting value for the curve segment
195        // and the target value for the curve segment.
196        let offset: f32;
197
198        let lut_idx = self.phase_accumulator.index();
199        // next idx is for interpolation, clamp at the end to avoid bad behavior, we don't want to wrap around here
200        let next_lut_idx = (lut_idx + 1).min(lookup_tables::ADSR_CURVE_LUT_SIZE - 1);
201
202        match self.state {
203            State::Attack => {
204                let y0 = lookup_tables::ADSR_ATTACK_TABLE[lut_idx];
205                let y1 = lookup_tables::ADSR_ATTACK_TABLE[next_lut_idx];
206                coefficient = 1.0_f32 - self.value_when_gate_on_received;
207                sample = linear_interp(y0, y1, self.phase_accumulator.fraction());
208                offset = self.value_when_gate_on_received;
209            }
210            State::Decay => {
211                let y0 = lookup_tables::ADSR_DECAY_TABLE[lut_idx];
212                let y1 = lookup_tables::ADSR_DECAY_TABLE[next_lut_idx];
213                coefficient = 1.0_f32 - self.sustain_level.0;
214                sample = linear_interp(y0, y1, self.phase_accumulator.fraction());
215                offset = self.sustain_level.0;
216            }
217            State::Sustain => {
218                coefficient = 1.0_f32;
219                sample = self.sustain_level.0;
220                offset = 0.0;
221            }
222            State::Release => {
223                let y0 = lookup_tables::ADSR_DECAY_TABLE[lut_idx];
224                let y1 = lookup_tables::ADSR_DECAY_TABLE[next_lut_idx];
225                coefficient = self.value_when_gate_off_received;
226                sample = linear_interp(y0, y1, self.phase_accumulator.fraction());
227                offset = 0.0;
228            }
229            State::AtRest => {
230                coefficient = 0.0_f32;
231                sample = 0.0_f32;
232                offset = 0.0;
233            }
234        };
235
236        coefficient * sample + offset
237    }
238}
239
240/// ADSR input types are represented here
241///
242/// A, D, and S are represented as positive-only time periods, S is represented as a number in `[0.0, 1.0]`
243#[derive(Debug, Clone, Copy, PartialEq)]
244pub enum Input {
245    Attack(TimePeriod),
246    Decay(TimePeriod),
247    Sustain(SustainLevel),
248    Release(TimePeriod),
249}
250
251/// A time period in seconds is represented here
252///
253/// Time periods are positive only numbers with min and max values in a pleasing range for users of the ADSR
254#[derive(Debug, Clone, Copy, PartialEq)]
255pub struct TimePeriod(f32);
256
257impl From<f32> for TimePeriod {
258    fn from(p: f32) -> Self {
259        Self(p.max(MIN_TIME_PERIOD_SEC).min(MAX_TIME_PERIOD_SEC))
260    }
261}
262
263impl From<TimePeriod> for f32 {
264    fn from(val: TimePeriod) -> Self {
265        val.0
266    }
267}
268
269/// A sustain level in the range `[0.0, 1.0]` is represented here
270#[derive(Debug, Clone, Copy, PartialEq)]
271pub struct SustainLevel(f32);
272
273impl From<f32> for SustainLevel {
274    fn from(val: f32) -> Self {
275        Self(val.max(0.0_f32).min(1.0_f32))
276    }
277}
278
279impl From<SustainLevel> for f32 {
280    fn from(val: SustainLevel) -> Self {
281        val.0
282    }
283}
284
285/// ADSR states are represented here
286///
287/// An ADSR is in exactly one of these states at any given time
288#[derive(Clone, Copy, Eq, PartialEq, Debug)]
289pub enum State {
290    AtRest,
291    Attack,
292    Decay,
293    Sustain,
294    Release,
295}
296
297/// The minimum time period for an ADSR state period
298pub const MIN_TIME_PERIOD_SEC: f32 = 0.001_f32;
299
300/// The maximum time period for an ADSR state period
301pub const MAX_TIME_PERIOD_SEC: f32 = 20.0_f32;
302
303/// The total number of bits to use for the phase accumulator
304///
305/// Must be in `[1..32]`
306const TOT_NUM_ACCUM_BITS: u32 = 24;
307
308/// The number of index bits, depends on the lookup tables used
309///
310/// Note that the lookup table size MUST be a power of 2
311const NUM_LUT_INDEX_BITS: u32 = ilog_2(lookup_tables::ADSR_CURVE_LUT_SIZE);
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316
317    #[test]
318    fn gate_on_starts_attack_phase_from_at_rest() {
319        let mut adsr = Adsr::new(1_000.0_f32);
320
321        //. it starts out at-rest
322        assert_eq!(adsr.state, State::AtRest);
323
324        adsr.gate_on();
325        assert_eq!(adsr.state, State::Attack);
326    }
327
328    #[test]
329    fn attack_transitions_to_decay_after_ticks() {
330        let mut adsr = Adsr::new(1_000.0_f32);
331
332        // 100 millisecond stages at 1kHz sample rate should complete after 101 ticks
333        adsr.set_input(Input::Attack(0.1.into()));
334
335        adsr.gate_on();
336        assert_eq!(adsr.state, State::Attack);
337
338        // almost done with attack phase
339        for _ in 0..100 {
340            adsr.tick();
341        }
342        assert_eq!(adsr.state, State::Attack);
343
344        // one more tick puts us into a decay phase
345        adsr.tick();
346        assert_eq!(adsr.state, State::Decay);
347    }
348
349    #[test]
350    fn transition_through_phases() {
351        let mut adsr = Adsr::new(1_000.0_f32);
352
353        // 100 millisecond stages at 1kHz sample rate should complete after 101 ticks
354        adsr.set_input(Input::Attack(0.1.into()));
355        adsr.set_input(Input::Decay(0.1.into()));
356        adsr.set_input(Input::Sustain(0.5.into()));
357        adsr.set_input(Input::Release(0.1.into()));
358
359        adsr.gate_on();
360
361        for _ in 0..101 {
362            adsr.tick();
363        }
364        assert_eq!(adsr.state, State::Decay);
365
366        for _ in 0..101 {
367            adsr.tick();
368        }
369        assert_eq!(adsr.state, State::Sustain);
370
371        // only a gate-off message initiates a release phase
372        adsr.gate_off();
373        assert_eq!(adsr.state, State::Release);
374
375        for _ in 0..101 {
376            adsr.tick();
377        }
378        assert_eq!(adsr.state, State::AtRest);
379    }
380
381    #[test]
382    fn attack_can_retrigger_in_any_phase() {
383        let mut adsr = Adsr::new(1_000.0_f32);
384
385        // 100 millisecond stages at 1kHz sample rate should complete after 101 ticks
386        adsr.set_input(Input::Attack(0.1.into()));
387        adsr.set_input(Input::Decay(0.1.into()));
388        adsr.set_input(Input::Sustain(0.5.into()));
389        adsr.set_input(Input::Release(0.1.into()));
390
391        adsr.gate_on();
392
393        for _ in 0..101 {
394            adsr.tick();
395        }
396        assert_eq!(adsr.state, State::Decay);
397
398        adsr.gate_on();
399        assert_eq!(adsr.state, State::Attack);
400
401        for _ in 0..202 {
402            adsr.tick();
403        }
404        assert_eq!(adsr.state, State::Sustain);
405
406        adsr.gate_on();
407        assert_eq!(adsr.state, State::Attack);
408
409        for _ in 0..202 {
410            adsr.tick();
411        }
412        adsr.gate_off();
413        assert_eq!(adsr.state, State::Release);
414
415        adsr.gate_on();
416        assert_eq!(adsr.state, State::Attack);
417    }
418
419    #[test]
420    fn release_can_start_from_any_phase_but_at_rest() {
421        let mut adsr = Adsr::new(1_000.0_f32);
422
423        // 100 millisecond stages at 1kHz sample rate should complete after 101 ticks
424        adsr.set_input(Input::Attack(0.1.into()));
425        adsr.set_input(Input::Decay(0.1.into()));
426        adsr.set_input(Input::Sustain(0.5.into()));
427        adsr.set_input(Input::Release(0.1.into()));
428
429        adsr.gate_on();
430
431        for _ in 0..50 {
432            adsr.tick();
433        }
434        assert_eq!(adsr.state, State::Attack);
435
436        adsr.gate_off();
437        assert_eq!(adsr.state, State::Release);
438
439        adsr.gate_on();
440        for _ in 0..101 {
441            adsr.tick();
442        }
443        assert_eq!(adsr.state, State::Decay);
444
445        adsr.gate_off();
446        assert_eq!(adsr.state, State::Release);
447
448        adsr.gate_on();
449        for _ in 0..202 {
450            adsr.tick();
451        }
452        assert_eq!(adsr.state, State::Sustain);
453
454        adsr.gate_off();
455        assert_eq!(adsr.state, State::Release);
456
457        for _ in 0..101 {
458            adsr.tick();
459        }
460        assert_eq!(adsr.state, State::AtRest);
461
462        // turning the gate off when we're already at rest doesn't start a new release phase
463        adsr.gate_off();
464        assert_eq!(adsr.state, State::AtRest);
465    }
466}