synfx_dsp/
delay.rs

1// Copyright (c) 2021-2022 Weird Constructor <weirdconstructor@gmail.com>
2// This file is a part of synfx-dsp. Released under GPL-3.0-or-later.
3// See README.md and COPYING for details.
4
5//! Interpolated delay line implementation and all-pass/comb filter implementations based on that.
6
7use crate::cubic_interpolate;
8use crate::{f, Flt};
9
10/// Default size of the delay buffer: 5 seconds at 8 times 48kHz
11const DEFAULT_DELAY_BUFFER_SAMPLES: usize = 8 * 48000 * 5;
12
13/// This is a delay buffer/line with linear and cubic interpolation.
14///
15/// It's the basic building block underneath the all-pass filter, comb filters and delay effects.
16/// You can use linear and cubic and no interpolation to access samples in the past. Either
17/// by sample offset or time (millisecond) based.
18#[derive(Debug, Clone, Default)]
19pub struct DelayBuffer<F: Flt> {
20    data: Vec<F>,
21    wr: usize,
22    srate: F,
23}
24
25impl<F: Flt> DelayBuffer<F> {
26    /// Creates a delay buffer with about 5 seconds of capacity at 8*48000Hz sample rate.
27    pub fn new() -> Self {
28        Self { data: vec![f(0.0); DEFAULT_DELAY_BUFFER_SAMPLES], wr: 0, srate: f(44100.0) }
29    }
30
31    /// Creates a delay buffer with the given amount of samples capacity.
32    pub fn new_with_size(size: usize) -> Self {
33        Self { data: vec![f(0.0); size], wr: 0, srate: f(44100.0) }
34    }
35
36    /// Sets the sample rate that is used for milliseconds => sample conversion.
37    pub fn set_sample_rate(&mut self, srate: F) {
38        self.srate = srate;
39    }
40
41    /// Reset the delay buffer contents and write position.
42    pub fn reset(&mut self) {
43        self.data.fill(f(0.0));
44        self.wr = 0;
45    }
46
47    /// Feed one sample into the delay line and increment the write pointer.
48    /// Please note: For sample accurate feedback you need to retrieve the
49    /// output of the delay line before feeding in a new signal.
50    #[inline]
51    pub fn feed(&mut self, input: F) {
52        self.data[self.wr] = input;
53        self.wr = (self.wr + 1) % self.data.len();
54    }
55
56    /// Combines [DelayBuffer::cubic_interpolate_at] and [DelayBuffer::feed]
57    /// into one convenient function.
58    #[inline]
59    pub fn next_cubic(&mut self, delay_time_ms: F, input: F) -> F {
60        let res = self.cubic_interpolate_at(delay_time_ms);
61        self.feed(input);
62        res
63    }
64
65    /// Combines [DelayBuffer::linear_interpolate_at] and [DelayBuffer::feed]
66    /// into one convenient function.
67    #[inline]
68    pub fn next_linear(&mut self, delay_time_ms: F, input: F) -> F {
69        let res = self.linear_interpolate_at(delay_time_ms);
70        self.feed(input);
71        res
72    }
73
74    /// Combines [DelayBuffer::nearest_at] and [DelayBuffer::feed]
75    /// into one convenient function.
76    #[inline]
77    pub fn next_nearest(&mut self, delay_time_ms: F, input: F) -> F {
78        let res = self.nearest_at(delay_time_ms);
79        self.feed(input);
80        res
81    }
82
83    /// Shorthand for [DelayBuffer::cubic_interpolate_at].
84    #[inline]
85    pub fn tap_c(&self, delay_time_ms: F) -> F {
86        self.cubic_interpolate_at(delay_time_ms)
87    }
88
89    /// Shorthand for [DelayBuffer::cubic_interpolate_at].
90    #[inline]
91    pub fn tap_n(&self, delay_time_ms: F) -> F {
92        self.nearest_at(delay_time_ms)
93    }
94
95    /// Shorthand for [DelayBuffer::cubic_interpolate_at].
96    #[inline]
97    pub fn tap_l(&self, delay_time_ms: F) -> F {
98        self.linear_interpolate_at(delay_time_ms)
99    }
100
101    /// Fetch a sample from the delay buffer at the given tim with linear interpolation.
102    ///
103    /// * `delay_time_ms` - Delay time in milliseconds.
104    #[inline]
105    pub fn linear_interpolate_at(&self, delay_time_ms: F) -> F {
106        self.linear_interpolate_at_s((delay_time_ms * self.srate) / f(1000.0))
107    }
108
109    /// Fetch a sample from the delay buffer at the given offset with linear interpolation.
110    ///
111    /// * `s_offs` - Sample offset in samples.
112    #[inline]
113    pub fn linear_interpolate_at_s(&self, s_offs: F) -> F {
114        let data = &self.data[..];
115        let len = data.len();
116        let offs = s_offs.floor().to_usize().unwrap_or(0) % len;
117        let fract = s_offs.fract();
118
119        // one extra offset, because feed() advances self.wr to the next writing position!
120        let i = (self.wr + len) - (offs + 1);
121        let x0 = data[i % len];
122        let x1 = data[(i - 1) % len];
123
124        let res = x0 + fract * (x1 - x0);
125        //d// eprintln!(
126        //d//     "INTERP: {:6.4} x0={:6.4} x1={:6.4} fract={:6.4} => {:6.4}",
127        //d//     s_offs.to_f64().unwrap_or(0.0),
128        //d//     x0.to_f64().unwrap(),
129        //d//     x1.to_f64().unwrap(),
130        //d//     fract.to_f64().unwrap(),
131        //d//     res.to_f64().unwrap(),
132        //d// );
133        res
134    }
135
136    /// Fetch a sample from the delay buffer at the given time with cubic interpolation.
137    ///
138    /// * `delay_time_ms` - Delay time in milliseconds.
139    #[inline]
140    pub fn cubic_interpolate_at(&self, delay_time_ms: F) -> F {
141        self.cubic_interpolate_at_s((delay_time_ms * self.srate) / f(1000.0))
142    }
143
144    /// Fetch a sample from the delay buffer at the given offset with cubic interpolation.
145    ///
146    /// * `s_offs` - Sample offset in samples into the past of the [DelayBuffer]
147    /// from the current write (or the "now") position.
148    #[inline]
149    pub fn cubic_interpolate_at_s(&self, s_offs: F) -> F {
150        let data = &self.data[..];
151        let len = data.len();
152        let offs = s_offs.floor().to_usize().unwrap_or(0) % len;
153        let fract = s_offs.fract();
154
155        // (offs + 1) offset for compensating that self.wr points to the next
156        // unwritten position.
157        // Additional (offs + 1 + 1) offset for cubic_interpolate, which
158        // interpolates into the past through the delay buffer.
159        let i = (self.wr + len) - (offs + 2);
160        let res = cubic_interpolate(data, len, i, f::<F>(1.0) - fract);
161        //        eprintln!(
162        //            "cubic at={} ({:6.4}) res={:6.4}",
163        //            i % len,
164        //            s_offs.to_f64().unwrap(),
165        //            res.to_f64().unwrap()
166        //        );
167        res
168    }
169
170    /// Fetch a sample from the delay buffer at the given time without any interpolation.
171    ///
172    /// * `delay_time_ms` - Delay time in milliseconds.
173    #[inline]
174    pub fn nearest_at(&self, delay_time_ms: F) -> F {
175        let len = self.data.len();
176        let offs = ((delay_time_ms * self.srate) / f(1000.0)).floor().to_usize().unwrap_or(0) % len;
177        // (offs + 1) one extra offset, because feed() advances
178        // self.wr to the next writing position!
179        let idx = ((self.wr + len) - (offs + 1)) % len;
180        self.data[idx]
181    }
182
183    /// Fetch a sample from the delay buffer at the given number of samples in the past.
184    #[inline]
185    pub fn at(&self, delay_sample_count: usize) -> F {
186        let len = self.data.len();
187        // (delay_sample_count + 1) one extra offset, because feed() advances self.wr to
188        // the next writing position!
189        let idx = ((self.wr + len) - (delay_sample_count + 1)) % len;
190        self.data[idx]
191    }
192}
193
194/// Default size of the delay buffer: 1 seconds at 8 times 48kHz
195const DEFAULT_ALLPASS_COMB_SAMPLES: usize = 8 * 48000;
196
197/// An all-pass filter based on a delay line.
198#[derive(Debug, Clone, Default)]
199pub struct AllPass<F: Flt> {
200    delay: DelayBuffer<F>,
201}
202
203impl<F: Flt> AllPass<F> {
204    /// Creates a new all-pass filter with about 1 seconds space for samples.
205    pub fn new() -> Self {
206        Self { delay: DelayBuffer::new_with_size(DEFAULT_ALLPASS_COMB_SAMPLES) }
207    }
208
209    /// Set the sample rate for millisecond based access.
210    pub fn set_sample_rate(&mut self, srate: F) {
211        self.delay.set_sample_rate(srate);
212    }
213
214    /// Reset the internal delay buffer.
215    pub fn reset(&mut self) {
216        self.delay.reset();
217    }
218
219    /// Access the internal delay at the given amount of milliseconds in the past.
220    #[inline]
221    pub fn delay_tap_n(&self, time_ms: F) -> F {
222        self.delay.tap_n(time_ms)
223    }
224
225    /// Retrieve the next (cubic interpolated) sample from the all-pass
226    /// filter while feeding in the next.
227    ///
228    /// * `time_ms` - Delay time in milliseconds.
229    /// * `g` - Feedback factor (usually something around 0.7 is common)
230    /// * `v` - The new input sample to feed the filter.
231    #[inline]
232    pub fn next(&mut self, time_ms: F, g: F, v: F) -> F {
233        let s = self.delay.cubic_interpolate_at(time_ms);
234        let input = v + -g * s;
235        self.delay.feed(input);
236        input * g + s
237    }
238
239    /// Retrieve the next (linear interpolated) sample from the all-pass
240    /// filter while feeding in the next.
241    ///
242    /// * `time_ms` - Delay time in milliseconds.
243    /// * `g` - Feedback factor (usually something around 0.7 is common)
244    /// * `v` - The new input sample to feed the filter.
245    #[inline]
246    pub fn next_linear(&mut self, time_ms: F, g: F, v: F) -> F {
247        let s = self.delay.linear_interpolate_at(time_ms);
248        let input = v + -g * s;
249        self.delay.feed(input);
250        input * g + s
251    }
252}
253
254#[derive(Debug, Clone)]
255pub struct Comb {
256    delay: DelayBuffer<f32>,
257}
258
259impl Comb {
260    pub fn new() -> Self {
261        Self { delay: DelayBuffer::new_with_size(DEFAULT_ALLPASS_COMB_SAMPLES) }
262    }
263
264    pub fn set_sample_rate(&mut self, srate: f32) {
265        self.delay.set_sample_rate(srate);
266    }
267
268    pub fn reset(&mut self) {
269        self.delay.reset();
270    }
271
272    #[inline]
273    pub fn delay_tap_c(&self, time_ms: f32) -> f32 {
274        self.delay.tap_c(time_ms)
275    }
276
277    #[inline]
278    pub fn delay_tap_n(&self, time_ms: f32) -> f32 {
279        self.delay.tap_n(time_ms)
280    }
281
282    #[inline]
283    pub fn next_feedback(&mut self, time: f32, g: f32, v: f32) -> f32 {
284        let s = self.delay.cubic_interpolate_at(time);
285        let v = v + s * g;
286        self.delay.feed(v);
287        v
288    }
289
290    #[inline]
291    pub fn next_feedforward(&mut self, time: f32, g: f32, v: f32) -> f32 {
292        let s = self.delay.next_cubic(time, v);
293        v + s * g
294    }
295}