Changed slew logix to a real slew rate limiter

This commit is contained in:
Weird Constructor 2021-08-13 06:04:48 +02:00
parent 99eead8402
commit 993c690bc4
3 changed files with 76 additions and 23 deletions

View file

@ -705,8 +705,55 @@ impl TriggerSampleClock {
} }
} }
/// A slew rate limiter, with a configurable time per 1.0 increase.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct SlewValue<F: Flt> { pub struct SlewValue<F: Flt> {
current: F,
slew_per_ms: F,
}
impl<F: Flt> SlewValue<F> {
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::<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<F: Flt> {
slew_count: u64, slew_count: u64,
current: F, current: F,
target: F, target: F,
@ -714,7 +761,7 @@ pub struct SlewValue<F: Flt> {
sr_ms: F, sr_ms: F,
} }
impl<F: Flt> SlewValue<F> { impl<F: Flt> RampValue<F> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
slew_count: 0, slew_count: 0,

View file

@ -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_exp6!{n_lfot d_lfot 0.1,300000.0}
define_exp!{n_time d_time 0.5, 5000.0} define_exp!{n_time d_time 0.5, 5000.0}
define_exp!{n_ftme d_ftme 0.1, 1000.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 // Special linear gain factor for the Out node, to be able
// to reach more exact "1.0". // 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) (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) (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) (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], [0 sig],
delay => Delay UIType::Generic UICategory::Signal delay => Delay UIType::Generic UICategory::Signal
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) (0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)

View file

@ -13,8 +13,9 @@ use crate::dsp::{
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RndWk { pub struct RndWk {
rng: Rng, rng: Rng,
slew_val: SlewValue<f32>, slew_val: SlewValue<f64>,
trig: Trigger, trig: Trigger,
target: f64,
} }
impl RndWk { impl RndWk {
@ -26,8 +27,9 @@ impl RndWk {
Self { Self {
rng, rng,
trig: Trigger::new(), trig: Trigger::new(),
slew_val: SlewValue::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 \ "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.\ that is outside of this range, it will be reflected back into it.\
\nRange: (0..1)"; \nRange: (0..1)";
pub const slewt : &'static str = pub const slew : &'static str =
"RndWk slewt\nThe slew time, the time it takes to reach the \ "RndWk slew\nThe slew rate limiting time. Thats the time it takes to \
new target value. This can be used to smooth off rough transitions and \ get to 1.0 from 0.0. Useful for smoothing modulation of audio signals. \
clicky noises.\nRange: (0..1)"; The higher the time, the smoother/slower the transition to new \
target values will be.\nRange: (0..1)";
pub const sig : &'static str = pub const sig : &'static str =
"RndWk sig\nOscillator output\nRange: (-1..1)\n"; "RndWk sig\nOscillator output\nRange: (-1..1)\n";
pub const DESC : &'static str = pub const DESC : &'static str =
r#"Random Walker 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 = pub const HELP : &'static str =
r#"RndWk - Random Walker 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. minimal step width each 'trig' has to change the target value.
For smoother transitions, if you want to modulate an audio signal with this, 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. You can disable all randomness by setting 'step' to 0.0.
Tip: Interesting and smooth results can be achieved if you set 'slewt' Tip: Interesting and smooth results can be achieved if you set 'slew'
to a longer time than the interval in that you trigger 'trig'. It will smooth to a (way) longer time than the 'trig' interval. It will smooth
off the step widths and the overall motion even more. off the step widths and the overall motion even more.
"#; "#;
@ -87,12 +90,13 @@ impl DspNode for RndWk {
fn outputs() -> usize { 1 } fn outputs() -> usize { 1 }
fn set_sample_rate(&mut self, srate: f32) { 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) { fn reset(&mut self) {
self.slew_val.reset(); self.slew_val.reset();
self.trig.reset(); self.trig.reset();
self.target = 0.0;
} }
#[inline] #[inline]
@ -109,7 +113,7 @@ impl DspNode for RndWk {
let offs = inp::RndWk::offs(inputs); let offs = inp::RndWk::offs(inputs);
let min = inp::RndWk::min(inputs); let min = inp::RndWk::min(inputs);
let max = inp::RndWk::max(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); let out = out::RndWk::sig(outputs);
for frame in 0..ctx.nframes() { 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 offs = denorm::RndWk::offs(offs, frame).clamp(-1.0, 1.0);
let target = let target =
self.slew_val.value() self.slew_val.value() as f32
+ ((self.rng.next() * 2.0 * step) - step) + ((self.rng.next() * 2.0 * step) - step)
+ offs; + offs;
let target = ((target - min) % delta).abs() + min; self.target = (((target - min) % delta).abs() + min) as f64;
let slew_time_ms = denorm::RndWk::slewt(slewt, frame);
self.slew_val.set_target(target, slew_time_ms);
} }
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)); ctx_vals[0].set(out.read(ctx.nframes() - 1));