Initial commit of a toy analogue audio generator
Key components: - no_std core so it can be used bare metal on embedded systems - default implementation for RPi 2350, with midi input and I2s output using PIO - Visualiser for the web to play with on a dev system
This commit is contained in:
66
crates/synth-core/src/envelope.rs
Normal file
66
crates/synth-core/src/envelope.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
//! ADSR envelope generator.
|
||||
|
||||
use crate::{AudioProcessor, config::SampleRate};
|
||||
|
||||
#[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 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user