diff --git a/src/dsp/helpers.rs b/src/dsp/helpers.rs index e9edcf6..d78ff00 100644 --- a/src/dsp/helpers.rs +++ b/src/dsp/helpers.rs @@ -811,7 +811,7 @@ impl DelayBuffer { let data = &self.data[..]; let len = data.len(); let s_offs = (delay_time_ms * self.srate) / f(1000.0); - let offs = s_offs.floor().to_usize().unwrap() % len; + let offs = s_offs.floor().to_usize().unwrap_or(0) % len; let fract = s_offs.fract(); let i = (self.wr + len) - offs; @@ -829,7 +829,7 @@ impl DelayBuffer { let data = &self.data[..]; let len = data.len(); let s_offs = (delay_time_ms * self.srate) / f(1000.0); - let offs = s_offs.floor().to_usize().unwrap() % len; + let offs = s_offs.floor().to_usize().unwrap_or(0) % len; let fract = s_offs.fract(); let i = (self.wr + len) - offs; @@ -862,7 +862,7 @@ impl DelayBuffer { let offs = ((delay_time_ms * self.srate) / f(1000.0)) - .floor().to_usize().unwrap() % len; + .floor().to_usize().unwrap_or(0) % len; let idx = ((self.wr + len) - offs) % len; self.data[idx] } diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index c357bef..d129011 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -45,7 +45,7 @@ mod node_tslfo; #[allow(non_upper_case_globals)] mod node_pverb; #[allow(non_upper_case_globals)] -mod node_rndwlk; +mod node_rndwk; pub mod biquad; pub mod tracker; @@ -106,7 +106,7 @@ use node_biqfilt::BiqFilt; use node_comb::Comb; use node_tslfo::TsLfo; use node_pverb::PVerb; -use node_rndwlk::RndWlk; +use node_rndwk::RndWk; pub const MIDI_MAX_FREQ : f32 = 13289.75; @@ -754,12 +754,12 @@ macro_rules! node_list { (1 trig n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) (2 rev n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) [0 sig], - rndwlk => RndWlk UIType::Generic UICategory::Mod + rndwk => RndWk UIType::Generic UICategory::Mod (0 trig n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) - (1 step n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (1 step n_id d_id r_id f_def stp_d 0.0, 1.0, 0.2) (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, 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) [0 sig], delay => Delay UIType::Generic UICategory::Signal diff --git a/src/dsp/node_rndwk.rs b/src/dsp/node_rndwk.rs new file mode 100644 index 0000000..163260b --- /dev/null +++ b/src/dsp/node_rndwk.rs @@ -0,0 +1,170 @@ +// Copyright (c) 2021 Weird Constructor +// This file is a part of HexoDSP. Released under GPL-3.0-or-later. +// See README.md and COPYING for details. + +use crate::nodes::{NodeAudioContext, NodeExecContext}; +use crate::dsp::helpers::{Rng, Trigger}; +use crate::dsp::{ + NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext, + GraphAtomData, GraphFun, +}; + +/// A triggered random walker +#[derive(Debug, Clone)] +pub struct RndWk { + sr_ms: f32, + rng: Rng, + target: f32, + target_inc: f32, + slew_count: u64, + current: f32, + trig: Trigger, +} + +impl RndWk { + pub fn new(nid: &NodeId) -> Self { + let mut rng = Rng::new(); + rng.seed( + (0x193a67f4a8a6d769_u64).wrapping_add( + 0x262829 * (nid.instance() as u64 + 1))); + + Self { + rng, + sr_ms: 44100.0 / 1000.0, + target: 0.0, + target_inc: 0.0, + slew_count: 0, + current: 0.0, + trig: Trigger::new(), + } + } + + pub const trig : &'static str = + "RndWk trig\n\n\nRange: (-1..1)"; + pub const step : &'static str = + "RndWk step\n\nRange: (-1..1)"; + pub const offs : &'static str = + "RndWk offs\n\nRange: (-1..1)"; + pub const min : &'static str = + "RndWk min\n\nRange: (0..1)"; + pub const max : &'static str = + "RndWk max\n\nRange: (0..1)"; + pub const slewt : &'static str = + "RndWk slewt\n\nRange: (0..1)"; + pub const sig : &'static str = + "RndWk sig\nOscillator output\nRange: (-1..1)\n"; + pub const DESC : &'static str = +r#"Random Walker +"#; + pub const HELP : &'static str = +r#"RndWk - Random Walker +"#; + +} + +impl DspNode for RndWk { + fn outputs() -> usize { 1 } + + fn set_sample_rate(&mut self, srate: f32) { + self.sr_ms = srate / 1000.0; + } + + fn reset(&mut self) { + self.target = 0.0; + self.current = 0.0; + self.target_inc = 0.0; + self.slew_count = 0; + self.trig.reset(); + } + + #[inline] + fn process( + &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], + outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) + { + use crate::dsp::{out, inp, denorm, denorm_offs, at}; + + let trig = inp::RndWk::trig(inputs); + let step = inp::RndWk::step(inputs); + 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 out = out::RndWk::sig(outputs); + + for frame in 0..ctx.nframes() { + if self.trig.check_trigger(denorm::RndWk::trig(trig, frame)) { + let mut min = denorm::RndWk::min(min, frame).clamp(0.0, 1.0); + let mut max = denorm::RndWk::max(max, frame).clamp(0.0, 1.0); + if min > max { + std::mem::swap(&mut min, &mut max); + } + let delta = (max - min).clamp(0.0001, 1.0); + + let step = denorm::RndWk::step(step, frame).clamp(-1.0, 1.0); + let offs = denorm::RndWk::offs(offs, frame).clamp(-1.0, 1.0); + + self.target = + self.current + + ((self.rng.next() * 2.0 * step) - step) + + offs; + self.target = ((self.target - min) % delta).abs() + min; + + let slew_time_ms = denorm::RndWk::slewt(slewt, frame); + + if slew_time_ms < 0.01 { + self.current = self.target; + self.slew_count = 0; + + } else { + let slew_samples = slew_time_ms * self.sr_ms; + self.slew_count = slew_samples as u64; + self.target_inc = (self.target - self.current) / slew_samples; + } + } + + if self.slew_count > 0 { + self.current += self.target_inc; + self.slew_count -= 1; + } else { + self.target_inc = 0.0; + self.current = self.target; + } + + out.write(frame, self.current); + } + + ctx_vals[0].set(out.read(ctx.nframes() - 1)); + } + +// fn graph_fun() -> Option { +// let mut osc = VPSOscillator::new(0.0); +// let israte = 1.0 / 128.0; +// +// Some(Box::new(move |gd: &dyn GraphAtomData, init: bool, _x: f32, _xn: f32| -> f32 { +// if init { +// osc.reset(); +// } +// +// let v = NodeId::RndWk(0).inp_param("v").unwrap().inp(); +// let vs = NodeId::RndWk(0).inp_param("vs").unwrap().inp(); +// let d = NodeId::RndWk(0).inp_param("d").unwrap().inp(); +// let damt = NodeId::RndWk(0).inp_param("damt").unwrap().inp(); +// let dist = NodeId::RndWk(0).inp_param("dist").unwrap().inp(); +// +// let v = gd.get_denorm(v as u32).clamp(0.0, 1.0); +// let d = gd.get_denorm(d as u32).clamp(0.0, 1.0); +// let vs = gd.get_denorm(vs as u32).clamp(0.0, 20.0); +// let damt = gd.get_denorm(damt as u32); +// let dist = gd.get(dist as u32).map(|a| a.i()).unwrap_or(0); +// +// let v = VPSOscillator::limit_v(d, v + vs); +// let s = osc.next(1.0, israte, d, v); +// let s = apply_distortion(s, damt, dist as u8); +// +// (s + 1.0) * 0.5 +// })) +// } +} diff --git a/src/dsp/node_rndwlk.rs b/src/dsp/node_rndwlk.rs deleted file mode 100644 index c34d92f..0000000 --- a/src/dsp/node_rndwlk.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2021 Weird Constructor -// This file is a part of HexoDSP. Released under GPL-3.0-or-later. -// See README.md and COPYING for details. - -use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::helpers::Rng; -use crate::dsp::{ - NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext, - GraphAtomData, GraphFun, -}; - -/// A triggered random walker -#[derive(Debug, Clone)] -pub struct RndWlk { - israte_ms: f64, - rng: Rng, - target: f64, - target_inc: f64, - current: f64, -} - -impl RndWlk { - pub fn new(nid: &NodeId) -> Self { - let mut rng = Rng::new(); - rng.seed( - (0x193a67f4a8a6d769_u64).wrapping_add( - 0x262829 * (nid.instance() as u64 + 1))); - - Self { - rng, - israte_ms: 1.0 / 44100.0, - target: 0.0, - target_inc: 0.0, - current: 0.0, - } - } - - pub const trig : &'static str = - "RndWlk trig\n\n\nRange: (-1..1)"; - pub const step : &'static str = - "RndWlk step\n\nRange: (-1..1)"; - pub const offs : &'static str = - "RndWlk offs\n\nRange: (-1..1)"; - pub const min : &'static str = - "RndWlk min\n\nRange: (0..1)"; - pub const max : &'static str = - "RndWlk max\n\nRange: (0..1)"; - pub const slewt : &'static str = - "RndWlk slewt\n\nRange: (0..1)"; - pub const sig : &'static str = - "RndWlk sig\nOscillator output\nRange: (-1..1)\n"; - pub const DESC : &'static str = -r#"Random Walker -"#; - pub const HELP : &'static str = -r#"RndWlk - Random Walker -"#; - -} - -impl DspNode for RndWlk { - fn outputs() -> usize { 1 } - - fn set_sample_rate(&mut self, srate: f32) { - self.israte_ms = (1.0 / (srate as f64)) / 1000.0; - } - - fn reset(&mut self) { - self.target = 0.0; - self.current = 0.0; - } - - #[inline] - fn process( - &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - _nctx: &NodeContext, - atoms: &[SAtom], inputs: &[ProcBuf], - outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) - { - use crate::dsp::{out, inp, denorm, denorm_offs, at}; - - // if trigger - // - initialize target - // - // let min = min.clamp(0.0, 1.0); - // let max = max.clamp(0.0, 1.0); - // if min > max { - // std::mem::swap(&mut min, &mut max); - // } - // let delta = (max - min).clamp(0.0001, 1.0); - // - // if self.trigger.check... { - // let step = step.clamp(-1.0, 1.0); - // let offs = offs.clamp(-1.0, 1.0); - // self.target = self.rng.next() as f64 * step + offs; - // - // if slew_time_ms < 0.01 { - // self.current = self.target; - // self.current = ((self.current - min) % delta).abs() + min; - // } else { - // let slew_samples = slew_time_ms * self.israte_ms; - // self.target_inc = self.target / slew_samples; - // } - // } - // - // if self.target_inc > 0.0 { - // self.current += self.target_inc; - // if (self.current - self.target).abs() < 0.00001 { - // self.target_inc = 0.0; - // self.current = self.target; - // } - // self.current = ((self.current - min) % delta).abs() + min; - // } - -// let freq = inp::RndWlk::freq(inputs); -// let det = inp::RndWlk::det(inputs); -// let d = inp::RndWlk::d(inputs); -// let v = inp::RndWlk::v(inputs); -// let vs = inp::RndWlk::vs(inputs); -// let damt = inp::RndWlk::damt(inputs); - let out = out::RndWlk::sig(outputs); -// let ovrsmpl = at::RndWlk::ovrsmpl(atoms); -// let dist = at::RndWlk::dist(atoms); -// -// let israte_ms = self.israte; -// -// let dist = dist.i() as u8; -// let oversample = ovrsmpl.i() == 1; -// -// let mut osc = &mut self.osc; -// -// if oversample { -// for frame in 0..ctx.nframes() { -// let freq = denorm_offs::RndWlk::freq(freq, det.read(frame), frame); -// let v = denorm::RndWlk::v(v, frame).clamp(0.0, 1.0); -// let d = denorm::RndWlk::d(d, frame).clamp(0.0, 1.0); -// let vs = denorm::RndWlk::vs(vs, frame).clamp(0.0, 20.0); -// let damt = denorm::RndWlk::damt(damt, frame).clamp(0.0, 1.0); -// -// let v = VPSOscillator::limit_v(d, v + vs); -// -// let overbuf = self.oversampling.resample_buffer(); -// for b in overbuf { -// let s = osc.next(freq, israte, d, v); -// *b = apply_distortion(s, damt, dist); -// } -// -// out.write(frame, self.oversampling.downsample()); -// } -// -// } else { -// for frame in 0..ctx.nframes() { -// let freq = denorm_offs::RndWlk::freq(freq, det.read(frame), frame); -// let v = denorm::RndWlk::v(v, frame).clamp(0.0, 1.0); -// let d = denorm::RndWlk::d(d, frame).clamp(0.0, 1.0); -// let vs = denorm::RndWlk::vs(vs, frame).clamp(0.0, 20.0); -// let damt = denorm::RndWlk::damt(damt, frame).clamp(0.0, 1.0); -// -// let v = VPSOscillator::limit_v(d, v + vs); -// let s = osc.next(freq, israte * (OVERSAMPLING as f32), d, v); -// let s = apply_distortion(s, damt, dist); -// -// out.write(frame, s); -// } -// } - - ctx_vals[0].set(out.read(ctx.nframes() - 1)); - } - -// fn graph_fun() -> Option { -// let mut osc = VPSOscillator::new(0.0); -// let israte = 1.0 / 128.0; -// -// Some(Box::new(move |gd: &dyn GraphAtomData, init: bool, _x: f32, _xn: f32| -> f32 { -// if init { -// osc.reset(); -// } -// -// let v = NodeId::RndWlk(0).inp_param("v").unwrap().inp(); -// let vs = NodeId::RndWlk(0).inp_param("vs").unwrap().inp(); -// let d = NodeId::RndWlk(0).inp_param("d").unwrap().inp(); -// let damt = NodeId::RndWlk(0).inp_param("damt").unwrap().inp(); -// let dist = NodeId::RndWlk(0).inp_param("dist").unwrap().inp(); -// -// let v = gd.get_denorm(v as u32).clamp(0.0, 1.0); -// let d = gd.get_denorm(d as u32).clamp(0.0, 1.0); -// let vs = gd.get_denorm(vs as u32).clamp(0.0, 20.0); -// let damt = gd.get_denorm(damt as u32); -// let dist = gd.get(dist as u32).map(|a| a.i()).unwrap_or(0); -// -// let v = VPSOscillator::limit_v(d, v + vs); -// let s = osc.next(1.0, israte, d, v); -// let s = apply_distortion(s, damt, dist as u8); -// -// (s + 1.0) * 0.5 -// })) -// } -}