rill_core/buffer/
delay.rs1use super::array_from_fn;
4use crate::buffer::{AtomicCell, AtomicStats, SignalBuffer, BufferStats};
5use crate::math::Transcendental;
6use core::marker::PhantomData;
7use core::ops::{Index, IndexMut};
8
9#[repr(align(64))]
18pub struct DelayLine<T: Transcendental, const MAX_DELAY: usize> {
19 buffer: [AtomicCell<T>; MAX_DELAY],
21
22 write_pos: usize,
24
25 delay_samples: usize,
27
28 sample_rate: f32,
30
31 stats: AtomicStats,
33
34 _phantom: PhantomData<T>,
36}
37
38impl<T: Transcendental, const MAX_DELAY: usize> DelayLine<T, MAX_DELAY> {
39 pub fn new(sample_rate: f32) -> Self {
47 assert!(MAX_DELAY > 0, "DelayLine must have MAX_DELAY > 0");
48
49 let buffer = array_from_fn(|_| AtomicCell::new(T::ZERO));
51
52 Self {
53 buffer,
54 write_pos: 0,
55 delay_samples: 0,
56 sample_rate,
57 stats: AtomicStats::new(),
58 _phantom: PhantomData,
59 }
60 }
61
62 #[inline(always)]
64 pub fn set_delay(&mut self, delay_sec: f32) {
65 let samples = (delay_sec * self.sample_rate) as usize;
66 self.delay_samples = samples.min(MAX_DELAY - 1);
67 }
68
69 #[inline(always)]
71 pub fn set_delay_samples(&mut self, samples: usize) {
72 self.delay_samples = samples.min(MAX_DELAY - 1);
73 }
74
75 #[inline(always)]
77 pub fn delay_samples(&self) -> usize {
78 self.delay_samples
79 }
80
81 #[inline(always)]
83 pub const fn max_delay(&self) -> usize {
84 MAX_DELAY
85 }
86
87 #[inline(always)]
89 pub fn sample_rate(&self) -> f32 {
90 self.sample_rate
91 }
92
93 #[inline(always)]
95 pub fn write(&mut self, input: T) -> T {
96 self.buffer[self.write_pos].store(input);
98
99 let read_pos = if self.write_pos >= self.delay_samples {
101 self.write_pos - self.delay_samples
102 } else {
103 MAX_DELAY + self.write_pos - self.delay_samples
104 };
105
106 let output = self.buffer[read_pos].load();
108
109 self.write_pos = (self.write_pos + 1) % MAX_DELAY;
111
112 self.stats.record_write();
114 self.stats.record_read();
115 self.stats.update_peak(MAX_DELAY);
116
117 output
118 }
119
120 #[inline(always)]
122 pub fn read(&self) -> T {
123 let read_pos = if self.write_pos >= self.delay_samples {
124 self.write_pos - self.delay_samples
125 } else {
126 MAX_DELAY + self.write_pos - self.delay_samples
127 };
128
129 self.buffer[read_pos].load()
130 }
131
132 #[inline(always)]
134 pub fn read_delayed(&self, delay: usize) -> T {
135 debug_assert!(
136 delay < MAX_DELAY,
137 "Delay {} out of range (max {})",
138 delay,
139 MAX_DELAY
140 );
141
142 let read_pos = if self.write_pos >= delay + 1 {
143 self.write_pos - 1 - delay
144 } else {
145 MAX_DELAY + self.write_pos - 1 - delay
146 };
147
148 self.buffer[read_pos].load()
149 }
150
151 #[inline(always)]
153 pub fn read_interpolated(&self, delay_frac: f32) -> T {
154 let delay_int = delay_frac.floor() as usize;
155 let frac = T::from_f32(delay_frac.fract());
156
157 let s1 = self.read_delayed(delay_int);
158
159 if delay_int == MAX_DELAY - 1 {
160 s1
161 } else {
162 let s2 = self.read_delayed(delay_int + 1);
163 s1 + (s2 - s1) * frac
164 }
165 }
166
167 pub fn clear(&mut self) {
169 for i in 0..MAX_DELAY {
170 self.buffer[i].store(T::ZERO);
171 }
172 self.write_pos = 0;
173 self.stats.reset();
174 }
175
176 #[inline(always)]
178 pub fn write_position(&self) -> usize {
179 self.write_pos
180 }
181}
182
183impl<T: Transcendental, const MAX_DELAY: usize> Index<usize> for DelayLine<T, MAX_DELAY> {
188 type Output = T;
189
190 fn index(&self, _index: usize) -> &Self::Output {
191 unimplemented!("Direct indexing not supported with AtomicCell")
195 }
196}
197
198impl<T: Transcendental, const MAX_DELAY: usize> IndexMut<usize> for DelayLine<T, MAX_DELAY> {
199 fn index_mut(&mut self, _index: usize) -> &mut Self::Output {
200 unimplemented!("Direct indexing not supported with AtomicCell")
201 }
202}
203
204impl<T: Transcendental, const MAX_DELAY: usize> SignalBuffer<T> for DelayLine<T, MAX_DELAY> {
209 fn capacity(&self) -> usize {
210 MAX_DELAY
211 }
212
213 fn len(&self) -> usize {
214 MAX_DELAY
215 }
216
217 fn is_empty(&self) -> bool {
218 false
219 }
220
221 fn is_full(&self) -> bool {
222 true
223 }
224
225 fn clear(&mut self) {
226 self.clear();
227 }
228
229 fn stats(&self) -> BufferStats {
230 let mut stats = self.stats.snapshot();
231 stats.fill_level = 1.0;
232 stats
233 }
234
235 fn reset_stats(&mut self) {
236 self.stats.reset();
237 }
238}
239
240#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[test]
249 fn test_delay_line_basic() {
250 let mut delay = DelayLine::<f32, 1024>::new(44100.0);
251 delay.set_delay_samples(100);
252
253 for i in 0..200 {
254 let out = delay.write(i as f32);
255 if i >= 100 {
256 assert_eq!(out, (i - 100) as f32);
257 }
258 }
259 }
260
261 #[test]
262 fn test_delay_line_read_delayed() {
263 let mut delay = DelayLine::<f32, 1024>::new(44100.0);
264
265 for i in 0..1024 {
266 delay.write(i as f32);
267 }
268
269 assert_eq!(delay.read_delayed(0), 1023.0);
270 assert_eq!(delay.read_delayed(100), 923.0);
271 }
272
273 #[test]
274 fn test_delay_line_interpolation() {
275 let mut delay = DelayLine::<f32, 1024>::new(44100.0);
276
277 for i in 0..1024 {
278 delay.write(i as f32);
279 }
280
281 let val = delay.read_interpolated(100.5);
282 assert!((val - 922.5).abs() < 0.01);
283 }
284}