rill_core_dsp/generators/
reader.rs1use rill_core::interpolate::Interpolate;
2use rill_core::Transcendental;
3
4fn len_remainder(pos: f64, len: f64) -> f64 {
5 let r = pos % len;
6 if r < 0.0 { r + len } else { r }
7}
8
9pub struct InterpolatedReader<T> {
14 buffer: Box<[T]>,
15 position: f64,
16 rate: f64,
17 cubic: bool,
18 wrap: bool,
19}
20
21impl<T: Transcendental + Copy> InterpolatedReader<T> {
22 pub fn new(buffer: Vec<T>) -> Self {
23 Self {
24 buffer: buffer.into_boxed_slice(),
25 position: 0.0,
26 rate: 1.0,
27 cubic: false,
28 wrap: false,
29 }
30 }
31
32 pub fn from_boxed(buffer: Box<[T]>) -> Self {
33 Self {
34 buffer,
35 position: 0.0,
36 rate: 1.0,
37 cubic: false,
38 wrap: false,
39 }
40 }
41
42 #[inline(always)]
43 pub fn len(&self) -> usize {
44 self.buffer.len()
45 }
46
47 #[inline(always)]
48 pub fn is_empty(&self) -> bool {
49 self.buffer.is_empty()
50 }
51
52 #[inline(always)]
53 pub fn position(&self) -> f64 {
54 self.position
55 }
56
57 #[inline(always)]
58 pub fn set_position(&mut self, pos: f64) {
59 self.position = pos;
60 }
61
62 #[inline(always)]
63 pub fn rate(&self) -> f64 {
64 self.rate
65 }
66
67 #[inline(always)]
68 pub fn set_rate(&mut self, rate: f64) {
69 self.rate = rate;
70 }
71
72 #[inline(always)]
73 pub fn is_cubic(&self) -> bool {
74 self.cubic
75 }
76
77 #[inline(always)]
78 pub fn set_cubic(&mut self, cubic: bool) {
79 self.cubic = cubic;
80 }
81
82 #[inline(always)]
83 pub fn is_wrap(&self) -> bool {
84 self.wrap
85 }
86
87 #[inline(always)]
88 pub fn set_wrap(&mut self, wrap: bool) {
89 self.wrap = wrap;
90 }
91
92 pub fn set_buffer(&mut self, buffer: Vec<T>) {
93 self.buffer = buffer.into_boxed_slice();
94 self.position = 0.0;
95 }
96
97 #[inline(always)]
98 pub fn as_slice(&self) -> &[T] {
99 &self.buffer
100 }
101
102 fn read_wrap_linear(&self, pos: f64) -> T {
104 let len = self.len();
105 let i0 = pos.floor() as usize % len;
106 let i1 = (i0 + 1) % len;
107 let frac = T::from_f64(pos.fract());
108 let a = self.buffer[i0];
109 let b = self.buffer[i1];
110 a + (b - a) * frac
111 }
112
113 fn read_wrap_cubic(&self, pos: f64) -> T {
115 let len = self.len();
116 let i = pos.floor() as usize;
117 let i0 = (i + len - 1) % len;
118 let i1 = i % len;
119 let i2 = (i + 1) % len;
120 let i3 = (i + 2) % len;
121 let frac = T::from_f64(pos.fract());
122
123 let c0 = self.buffer[i1];
124 let c1 = (self.buffer[i2] - self.buffer[i0]) * T::from_f32(0.5);
125 let c2 = self.buffer[i0] * T::from_f32(-1.5)
126 + self.buffer[i1] * T::from_f32(2.0)
127 + self.buffer[i2] * T::from_f32(-0.5);
128 let c3 = self.buffer[i0] * T::from_f32(-0.5)
129 + self.buffer[i1] * T::from_f32(1.5)
130 + self.buffer[i2] * T::from_f32(-1.5)
131 + self.buffer[i3] * T::from_f32(0.5);
132
133 let f2 = frac * frac;
134 let f3 = f2 * frac;
135 c0 + c1 * frac + c2 * f2 + c3 * f3
136 }
137
138 #[inline(always)]
140 pub fn read_one(&self) -> T {
141 if self.is_empty() {
142 return T::ZERO;
143 }
144 let pos = if self.wrap {
145 len_remainder(self.position, self.len() as f64)
146 } else {
147 self.position
148 };
149 if self.cubic && self.len() >= 4 {
150 if self.wrap {
151 self.read_wrap_cubic(pos)
152 } else {
153 self.buffer.interpolate_cubic(pos)
154 }
155 } else if self.wrap {
156 self.read_wrap_linear(pos)
157 } else {
158 self.buffer.interpolate_linear(pos)
159 }
160 }
161
162 #[inline(always)]
164 pub fn advance(&mut self) {
165 self.position += self.rate;
166 }
167
168 pub fn render_block(&mut self, output: &mut [T]) {
170 if self.is_empty() {
171 for s in output.iter_mut() {
172 *s = T::ZERO;
173 }
174 return;
175 }
176
177 if self.wrap {
178 let len_f = self.len() as f64;
179 if self.cubic && self.len() >= 4 {
180 for s in output.iter_mut() {
181 *s = self.read_wrap_cubic(len_remainder(self.position, len_f));
182 self.position += self.rate;
183 }
184 } else {
185 for s in output.iter_mut() {
186 *s = self.read_wrap_linear(len_remainder(self.position, len_f));
187 self.position += self.rate;
188 }
189 }
190 } else if self.cubic {
191 for s in output.iter_mut() {
192 *s = self.buffer.interpolate_cubic(self.position);
193 self.position += self.rate;
194 }
195 } else {
196 for s in output.iter_mut() {
197 *s = self.buffer.interpolate_linear(self.position);
198 self.position += self.rate;
199 }
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn test_basic_read() {
210 let buf = vec![0.0, 1.0, 2.0, 3.0, 4.0];
211 let mut reader = InterpolatedReader::new(buf);
212 let mut out = [0.0f64; 4];
213 reader.render_block(&mut out);
214 assert_eq!(out, [0.0, 1.0, 2.0, 3.0]);
215 assert!((reader.position() - 4.0).abs() < 1e-10);
216 }
217
218 #[test]
219 fn test_rate_half() {
220 let buf = vec![0.0, 2.0, 4.0, 6.0, 8.0];
221 let mut reader = InterpolatedReader::new(buf);
222 reader.set_rate(0.5);
223 let mut out = [0.0f64; 4];
224 reader.render_block(&mut out);
225 assert!((out[0] - 0.0).abs() < 1e-10);
226 assert!((out[1] - 1.0).abs() < 1e-10);
227 assert!((out[2] - 2.0).abs() < 1e-10);
228 assert!((out[3] - 3.0).abs() < 1e-10);
229 assert!((reader.position() - 2.0).abs() < 1e-10);
230 }
231
232 #[test]
233 fn test_empty_buffer() {
234 let buf: Vec<f64> = vec![];
235 let mut reader = InterpolatedReader::new(buf);
236 let mut out = [1.0f64; 4];
237 reader.render_block(&mut out);
238 assert_eq!(out, [0.0; 4]);
239 }
240
241 #[test]
242 fn test_set_buffer() {
243 let buf = vec![0.0, 0.0];
244 let mut reader = InterpolatedReader::new(buf);
245 reader.set_position(10.0);
246 reader.set_buffer(vec![10.0, 20.0, 30.0]);
247 assert_eq!(reader.position(), 0.0);
248 assert_eq!(reader.len(), 3);
249 }
250
251 #[test]
252 fn test_wrap_linear() {
253 let buf = vec![0.0, 1.0, 2.0, 3.0];
254 let mut reader = InterpolatedReader::new(buf);
255 reader.set_wrap(true);
256 reader.set_position(3.5);
257 let mut out = [0.0f64; 2];
258 reader.render_block(&mut out);
259 assert!((out[0] - 1.5).abs() < 1e-10, "wrap 3.5 -> 1.5, got {}", out[0]);
260 assert!((out[1] - 0.5).abs() < 1e-10, "wrap 4.5 -> 0.5, got {}", out[1]);
261 }
262
263 #[test]
264 fn test_wrap_cubic_at_boundary() {
265 let buf = vec![0.0, 1.0, 2.0, 3.0];
266 let mut reader = InterpolatedReader::new(buf);
267 reader.set_wrap(true);
268 reader.set_cubic(true);
269 reader.set_position(0.0);
270 let mut out = [0.0f64; 1];
271 reader.render_block(&mut out);
272 assert!((out[0] - 0.0).abs() < 1e-10, "cubic wrap at 0 -> 0, got {}", out[0]);
273 }
274
275 #[test]
276 fn test_clamp_at_end() {
277 let buf = vec![10.0, 20.0];
278 let mut reader = InterpolatedReader::new(buf);
279 reader.set_position(5.0);
280 let mut out = [0.0f64; 3];
281 reader.render_block(&mut out);
282 assert_eq!(out, [20.0, 20.0, 20.0]);
283 }
284}