From af484658565920005a502553422530493a5d91de Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Wed, 24 Nov 2021 23:32:31 -0500 Subject: Refactor envelope to use line iterator --- klangfarbrs/src/envelope.rs | 88 +++++++++------------------------------------ klangfarbrs/src/lib.rs | 35 ++++++++++++++++-- klangfarbrs/src/line.rs | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 74 deletions(-) create mode 100644 klangfarbrs/src/line.rs (limited to 'klangfarbrs') diff --git a/klangfarbrs/src/envelope.rs b/klangfarbrs/src/envelope.rs index 3da7977..ce3071d 100644 --- a/klangfarbrs/src/envelope.rs +++ b/klangfarbrs/src/envelope.rs @@ -1,25 +1,20 @@ -use super::{Millisecond, Amplitude, SamplesPerSecond}; +use super::{Millisecond, Amplitude, SamplesPerSecond, Line}; pub struct Envelope { - pub attack: Vec, - pub decay: Vec, - pub release: Vec, - pub index: usize, + pub attack: Line, + pub decay: Line, + pub release: Line, } impl Envelope { pub fn new( attack: Millisecond, decay: Millisecond, sustain: Amplitude, release: Millisecond, sample_rate: SamplesPerSecond ) -> Self { - let attack = interpolate(0.0, 1.0, ms_to_samples(attack, sample_rate)); - let decay = interpolate(1.0, sustain, ms_to_samples(decay, sample_rate)); - let release = interpolate(sustain, 0.0, ms_to_samples(release, sample_rate)); + let attack = Line::new(0.0, 1.0, attack, sample_rate); + let decay = Line::new(1.0, sustain, decay, sample_rate); + let release = Line::new(sustain, 0.0, release, sample_rate); - Self { attack, decay, release, index: 0 } - } - - pub fn len(&self) -> usize { - self.attack.len() + self.decay.len() + self.release.len() + Self { attack, decay, release } } } @@ -27,67 +22,16 @@ impl Iterator for Envelope { type Item = Amplitude; fn next(&mut self) -> Option { - let idx = self.index; - let atk = self.attack.len(); - let atkdcy = atk + self.decay.len(); - - self.index += 1; - - if idx < self.len() { - let val = if idx < atk { - self.attack[idx] - } else if idx >= atk && idx < atkdcy { - self.decay[idx - atk] + let mut val = self.attack.next(); + if val.is_none() { + val = self.decay.next(); + if val.is_none() { + self.release.next() } else { - self.release[idx - atkdcy] - }; - - Some(val) - } else { - None - } - } -} - -fn interpolate(start: Amplitude, end: Amplitude, milliseconds: Millisecond) -> Vec { - let step_size = (end - start) / milliseconds as f32; - let mut amps = vec!(); - let mut current_val = start + step_size; - - for i in 0..=milliseconds { - if i == 0 { - amps.push(start) - } else if i == milliseconds { - amps.push(end) + val + } } else { - amps.push(current_val); - current_val += step_size; + val } } - - amps -} - -fn ms_to_samples(ms: Millisecond, sample_rate: SamplesPerSecond) -> u32 { - let multiplier = sample_rate as u32 / 1000; - multiplier * ms -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_has_expected_total_length() { - let expected = 3 + ms_to_samples(30, 48000.0) as usize; - let total = Envelope::new(10, 10, 1.0, 10, 48000.0).len(); - assert_eq! (expected, total) - } - - #[test] - fn interpolate_works() { - let expected = vec![0.0, 0.2, 0.4, 0.6, 0.8, 1.0]; - let result = interpolate(0.0, 1.0, 5); - assert_eq!(expected, result) - } } diff --git a/klangfarbrs/src/lib.rs b/klangfarbrs/src/lib.rs index fc5e6f9..e64246c 100644 --- a/klangfarbrs/src/lib.rs +++ b/klangfarbrs/src/lib.rs @@ -13,6 +13,8 @@ use gdnative::prelude::*; use gdnative::core_types::TypedArray; mod phasor; +mod line; +use line::Line; mod osc; use osc::{Osc, Waveform}; @@ -46,6 +48,10 @@ pub struct MonoSynth { pub frequency_modulation: bool, pub fm_frequency: Hz, pub fm_depth: Amplitude, + pub attack: Millisecond, + pub decay: Millisecond, + pub sustain: Amplitude, + pub release: Millisecond, fm_osc: Osc, } @@ -77,6 +83,10 @@ impl MonoSynth { fm_frequency: 10.0, fm_depth: 0.1, fm_osc: Osc::new(freq * 2.0, sprt), + attack: 10, + decay: 100, + sustain: 0.5, + release: 500, } } @@ -156,6 +166,26 @@ impl MonoSynth { self.fm_depth = fm_depth } + #[export] + fn set_attack(&mut self, _owner: &Node, attack: Millisecond) { + self.attack = attack + } + + #[export] + fn set_decay(&mut self, _owner: &Node, decay: Millisecond) { + self.decay = decay + } + + #[export] + fn set_sustain(&mut self, _owner: &Node, sustain: Amplitude) { + self.sustain = sustain + } + + #[export] + fn set_release(&mut self, _owner: &Node, release: Millisecond) { + self.release = release + } + #[export] fn envelope( &mut self, _owner: &Node, @@ -165,8 +195,9 @@ impl MonoSynth { } #[export] - fn trigger(&mut self, _owner: &Node) { - self.envelope.index = 0; + fn trigger(&mut self, _owner: &Node, + ) { + self.envelope = Envelope::new(self.attack, self.decay, self.sustain, self.release, self.sample_rate); } #[export] diff --git a/klangfarbrs/src/line.rs b/klangfarbrs/src/line.rs new file mode 100644 index 0000000..f57b103 --- /dev/null +++ b/klangfarbrs/src/line.rs @@ -0,0 +1,68 @@ +use super::{Millisecond, Amplitude, SamplesPerSecond}; + +pub struct Line { + pub start: Amplitude, + pub end: Amplitude, + pub duration: Millisecond, + pub index: u32, + slope: f32, + samples: u32 +} + +impl Line { + pub fn new( + start: Amplitude, end: Amplitude, duration: Millisecond, sample_rate: SamplesPerSecond + ) -> Self { + Self { start, end, duration, index: 0, slope: slope(start, end, ms_to_samples(duration, sample_rate)), samples: ms_to_samples(duration, sample_rate) } + } +} + +impl Iterator for Line { + type Item = Amplitude; + + fn next(&mut self) -> Option { + let idx = self.index; + let val = self.slope * idx as f32 + self.start; + self.index += 1; + + if idx <= self.samples { + Some(val) + } else { + None + } + + } +} + +fn slope(start: Amplitude, end: Amplitude, duration: Millisecond) -> f32 { + return (end - start) / duration as f32 ; +} + +fn ms_to_samples(ms: Millisecond, sample_rate: SamplesPerSecond) -> u32 { + let multiplier = sample_rate as u32 / 1000; + multiplier * ms +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_calculates_a_slope() { + let expected = 0.5; + let slope = slope(0.0, 0.5, 1); + assert_eq! (expected, slope) + } + + #[test] + fn it_calculates_the_next_values() { + let mut line = Line::new(0.0, 0.5, 1, 5000.0); + assert_eq!(0.0, line.next().unwrap()); + assert_eq!(0.1, line.next().unwrap()); + assert_eq!(0.2, line.next().unwrap()); + assert_eq!(0.3, line.next().unwrap()); + assert_eq!(0.4, line.next().unwrap()); + assert_eq!(0.5, line.next().unwrap()); + assert_eq!(None, line.next()); + } +} -- cgit v1.2.3