From c7e88b7d605fd19d9ff54681a42fd69d079d92b1 Mon Sep 17 00:00:00 2001 From: Grant Shangreaux Date: Sat, 13 Nov 2021 19:55:09 -0600 Subject: Add: Envelope Iterator implementation and click to trigger note --- klangfarb/main.gd | 17 +++++---- klangfarbrs/src/adsr.rs | 66 -------------------------------- klangfarbrs/src/envelope.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++ klangfarbrs/src/lib.rs | 32 ++++++---------- 4 files changed, 114 insertions(+), 94 deletions(-) delete mode 100644 klangfarbrs/src/adsr.rs create mode 100644 klangfarbrs/src/envelope.rs diff --git a/klangfarb/main.gd b/klangfarb/main.gd index 268934f..751527a 100644 --- a/klangfarb/main.gd +++ b/klangfarb/main.gd @@ -57,19 +57,19 @@ func _check_waveform(): func _process(_delta): if self.is_playing(): - synth.apply_bend(apply_bend) - synth.frequency(freq) - synth.phasor_bend(phasor_bend) +# synth.apply_bend(apply_bend) +# synth.frequency(freq) +# synth.phasor_bend(phasor_bend) synth.frequency_modulation(frequency_modulation) - synth.fm_frequency(fm_multiplier * freq) +# synth.fm_frequency(fm_multiplier * freq) synth.fm_depth(fm_index) synth.continuous(continuous) _check_waveform() _fill_buffer() func _ready() -> void: - # buffer length of 100ms gives us ~realtime response to input changes - self.stream.buffer_length = 0.1 + # buffer length of 10ms gives us ~realtime response to input changes + self.stream.buffer_length = 0.05 # ensure Godot/Sine have the same sample rate synth.set_sample_rate(self.stream.mix_rate) # get our AudioStreamPlayback object @@ -80,10 +80,13 @@ func _ready() -> void: func _input(event): # Mouse in viewport coordinates. - if event is InputEventMouseButton: + if event is InputEventMouseButton && event.is_pressed(): print("Mouse Click/Unclick at: ", event.position) + synth.trigger() elif event is InputEventMouseMotion: freq = event.position.x + synth.frequency(freq) # phasor_bend.x = event.position.x / 1024 # phasor_bend.y = event.position.y / 600 fm_multiplier = 600 / (event.position.y + 1) + synth.fm_frequency(fm_multiplier * freq) diff --git a/klangfarbrs/src/adsr.rs b/klangfarbrs/src/adsr.rs deleted file mode 100644 index 9d51ba3..0000000 --- a/klangfarbrs/src/adsr.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::{Millisecond, Amplitude, SamplesPerSecond}; - -pub struct Envelope { - pub attack: Vec, - pub decay: Vec, - pub release: Vec, -} - -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)); - - Self{attack, decay, release} - } - - pub fn len(&self) -> usize { - self.attack.len() + self.decay.len() + self.release.len() - } -} - -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) - } else { - amps.push(current_val); - current_val += step_size; - } - } - - 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/envelope.rs b/klangfarbrs/src/envelope.rs new file mode 100644 index 0000000..3da7977 --- /dev/null +++ b/klangfarbrs/src/envelope.rs @@ -0,0 +1,93 @@ +use super::{Millisecond, Amplitude, SamplesPerSecond}; + +pub struct Envelope { + pub attack: Vec, + pub decay: Vec, + pub release: Vec, + pub index: usize, +} + +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)); + + Self { attack, decay, release, index: 0 } + } + + pub fn len(&self) -> usize { + self.attack.len() + self.decay.len() + self.release.len() + } +} + +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] + } 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) + } else { + amps.push(current_val); + current_val += step_size; + } + } + + 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 8bd1e50..fc5e6f9 100644 --- a/klangfarbrs/src/lib.rs +++ b/klangfarbrs/src/lib.rs @@ -17,8 +17,8 @@ mod phasor; mod osc; use osc::{Osc, Waveform}; -pub mod adsr; -use adsr::Envelope; +pub mod envelope; +use envelope::Envelope; /// Aliasing some types to distinguish various audio properties. type Sample = f32; @@ -47,7 +47,6 @@ pub struct MonoSynth { pub fm_frequency: Hz, pub fm_depth: Amplitude, fm_osc: Osc, - current_envelope_position: usize, } #[methods] @@ -72,13 +71,12 @@ impl MonoSynth { phasor_bend: Vector2::new(0.0, 0.0), continuous: true, duration: 0, - envelope: Envelope::new(500, 1000, 0.5, 4000, sprt), + envelope: Envelope::new(30, 500, 0.5, 1000, sprt), cutoff: 0.0, frequency_modulation: false, fm_frequency: 10.0, fm_depth: 0.1, fm_osc: Osc::new(freq * 2.0, sprt), - current_envelope_position: 0, } } @@ -166,6 +164,11 @@ impl MonoSynth { self.envelope = Envelope::new(attack, decay, sustain, release, self.sample_rate); } + #[export] + fn trigger(&mut self, _owner: &Node) { + self.envelope.index = 0; + } + #[export] pub fn frames(&mut self, _owner: &Node, samples: i32) -> TypedArray { let mut frames = TypedArray::new(); @@ -189,22 +192,9 @@ impl MonoSynth { // } if !self.continuous { - let pos = self.current_envelope_position; - let atk = self.envelope.attack.len(); - let atkdcy = atk + self.envelope.decay.len(); - - if pos < atk { - sample = sample * self.envelope.attack[pos] - } else if pos >= atk && pos < atkdcy { - sample = sample * self.envelope.decay[pos - atk] - } else if pos < self.envelope.len() { - sample = sample * self.envelope.release[pos - atkdcy] - } - - self.current_envelope_position += 1; - - if self.current_envelope_position >= self.envelope.len() { - self.current_envelope_position = 0; + sample *= match self.envelope.next() { + Some(a) => a, + None => 0.0, } } -- cgit v1.2.3