1use crate::buffer::Buffer;
2use crate::math::Transcendental;
3
4#[derive(Debug)]
26pub struct TapeLoop<T> {
27 buffer: Box<[T]>,
28 capacity: usize,
29 write_pos: usize,
30}
31
32impl<T: Transcendental> TapeLoop<T> {
33 pub fn new(capacity: usize) -> Option<Self> {
35 if capacity == 0 {
36 return None;
37 }
38 let mut vec = Vec::with_capacity(capacity);
39 for _ in 0..capacity {
40 vec.push(T::ZERO);
41 }
42 Some(Self {
43 buffer: vec.into_boxed_slice(),
44 capacity,
45 write_pos: 0,
46 })
47 }
48
49 pub fn capacity(&self) -> usize {
51 self.capacity
52 }
53 pub fn write_pos(&self) -> usize {
55 self.write_pos
56 }
57
58 #[inline(always)]
60 pub fn write(&mut self, sample: T) {
61 self.buffer[self.write_pos] = sample;
62 self.write_pos = (self.write_pos + 1) % self.capacity;
63 }
64
65 #[inline(always)]
67 pub fn read(&self, delay: usize) -> T {
68 let d = delay.min(self.capacity - 1);
69 let read_pos = if self.write_pos > d {
70 self.write_pos - 1 - d
71 } else {
72 self.capacity + self.write_pos - 1 - d
73 };
74 self.buffer[read_pos]
75 }
76
77 #[inline(always)]
79 pub fn read_interpolated(&self, delay: f64) -> T {
80 let d = delay as usize;
81 let frac = T::from_f64(delay.fract());
82 let s1 = self.read(d);
83 let s2 = self.read(d + 1);
84 s1 + (s2 - s1) * frac
85 }
86
87 #[inline(always)]
89 pub fn write_block(&mut self, block: &[T]) {
90 let len = block.len().min(self.capacity);
91 for (i, &b) in block.iter().enumerate().take(len) {
92 self.buffer[(self.write_pos + i) % self.capacity] = b;
93 }
94 self.write_pos = (self.write_pos + len) % self.capacity;
95 }
96
97 #[inline(always)]
99 pub fn read_block(&self, delay: usize, output: &mut [T]) {
100 let len = output.len().min(self.capacity);
101 let d = delay.min(self.capacity - 1);
102 for (i, out) in output.iter_mut().enumerate().take(len) {
103 *out = self.read(d + len - 1 - i);
104 }
105 }
106
107 pub fn fill(&mut self, value: T) {
109 for slot in self.buffer.iter_mut() {
110 *slot = value;
111 }
112 }
113
114 pub fn clear(&mut self) {
116 for slot in self.buffer.iter_mut() {
117 *slot = T::ZERO;
118 }
119 self.write_pos = 0;
120 }
121}
122
123impl<T: Transcendental> Buffer<T> for TapeLoop<T> {
126 fn len(&self) -> usize {
127 self.capacity
128 }
129
130 fn as_slice(&self) -> &[T] {
131 &self.buffer
132 }
133
134 fn as_mut_slice(&mut self) -> &mut [T] {
135 &mut self.buffer
136 }
137
138 fn fill(&mut self, value: T)
139 where
140 T: Copy,
141 {
142 for slot in self.buffer.iter_mut() {
143 *slot = value;
144 }
145 }
146
147 fn copy_from(&mut self, src: &[T])
148 where
149 T: Copy,
150 {
151 let len = src.len().min(self.capacity);
152 self.buffer[..len].copy_from_slice(&src[..len]);
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_tape_basic_write_read() {
162 let mut tape = TapeLoop::<f32>::new(1024).unwrap();
163 tape.write(1.0);
164 tape.write(2.0);
165 tape.write(3.0);
166 assert_eq!(tape.read(0), 3.0);
167 assert_eq!(tape.read(1), 2.0);
168 assert_eq!(tape.read(2), 1.0);
169 }
170
171 #[test]
172 fn test_tape_wraparound() {
173 let mut tape = TapeLoop::<f32>::new(4).unwrap();
174 for i in 0..10 {
175 tape.write(i as f32);
176 }
177 assert_eq!(tape.read(0), 9.0);
178 assert_eq!(tape.read(1), 8.0);
179 assert_eq!(tape.read(2), 7.0);
180 assert_eq!(tape.read(3), 6.0);
181 }
182
183 #[test]
184 fn test_tape_block_ops() {
185 let mut tape = TapeLoop::<f32>::new(64).unwrap();
186 let block = [1.0f32; 64];
187 tape.write_block(&block);
188 let mut out = [0.0f32; 64];
189 tape.read_block(63, &mut out);
190 assert_eq!(out[0], 1.0);
191 }
192
193 #[test]
194 fn test_tape_large_capacity() {
195 let tape = TapeLoop::<f32>::new(1_000_000).unwrap();
196 assert_eq!(tape.capacity(), 1_000_000);
197 }
198
199 #[test]
200 fn test_tape_zero_capacity() {
201 assert!(TapeLoop::<f32>::new(0).is_none());
202 }
203
204 #[test]
205 fn test_read_interpolated() {
206 let mut tape = TapeLoop::<f32>::new(1024).unwrap();
207 tape.write(0.0);
208 tape.write(1.0);
209 let v = tape.read_interpolated(0.5);
210 assert!((v - 0.5).abs() < 0.01);
211 }
212}