83 lines
2.9 KiB
Rust
83 lines
2.9 KiB
Rust
//! ADSR envelope generator.
|
||
|
||
use crate::{AudioProcessor, config::SampleRate};
|
||
use crate::descriptor::{ComponentDescriptor, Direction, JackDescriptor, ParamDescriptor, SignalKind};
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||
enum Stage { Idle, Attack, Decay, Sustain, Release }
|
||
|
||
pub struct Adsr {
|
||
pub attack_s: f32,
|
||
pub decay_s: f32,
|
||
pub sustain: f32, // 0.0–1.0
|
||
pub release_s: f32,
|
||
sample_rate: SampleRate,
|
||
stage: Stage,
|
||
level: f32,
|
||
}
|
||
|
||
impl Adsr {
|
||
pub fn new(sample_rate: SampleRate) -> Self {
|
||
Self {
|
||
attack_s: 0.01, decay_s: 0.1, sustain: 0.7, release_s: 0.3,
|
||
sample_rate,
|
||
stage: Stage::Idle,
|
||
level: 0.0,
|
||
}
|
||
}
|
||
|
||
pub const DESCRIPTOR: ComponentDescriptor = ComponentDescriptor {
|
||
kind: "adsr",
|
||
label: "ADSR",
|
||
jacks: &[
|
||
JackDescriptor { id: "gate_in", label: "Gate", direction: Direction::Input, signal: SignalKind::Gate },
|
||
JackDescriptor { id: "env_out", label: "Env", direction: Direction::Output, signal: SignalKind::Cv },
|
||
],
|
||
params: &[
|
||
ParamDescriptor { id: "attack_s", label: "Attack", min: 0.001, max: 4.0, default: 0.01, unit: "s" },
|
||
ParamDescriptor { id: "decay_s", label: "Decay", min: 0.001, max: 4.0, default: 0.1, unit: "s" },
|
||
ParamDescriptor { id: "sustain", label: "Sustain", min: 0.0, max: 1.0, default: 0.7, unit: "" },
|
||
ParamDescriptor { id: "release_s", label: "Release", min: 0.001, max: 8.0, default: 0.3, unit: "s" },
|
||
],
|
||
};
|
||
|
||
pub fn gate_on(&mut self) { self.stage = Stage::Attack; }
|
||
pub fn gate_off(&mut self) { self.stage = Stage::Release; }
|
||
pub fn is_idle(&self) -> bool { self.stage == Stage::Idle }
|
||
|
||
#[inline]
|
||
fn next_sample(&mut self) -> f32 {
|
||
let dt = self.sample_rate.period();
|
||
match self.stage {
|
||
Stage::Idle => {},
|
||
Stage::Attack => {
|
||
self.level += dt / self.attack_s.max(dt);
|
||
if self.level >= 1.0 { self.level = 1.0; self.stage = Stage::Decay; }
|
||
}
|
||
Stage::Decay => {
|
||
self.level -= dt / self.decay_s.max(dt) * (1.0 - self.sustain);
|
||
if self.level <= self.sustain { self.level = self.sustain; self.stage = Stage::Sustain; }
|
||
}
|
||
Stage::Sustain => { self.level = self.sustain; }
|
||
Stage::Release => {
|
||
self.level -= dt / self.release_s.max(dt) * self.level;
|
||
if self.level <= 0.0001 { self.level = 0.0; self.stage = Stage::Idle; }
|
||
}
|
||
}
|
||
self.level
|
||
}
|
||
}
|
||
|
||
impl<const B: usize> AudioProcessor<B> for Adsr {
|
||
fn process(&mut self, out: &mut [f32; B]) {
|
||
for s in out.iter_mut() {
|
||
*s = self.next_sample();
|
||
}
|
||
}
|
||
|
||
fn reset(&mut self) {
|
||
self.stage = Stage::Idle;
|
||
self.level = 0.0;
|
||
}
|
||
}
|