From a11b7b1541e6fe6a6cabb676ce22f7cafc6025f0 Mon Sep 17 00:00:00 2001 From: Niklas Reppel Date: Mon, 13 Jun 2022 10:48:46 +0200 Subject: [PATCH] modulator envelopes --- .gitignore | 1 + src/building_blocks.rs | 2 + src/building_blocks/mod_env.rs | 420 ++++++++++++++++++ src/building_blocks/oscillators/lf_cub.rs | 2 + src/building_blocks/oscillators/lf_rsaw.rs | 1 + src/building_blocks/oscillators/lf_saw.rs | 1 + src/building_blocks/oscillators/lf_square.rs | 1 + src/building_blocks/oscillators/lf_tri.rs | 2 + src/building_blocks/oscillators/sine_osc.rs | 1 + src/building_blocks/oscillators/wavematrix.rs | 1 + src/building_blocks/oscillators/wavetable.rs | 1 + src/building_blocks/sampler.rs | 1 + 12 files changed, 434 insertions(+) create mode 100644 src/building_blocks/mod_env.rs diff --git a/.gitignore b/.gitignore index a8b3f22..34caa9d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .\#* *.lock target/* +plots/* diff --git a/src/building_blocks.rs b/src/building_blocks.rs index 2396df7..37880fc 100644 --- a/src/building_blocks.rs +++ b/src/building_blocks.rs @@ -6,6 +6,7 @@ pub mod envelopes; pub mod filters; pub mod freeverb; pub mod interpolation; +pub mod mod_env; pub mod modulator; pub mod oscillators; pub mod routing; @@ -93,6 +94,7 @@ pub trait MonoSource: MonoSourceClone { fn set_parameter(&mut self, par: SynthParameterLabel, value: &SynthParameterValue); fn set_modulator(&mut self, par: SynthParameterLabel, init: f32, modulator: Modulator); fn finish(&mut self); + fn reset(&mut self); fn is_finished(&self) -> bool; fn get_next_block(&mut self, start_sample: usize, in_buffers: &[Vec]) -> [f32; BUFSIZE]; } diff --git a/src/building_blocks/mod_env.rs b/src/building_blocks/mod_env.rs new file mode 100644 index 0000000..d001ae7 --- /dev/null +++ b/src/building_blocks/mod_env.rs @@ -0,0 +1,420 @@ +use crate::building_blocks::{ + Modulator, MonoSource, SynthParameterLabel, SynthParameterValue, SynthState, +}; + +/** + * Linear Ramp + */ +#[derive(Clone, Copy)] +pub struct LinearRamp { + ramp_samples: usize, + sample_count: usize, + cur_lvl: f32, + to: f32, + from: f32, + inc_dec: f32, + state: SynthState, +} + +impl LinearRamp { + pub fn new(from: f32, to: f32, ramp_time: f32, samplerate: f32) -> Self { + let ramp_samples = (samplerate * ramp_time).round(); + + LinearRamp { + ramp_samples: ramp_samples as usize, + sample_count: 0, + cur_lvl: from, + to, + from, + inc_dec: (to - from) / ramp_samples, + state: SynthState::Fresh, + } + } +} + +impl MonoSource for LinearRamp { + fn reset(&mut self) { + self.sample_count = 0; + self.cur_lvl = self.from; + } + + fn finish(&mut self) { + self.state = SynthState::Finished; + } + + fn is_finished(&self) -> bool { + false + } + + fn set_modulator(&mut self, _: SynthParameterLabel, _: f32, _: Modulator) {} + + fn set_parameter(&mut self, _: SynthParameterLabel, _: &SynthParameterValue) {} + + fn get_next_block(&mut self, start_sample: usize, _: &[Vec]) -> [f32; BUFSIZE] { + let mut out: [f32; BUFSIZE] = [0.0; BUFSIZE]; + + for i in start_sample..BUFSIZE { + out[i] = self.cur_lvl; + + if self.sample_count < self.ramp_samples { + self.cur_lvl += self.inc_dec; + } else { + self.cur_lvl = self.to; + } + + self.sample_count += 1; + } + out + } +} + +/** + * Logarithmic Ramp + */ +#[derive(Clone, Copy)] +pub struct LogRamp { + ramp_samples: usize, + sample_count: usize, + time_inc: f32, + time_count: f32, + curve: f32, + cur_lvl: f32, + mul: f32, + from: f32, + state: SynthState, +} + +impl LogRamp { + pub fn new(from: f32, to: f32, ramp_time: f32, samplerate: f32) -> Self { + let ramp_samples = (samplerate * ramp_time).round(); + let time_inc = 1.0 / ramp_samples; + let mul = to - from; + + LogRamp { + ramp_samples: ramp_samples as usize, + sample_count: 0, + cur_lvl: 0.0, + time_inc, + curve: -4.5 * mul.signum(), + time_count: 0.0, + mul, + from, + state: SynthState::Fresh, + } + } +} + +impl MonoSource for LogRamp { + fn reset(&mut self) { + self.sample_count = 0; + self.cur_lvl = self.from; + } + + fn finish(&mut self) { + self.state = SynthState::Finished; + } + + fn is_finished(&self) -> bool { + false + } + + fn set_modulator(&mut self, _: SynthParameterLabel, _: f32, _: Modulator) {} + + fn set_parameter(&mut self, _: SynthParameterLabel, _: &SynthParameterValue) {} + + fn get_next_block(&mut self, start_sample: usize, _: &[Vec]) -> [f32; BUFSIZE] { + let mut out: [f32; BUFSIZE] = [0.0; BUFSIZE]; + + for i in start_sample..BUFSIZE { + out[i] = self.from + self.cur_lvl * self.mul; + self.cur_lvl = if self.sample_count < self.ramp_samples { + ((self.curve * self.time_count).exp() - 1.0) / (self.curve.exp() - 1.0) + } else { + 1.0 + }; + + self.time_count += self.time_inc; + + self.sample_count += 1; + } + out + } +} + +/** + * Exponential Ramp + */ +#[derive(Clone, Copy)] +pub struct ExpRamp { + ramp_samples: usize, + sample_count: usize, + time_inc: f32, + time_count: f32, + curve: f32, + cur_lvl: f32, + mul: f32, + from: f32, + state: SynthState, +} + +impl ExpRamp { + pub fn new(from: f32, to: f32, ramp_time: f32, samplerate: f32) -> Self { + let ramp_samples = (samplerate * ramp_time).round(); + let time_inc = 1.0 / ramp_samples; + let mul = to - from; + + ExpRamp { + ramp_samples: ramp_samples as usize, + sample_count: 0, + cur_lvl: 0.0, + time_inc, + curve: 4.5 * mul.signum(), + time_count: 0.0, + mul, + from, + state: SynthState::Fresh, + } + } +} + +impl MonoSource for ExpRamp { + fn reset(&mut self) { + self.sample_count = 0; + self.cur_lvl = self.from; + } + + fn finish(&mut self) { + self.state = SynthState::Finished; + } + + fn is_finished(&self) -> bool { + false + } + + fn set_modulator(&mut self, _: SynthParameterLabel, _: f32, _: Modulator) {} + + fn set_parameter(&mut self, _: SynthParameterLabel, _: &SynthParameterValue) {} + + fn get_next_block(&mut self, start_sample: usize, _: &[Vec]) -> [f32; BUFSIZE] { + let mut out: [f32; BUFSIZE] = [0.0; BUFSIZE]; + + for i in start_sample..BUFSIZE { + out[i] = self.from + self.cur_lvl * self.mul; + self.cur_lvl = if self.sample_count < self.ramp_samples { + ((self.curve * self.time_count).exp() - 1.0) / (self.curve.exp() - 1.0) + } else { + 1.0 + }; + + self.time_count += self.time_inc; + + self.sample_count += 1; + } + out + } +} + +pub enum SegmentType { + Lin, + Log, + Exp, +} + +pub struct SegmentInfo { + pub from: f32, + pub to: f32, + pub time: f32, + pub segment_type: SegmentType, +} + +/** + * Exponential Ramp + */ +#[derive(Clone)] +pub struct MultiPointEnvelope { + segments: Vec + Sync + Send>>, + segment_samples: Vec, + segment_idx: usize, + sample_count: usize, + loop_env: bool, + state: SynthState, +} + +impl MultiPointEnvelope { + pub fn new(segment_infos: Vec, loop_env: bool, samplerate: f32) -> Self { + let mut segments: Vec + Sync + Send>> = Vec::new(); + let mut segment_samples = Vec::new(); + + for info in segment_infos.iter() { + segment_samples.push((info.time * samplerate).round() as usize); + segments.push(match info.segment_type { + SegmentType::Lin => { + Box::new(LinearRamp::new(info.from, info.to, info.time, samplerate)) + } + SegmentType::Log => { + Box::new(LogRamp::new(info.from, info.to, info.time, samplerate)) + } + SegmentType::Exp => { + Box::new(ExpRamp::new(info.from, info.to, info.time, samplerate)) + } + }); + } + + MultiPointEnvelope { + segments, + segment_samples, + segment_idx: 0, + sample_count: 0, + loop_env, + state: SynthState::Fresh, + } + } +} + +impl MonoSource for MultiPointEnvelope { + fn reset(&mut self) { + self.sample_count = 0; + self.segment_idx = 0; + for s in self.segments.iter_mut() { + s.reset(); + } + } + + fn finish(&mut self) { + self.state = SynthState::Finished; + } + + fn is_finished(&self) -> bool { + false + } + + fn set_modulator(&mut self, _: SynthParameterLabel, _: f32, _: Modulator) {} + + fn set_parameter(&mut self, _: SynthParameterLabel, _: &SynthParameterValue) {} + + fn get_next_block(&mut self, start_sample: usize, bufs: &[Vec]) -> [f32; BUFSIZE] { + if self.segment_idx >= self.segments.len() { + if self.loop_env { + self.reset(); + } else { + if let Some(last_seg) = self.segments.last_mut() { + return last_seg.get_next_block(start_sample, bufs); + } else { + return [0.0; BUFSIZE]; // last value ? + } + } + } + + // loop handling ? + // what if there's 2 segments within one block ? --> IF we need to switch blocks, do this check ! + + let samples_to_fill = BUFSIZE - start_sample; + let samples_left_in_segment = self.segment_samples[self.segment_idx] - self.sample_count; + + if samples_to_fill < samples_left_in_segment { + self.sample_count += samples_to_fill; + return self.segments[self.segment_idx].get_next_block(start_sample, bufs); + } else { + let mut out: [f32; BUFSIZE] = [0.0; BUFSIZE]; + let left_from_current_segment = + self.segment_samples[self.segment_idx] - self.sample_count; + let out_last = self.segments[self.segment_idx].get_next_block(start_sample, bufs); + let out_next = + self.segments[self.segment_idx].get_next_block(left_from_current_segment, bufs); + + for i in start_sample..left_from_current_segment { + out[i] = out_last[i] + } + + for i in left_from_current_segment..samples_to_fill { + out[i] = out_next[i] + } + + self.segment_idx += 1; + self.sample_count = samples_to_fill - left_from_current_segment; + + return out; + } + } +} + +#[cfg(test)] +mod tests { + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + use crate::building_blocks::MonoSource; + + #[test] + fn test_lin_ramp() { + let mut linramp = LinearRamp::<512>::new(200.0, 10.0, 2.0, 44100.0); + + let num_blocks = (88200.0 / 512.0) as usize + 30; + + for _ in 0..num_blocks { + let block = linramp.get_next_block(0, &Vec::new()); + for i in 0..512 { + let a = block[i]; + debug_plotter::plot!(a where caption = "LinRampRevTest"); + } + } + } + + #[test] + fn test_log_ramp() { + let mut logramp = LogRamp::<512>::new(-20.0, -200.0, 2.0, 44100.0); + + let num_blocks = (88200.0 / 512.0) as usize + 30; + + for _ in 0..num_blocks { + let block = logramp.get_next_block(0, &Vec::new()); + for i in 0..512 { + let a = block[i]; + debug_plotter::plot!(a where caption = "ExpRampTest"); + } + } + } + + #[test] + fn test_exp_ramp() { + let mut expramp = ExpRamp::<512>::new(200.0, 20.0, 2.0, 44100.0); + + let num_blocks = (88200.0 / 512.0) as usize + 30; + + for _ in 0..num_blocks { + let block = expramp.get_next_block(0, &Vec::new()); + for i in 0..512 { + let a = block[i]; + debug_plotter::plot!(a where caption = "ExpRampTest"); + } + } + } + + #[test] + fn test_multi_point() { + let segments = vec![ + SegmentInfo { + from: 0.0, + to: 200.0, + time: 2.0, + segment_type: SegmentType::Lin, + }, + SegmentInfo { + from: 200.0, + to: 100.0, + time: 1.0, + segment_type: SegmentType::Exp, + }, + ]; + + let mut mpenv = MultiPointEnvelope::<512>::new(segments, false, 44100.0); + let num_blocks = (3.0 * 44100.0 / 512.0) as usize + 30; + + for _ in 0..num_blocks { + let block = mpenv.get_next_block(0, &Vec::new()); + for i in 0..512 { + let a = block[i]; + debug_plotter::plot!(a where caption = "MultiPointTest"); + } + } + } +} diff --git a/src/building_blocks/oscillators/lf_cub.rs b/src/building_blocks/oscillators/lf_cub.rs index dc003de..ee43bea 100644 --- a/src/building_blocks/oscillators/lf_cub.rs +++ b/src/building_blocks/oscillators/lf_cub.rs @@ -34,6 +34,8 @@ impl LFCub { } impl MonoSource for LFCub { + fn reset(&mut self) {} + fn set_modulator( &mut self, par: SynthParameterLabel, diff --git a/src/building_blocks/oscillators/lf_rsaw.rs b/src/building_blocks/oscillators/lf_rsaw.rs index 51ff387..cba7f11 100644 --- a/src/building_blocks/oscillators/lf_rsaw.rs +++ b/src/building_blocks/oscillators/lf_rsaw.rs @@ -34,6 +34,7 @@ impl LFRSaw { } impl MonoSource for LFRSaw { + fn reset(&mut self) {} fn set_modulator( &mut self, par: SynthParameterLabel, diff --git a/src/building_blocks/oscillators/lf_saw.rs b/src/building_blocks/oscillators/lf_saw.rs index 573c9b0..014ba78 100644 --- a/src/building_blocks/oscillators/lf_saw.rs +++ b/src/building_blocks/oscillators/lf_saw.rs @@ -34,6 +34,7 @@ impl LFSaw { } impl MonoSource for LFSaw { + fn reset(&mut self) {} fn set_modulator( &mut self, par: SynthParameterLabel, diff --git a/src/building_blocks/oscillators/lf_square.rs b/src/building_blocks/oscillators/lf_square.rs index 5e6c3af..3e4fe35 100644 --- a/src/building_blocks/oscillators/lf_square.rs +++ b/src/building_blocks/oscillators/lf_square.rs @@ -40,6 +40,7 @@ impl LFSquare { } impl MonoSource for LFSquare { + fn reset(&mut self) {} fn set_modulator( &mut self, par: SynthParameterLabel, diff --git a/src/building_blocks/oscillators/lf_tri.rs b/src/building_blocks/oscillators/lf_tri.rs index 04189bf..641d7f2 100644 --- a/src/building_blocks/oscillators/lf_tri.rs +++ b/src/building_blocks/oscillators/lf_tri.rs @@ -86,6 +86,8 @@ impl MonoSource for LFTri { false } + fn reset(&mut self) {} + fn get_next_block(&mut self, start_sample: usize, in_buffers: &[Vec]) -> [f32; BUFSIZE] { let mut out_buf: [f32; BUFSIZE] = [0.0; BUFSIZE]; diff --git a/src/building_blocks/oscillators/sine_osc.rs b/src/building_blocks/oscillators/sine_osc.rs index a0af6a3..d0e264e 100644 --- a/src/building_blocks/oscillators/sine_osc.rs +++ b/src/building_blocks/oscillators/sine_osc.rs @@ -45,6 +45,7 @@ impl SineOsc { } impl MonoSource for SineOsc { + fn reset(&mut self) {} fn set_modulator( &mut self, par: SynthParameterLabel, diff --git a/src/building_blocks/oscillators/wavematrix.rs b/src/building_blocks/oscillators/wavematrix.rs index 3b53747..4795140 100644 --- a/src/building_blocks/oscillators/wavematrix.rs +++ b/src/building_blocks/oscillators/wavematrix.rs @@ -52,6 +52,7 @@ impl Wavematrix { } impl MonoSource for Wavematrix { + fn reset(&mut self) {} fn set_modulator( &mut self, par: SynthParameterLabel, diff --git a/src/building_blocks/oscillators/wavetable.rs b/src/building_blocks/oscillators/wavetable.rs index ac9dc56..d845515 100644 --- a/src/building_blocks/oscillators/wavetable.rs +++ b/src/building_blocks/oscillators/wavetable.rs @@ -44,6 +44,7 @@ impl Wavetable { } impl MonoSource for Wavetable { + fn reset(&mut self) {} fn set_modulator( &mut self, par: SynthParameterLabel, diff --git a/src/building_blocks/sampler.rs b/src/building_blocks/sampler.rs index 53edbdb..7c195d6 100644 --- a/src/building_blocks/sampler.rs +++ b/src/building_blocks/sampler.rs @@ -159,6 +159,7 @@ impl Sampler { } impl MonoSource for Sampler { + fn reset(&mut self) {} fn set_modulator( &mut self, par: SynthParameterLabel,