cute_dsp/
delay.rs

1//! Delay utilities
2//!
3//! This module provides standalone structures for implementing delay lines with
4//! various interpolation methods.
5
6#![allow(unused_imports)]
7
8#[cfg(feature = "std")]
9use std::{vec::Vec, marker::PhantomData};
10
11#[cfg(not(feature = "std"))]
12use core::marker::PhantomData;
13
14#[cfg(all(not(feature = "std"), feature = "alloc"))]
15use alloc::vec::Vec;
16
17use num_traits::Float;
18
19/// Single-channel delay buffer
20///
21/// Access is used with `buffer[index]`, relative to the internal read/write position ("head").
22/// This head is moved using `buffer.advance(1)` (or `buffer.advance(n)`), such that
23/// `buffer[1] == buffer.advance(1)[0]`.
24///
25/// The capacity includes both positive and negative indices. For example, a capacity of 100
26/// would support using any of the ranges:
27///
28/// * `buffer[-99]` to `buffer[0]`
29/// * `buffer[-50]` to `buffer[49]`
30/// * `buffer[0]` to `buffer[99]`
31///
32/// Although buffers are usually used with historical samples accessed using negative indices
33/// e.g. `buffer[-10]`, you could equally use it flipped around (moving the head backwards
34/// through the buffer using `buffer.advance(-1)`).
35pub struct Buffer<T: Float> {
36    buffer_index: usize,
37    buffer_mask: usize,
38    buffer: Vec<T>,
39}
40
41impl<T: Float> Buffer<T> {
42    /// Create a new buffer with the specified minimum capacity
43    pub fn new(min_capacity: usize) -> Self {
44        let mut result = Self {
45            buffer_index: 0,
46            buffer_mask: 0,
47            buffer: Vec::new(),
48        };
49        result.resize(min_capacity, T::zero());
50        result
51    }
52
53    /// Resize the buffer to have at least the specified capacity
54    pub fn resize(&mut self, min_capacity: usize, value: T) {
55        let mut buffer_length = 1;
56        while buffer_length < min_capacity {
57            buffer_length *= 2;
58        }
59        self.buffer = vec![value; buffer_length];
60        self.buffer_mask = buffer_length - 1;
61        self.buffer_index = 0;
62    }
63
64    /// Reset the buffer to a specific value
65    pub fn reset(&mut self, value: T) {
66        for i in 0..self.buffer.len() {
67            self.buffer[i] = value;
68        }
69    }
70
71    /// Access a sample relative to the current position
72    pub fn get(&self, offset: isize) -> T {
73        let index = (self.buffer_index as isize + offset) as usize & self.buffer_mask;
74        self.buffer[index]
75    }
76
77    /// Set a sample relative to the current position
78    pub fn set(&mut self, offset: isize, value: T) {
79        let index = (self.buffer_index as isize + offset) as usize & self.buffer_mask;
80        self.buffer[index] = value;
81    }
82
83    /// Advance the buffer position by the specified amount
84    pub fn advance(&mut self, amount: isize) -> &mut Self {
85        if amount >= 0 {
86            self.buffer_index = self.buffer_index.wrapping_add(amount as usize);
87        } else {
88            self.buffer_index = self.buffer_index.wrapping_sub((-amount) as usize);
89        }
90        self
91    }
92
93    /// Write data into the buffer
94    pub fn write<D>(&mut self, data: &[D], length: usize)
95    where
96        D: Copy + Into<T>,
97    {
98        for i in 0..length {
99            self.set(i as isize, data[i].into());
100        }
101    }
102
103    /// Read data out from the buffer
104    pub fn read<D>(&self, length: usize, data: &mut [D])
105    where
106        T: Into<D>,
107        D: Copy,
108    {
109        for i in 0..length {
110            data[i] = self.get(i as isize).into();
111        }
112    }
113
114    /// Create a view at the current position
115    pub fn view(&self) -> View<T> {
116        View {
117            buffer: self,
118            offset: 0,
119        }
120    }
121
122    /// Create a view at a specific offset from the current position
123    pub fn view_at(&self, offset: isize) -> View<T> {
124        View {
125            buffer: self,
126            offset,
127        }
128    }
129}
130
131/// A view into a buffer at a specific position
132pub struct View<'a, T: Float> {
133    buffer: &'a Buffer<T>,
134    offset: isize,
135}
136
137impl<'a, T: Float> View<'a, T> {
138    /// Access a sample relative to this view's position
139    pub fn get(&self, offset: isize) -> T {
140        self.buffer.get(self.offset + offset)
141    }
142
143    /// Create a new view at an offset from this view
144    pub fn offset(&self, offset: isize) -> View<'a, T> {
145        View {
146            buffer: self.buffer,
147            offset: self.offset + offset,
148        }
149    }
150}
151
152/// Multi-channel delay buffer
153///
154/// This behaves similarly to the single-channel `Buffer`, with the following differences:
155///
156/// * `buffer.channel(c)` returns a view for a single channel
157/// * The constructor and `.resize()` take an additional first `channels` argument.
158pub struct MultiBuffer<T: Float> {
159    channels: usize,
160    stride: usize,
161    buffer: Buffer<T>,
162}
163
164impl<T: Float> MultiBuffer<T> {
165    /// Create a new multi-channel buffer
166    pub fn new(channels: usize, capacity: usize) -> Self {
167        Self {
168            channels,
169            stride: capacity,
170            buffer: Buffer::new(channels * capacity),
171        }
172    }
173
174    /// Resize the buffer
175    pub fn resize(&mut self, channels: usize, capacity: usize, value: T) {
176        self.channels = channels;
177        self.stride = capacity;
178        self.buffer.resize(channels * capacity, value);
179    }
180
181    /// Reset the buffer to a specific value
182    pub fn reset(&mut self, value: T) {
183        self.buffer.reset(value);
184    }
185
186    /// Advance the buffer position
187    pub fn advance(&mut self, amount: isize) -> &mut Self {
188        self.buffer.advance(amount);
189        self
190    }
191
192    /// Get a view for a specific channel
193    pub fn channel(&self, channel: usize) -> View<T> {
194        self.buffer.view_at((channel * self.stride) as isize)
195    }
196
197    /// Get a sample at a specific channel and offset
198    pub fn get(&self, channel: usize, offset: isize) -> T {
199        self.buffer.get((channel * self.stride) as isize + offset)
200    }
201
202    /// Set a sample at a specific channel and offset
203    pub fn set(&mut self, channel: usize, offset: isize, value: T) {
204        self.buffer.set((channel * self.stride) as isize + offset, value);
205    }
206}
207
208/// Nearest-neighbour interpolator
209pub struct InterpolatorNearest<T: Float> {
210    _marker: PhantomData<T>,
211}
212
213impl<T: Float> InterpolatorNearest<T> {
214    /// Create a new nearest-neighbour interpolator
215    pub fn new() -> Self {
216        Self { _marker: PhantomData }
217    }
218
219    /// The number of input samples required
220    pub const INPUT_LENGTH: usize = 1;
221
222    /// The latency introduced by the interpolator
223    pub fn latency() -> T {
224        -T::from(0.5).unwrap()
225    }
226
227    /// Interpolate a fractional sample
228    pub fn fractional<D>(&self, data: &D, _fractional: T) -> T
229    where
230        D: core::ops::Index<usize, Output = T>,
231    {
232        data[0]
233    }
234}
235
236/// Linear interpolator
237pub struct InterpolatorLinear<T: Float> {
238    _marker: PhantomData<T>,
239}
240
241impl<T: Float> InterpolatorLinear<T> {
242    /// Create a new linear interpolator
243    pub fn new() -> Self {
244        Self { _marker: PhantomData }
245    }
246
247    /// The number of input samples required
248    pub const INPUT_LENGTH: usize = 2;
249
250    /// The latency introduced by the interpolator
251    pub fn latency() -> T {
252        T::zero()
253    }
254
255    /// Interpolate a fractional sample
256    pub fn fractional<D>(&self, data: &D, fractional: T) -> T
257    where
258        D: core::ops::Index<usize, Output = T>,
259    {
260        let a = data[0];
261        let b = data[1];
262        a + fractional * (b - a)
263    }
264}
265
266/// Spline cubic interpolator
267pub struct InterpolatorCubic<T: Float> {
268    _marker: PhantomData<T>,
269}
270
271impl<T: Float> InterpolatorCubic<T> {
272    /// Create a new cubic interpolator
273    pub fn new() -> Self {
274        Self { _marker: PhantomData }
275    }
276
277    /// The number of input samples required
278    pub const INPUT_LENGTH: usize = 4;
279
280    /// The latency introduced by the interpolator
281    pub fn latency() -> T {
282        T::one()
283    }
284
285    /// Interpolate a fractional sample
286    pub fn fractional<D>(&self, data: &D, fractional: T) -> T
287    where
288        D: core::ops::Index<usize, Output = T>,
289    {
290        let a = data[0];
291        let b = data[1];
292        let c = data[2];
293        let d = data[3];
294
295        let cb_diff = c - b;
296        let half = T::from(0.5).unwrap();
297        let k1 = (c - a) * half;
298        let two = T::one() + T::one();
299        let k3 = k1 + (d - b) * half - cb_diff * two;
300        let k2 = cb_diff - k3 - k1;
301
302        b + fractional * (k1 + fractional * (k2 + fractional * k3))
303    }
304}
305
306/// A delay-line reader which uses an external buffer
307pub struct Reader<T: Float, I> {
308    interpolator: I,
309    _marker: PhantomData<T>,
310}
311
312impl<T: Float, I> Reader<T, I> {
313    /// Create a new reader with the default interpolator
314    pub fn new(interpolator: I) -> Self {
315        Self {
316            interpolator,
317            _marker: PhantomData,
318        }
319    }
320
321    /// Read a sample from the buffer with the specified delay
322    pub fn read<B>(&self, buffer: &B, delay_samples: T) -> T
323    where
324        I: InterpolatorTrait<T>,
325        B: BufferTrait<T>,
326    {
327        let start_index = delay_samples.floor().to_usize().unwrap_or(0);
328        let remainder = delay_samples - T::from(start_index).unwrap();
329
330        // Create a flipped view for the interpolator
331        struct Flipped<'a, T: Float, B: BufferTrait<T>> {
332            view: &'a B,
333            start_index: usize,
334            _marker: PhantomData<T>,
335        }
336
337        impl<'a, T: Float, B: BufferTrait<T>> core::ops::Index<usize> for Flipped<'a, T, B> {
338            type Output = T;
339
340            fn index(&self, index: usize) -> &Self::Output {
341                // Delay buffers use negative indices, but interpolators use positive ones
342                let offset = -(index as isize) - (self.start_index as isize);
343                self.view.get_ref(offset)
344            }
345        }
346
347        let flipped = Flipped {
348            view: buffer,
349            start_index,
350            _marker: PhantomData,
351        };
352
353        self.interpolator.fractional(&flipped, remainder)
354    }
355}
356
357/// A trait for buffer types that can be used with readers
358pub trait BufferTrait<T: Float> {
359    /// Get a reference to a sample at the specified offset
360    fn get_ref(&self, offset: isize) -> &T;
361}
362
363impl<T: Float> BufferTrait<T> for Buffer<T> {
364    fn get_ref(&self, offset: isize) -> &T {
365        let index = (self.buffer_index as isize + offset) as usize & self.buffer_mask;
366        &self.buffer[index]
367    }
368}
369
370impl<'a, T: Float> BufferTrait<T> for View<'a, T> {
371    fn get_ref(&self, offset: isize) -> &T {
372        let index = (self.buffer.buffer_index as isize + self.offset + offset) as usize 
373            & self.buffer.buffer_mask;
374        &self.buffer.buffer[index]
375    }
376}
377
378/// A trait for interpolator types
379pub trait InterpolatorTrait<T: Float> {
380    /// Interpolate a fractional sample
381    fn fractional<D>(&self, data: &D, fractional: T) -> T
382    where
383        D: core::ops::Index<usize, Output = T>;
384}
385
386impl<T: Float> InterpolatorTrait<T> for InterpolatorNearest<T> {
387    fn fractional<D>(&self, data: &D, fractional: T) -> T
388    where
389        D: core::ops::Index<usize, Output = T>,
390    {
391        self.fractional(data, fractional)
392    }
393}
394
395impl<T: Float> InterpolatorTrait<T> for InterpolatorLinear<T> {
396    fn fractional<D>(&self, data: &D, fractional: T) -> T
397    where
398        D: core::ops::Index<usize, Output = T>,
399    {
400        self.fractional(data, fractional)
401    }
402}
403
404impl<T: Float> InterpolatorTrait<T> for InterpolatorCubic<T> {
405    fn fractional<D>(&self, data: &D, fractional: T) -> T
406    where
407        D: core::ops::Index<usize, Output = T>,
408    {
409        self.fractional(data, fractional)
410    }
411}
412
413/// A single-channel delay-line containing its own buffer
414pub struct Delay<T: Float, I> {
415    reader: Reader<T, I>,
416    buffer: Buffer<T>,
417}
418
419impl<T: Float, I: InterpolatorTrait<T>> Delay<T, I> {
420    /// Create a new delay line with the specified capacity and interpolator
421    pub fn new(interpolator: I, capacity: usize) -> Self {
422        Self {
423            reader: Reader::new(interpolator),
424            buffer: Buffer::new(capacity),
425        }
426    }
427
428    /// Reset the delay line to a specific value
429    pub fn reset(&mut self, value: T) {
430        self.buffer.reset(value);
431    }
432
433    /// Resize the delay line
434    pub fn resize(&mut self, min_capacity: usize, value: T) {
435        self.buffer.resize(min_capacity, value);
436    }
437
438    /// Read a sample from the delay line
439    pub fn read(&self, delay_samples: T) -> T {
440        self.reader.read(&self.buffer, delay_samples)
441    }
442
443    /// Write a sample to the delay line
444    pub fn write(&mut self, value: T) -> &mut Self {
445        self.buffer.advance(1);
446        self.buffer.set(0, value);
447        self
448    }
449}
450
451/// A multi-channel delay-line with its own buffer
452pub struct MultiDelay<T: Float, I> {
453    reader: Reader<T, I>,
454    channels: usize,
455    buffer: MultiBuffer<T>,
456}
457
458impl<T: Float, I: InterpolatorTrait<T>> MultiDelay<T, I> {
459    /// Create a new multi-channel delay line
460    pub fn new(interpolator: I, channels: usize, capacity: usize) -> Self {
461        Self {
462            reader: Reader::new(interpolator),
463            channels,
464            buffer: MultiBuffer::new(channels, capacity),
465        }
466    }
467
468    /// Reset the delay line to a specific value
469    pub fn reset(&mut self, value: T) {
470        self.buffer.reset(value);
471    }
472
473    /// Resize the delay line
474    pub fn resize(&mut self, channels: usize, capacity: usize, value: T) {
475        self.channels = channels;
476        self.buffer.resize(channels, capacity, value);
477    }
478
479    /// Read a sample from a specific channel
480    pub fn read_channel(&self, channel: usize, delay_samples: T) -> T {
481        self.reader.read(&self.buffer.channel(channel), delay_samples)
482    }
483
484    /// Read samples from all channels with the same delay
485    pub fn read(&self, delay_samples: T, output: &mut [T]) {
486        for c in 0..self.channels {
487            output[c] = self.read_channel(c, delay_samples);
488        }
489    }
490
491    /// Read samples from all channels with different delays
492    pub fn read_multi(&self, delays: &[T], output: &mut [T]) {
493        for c in 0..self.channels {
494            output[c] = self.read_channel(c, delays[c]);
495        }
496    }
497
498    /// Write samples to all channels
499    pub fn write(&mut self, data: &[T]) -> &mut Self {
500        self.buffer.advance(1);
501        for c in 0..self.channels {
502            self.buffer.set(c, 0, data[c]);
503        }
504        self
505    }
506}
507
508#[cfg(test)]
509mod tests {
510    use super::*;
511
512    #[test]
513    fn test_buffer() {
514        let mut buffer = Buffer::<f32>::new(16);
515
516        // Write some values
517        buffer.set(0, 1.0);
518        buffer.set(1, 2.0);
519        buffer.set(2, 3.0);
520
521        // Check values
522        assert_eq!(buffer.get(0), 1.0);
523        assert_eq!(buffer.get(1), 2.0);
524        assert_eq!(buffer.get(2), 3.0);
525
526        // Advance and check again
527        buffer.advance(1);
528        assert_eq!(buffer.get(-1), 1.0);
529        assert_eq!(buffer.get(0), 2.0);
530        assert_eq!(buffer.get(1), 3.0);
531    }
532
533    #[test]
534    fn test_multi_buffer() {
535        let mut buffer = MultiBuffer::<f32>::new(2, 16);
536
537        // Write some values
538        buffer.set(0, 0, 1.0);
539        buffer.set(0, 1, 2.0);
540        buffer.set(1, 0, 3.0);
541        buffer.set(1, 1, 4.0);
542
543        // Check values
544        assert_eq!(buffer.get(0, 0), 1.0);
545        assert_eq!(buffer.get(0, 1), 2.0);
546        assert_eq!(buffer.get(1, 0), 3.0);
547        assert_eq!(buffer.get(1, 1), 4.0);
548
549        // Advance and check again
550        buffer.advance(1);
551        assert_eq!(buffer.get(0, -1), 1.0);
552        assert_eq!(buffer.get(0, 0), 2.0);
553        assert_eq!(buffer.get(1, -1), 3.0);
554        assert_eq!(buffer.get(1, 0), 4.0);
555    }
556
557    #[test]
558    fn test_interpolators() {
559        // Create some test data
560        let data = [1.0, 2.0, 3.0, 4.0];
561
562        // Test nearest interpolator
563        let nearest = InterpolatorNearest::<f32>::new();
564        assert_eq!(nearest.fractional(&data, 0.0), 1.0);
565        assert_eq!(nearest.fractional(&data, 0.4), 1.0);
566        assert_eq!(nearest.fractional(&data, 0.6), 1.0);
567
568        // Test linear interpolator
569        let linear = InterpolatorLinear::<f32>::new();
570        assert_eq!(linear.fractional(&data, 0.0), 1.0);
571        assert_eq!(linear.fractional(&data, 0.5), 1.5);
572        assert_eq!(linear.fractional(&data, 1.0), 2.0);
573
574        // Test cubic interpolator
575        let cubic = InterpolatorCubic::<f32>::new();
576        assert_eq!(cubic.fractional(&data, 0.0), 2.0);
577        // The result of cubic interpolation at 0.5 should be between 2.0 and 3.0
578        let cubic_result = cubic.fractional(&data, 0.5);
579        assert!(cubic_result > 2.0 && cubic_result < 3.0);
580    }
581
582    #[test]
583    fn test_delay() {
584        let interpolator = InterpolatorLinear::<f32>::new();
585        let mut delay = Delay::new(interpolator, 16);
586
587        // Write some values
588        delay.write(1.0).write(2.0).write(3.0);
589
590        // Read with different delays
591        assert_eq!(delay.read(0.0), 3.0);
592        assert_eq!(delay.read(1.0), 2.0);
593        assert_eq!(delay.read(2.0), 1.0);
594        assert_eq!(delay.read(0.5), 2.5); // Interpolated
595    }
596
597    #[test]
598    fn test_multi_delay() {
599        let interpolator = InterpolatorLinear::<f32>::new();
600        let mut delay = MultiDelay::new(interpolator, 2, 16);
601
602        // Write some values
603        delay.write(&[1.0, 3.0]).write(&[2.0, 4.0]);
604
605        // Read with different delays
606        let mut output = [0.0, 0.0];
607        delay.read(0.0, &mut output);
608        assert_eq!(output, [2.0, 4.0]);
609
610        delay.read(1.0, &mut output);
611        assert_eq!(output, [1.0, 3.0]);
612
613        // Read with different delays per channel
614        delay.read_multi(&[0.0, 1.0], &mut output);
615        assert_eq!(output, [2.0, 3.0]);
616    }
617}