Skip to main content

rill_core_dsp/generators/
envelope.rs

1//! Envelope generators (ADSR, AR, ASR)
2
3use super::Generator;
4use crate::math::Smoother;
5use crate::vector::prelude::*;
6use rill_core::traits::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
7use rill_core::traits::ProcessResult;
8use rill_core::Transcendental;
9
10/// Current stage of an envelope generator.
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum EnvelopeStage {
13    /// Attack phase — level rises from 0 to 1.
14    Attack,
15    /// Decay phase — level falls from 1 to sustain level.
16    Decay,
17    /// Sustain phase — level holds at the sustain value.
18    Sustain,
19    /// Release phase — level falls from sustain to 0.
20    Release,
21    /// Off — level is 0 and the envelope is inactive.
22    Off,
23}
24
25impl EnvelopeStage {
26    /// Human-readable name of the current stage.
27    pub fn name(&self) -> &'static str {
28        match self {
29            EnvelopeStage::Attack => "Attack",
30            EnvelopeStage::Decay => "Decay",
31            EnvelopeStage::Sustain => "Sustain",
32            EnvelopeStage::Release => "Release",
33            EnvelopeStage::Off => "Off",
34        }
35    }
36}
37
38/// Envelope shape variant.
39#[derive(Debug, Clone, Copy, PartialEq)]
40pub enum EnvelopeType {
41    /// Attack, Decay, Sustain, Release — the classic four-stage envelope.
42    ADSR,
43    /// Attack, Release — no sustain, for percussive sounds.
44    AR,
45    /// Attack, Sustain, Release — sustain holds until gate-off, for organ sounds.
46    ASR,
47}
48
49/// Multi-stage envelope generator (ADSR, AR, ASR).
50///
51/// Processes an audio-rate gate signal: values > 0.5 trigger the attack
52/// phase, values ≤ 0.5 trigger the release phase. Output ramps smoothly
53/// between stages.
54pub struct EnvelopeGenerator<T: Transcendental> {
55    env_type: EnvelopeType,
56    attack: f32,
57    decay: f32,
58    sustain: ScalarVector1<T>,
59    release: f32,
60    stage: EnvelopeStage,
61    level: ScalarVector1<T>,
62    smoother: Smoother<T>,
63    attack_samples: usize,
64    decay_samples: usize,
65    release_samples: usize,
66    position: usize,
67    sample_rate: f32,
68    gate: bool,
69}
70
71impl<T: Transcendental> EnvelopeGenerator<T> {
72    /// Create a new ADSR envelope.
73    ///
74    /// * `attack` — attack time in seconds.
75    /// * `decay` — decay time in seconds.
76    /// * `sustain` — sustain level (0..1).
77    /// * `release` — release time in seconds.
78    pub fn adsr(attack: f32, decay: f32, sustain: T, release: f32) -> Self {
79        Self {
80            env_type: EnvelopeType::ADSR,
81            attack,
82            decay,
83            sustain: ScalarVector1::splat(sustain),
84            release,
85            stage: EnvelopeStage::Off,
86            level: ScalarVector1::splat(T::ZERO),
87            smoother: Smoother::new(T::from_f32(0.5)),
88            attack_samples: 0,
89            decay_samples: 0,
90            release_samples: 0,
91            position: 0,
92            sample_rate: 44100.0,
93            gate: false,
94        }
95    }
96
97    /// Create a new AR (Attack-Release) envelope for percussive sounds.
98    pub fn ar(attack: f32, release: f32) -> Self {
99        Self {
100            env_type: EnvelopeType::AR,
101            attack,
102            decay: 0.0,
103            sustain: ScalarVector1::splat(T::ZERO),
104            release,
105            stage: EnvelopeStage::Off,
106            level: ScalarVector1::splat(T::ZERO),
107            smoother: Smoother::new(T::from_f32(0.5)),
108            attack_samples: 0,
109            decay_samples: 0,
110            release_samples: 0,
111            position: 0,
112            sample_rate: 44100.0,
113            gate: false,
114        }
115    }
116
117    /// Create a new ASR (Attack-Sustain-Release) envelope for organ-like sounds.
118    pub fn asr(attack: f32, sustain: T, release: f32) -> Self {
119        Self {
120            env_type: EnvelopeType::ASR,
121            attack,
122            decay: 0.0,
123            sustain: ScalarVector1::splat(sustain),
124            release,
125            stage: EnvelopeStage::Off,
126            level: ScalarVector1::splat(T::ZERO),
127            smoother: Smoother::new(T::from_f32(0.5)),
128            attack_samples: 0,
129            decay_samples: 0,
130            release_samples: 0,
131            position: 0,
132            sample_rate: 44100.0,
133            gate: false,
134        }
135    }
136
137    /// Update sample counters
138    fn update_samples(&mut self) {
139        self.attack_samples = (self.attack * self.sample_rate) as usize;
140        self.decay_samples = (self.decay * self.sample_rate) as usize;
141        self.release_samples = (self.release * self.sample_rate) as usize;
142    }
143
144    /// Start the envelope (gate on). Resets to the attack phase.
145    pub fn trigger(&mut self) {
146        self.gate = true;
147        self.stage = EnvelopeStage::Attack;
148        self.position = 0;
149    }
150
151    /// Release the envelope (gate off). Enters the release phase.
152    pub fn release(&mut self) {
153        self.gate = false;
154        self.stage = EnvelopeStage::Release;
155        self.position = 0;
156    }
157
158    /// Current envelope stage.
159    pub fn stage(&self) -> EnvelopeStage {
160        self.stage
161    }
162
163    /// Returns `true` if the envelope is not in the `Off` stage.
164    pub fn is_active(&self) -> bool {
165        self.stage != EnvelopeStage::Off
166    }
167}
168
169impl<T: Transcendental> Algorithm<T> for EnvelopeGenerator<T> {
170    fn init(&mut self, sample_rate: f32) {
171        self.sample_rate = sample_rate;
172        self.update_samples();
173        self.reset();
174    }
175
176    fn reset(&mut self) {
177        self.stage = EnvelopeStage::Off;
178        self.level = ScalarVector1::splat(T::ZERO);
179        self.position = 0;
180        self.gate = false;
181        self.smoother.set_current(T::ZERO);
182    }
183
184    fn process(&mut self, input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
185        let input = input.unwrap_or(&[]);
186        let len = input.len().min(output.len());
187        for i in 0..len {
188            let gate_signal = input[i];
189            // Update gate from input signal if present
190            if gate_signal.to_f32() > 0.5 && !self.gate {
191                self.trigger();
192            } else if gate_signal.to_f32() <= 0.5 && self.gate {
193                self.release();
194            }
195
196            // Envelope generation
197            match self.stage {
198                EnvelopeStage::Attack => {
199                    let target = ScalarVector1::splat(T::from_f32(1.0));
200                    self.level = self.level
201                        + (target - self.level)
202                            * ScalarVector1::splat(T::from_f32(1.0 / self.attack_samples as f32));
203                    self.position += 1;
204
205                    if self.position >= self.attack_samples {
206                        match self.env_type {
207                            EnvelopeType::ADSR => self.stage = EnvelopeStage::Decay,
208                            EnvelopeType::AR => self.stage = EnvelopeStage::Release,
209                            EnvelopeType::ASR => self.stage = EnvelopeStage::Sustain,
210                        }
211                        self.position = 0;
212                    }
213                }
214
215                EnvelopeStage::Decay => {
216                    let target = self.sustain;
217                    self.level = self.level
218                        + (target - self.level)
219                            * ScalarVector1::splat(T::from_f32(1.0 / self.decay_samples as f32));
220                    self.position += 1;
221
222                    if self.position >= self.decay_samples {
223                        self.stage = EnvelopeStage::Sustain;
224                        self.position = 0;
225                    }
226                }
227
228                EnvelopeStage::Sustain => {
229                    // Hold level
230                    self.level = self.sustain;
231                }
232
233                EnvelopeStage::Release => {
234                    let target = ScalarVector1::splat(T::ZERO);
235                    self.level = self.level
236                        + (target - self.level)
237                            * ScalarVector1::splat(T::from_f32(1.0 / self.release_samples as f32));
238                    self.position += 1;
239
240                    if self.position >= self.release_samples {
241                        self.stage = EnvelopeStage::Off;
242                        self.position = 0;
243                    }
244                }
245
246                EnvelopeStage::Off => {
247                    self.level = ScalarVector1::splat(T::ZERO);
248                }
249            }
250
251            // Apply smoothing
252            output[i] = self.smoother.process_sample(self.level.extract(0));
253        }
254        Ok(())
255    }
256
257    fn metadata(&self) -> AlgorithmMetadata {
258        AlgorithmMetadata {
259            name: match self.env_type {
260                EnvelopeType::ADSR => "ADSR Envelope",
261                EnvelopeType::AR => "AR Envelope",
262                EnvelopeType::ASR => "ASR Envelope",
263            },
264            category: AlgorithmCategory::Generator,
265            description: match self.env_type {
266                EnvelopeType::ADSR => "Attack-Decay-Sustain-Release envelope generator",
267                EnvelopeType::AR => "Attack-Release envelope generator (percussion)",
268                EnvelopeType::ASR => "Attack-Sustain-Release envelope generator (organ)",
269            },
270            author: "Rill",
271            version: env!("CARGO_PKG_VERSION"),
272        }
273    }
274}
275
276impl<T: Transcendental> Generator<T> for EnvelopeGenerator<T> {
277    fn phase(&self) -> T {
278        T::ZERO
279    }
280    fn set_phase(&mut self, _phase: T) {}
281    fn frequency(&self) -> f32 {
282        0.0
283    }
284    fn set_frequency(&mut self, _freq: f32) {}
285    fn amplitude(&self) -> T {
286        self.level.extract(0)
287    }
288    fn set_amplitude(&mut self, _amp: T) {}
289}