From 993c690bc4e8444d732dff9c290a6a3e53f59f84 Mon Sep 17 00:00:00 2001 From: Weird Constructor Date: Fri, 13 Aug 2021 06:04:48 +0200 Subject: [PATCH] Changed slew logix to a real slew rate limiter --- src/dsp/helpers.rs | 49 ++++++++++++++++++++++++++++++++++++++++++- src/dsp/mod.rs | 4 ++-- src/dsp/node_rndwk.rs | 46 ++++++++++++++++++++++------------------ 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/src/dsp/helpers.rs b/src/dsp/helpers.rs index 642f1c3..ede2b5d 100644 --- a/src/dsp/helpers.rs +++ b/src/dsp/helpers.rs @@ -705,8 +705,55 @@ impl TriggerSampleClock { } } +/// A slew rate limiter, with a configurable time per 1.0 increase. #[derive(Debug, Clone, Copy)] pub struct SlewValue { + current: F, + slew_per_ms: F, +} + +impl SlewValue { + pub fn new() -> Self { + Self { + current: f(0.0), + slew_per_ms: f(1000.0 / 44100.0), + } + } + + pub fn reset(&mut self) { + self.current = f(0.0); + } + + pub fn set_sample_rate(&mut self, srate: F) { + self.slew_per_ms = f::(1000.0) / srate; + } + + #[inline] + pub fn value(&self) -> F { self.current } + + /// * `slew_ms_per_1` - The time (in milliseconds) it should take + /// to get to 1.0 from 0.0. + #[inline] + pub fn next(&mut self, target: F, slew_ms_per_1: F) -> F { + // at 0.11ms, there are barely enough samples for proper slew. + if slew_ms_per_1 < f(0.11) { + self.current = target; + + } else { + let max_delta = self.slew_per_ms / slew_ms_per_1; + self.current = + target + .min(self.current + max_delta) + .max(self.current - max_delta); + } + + self.current + } +} + +/// A ramped value changer, with a configurable time to reach the target value. +#[derive(Debug, Clone, Copy)] +pub struct RampValue { slew_count: u64, current: F, target: F, @@ -714,7 +761,7 @@ pub struct SlewValue { sr_ms: F, } -impl SlewValue { +impl RampValue { pub fn new() -> Self { Self { slew_count: 0, diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 25525f7..17b9fc4 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -610,7 +610,7 @@ define_exp!{n_env d_env 0.0, 1000.0} define_exp6!{n_lfot d_lfot 0.1,300000.0} define_exp!{n_time d_time 0.5, 5000.0} define_exp!{n_ftme d_ftme 0.1, 1000.0} -define_exp!{n_ftmz d_ftmz 0.0, 1000.0} +define_exp!{n_ftmz d_ftmz 0.0, 5000.0} // Special linear gain factor for the Out node, to be able // to reach more exact "1.0". @@ -760,7 +760,7 @@ macro_rules! node_list { (2 offs n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) (3 min n_id d_id r_id f_def stp_d 0.0, 1.0, 0.0) (4 max n_id d_id r_id f_def stp_d 0.0, 1.0, 1.0) - (5 slewt n_ftmz d_ftmz r_fmz f_ms stp_m 0.0, 1.0, 10.0) + (5 slew n_ftmz d_ftmz r_fmz f_ms stp_m 0.0, 1.0, 75.0) [0 sig], delay => Delay UIType::Generic UICategory::Signal (0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) diff --git a/src/dsp/node_rndwk.rs b/src/dsp/node_rndwk.rs index b08f96d..7511b78 100644 --- a/src/dsp/node_rndwk.rs +++ b/src/dsp/node_rndwk.rs @@ -13,8 +13,9 @@ use crate::dsp::{ #[derive(Debug, Clone)] pub struct RndWk { rng: Rng, - slew_val: SlewValue, + slew_val: SlewValue, trig: Trigger, + target: f64, } impl RndWk { @@ -26,8 +27,9 @@ impl RndWk { Self { rng, - trig: Trigger::new(), - slew_val: SlewValue::new(), + trig: Trigger::new(), + slew_val: SlewValue::new(), + target: 0.0, } } @@ -52,16 +54,17 @@ impl RndWk { "RndWk max\nThe maximum of the new target value. If a value is drawn \ that is outside of this range, it will be reflected back into it.\ \nRange: (0..1)"; - pub const slewt : &'static str = - "RndWk slewt\nThe slew time, the time it takes to reach the \ - new target value. This can be used to smooth off rough transitions and \ - clicky noises.\nRange: (0..1)"; + pub const slew : &'static str = + "RndWk slew\nThe slew rate limiting time. Thats the time it takes to \ + get to 1.0 from 0.0. Useful for smoothing modulation of audio signals. \ + The higher the time, the smoother/slower the transition to new \ + target values will be.\nRange: (0..1)"; pub const sig : &'static str = "RndWk sig\nOscillator output\nRange: (-1..1)\n"; pub const DESC : &'static str = r#"Random Walker -This modulator generates a random number by walking a pre defined maximum random 'step' width. For smoother transitions a slew time is integrated. +This modulator generates a random number by walking a pre defined maximum random 'step' width. For smoother transitions a slew rate limiter is integrated. "#; pub const HELP : &'static str = r#"RndWk - Random Walker @@ -72,12 +75,12 @@ be folded within the defined 'min'/'max' range. The 'offs' parameter defines a minimal step width each 'trig' has to change the target value. For smoother transitions, if you want to modulate an audio signal with this, -a slew time ('slewt') is integrated. +a slew rate limiter ('slew') is integrated. You can disable all randomness by setting 'step' to 0.0. -Tip: Interesting and smooth results can be achieved if you set 'slewt' -to a longer time than the interval in that you trigger 'trig'. It will smooth +Tip: Interesting and smooth results can be achieved if you set 'slew' +to a (way) longer time than the 'trig' interval. It will smooth off the step widths and the overall motion even more. "#; @@ -87,12 +90,13 @@ impl DspNode for RndWk { fn outputs() -> usize { 1 } fn set_sample_rate(&mut self, srate: f32) { - self.slew_val.set_sample_rate(srate); + self.slew_val.set_sample_rate(srate as f64); } fn reset(&mut self) { self.slew_val.reset(); self.trig.reset(); + self.target = 0.0; } #[inline] @@ -109,7 +113,7 @@ impl DspNode for RndWk { let offs = inp::RndWk::offs(inputs); let min = inp::RndWk::min(inputs); let max = inp::RndWk::max(inputs); - let slewt = inp::RndWk::slewt(inputs); + let slew = inp::RndWk::slew(inputs); let out = out::RndWk::sig(outputs); for frame in 0..ctx.nframes() { @@ -125,17 +129,19 @@ impl DspNode for RndWk { let offs = denorm::RndWk::offs(offs, frame).clamp(-1.0, 1.0); let target = - self.slew_val.value() + self.slew_val.value() as f32 + ((self.rng.next() * 2.0 * step) - step) + offs; - let target = ((target - min) % delta).abs() + min; - - let slew_time_ms = denorm::RndWk::slewt(slewt, frame); - - self.slew_val.set_target(target, slew_time_ms); + self.target = (((target - min) % delta).abs() + min) as f64; } - out.write(frame, self.slew_val.next()); + let slew_time_ms = denorm::RndWk::slew(slew, frame); + + out.write( + frame, + self.slew_val.next( + self.target, + slew_time_ms as f64) as f32); } ctx_vals[0].set(out.read(ctx.nframes() - 1));