Skip to main content

rill_core/buffer/
delay.rs

1use crate::buffer::{AtomicStats, SignalBuffer, BufferStats};
2use crate::math::Transcendental;
3use core::marker::PhantomData;
4use core::ops::{Index, IndexMut};
5
6/// Delay line for audio effects — single-threaded circular buffer.
7///
8/// Provides a circular buffer for implementing delay, reverb,
9/// and other time-based effects.
10///
11/// # Thread safety
12///
13/// `DelayLine` is **not** thread-safe. It is designed for single-threaded
14/// use inside the audio graph. Use [`PipeBuffer`](super::PipeBuffer) or
15/// [`RingBuffer`](super::RingBuffer) for cross-thread communication.
16#[repr(align(64))]
17pub struct DelayLine<T: Transcendental, const MAX_DELAY: usize> {
18    buffer: [T; MAX_DELAY],
19    write_pos: usize,
20    delay_samples: usize,
21    sample_rate: f32,
22    stats: AtomicStats,
23    _phantom: PhantomData<T>,
24}
25
26impl<T: Transcendental, const MAX_DELAY: usize> DelayLine<T, MAX_DELAY> {
27    /// Create a new delay line with the given sample rate.
28    ///
29    /// # Panics
30    /// Panics if `MAX_DELAY` is 0.
31    pub fn new(sample_rate: f32) -> Self {
32        assert!(MAX_DELAY > 0, "DelayLine must have MAX_DELAY > 0");
33        Self {
34            buffer: [T::ZERO; MAX_DELAY],
35            write_pos: 0,
36            delay_samples: 0,
37            sample_rate,
38            stats: AtomicStats::new(),
39            _phantom: PhantomData,
40        }
41    }
42
43    /// Set the delay time in seconds.
44    pub fn set_delay(&mut self, delay_sec: f32) {
45        let samples = (delay_sec * self.sample_rate) as usize;
46        self.delay_samples = samples.min(MAX_DELAY - 1);
47    }
48
49    /// Set the delay time in samples.
50    pub fn set_delay_samples(&mut self, samples: usize) {
51        self.delay_samples = samples.min(MAX_DELAY - 1);
52    }
53
54    /// Current delay in samples.
55    pub fn delay_samples(&self) -> usize { self.delay_samples }
56    /// Maximum possible delay (const generic parameter).
57    pub const fn max_delay(&self) -> usize { MAX_DELAY }
58    /// The sample rate used for sec-to-sample conversion.
59    pub fn sample_rate(&self) -> f32 { self.sample_rate }
60
61    /// Write a sample and return the delayed sample.
62    #[inline(always)]
63    pub fn write(&mut self, input: T) -> T {
64        self.buffer[self.write_pos] = input;
65        let read_pos = if self.write_pos >= self.delay_samples {
66            self.write_pos - self.delay_samples
67        } else {
68            MAX_DELAY + self.write_pos - self.delay_samples
69        };
70        let output = self.buffer[read_pos];
71        self.write_pos = (self.write_pos + 1) % MAX_DELAY;
72        self.stats.record_write();
73        self.stats.record_read();
74        self.stats.update_peak(MAX_DELAY);
75        output
76    }
77
78    /// Read the delayed sample without writing.
79    #[inline(always)]
80    pub fn read(&self) -> T {
81        let read_pos = if self.write_pos >= self.delay_samples {
82            self.write_pos - self.delay_samples
83        } else {
84            MAX_DELAY + self.write_pos - self.delay_samples
85        };
86        self.buffer[read_pos]
87    }
88
89    /// Read sample at arbitrary delay (0 = most recent).
90    #[inline(always)]
91    pub fn read_delayed(&self, delay: usize) -> T {
92        debug_assert!(delay < MAX_DELAY, "Delay {} out of range (max {})", delay, MAX_DELAY);
93        let read_pos = if self.write_pos > delay {
94            self.write_pos - 1 - delay
95        } else {
96            MAX_DELAY + self.write_pos - 1 - delay
97        };
98        self.buffer[read_pos]
99    }
100
101    /// Read with linear interpolation between samples.
102    #[inline(always)]
103    pub fn read_interpolated(&self, delay_frac: f32) -> T {
104        let delay_int = delay_frac.floor() as usize;
105        let frac = T::from_f32(delay_frac.fract());
106        let s1 = self.read_delayed(delay_int);
107        if delay_int == MAX_DELAY - 1 {
108            s1
109        } else {
110            let s2 = self.read_delayed(delay_int + 1);
111            s1 + (s2 - s1) * frac
112        }
113    }
114
115    /// Clear the delay line (fill with zeros).
116    pub fn clear(&mut self) {
117        self.buffer.fill(T::ZERO);
118        self.write_pos = 0;
119        self.stats.reset();
120    }
121
122    /// Current write cursor position in the circular buffer.
123    pub fn write_position(&self) -> usize { self.write_pos }
124}
125
126// ============================================================================
127// Index implementations
128// ============================================================================
129
130impl<T: Transcendental, const MAX_DELAY: usize> Index<usize> for DelayLine<T, MAX_DELAY> {
131    type Output = T;
132    fn index(&self, index: usize) -> &Self::Output {
133        &self.buffer[index]
134    }
135}
136
137impl<T: Transcendental, const MAX_DELAY: usize> IndexMut<usize> for DelayLine<T, MAX_DELAY> {
138    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
139        &mut self.buffer[index]
140    }
141}
142
143// ============================================================================
144// SignalBuffer Implementation
145// ============================================================================
146
147impl<T: Transcendental, const MAX_DELAY: usize> SignalBuffer<T> for DelayLine<T, MAX_DELAY> {
148    fn capacity(&self) -> usize { MAX_DELAY }
149    fn len(&self) -> usize { MAX_DELAY }
150    fn is_empty(&self) -> bool { false }
151    fn is_full(&self) -> bool { true }
152    fn clear(&mut self) { self.clear(); }
153    fn stats(&self) -> BufferStats {
154        let mut stats = self.stats.snapshot();
155        stats.fill_level = 1.0;
156        stats
157    }
158    fn reset_stats(&mut self) { self.stats.reset(); }
159}
160
161// ============================================================================
162// Tests
163// ============================================================================
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn test_delay_line_basic() {
171        let mut delay = DelayLine::<f32, 1024>::new(44100.0);
172        delay.set_delay_samples(100);
173        for i in 0..200 {
174            let out = delay.write(i as f32);
175            if i >= 100 {
176                assert_eq!(out, (i - 100) as f32);
177            }
178        }
179    }
180
181    #[test]
182    fn test_delay_line_read_delayed() {
183        let mut delay = DelayLine::<f32, 1024>::new(44100.0);
184        for i in 0..1024 { delay.write(i as f32); }
185        assert_eq!(delay.read_delayed(0), 1023.0);
186        assert_eq!(delay.read_delayed(100), 923.0);
187    }
188
189    #[test]
190    fn test_delay_line_interpolation() {
191        let mut delay = DelayLine::<f32, 1024>::new(44100.0);
192        for i in 0..1024 { delay.write(i as f32); }
193        let val = delay.read_interpolated(100.5);
194        assert!((val - 922.5).abs() < 0.01);
195    }
196}