diff --git a/src/dsp/helpers.rs b/src/dsp/helpers.rs index b3d2ff9..1c123df 100644 --- a/src/dsp/helpers.rs +++ b/src/dsp/helpers.rs @@ -3,7 +3,16 @@ // See README.md and COPYING for details. use std::cell::RefCell; -use num_traits::{Float, FloatConst, cast::FromPrimitive}; +use num_traits::{Float, FloatConst, cast::FromPrimitive, cast::ToPrimitive}; + +macro_rules! trait_alias { + ($name:ident = $base1:ident + $($base2:ident +)+) => { + pub trait $name: $base1 $(+ $base2)+ { } + impl $name for T { } + }; +} + +trait_alias!(Flt = Float + FloatConst + ToPrimitive + FromPrimitive +); /// Logarithmic table size of the table in [fast_cos] / [fast_sin]. static FAST_COS_TAB_LOG2_SIZE : usize = 9; @@ -703,27 +712,32 @@ macro_rules! fc { ($F: ident, $e: expr) => { F::from_f64($e).unwrap() } } +#[inline] +fn f(x: f64) -> F { F::from_f64(x).unwrap() } +fn fclamp(x: F, mi: F, mx: F) -> F { x.max(mi).min(mx) } +fn fclampc(x: F, mi: f64, mx: f64) -> F { x.max(f(mi)).min(f(mx)) } + #[derive(Debug, Clone, Default)] -pub struct DelayBuffer { +pub struct DelayBuffer { data: Vec, wr: usize, srate: F, } -impl DelayBuffer { +impl DelayBuffer { pub fn new() -> Self { Self { - data: vec![fc!(F,0.0); DEFAULT_DELAY_BUFFER_SAMPLES], + data: vec![f(0.0); DEFAULT_DELAY_BUFFER_SAMPLES], wr: 0, - srate: fc!(F, 44100.0), + srate: f(44100.0), } } pub fn new_with_size(size: usize) -> Self { Self { - data: vec![fc!(F, 0.0); size], + data: vec![f(0.0); size], wr: 0, - srate: fc!(F, 44100.0), + srate: f(44100.0), } } @@ -732,7 +746,7 @@ impl DelayBuffer { } pub fn reset(&mut self) { - self.data.fill(0.0); + self.data.fill(f(0.0)); self.wr = 0; } @@ -778,8 +792,8 @@ impl DelayBuffer { pub fn linear_interpolate_at(&self, delay_time_ms: F) -> F { let data = &self.data[..]; let len = data.len(); - let s_offs = (delay_time_ms * self.srate) / 1000.0; - let offs = s_offs.floor() as usize % len; + let s_offs = (delay_time_ms * self.srate) / f(1000.0); + let offs = s_offs.floor().to_usize().unwrap() % len; let fract = s_offs.fract(); let i = (self.wr + len) - offs; @@ -787,7 +801,7 @@ impl DelayBuffer { let x1 = data[(i + 1) % len]; let fract = fract as F; - x0 * (1.0 - fract) + x1 * fract + x0 * (f::(1.0) - fract) + x1 * fract } /// Fetch a sample from the delay buffer at the given time. @@ -797,8 +811,8 @@ impl DelayBuffer { pub fn cubic_interpolate_at(&self, delay_time_ms: F) -> F { let data = &self.data[..]; let len = data.len(); - let s_offs = (delay_time_ms * self.srate) / 1000.0; - let offs = s_offs.floor() as usize % len; + let s_offs = (delay_time_ms * self.srate) / f(1000.0); + let offs = s_offs.floor().to_usize().unwrap() % len; let fract = s_offs.fract(); let i = (self.wr + len) - offs; @@ -815,10 +829,10 @@ impl DelayBuffer { let x1 = data[(i + 1) % len]; let x2 = data[(i + 2) % len]; - let c = (x1 - xm1) * 0.5; + let c = (x1 - xm1) * f(0.5); let v = x0 - x1; let w = c + v; - let a = w + v + (x2 - x0) * 0.5; + let a = w + v + (x2 - x0) * f(0.5); let b_neg = w + a; let fract = fract as F; @@ -828,7 +842,10 @@ impl DelayBuffer { #[inline] pub fn nearest_at(&self, delay_time_ms: F) -> F { let len = self.data.len(); - let offs = ((delay_time_ms * self.srate) / 1000.0).floor() as usize % len; + let offs = + ((delay_time_ms * self.srate) + / f(1000.0)) + .floor().to_usize().unwrap() % len; let idx = ((self.wr + len) - offs) % len; self.data[idx] } @@ -845,11 +862,11 @@ impl DelayBuffer { const DEFAULT_ALLPASS_COMB_SAMPLES : usize = 8 * 48000; #[derive(Debug, Clone, Default)] -pub struct AllPass { +pub struct AllPass { delay: DelayBuffer, } -impl AllPass { +impl AllPass { pub fn new() -> Self { Self { delay: DelayBuffer::new_with_size(DEFAULT_ALLPASS_COMB_SAMPLES), @@ -956,7 +973,7 @@ pub fn process_1pole_lowpass(input: f32, freq: f32, israte: f32, z: &mut f32) -> } #[derive(Debug, Clone, Copy, Default)] -pub struct OnePoleLPF { +pub struct OnePoleLPF { israte: F, a: F, b: F, @@ -964,29 +981,29 @@ pub struct OnePoleLPF { z: F, } -impl OnePoleLPF { +impl OnePoleLPF { pub fn new() -> Self { Self { - israte: 1.0 / 44100.0, - a: 0.0, - b: 0.0, - freq: 1000.0, - z: 0.0, + israte: f::(1.0) / f(44100.0), + a: f::(0.0), + b: f::(0.0), + freq: f::(1000.0), + z: f::(0.0), } } pub fn reset(&mut self) { - self.z = 0.0; + self.z = f(0.0); } #[inline] fn recalc(&mut self) { - self.b = (-F::TAU * self.freq * self.israte).exp(); - self.a = 1.0 - self.b; + self.b = (f::(-1.0) * F::TAU() * self.freq * self.israte).exp(); + self.a = f::(1.0) - self.b; } pub fn set_sample_rate(&mut self, srate: F) { - self.israte = 1.0 / srate; + self.israte = f::(1.0) / srate; self.recalc(); } @@ -1045,7 +1062,7 @@ pub fn process_1pole_highpass(input: f32, freq: f32, israte: f32, z: &mut f32, y } #[derive(Debug, Clone, Copy, Default)] -pub struct OnePoleHPF { +pub struct OnePoleHPF { israte: F, a: F, b: F, @@ -1054,32 +1071,32 @@ pub struct OnePoleHPF { y: F, } -impl OnePoleHPF { +impl OnePoleHPF { pub fn new() -> Self { Self { - israte: 1.0 / 44100.0, - a: 0.0, - b: 0.0, - freq: 1000.0, - z: 0.0, - y: 0.0, + israte: f(1.0 / 44100.0), + a: f(0.0), + b: f(0.0), + freq: f(1000.0), + z: f(0.0), + y: f(0.0), } } pub fn reset(&mut self) { - self.z = 0.0; - self.y = 0.0; + self.z = f(0.0); + self.y = f(0.0); } #[inline] fn recalc(&mut self) { - self.b = (-F::TAU * self.freq * self.israte).exp(); - self.a = (1.0 + self.b) / 2.0; + self.b = (f::(-1.0) * F::TAU() * self.freq * self.israte).exp(); + self.a = (f::(1.0) + self.b) / f(2.0); } pub fn set_sample_rate(&mut self, srate: F) { - self.israte = 1.0 / srate; + self.israte = f::(1.0) / srate; self.recalc(); } @@ -1401,38 +1418,38 @@ pub fn process_stilson_moog( // Copyright (C) 2020 TheWaveWarden // under GPLv3 or any later #[derive(Debug, Clone, Copy)] -pub struct DCBlockFilter { +pub struct DCBlockFilter { xm1: F, ym1: F, r: F, } -impl DCBlockFilter { +impl DCBlockFilter { pub fn new() -> Self { Self { - xm1: 0.0, - ym1: 0.0, - r: 0.995, + xm1: f(0.0), + ym1: f(0.0), + r: f(0.995), } } pub fn reset(&mut self) { - self.xm1 = 0.0; - self.ym1 = 0.0; + self.xm1 = f(0.0); + self.ym1 = f(0.0); } pub fn set_sample_rate(&mut self, srate: F) { - self.r = 0.995; - if srate > 90000.0 { - self.r = 0.9965; - } else if srate > 120000.0 { - self.r = 0.997; + self.r = f(0.995); + if srate > f(90000.0) { + self.r = f(0.9965); + } else if srate > f(120000.0) { + self.r = f(0.997); } } pub fn next(&mut self, input: F) -> F { - let y = input as f64 - self.xm1 + self.r * self.ym1; - self.xm1 = input as f64; + let y = input - self.xm1 + self.r * self.ym1; + self.xm1 = input; self.ym1 = y; y as F } @@ -1883,7 +1900,7 @@ impl VPSOscillator { /// An LFO with a variable reverse point, which can go from reverse Saw, to Tri /// and to Saw, depending on the reverse point. #[derive(Debug, Clone, Copy)] -pub struct TriSawLFO { +pub struct TriSawLFO { /// The (inverse) sample rate. Eg. 1.0 / 44100.0. israte: F, /// The current oscillator phase. @@ -1901,17 +1918,17 @@ pub struct TriSawLFO { init_phase: F, } -impl TriSawLFO { +impl TriSawLFO { pub fn new() -> Self { let mut this = Self { - israte: 1.0 / 44100.0, - phase: 0.0, - rev: 0.5, + israte: f(1.0 / 44100.0), + phase: f(0.0), + rev: f(0.5), rising: true, - freq: 1.0, - fall_r: 0.0, - rise_r: 0.0, - init_phase: 0.0, + freq: f(1.0), + fall_r: f(0.0), + rise_r: f(0.0), + init_phase: f(0.0), }; this.recalc(); this @@ -1924,19 +1941,19 @@ impl TriSawLFO { #[inline] fn recalc(&mut self) { - self.rev = self.rev.clamp(0.0001, 0.999); - self.rise_r = 1.0 / self.rev; - self.fall_r = -1.0 / (1.0 - self.rev); + self.rev = fclampc(self.rev, 0.0001, 0.999); + self.rise_r = f::( 1.0) / self.rev; + self.fall_r = f::(-1.0) / (f::(1.0) - self.rev); } pub fn set_sample_rate(&mut self, srate: F) { - self.israte = 1.0 / (srate as F); + self.israte = f::(1.0) / (srate as F); self.recalc(); } pub fn reset(&mut self) { self.phase = self.init_phase; - self.rev = 0.5; + self.rev = f(0.5); self.rising = true; } @@ -1949,8 +1966,8 @@ impl TriSawLFO { #[inline] pub fn next_unipolar(&mut self) -> F { - if self.phase >= 1.0 { - self.phase -= 1.0; + if self.phase >= f(1.0) { + self.phase = self.phase - f(1.0); self.rising = true; } @@ -1965,14 +1982,14 @@ impl TriSawLFO { self.phase * self.fall_r - self.fall_r }; - self.phase += self.freq * self.israte; + self.phase = self.phase + self.freq * self.israte; s } #[inline] pub fn next_bipolar(&mut self) -> F { - (self.next_unipolar() * 2.0) - 1.0 + (self.next_unipolar() * f(2.0)) - f(1.0) } } diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 9c0413f..42e4ee2 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -1845,7 +1845,7 @@ mod tests { #[test] fn check_node_size_staying_small() { - assert_eq!(std::mem::size_of::(), 48); + assert_eq!(std::mem::size_of::(), 56); assert_eq!(std::mem::size_of::(), 2); assert_eq!(std::mem::size_of::(), 24); } diff --git a/src/dsp/node_pverb.rs b/src/dsp/node_pverb.rs index 619d2be..b7dc3a9 100644 --- a/src/dsp/node_pverb.rs +++ b/src/dsp/node_pverb.rs @@ -4,9 +4,7 @@ use crate::nodes::{NodeAudioContext, NodeExecContext}; use crate::dsp::{ - NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, - GraphAtomData, GraphFun, NodeContext, - denorm + NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext, denorm }; use super::helpers::crossfade; use super::dattorro::{ @@ -161,7 +159,7 @@ impl DspNode for PVerb { _atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { - use crate::dsp::{out, inp, out_idx}; + use crate::dsp::{inp, out_idx}; let in_l = inp::PVerb::in_l(inputs); let in_r = inp::PVerb::in_r(inputs); diff --git a/src/dsp/node_tslfo.rs b/src/dsp/node_tslfo.rs index d524236..a822529 100644 --- a/src/dsp/node_tslfo.rs +++ b/src/dsp/node_tslfo.rs @@ -73,7 +73,7 @@ impl DspNode for TsLfo { atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { - use crate::dsp::{out, inp, denorm, at}; + use crate::dsp::{out, inp, denorm}; let time = inp::TsLfo::time(inputs); let trig = inp::TsLfo::trig(inputs); diff --git a/tests/node_allp.rs b/tests/node_allp.rs index a4b9cdd..94bcc57 100644 --- a/tests/node_allp.rs +++ b/tests/node_allp.rs @@ -47,17 +47,16 @@ fn check_node_allp() { // allpass feedback of the original signal for 2ms: // XXX: the smearing before and after the allpass is due to the // cubic interpolation! - v.append(&mut vec![-0.01606654, 0.13159, 0.54748535]); - v.append(&mut vec![0.51; (2.0 * 44.1_f32).ceil() as usize - 3]); - v.append(&mut vec![0.52606, 0.37840945, -0.037485]); + v.append(&mut vec![0.0, 0.0]); + v.append(&mut vec![0.51; (2.0 * 44.1_f32).ceil() as usize]); // 1ms allpass silence like before: - v.append(&mut vec![0.0; (1.0 * 44.1_f32).floor() as usize - 6]); + v.append(&mut vec![0.0; (1.0 * 44.1_f32).floor() as usize - 1]); // 2ms the previous 1.0 * 0.7 fed back into the filter, // including even more smearing due to cubic interpolation: - v.append(&mut vec![-0.000354, 0.00615, -0.005424, -0.17565, -0.39786, -0.3550714]); - v.append(&mut vec![-0.357; (2.0 * 44.1_f32).floor() as usize - 5]); - v.append(&mut vec![-0.3566457, -0.363158, -0.35157552, -0.18134634, 0.040867306, -0.0019286368]); +// v.append(&mut vec![-0.000354, 0.00615, -0.005424, -0.17565, -0.39786, -0.3550714]); + v.append(&mut vec![-0.357; (2.0 * 44.1_f32).floor() as usize + 1]); + v.append(&mut vec![0.0; 10]); //d// println!("res={:?}", res.1); assert_vec_feq!(res.0, v); diff --git a/tests/node_sfilter.rs b/tests/node_sfilter.rs index 9011298..72a7276 100644 --- a/tests/node_sfilter.rs +++ b/tests/node_sfilter.rs @@ -1064,8 +1064,8 @@ fn check_node_sfilter_moog_lowpass() { let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 22050.0, 0.0); assert_eq!( avg_fft_freqs(8.0, &[100, 1000, 4000, 12000, 16000, 20000, 22050, 22051], &fft[..]), vec![ - (0, 16), (100, 16), (1000, 16), (4000, 16), (12000, 16), - (16000, 16), (20000, 8), (22050, 8) + (0, 16), (100, 16), (1000, 16), (4000, 16), (12000, 8), + (16000, 16), (20000, 8), (22050, 0) ]); // Low Pass Stilson/Moog @ 22050Hz RES=1.0