ruffbox_synth/building_blocks/
delay.rs

1use crate::building_blocks::filters::*;
2use crate::building_blocks::interpolation::*;
3use crate::building_blocks::{
4    Modulator, MonoEffect, SampleBuffer, SynthParameterLabel, SynthParameterValue, ValueOrModulator,
5};
6
7use super::FilterType;
8
9pub struct MonoDelay<const BUFSIZE: usize> {
10    // user parameters
11    rate: f32,
12    time: f32,
13    feedback: f32,
14
15    // internal parameters
16    buffer: Vec<f32>, // max 2 sec for now
17    buffer_ptr: f32,
18    max_buffer_ptr: f32,
19    dampening_filter: Box<dyn MonoEffect<BUFSIZE> + Sync + Send>,
20    samplerate: f32,
21
22    // modulator slots
23    rate_mod: Option<Modulator<BUFSIZE>>,
24    time_mod: Option<Modulator<BUFSIZE>>,
25    fb_mod: Option<Modulator<BUFSIZE>>,
26}
27
28impl<const BUFSIZE: usize> MonoDelay<BUFSIZE> {
29    pub fn with_filter_type(sr: f32, filter_type: FilterType) -> Self {
30        MonoDelay {
31            rate: 1.0,
32            time: 0.256,
33            buffer: vec![0.0; sr as usize * 2 + 3],
34            buffer_ptr: 1.0,
35            max_buffer_ptr: (sr * 0.256) + 1.0, // 256 ms default time
36            feedback: 0.5,
37            dampening_filter: match filter_type {
38                FilterType::Dummy => Box::new(DummyFilter::new()),
39                FilterType::Lpf18 => Box::new(Lpf18::new(1500.0, 0.5, 0.1, sr)),
40                FilterType::BiquadLpf12dB => Box::new(BiquadLpf12dB::new(1500.0, 0.5, sr)),
41                FilterType::BiquadLpf24dB => Box::new(BiquadLpf24dB::new(1500.0, 0.5, sr)),
42                FilterType::BiquadHpf12dB => Box::new(BiquadHpf12dB::new(1500.0, 0.5, sr)),
43                FilterType::BiquadHpf24dB => Box::new(BiquadHpf24dB::new(1500.0, 0.5, sr)),
44                FilterType::ButterworthLpf(order) => {
45                    Box::new(ButterworthLpf::new(1500.0, order, sr))
46                }
47                FilterType::ButterworthHpf(order) => {
48                    Box::new(ButterworthHpf::new(1500.0, order, sr))
49                }
50                FilterType::PeakEQ => Box::new(PeakEq::new(1500.0, 100.0, 0.0, sr)),
51            },
52            samplerate: sr,
53            rate_mod: None,
54            time_mod: None,
55            fb_mod: None,
56        }
57    }
58
59    pub fn new(sr: f32) -> Self {
60        MonoDelay {
61            rate: 1.0,
62            time: 0.256,
63            buffer: vec![0.0; sr as usize * 2 + 3],
64            buffer_ptr: 1.0,
65            max_buffer_ptr: (sr * 0.256) + 1.0, // 256 ms default time
66            feedback: 0.5,
67            dampening_filter: Box::new(Lpf18::new(3000.0, 0.4, 0.3, 44100.0)),
68            samplerate: sr,
69            rate_mod: None,
70            time_mod: None,
71            fb_mod: None,
72        }
73    }
74}
75
76impl<const BUFSIZE: usize> MonoEffect<BUFSIZE> for MonoDelay<BUFSIZE> {
77    fn set_modulator(
78        &mut self,
79        par: SynthParameterLabel,
80        init: f32,
81        modulator: Modulator<BUFSIZE>,
82    ) {
83        match par {
84            SynthParameterLabel::DelayDampeningFrequency => {
85                self.dampening_filter.set_modulator(
86                    SynthParameterLabel::LowpassCutoffFrequency,
87                    init,
88                    modulator,
89                );
90            }
91            SynthParameterLabel::DelayFeedback => {
92                self.feedback = init;
93                self.fb_mod = Some(modulator);
94            }
95            SynthParameterLabel::DelayRate => {
96                self.rate = init;
97                self.rate_mod = Some(modulator);
98            }
99            SynthParameterLabel::DelayTime => {
100                self.time = init;
101                self.time_mod = Some(modulator);
102            }
103            _ => {}
104        }
105    }
106    // some parameter limits might be nice ...
107    fn set_parameter(&mut self, par: SynthParameterLabel, value: &SynthParameterValue) {
108        if let SynthParameterValue::ScalarF32(val) = value {
109            match par {
110                SynthParameterLabel::DelayDampeningFrequency => self
111                    .dampening_filter
112                    .set_parameter(SynthParameterLabel::LowpassCutoffFrequency, value),
113                SynthParameterLabel::DelayFeedback => self.feedback = *val,
114                SynthParameterLabel::DelayRate => self.rate = *val,
115                SynthParameterLabel::DelayTime => {
116                    self.time = *val;
117                    self.max_buffer_ptr = self.samplerate * self.time + 1.0;
118                }
119                _ => (),
120            };
121        }
122    }
123
124    fn finish(&mut self) {} // this effect is stateless
125    fn is_finished(&self) -> bool {
126        false
127    } // it's never finished ..
128
129    // start sample isn't really needed either ...
130    fn process_block(
131        &mut self,
132        block: [f32; BUFSIZE],
133        start_sample: usize,
134        in_buffers: &[SampleBuffer],
135    ) -> [f32; BUFSIZE] {
136        let mut out_buf: [f32; BUFSIZE] = [0.0; BUFSIZE];
137
138        if self.fb_mod.is_some() || self.rate_mod.is_some() || self.time_mod.is_some() {
139            let fb_buf = if let Some(m) = self.fb_mod.as_mut() {
140                m.process(self.feedback, start_sample, in_buffers)
141            } else {
142                [self.feedback; BUFSIZE]
143            };
144
145            let rate_buf = if let Some(m) = self.rate_mod.as_mut() {
146                m.process(self.rate, start_sample, in_buffers)
147            } else {
148                [self.rate; BUFSIZE]
149            };
150
151            let time_buf = if let Some(m) = self.time_mod.as_mut() {
152                m.process(self.time, start_sample, in_buffers)
153                    .map(|x| (self.samplerate * x) + 1.0)
154            } else {
155                [self.samplerate * self.time; BUFSIZE]
156            };
157
158            for i in 0..BUFSIZE {
159                // get sample:
160                let idx = self.buffer_ptr.floor();
161                let frac = self.buffer_ptr - idx;
162                let idx_u = idx as usize;
163
164                // 4-point, 3rd-order Hermite
165                let buf_out = interpolate(
166                    frac,
167                    self.buffer[idx_u - 1],
168                    self.buffer[idx_u],
169                    self.buffer[idx_u + 1],
170                    self.buffer[idx_u + 2],
171                    1.0,
172                );
173
174                self.buffer[idx_u] =
175                    (self.dampening_filter.maybe_process_sample(buf_out) * fb_buf[i]) + block[i];
176
177                out_buf[i] = self.buffer[idx_u];
178
179                // increment delay idx
180                self.buffer_ptr += rate_buf[i];
181                if self.buffer_ptr >= time_buf[i] {
182                    self.buffer_ptr = 1.0 + (self.buffer_ptr - time_buf[i]);
183                }
184            }
185        } else {
186            for i in 0..BUFSIZE {
187                // get sample:
188                let idx = self.buffer_ptr.floor();
189                let frac = self.buffer_ptr - idx;
190                let idx_u = idx as usize;
191
192                // 4-point, 3rd-order Hermite
193                let buf_out = interpolate(
194                    frac,
195                    self.buffer[idx_u - 1],
196                    self.buffer[idx_u],
197                    self.buffer[idx_u + 1],
198                    self.buffer[idx_u + 2],
199                    1.0,
200                );
201
202                self.buffer[idx_u] = (self.dampening_filter.maybe_process_sample(buf_out)
203                    * self.feedback)
204                    + block[i];
205
206                out_buf[i] = self.buffer[idx_u];
207
208                // increment delay idx
209                self.buffer_ptr += self.rate;
210                if self.buffer_ptr >= self.max_buffer_ptr {
211                    self.buffer_ptr = 1.0 + (self.buffer_ptr - self.max_buffer_ptr);
212                }
213            }
214        }
215
216        out_buf
217    }
218}
219
220pub struct MultichannelDelay<const BUFSIZE: usize, const NCHAN: usize> {
221    delays: Vec<MonoDelay<BUFSIZE>>,
222}
223
224impl<const BUFSIZE: usize, const NCHAN: usize> MultichannelDelay<BUFSIZE, NCHAN> {
225    pub fn new(sr: f32) -> Self {
226        let mut delays = Vec::new();
227
228        for _ in 0..NCHAN {
229            delays.push(MonoDelay::<BUFSIZE>::new(sr));
230        }
231
232        MultichannelDelay { delays }
233    }
234
235    pub fn set_parameter(&mut self, par: SynthParameterLabel, val: &SynthParameterValue) {
236        for c in 0..NCHAN {
237            self.delays[c].set_parameter(par, val);
238        }
239    }
240
241    pub fn set_param_or_modulator(
242        &mut self,
243        par: SynthParameterLabel,
244        val_or_mod: ValueOrModulator<BUFSIZE>,
245    ) {
246        match val_or_mod {
247            ValueOrModulator::Val(val) => self.set_parameter(par, &val),
248            ValueOrModulator::Mod(init, modulator) => self.set_modulator(par, init, modulator),
249        }
250    }
251
252    pub fn set_modulator(
253        &mut self,
254        par: SynthParameterLabel,
255        init: f32,
256        modulator: Modulator<BUFSIZE>,
257    ) {
258        for c in 0..NCHAN {
259            // i might want to think of a better method because
260            // this clone will be called in the audio thread, even though
261            // this won't be super common, but still ...
262            self.delays[c].set_modulator(par, init, modulator.clone());
263        }
264    }
265
266    pub fn process(
267        &mut self,
268        block: [[f32; BUFSIZE]; NCHAN],
269        sample_buffers: &[SampleBuffer],
270    ) -> [[f32; BUFSIZE]; NCHAN] {
271        let mut out_buf = [[0.0; BUFSIZE]; NCHAN];
272
273        for c in 0..NCHAN {
274            out_buf[c] = self.delays[c].process_block(block[c], 0, sample_buffers);
275        }
276
277        out_buf
278    }
279}