diff --git a/src/dsp/helpers.rs b/src/dsp/helpers.rs index 067e9ed..a8e1837 100644 --- a/src/dsp/helpers.rs +++ b/src/dsp/helpers.rs @@ -160,10 +160,12 @@ impl SplitMix64 { } } -pub fn mix(v1: f32, v2: f32, mix: f32) -> f32 { +#[inline] +pub fn crossfade(v1: f32, v2: f32, mix: f32) -> f32 { v1 * (1.0 - mix) + v2 * mix } +#[inline] pub fn clamp(f: f32, min: f32, max: f32) -> f32 { if f < min { min } else if f > max { max } @@ -482,7 +484,8 @@ impl TriggerClock { /// Default size of the delay buffer: 5 seconds at 8 times 48kHz const DEFAULT_DELAY_BUFFER_SAMPLES : usize = 8 * 48000 * 5; -struct DelayBuffer { +#[derive(Debug, Clone)] +pub struct DelayBuffer { data: Vec, wr: usize, srate: f32, diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 087ee30..12e11ac 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -363,10 +363,11 @@ define_exp!{n_declick d_declick 0.0, 50.0} define_exp!{n_env d_env 0.0, 1000.0} +define_exp!{n_time d_time 0.0, 5000.0} + // Special linear gain factor for the Out node, to be able // to reach more exact "1.0". define_lin!{n_ogin d_ogin 0.0, 2.0} -define_lin!{n_time d_time 0.0, 5000.0} // A note about the input-indicies: // diff --git a/src/dsp/node_delay.rs b/src/dsp/node_delay.rs index f34c4fd..02e7a09 100644 --- a/src/dsp/node_delay.rs +++ b/src/dsp/node_delay.rs @@ -3,7 +3,8 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, GraphFun, GraphAtomData, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::helpers::{DelayBuffer, crossfade}; #[macro_export] macro_rules! fa_dly_s { ($formatter: expr, $v: expr, $denorm_v: expr) => { { @@ -28,11 +29,15 @@ macro_rules! fa_dly_s { ($formatter: expr, $v: expr, $denorm_v: expr) => { { /// A simple amplifier #[derive(Debug, Clone)] pub struct Delay { + buffer: Box, + fb_sample: f32, } impl Delay { pub fn new(_nid: &NodeId) -> Self { Self { + buffer: Box::new(DelayBuffer::new()), + fb_sample: 0.0, } } @@ -75,27 +80,48 @@ For other kinds of delay/feedback please see also the 'FbWr'/'FbRd' nodes. impl DspNode for Delay { fn outputs() -> usize { 1 } - fn set_sample_rate(&mut self, _srate: f32) { } - fn reset(&mut self) { } + fn set_sample_rate(&mut self, srate: f32) { + self.buffer.set_sample_rate(srate); + } + + fn reset(&mut self) { + self.buffer.reset(); + } #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], _inputs: &[ProcBuf], + _atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], outputs: &mut [ProcBuf], _led: LedPhaseVals) { - use crate::dsp::{out, at}; + use crate::dsp::{out, inp, denorm}; -// let p = at::Delay::p(atoms); + let buffer = &mut *self.buffer; + + let inp = inp::Delay::inp(inputs); + let time = inp::Delay::time(inputs); + let fb = inp::Delay::fb(inputs); + let mix = inp::Delay::mix(inputs); let out = out::Delay::sig(outputs); - for frame in 0..ctx.nframes() { - out.write(frame, 0.0); - } - } - fn graph_fun() -> Option { - Some(Box::new(|_gd: &dyn GraphAtomData, _init: bool, x: f32| -> f32 { - x - })) + let mut fb_s = self.fb_sample; + + for frame in 0..ctx.nframes() { + let dry = inp.read(frame); + buffer.feed( + dry + fb_s * denorm::Delay::fb(fb, frame)); + + let out_sample = + buffer.cubic_interpolate_at( + denorm::Delay::time(time, frame)); + + out.write(frame, + crossfade(dry, out_sample, + denorm::Delay::mix(mix, frame).clamp(0.0, 1.0))); + + fb_s = out_sample; + } + + self.fb_sample = fb_s; } }