1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// Copyright (c) 2021-2022 Weird Constructor <weirdconstructor@gmail.com>
// This file is a part of synfx-dsp. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.

//! Interpolated delay line implementation and all-pass/comb filter implementations based on that.

use crate::cubic_interpolate;
use crate::{f, Flt};

/// Default size of the delay buffer: 5 seconds at 8 times 48kHz
const DEFAULT_DELAY_BUFFER_SAMPLES: usize = 8 * 48000 * 5;

/// This is a delay buffer/line with linear and cubic interpolation.
///
/// It's the basic building block underneath the all-pass filter, comb filters and delay effects.
/// You can use linear and cubic and no interpolation to access samples in the past. Either
/// by sample offset or time (millisecond) based.
#[derive(Debug, Clone, Default)]
pub struct DelayBuffer<F: Flt> {
    data: Vec<F>,
    wr: usize,
    srate: F,
}

impl<F: Flt> DelayBuffer<F> {
    /// Creates a delay buffer with about 5 seconds of capacity at 8*48000Hz sample rate.
    pub fn new() -> Self {
        Self { data: vec![f(0.0); DEFAULT_DELAY_BUFFER_SAMPLES], wr: 0, srate: f(44100.0) }
    }

    /// Creates a delay buffer with the given amount of samples capacity.
    pub fn new_with_size(size: usize) -> Self {
        Self { data: vec![f(0.0); size], wr: 0, srate: f(44100.0) }
    }

    /// Sets the sample rate that is used for milliseconds => sample conversion.
    pub fn set_sample_rate(&mut self, srate: F) {
        self.srate = srate;
    }

    /// Reset the delay buffer contents and write position.
    pub fn reset(&mut self) {
        self.data.fill(f(0.0));
        self.wr = 0;
    }

    /// Feed one sample into the delay line and increment the write pointer.
    /// Please note: For sample accurate feedback you need to retrieve the
    /// output of the delay line before feeding in a new signal.
    #[inline]
    pub fn feed(&mut self, input: F) {
        self.data[self.wr] = input;
        self.wr = (self.wr + 1) % self.data.len();
    }

    /// Combines [DelayBuffer::cubic_interpolate_at] and [DelayBuffer::feed]
    /// into one convenient function.
    #[inline]
    pub fn next_cubic(&mut self, delay_time_ms: F, input: F) -> F {
        let res = self.cubic_interpolate_at(delay_time_ms);
        self.feed(input);
        res
    }

    /// Combines [DelayBuffer::linear_interpolate_at] and [DelayBuffer::feed]
    /// into one convenient function.
    #[inline]
    pub fn next_linear(&mut self, delay_time_ms: F, input: F) -> F {
        let res = self.linear_interpolate_at(delay_time_ms);
        self.feed(input);
        res
    }

    /// Combines [DelayBuffer::nearest_at] and [DelayBuffer::feed]
    /// into one convenient function.
    #[inline]
    pub fn next_nearest(&mut self, delay_time_ms: F, input: F) -> F {
        let res = self.nearest_at(delay_time_ms);
        self.feed(input);
        res
    }

    /// Shorthand for [DelayBuffer::cubic_interpolate_at].
    #[inline]
    pub fn tap_c(&self, delay_time_ms: F) -> F {
        self.cubic_interpolate_at(delay_time_ms)
    }

    /// Shorthand for [DelayBuffer::cubic_interpolate_at].
    #[inline]
    pub fn tap_n(&self, delay_time_ms: F) -> F {
        self.nearest_at(delay_time_ms)
    }

    /// Shorthand for [DelayBuffer::cubic_interpolate_at].
    #[inline]
    pub fn tap_l(&self, delay_time_ms: F) -> F {
        self.linear_interpolate_at(delay_time_ms)
    }

    /// Fetch a sample from the delay buffer at the given tim with linear interpolation.
    ///
    /// * `delay_time_ms` - Delay time in milliseconds.
    #[inline]
    pub fn linear_interpolate_at(&self, delay_time_ms: F) -> F {
        self.linear_interpolate_at_s((delay_time_ms * self.srate) / f(1000.0))
    }

    /// Fetch a sample from the delay buffer at the given offset with linear interpolation.
    ///
    /// * `s_offs` - Sample offset in samples.
    #[inline]
    pub fn linear_interpolate_at_s(&self, s_offs: F) -> F {
        let data = &self.data[..];
        let len = data.len();
        let offs = s_offs.floor().to_usize().unwrap_or(0) % len;
        let fract = s_offs.fract();

        // one extra offset, because feed() advances self.wr to the next writing position!
        let i = (self.wr + len) - (offs + 1);
        let x0 = data[i % len];
        let x1 = data[(i - 1) % len];

        let res = x0 + fract * (x1 - x0);
        //d// eprintln!(
        //d//     "INTERP: {:6.4} x0={:6.4} x1={:6.4} fract={:6.4} => {:6.4}",
        //d//     s_offs.to_f64().unwrap_or(0.0),
        //d//     x0.to_f64().unwrap(),
        //d//     x1.to_f64().unwrap(),
        //d//     fract.to_f64().unwrap(),
        //d//     res.to_f64().unwrap(),
        //d// );
        res
    }

    /// Fetch a sample from the delay buffer at the given time with cubic interpolation.
    ///
    /// * `delay_time_ms` - Delay time in milliseconds.
    #[inline]
    pub fn cubic_interpolate_at(&self, delay_time_ms: F) -> F {
        self.cubic_interpolate_at_s((delay_time_ms * self.srate) / f(1000.0))
    }

    /// Fetch a sample from the delay buffer at the given offset with cubic interpolation.
    ///
    /// * `s_offs` - Sample offset in samples into the past of the [DelayBuffer]
    /// from the current write (or the "now") position.
    #[inline]
    pub fn cubic_interpolate_at_s(&self, s_offs: F) -> F {
        let data = &self.data[..];
        let len = data.len();
        let offs = s_offs.floor().to_usize().unwrap_or(0) % len;
        let fract = s_offs.fract();

        // (offs + 1) offset for compensating that self.wr points to the next
        // unwritten position.
        // Additional (offs + 1 + 1) offset for cubic_interpolate, which
        // interpolates into the past through the delay buffer.
        let i = (self.wr + len) - (offs + 2);
        let res = cubic_interpolate(data, len, i, f::<F>(1.0) - fract);
        //        eprintln!(
        //            "cubic at={} ({:6.4}) res={:6.4}",
        //            i % len,
        //            s_offs.to_f64().unwrap(),
        //            res.to_f64().unwrap()
        //        );
        res
    }

    /// Fetch a sample from the delay buffer at the given time without any interpolation.
    ///
    /// * `delay_time_ms` - Delay time in milliseconds.
    #[inline]
    pub fn nearest_at(&self, delay_time_ms: F) -> F {
        let len = self.data.len();
        let offs = ((delay_time_ms * self.srate) / f(1000.0)).floor().to_usize().unwrap_or(0) % len;
        // (offs + 1) one extra offset, because feed() advances
        // self.wr to the next writing position!
        let idx = ((self.wr + len) - (offs + 1)) % len;
        self.data[idx]
    }

    /// Fetch a sample from the delay buffer at the given number of samples in the past.
    #[inline]
    pub fn at(&self, delay_sample_count: usize) -> F {
        let len = self.data.len();
        // (delay_sample_count + 1) one extra offset, because feed() advances self.wr to
        // the next writing position!
        let idx = ((self.wr + len) - (delay_sample_count + 1)) % len;
        self.data[idx]
    }
}

/// Default size of the delay buffer: 1 seconds at 8 times 48kHz
const DEFAULT_ALLPASS_COMB_SAMPLES: usize = 8 * 48000;

/// An all-pass filter based on a delay line.
#[derive(Debug, Clone, Default)]
pub struct AllPass<F: Flt> {
    delay: DelayBuffer<F>,
}

impl<F: Flt> AllPass<F> {
    /// Creates a new all-pass filter with about 1 seconds space for samples.
    pub fn new() -> Self {
        Self { delay: DelayBuffer::new_with_size(DEFAULT_ALLPASS_COMB_SAMPLES) }
    }

    /// Set the sample rate for millisecond based access.
    pub fn set_sample_rate(&mut self, srate: F) {
        self.delay.set_sample_rate(srate);
    }

    /// Reset the internal delay buffer.
    pub fn reset(&mut self) {
        self.delay.reset();
    }

    /// Access the internal delay at the given amount of milliseconds in the past.
    #[inline]
    pub fn delay_tap_n(&self, time_ms: F) -> F {
        self.delay.tap_n(time_ms)
    }

    /// Retrieve the next (cubic interpolated) sample from the all-pass
    /// filter while feeding in the next.
    ///
    /// * `time_ms` - Delay time in milliseconds.
    /// * `g` - Feedback factor (usually something around 0.7 is common)
    /// * `v` - The new input sample to feed the filter.
    #[inline]
    pub fn next(&mut self, time_ms: F, g: F, v: F) -> F {
        let s = self.delay.cubic_interpolate_at(time_ms);
        let input = v + -g * s;
        self.delay.feed(input);
        input * g + s
    }

    /// Retrieve the next (linear interpolated) sample from the all-pass
    /// filter while feeding in the next.
    ///
    /// * `time_ms` - Delay time in milliseconds.
    /// * `g` - Feedback factor (usually something around 0.7 is common)
    /// * `v` - The new input sample to feed the filter.
    #[inline]
    pub fn next_linear(&mut self, time_ms: F, g: F, v: F) -> F {
        let s = self.delay.linear_interpolate_at(time_ms);
        let input = v + -g * s;
        self.delay.feed(input);
        input * g + s
    }
}

#[derive(Debug, Clone)]
pub struct Comb {
    delay: DelayBuffer<f32>,
}

impl Comb {
    pub fn new() -> Self {
        Self { delay: DelayBuffer::new_with_size(DEFAULT_ALLPASS_COMB_SAMPLES) }
    }

    pub fn set_sample_rate(&mut self, srate: f32) {
        self.delay.set_sample_rate(srate);
    }

    pub fn reset(&mut self) {
        self.delay.reset();
    }

    #[inline]
    pub fn delay_tap_c(&self, time_ms: f32) -> f32 {
        self.delay.tap_c(time_ms)
    }

    #[inline]
    pub fn delay_tap_n(&self, time_ms: f32) -> f32 {
        self.delay.tap_n(time_ms)
    }

    #[inline]
    pub fn next_feedback(&mut self, time: f32, g: f32, v: f32) -> f32 {
        let s = self.delay.cubic_interpolate_at(time);
        let v = v + s * g;
        self.delay.feed(v);
        v
    }

    #[inline]
    pub fn next_feedforward(&mut self, time: f32, g: f32, v: f32) -> f32 {
        let s = self.delay.next_cubic(time, v);
        v + s * g
    }
}