Skip to main content

rill_core/buffer/
delay.rs

1use crate::buffer::{AtomicStats, Buffer, BufferStats};
2use crate::math::Transcendental;
3use core::marker::PhantomData;
4use core::ops::{Index, IndexMut};
5
6/// Delay line for signal 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 signal 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 {
56        self.delay_samples
57    }
58    /// Maximum possible delay (const generic parameter).
59    pub const fn max_delay(&self) -> usize {
60        MAX_DELAY
61    }
62    /// The sample rate used for sec-to-sample conversion.
63    pub fn sample_rate(&self) -> f32 {
64        self.sample_rate
65    }
66
67    /// Write a sample and return the delayed sample.
68    #[inline(always)]
69    pub fn write(&mut self, input: T) -> T {
70        self.buffer[self.write_pos] = input;
71        let read_pos = if self.write_pos >= self.delay_samples {
72            self.write_pos - self.delay_samples
73        } else {
74            MAX_DELAY + self.write_pos - self.delay_samples
75        };
76        let output = self.buffer[read_pos];
77        self.write_pos = (self.write_pos + 1) % MAX_DELAY;
78        self.stats.record_write();
79        self.stats.record_read();
80        self.stats.update_peak(MAX_DELAY);
81        output
82    }
83
84    /// Read the delayed sample without writing.
85    #[inline(always)]
86    pub fn read(&self) -> T {
87        let read_pos = if self.write_pos >= self.delay_samples {
88            self.write_pos - self.delay_samples
89        } else {
90            MAX_DELAY + self.write_pos - self.delay_samples
91        };
92        self.buffer[read_pos]
93    }
94
95    /// Read sample at arbitrary delay (0 = most recent).
96    #[inline(always)]
97    pub fn read_delayed(&self, delay: usize) -> T {
98        debug_assert!(
99            delay < MAX_DELAY,
100            "Delay {} out of range (max {})",
101            delay,
102            MAX_DELAY
103        );
104        let read_pos = if self.write_pos > delay {
105            self.write_pos - 1 - delay
106        } else {
107            MAX_DELAY + self.write_pos - 1 - delay
108        };
109        self.buffer[read_pos]
110    }
111
112    /// Read with linear interpolation between samples.
113    #[inline(always)]
114    pub fn read_interpolated(&self, delay_frac: f32) -> T {
115        let delay_int = delay_frac.floor() as usize;
116        let frac = T::from_f32(delay_frac.fract());
117        let s1 = self.read_delayed(delay_int);
118        if delay_int == MAX_DELAY - 1 {
119            s1
120        } else {
121            let s2 = self.read_delayed(delay_int + 1);
122            s1 + (s2 - s1) * frac
123        }
124    }
125
126    /// Clear the delay line (fill with zeros).
127    pub fn clear(&mut self) {
128        self.buffer.fill(T::ZERO);
129        self.write_pos = 0;
130        self.stats.reset();
131    }
132
133    /// Current write cursor position in the circular buffer.
134    pub fn write_position(&self) -> usize {
135        self.write_pos
136    }
137}
138
139// ============================================================================
140// Index implementations
141// ============================================================================
142
143impl<T: Transcendental, const MAX_DELAY: usize> Index<usize> for DelayLine<T, MAX_DELAY> {
144    type Output = T;
145    fn index(&self, index: usize) -> &Self::Output {
146        &self.buffer[index]
147    }
148}
149
150impl<T: Transcendental, const MAX_DELAY: usize> IndexMut<usize> for DelayLine<T, MAX_DELAY> {
151    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
152        &mut self.buffer[index]
153    }
154}
155
156// ============================================================================
157// Buffer trait implementation
158// ============================================================================
159
160impl<T: Transcendental, const MAX_DELAY: usize> Buffer<T> for DelayLine<T, MAX_DELAY> {
161    fn capacity(&self) -> usize {
162        MAX_DELAY
163    }
164    fn len(&self) -> usize {
165        MAX_DELAY
166    }
167    fn is_empty(&self) -> bool {
168        false
169    }
170    fn is_full(&self) -> bool {
171        true
172    }
173    fn as_slice(&self) -> &[T] {
174        &self.buffer
175    }
176    fn as_mut_slice(&mut self) -> &mut [T] {
177        &mut self.buffer
178    }
179    fn fill(&mut self, value: T) {
180        self.buffer.fill(value);
181    }
182    fn copy_from(&mut self, src: &[T]) {
183        let len = src.len().min(MAX_DELAY);
184        self.buffer[..len].copy_from_slice(&src[..len]);
185    }
186    fn clear(&mut self) {
187        self.buffer.fill(T::ZERO);
188        self.write_pos = 0;
189        self.stats.reset();
190    }
191    fn stats(&self) -> BufferStats {
192        let mut stats = self.stats.snapshot();
193        stats.fill_level = 1.0;
194        stats
195    }
196    fn reset_stats(&mut self) {
197        self.stats.reset();
198    }
199}
200
201// ============================================================================
202// Tests
203// ============================================================================
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208
209    #[test]
210    fn test_delay_line_basic() {
211        let mut delay = DelayLine::<f32, 1024>::new(44100.0);
212        delay.set_delay_samples(100);
213        for i in 0..200 {
214            let out = delay.write(i as f32);
215            if i >= 100 {
216                assert_eq!(out, (i - 100) as f32);
217            }
218        }
219    }
220
221    #[test]
222    fn test_delay_line_read_delayed() {
223        let mut delay = DelayLine::<f32, 1024>::new(44100.0);
224        for i in 0..1024 {
225            delay.write(i as f32);
226        }
227        assert_eq!(delay.read_delayed(0), 1023.0);
228        assert_eq!(delay.read_delayed(100), 923.0);
229    }
230
231    #[test]
232    fn test_delay_line_interpolation() {
233        let mut delay = DelayLine::<f32, 1024>::new(44100.0);
234        for i in 0..1024 {
235            delay.write(i as f32);
236        }
237        let val = delay.read_interpolated(100.5);
238        assert!((val - 922.5).abs() < 0.01);
239    }
240}