diff --git a/src/main.rs b/src/main.rs index 6b6dca6..1fa7343 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ use crate::dsp::Voice; mod app; mod dsp; +mod modulation; const APP_NAME: &str = "Ichiro"; @@ -155,7 +156,7 @@ fn main() -> Result<(), anyhow::Error> { Box::new(|cc| { Box::new(SynthApp::new(cc, cancel_rx, state_tx, plot_rx)) }), - ); + ).expect("TODO: panic message"); Ok(()) } diff --git a/src/modulation.rs b/src/modulation.rs index 61ec23c..af65e7d 100644 --- a/src/modulation.rs +++ b/src/modulation.rs @@ -1,32 +1,70 @@ - const SUSTAIN_LEVEL_DEFAULT: Output = 1.0, - const ATTACK_DEFAULT: usize = 10, - const DECAY_DEFAULT: usize = 10, - const RELEASE_DEFAULT: usize = 10, -type Samples: isize; - -enum AdsrEnvelopeState { - Attack(remanining: usize), - Decay(remaining: usize), - Sustain(level: f32,), - Release(remaining: usize,), +use std::ops::Mul; + +#[derive(Copy, Clone)] +struct Samples(isize); + +const SUSTAIN_LEVEL_DEFAULT: Sample = Sample(1.0); +const ATTACK_DEFAULT: Samples = Samples(10); +const DECAY_DEFAULT: Samples = Samples(10); +const RELEASE_DEFAULT: Samples = Samples(10); + +#[derive(Copy, Clone)] +enum EnvelopePhase { + Attack{remaining: Samples}, + Decay{remaining: Samples}, + Sustain{level: Sample }, + Release{remaining: Samples}, Off, } +#[derive(Copy, Clone)] struct AdsrEnvelope { - state: AdsrEnvelopeState, - sustain_level: TickResult, + level: Sample, + phase: EnvelopePhase, + sustain_level: Sample, attack: Samples, decay: Samples, release: Samples, } -type Output = f32; +impl Default for AdsrEnvelope { + fn default() -> Self { + AdsrEnvelope { + phase: EnvelopePhase::Off, + sustain_level: Samples(SUSTAIN_LEVEL_DEFAULT), + attack: Samples(ATTACK_DEFAULT), + decay: Samples(DECAY_DEFAULT), + release: Samples(RELEASE_DEFAULT), + } + } +} + +#[derive(Default, Copy, Clone)] +struct Sample(f32); +impl Into for f32 { + fn into(self) -> Sample { + Sample(self) + } +} + +enum Event { + NoteOn, + NoteOff, +} + +impl Mul for Sample { + type Output = Sample; + + fn mul(self, rhs: Self) -> Self::Output { + Sample(self.0 * rhs.0) + } +} impl AdsrEnvelope { fn new() -> Self { Self { - state: AdsrEnvelopeState::Off, + phase: EnvelopePhase::Off, sustain_level: SUSTAIN_LEVEL_DEFAULT, attack: ATTACK_DEFAULT, decay: DECAY_DEFAULT, @@ -34,12 +72,20 @@ impl AdsrEnvelope { } } - fn tick() -> Output { + fn tick() { + // TODO! progress state! self.state = match self.state { - AdsrEnvelopeState::Attack => , - Decay =>, - Sustain(n) => Sustain - Off => Off, + EnvelopePhase::Attack => EnvelopePhase::Off, + Decay => EnvelopePhase::Off, + Sustain(n) => EnvelopePhase::Off, + Off => EnvelopePhase::Off, + } + } + + fn process_event(&mut self, event: Event) { + match event { + Event::NoteOn => self.note_on(), + Event::NoteOff => self.note_off(), } } @@ -50,32 +96,41 @@ impl AdsrEnvelope { // Should it? (re-trigger) fn note_off(&mut self) { // Refactor -> implement state machine on the state enum - self.state = match self.state { - Self::Off { _ } => - _ => u:qnimplemented!() + self.phase = match self.phase { + EnvelopePhase::Off => EnvelopePhase::Off, + _ => unimplemented!() }; } fn note_on(&mut self) { - match self.state { - _ => unimplemented!() - }; + self.phase = match self.phase { + EnvelopePhase::Off => EnvelopePhase::Attack {remaining: self.attack}, + EnvelopePhase::Attack {} + } } - fn change_attack(&mut self, attack: Sample) { + fn change_attack(&mut self, attack: Samples) { self.attack = attack; } - fn change_decay(&mut self, decay: Sample) { + fn change_decay(&mut self, decay: Samples) { self.decay = decay; } - fn change_sustain(&mut self, sustain: f32) { - self.sustain = sustain; + fn change_sustain(&mut self, sustain_level: Sample) { + self.sustain_level = sustain_level; } - fn vca() -> f32 { - 0f32 + fn change_release(&mut self, release: Samples) { + self.release = release; + } + + fn reset(&mut self) { + *self = AdsrEnvelope::default(); + } + + fn vca(&self, input: Sample) -> Sample { + input * self.level } }