1use crate::{
9 DspError,
10 convolution::{FirRoute, MatrixConvolver},
11 fir::firwin,
12 invalid,
13};
14
15#[derive(Debug)]
16pub struct MultirateMatrixConvolver {
17 factor: usize,
18 block: usize,
19 prototype: Vec<f64>,
20 low: MatrixConvolver,
21 down_history: Vec<Vec<f64>>,
22 down_head: usize,
23 up_history: Vec<Vec<f64>>,
24 up_head: usize,
25 low_inputs: Vec<Vec<f64>>,
26 low_outputs: Vec<Vec<f64>>,
27 tail: usize,
28}
29
30impl MultirateMatrixConvolver {
31 pub fn new(
34 inputs: usize,
35 outputs: usize,
36 low_block_size: usize,
37 factor: usize,
38 routes: &[FirRoute],
39 ) -> Result<Self, DspError> {
40 if factor == 0 || low_block_size == 0 || inputs == 0 || outputs == 0 {
41 return Err(invalid("multirate sizes must be positive"));
42 }
43 let length = factor
44 .checked_mul(64)
45 .and_then(|n| n.checked_add(1))
46 .ok_or_else(|| invalid("multirate prototype size overflow"))?;
47 low_block_size
48 .checked_mul(factor)
49 .ok_or_else(|| invalid("multirate block size overflow"))?;
50 length
51 .checked_mul(std::mem::size_of::<f64>())
52 .ok_or_else(|| invalid("multirate prototype size overflow"))?;
53 let mut prototype = firwin(length, 0.4 * (48000.0 / factor as f64), 48000.0)?;
54 for k in 0..length / 2 {
57 let value = (prototype[k] + prototype[length - 1 - k]) * 0.5;
58 prototype[k] = value;
59 prototype[length - 1 - k] = value;
60 }
61 Self::with_prototype(inputs, outputs, low_block_size, factor, routes, &prototype)
62 }
63
64 pub fn with_prototype(
68 inputs: usize,
69 outputs: usize,
70 low_block_size: usize,
71 factor: usize,
72 routes: &[FirRoute],
73 prototype: &[f64],
74 ) -> Result<Self, DspError> {
75 if factor == 0 || low_block_size == 0 || inputs == 0 || outputs == 0 {
76 return Err(invalid("multirate sizes must be positive"));
77 }
78 if prototype.is_empty()
79 || prototype.len().is_multiple_of(2)
80 || prototype.iter().any(|v| !v.is_finite())
81 || prototype.iter().zip(prototype.iter().rev()).any(|(a, b)| {
82 (a - b).abs() > 8.0 * f64::EPSILON * a.abs().max(b.abs()).max(f64::MIN_POSITIVE)
83 })
84 {
85 return Err(invalid(
86 "multirate prototype must be finite, odd and symmetric",
87 ));
88 }
89 let block = low_block_size
90 .checked_mul(factor)
91 .ok_or_else(|| invalid("multirate block size overflow"))?;
92 let up_length = prototype.len().div_ceil(factor);
93 inputs
94 .checked_mul(prototype.len())
95 .and_then(|n| n.checked_mul(8))
96 .ok_or_else(|| invalid("multirate decimator size overflow"))?;
97 outputs
98 .checked_mul(up_length)
99 .and_then(|n| n.checked_mul(8))
100 .ok_or_else(|| invalid("multirate interpolator size overflow"))?;
101 let low = MatrixConvolver::new(inputs, outputs, low_block_size, routes)?;
102 let tail = low
103 .tail_samples()
104 .checked_mul(factor)
105 .and_then(|n| {
106 (prototype.len() - 1)
107 .checked_mul(2)
108 .and_then(|m| n.checked_add(m))
109 })
110 .ok_or_else(|| invalid("multirate tail size overflow"))?;
111 Ok(Self {
112 factor,
113 block,
114 prototype: prototype.to_vec(),
115 low,
116 down_history: vec![vec![0.0; prototype.len()]; inputs],
117 down_head: 0,
118 up_history: vec![vec![0.0; up_length]; outputs],
119 up_head: 0,
120 low_inputs: vec![vec![0.0; low_block_size]; inputs],
121 low_outputs: vec![vec![0.0; low_block_size]; outputs],
122 tail,
123 })
124 }
125
126 pub fn process(
129 &mut self,
130 inputs: &[&[f64]],
131 outputs: &mut [&mut [f64]],
132 ) -> Result<(), DspError> {
133 self.process_buffers(inputs, outputs)
134 }
135
136 pub fn process_buffers<I: AsRef<[f64]>, O: AsMut<[f64]>>(
138 &mut self,
139 inputs: &[I],
140 outputs: &mut [O],
141 ) -> Result<(), DspError> {
142 if inputs.len() != self.input_count()
143 || outputs.len() != self.output_count()
144 || inputs.iter().any(|x| {
145 x.as_ref().len() != self.block || x.as_ref().iter().any(|v| !v.is_finite())
146 })
147 || outputs.iter_mut().any(|y| y.as_mut().len() != self.block)
148 {
149 return Err(invalid("invalid multirate input/output block"));
150 }
151 let length = self.prototype.len();
152 for t in 0..self.block {
153 for (channel, input) in inputs.iter().enumerate() {
154 self.down_history[channel][self.down_head] = input.as_ref()[t];
155 }
156 if t.is_multiple_of(self.factor) {
157 for (channel, history) in self.down_history.iter().enumerate() {
158 let mut value = 0.0;
159 for (k, h) in self.prototype.iter().enumerate() {
160 value += h * history[(self.down_head + length - k) % length];
161 }
162 self.low_inputs[channel][t / self.factor] = value;
163 }
164 }
165 self.down_head = (self.down_head + 1) % length;
166 }
167 self.low
168 .process_buffers(&self.low_inputs, &mut self.low_outputs)?;
169 let up_length = self.up_history[0].len();
170 for t in 0..self.low.block_size() {
171 for (channel, history) in self.up_history.iter_mut().enumerate() {
172 history[self.up_head] = self.low_outputs[channel][t];
173 }
174 for phase in 0..self.factor {
175 for (channel, output) in outputs.iter_mut().enumerate() {
176 let mut value = 0.0;
177 for k in (phase..length).step_by(self.factor) {
178 value += self.prototype[k]
179 * self.up_history[channel]
180 [(self.up_head + up_length - k / self.factor) % up_length];
181 }
182 output.as_mut()[t * self.factor + phase] = value * self.factor as f64;
183 }
184 }
185 self.up_head = (self.up_head + 1) % up_length;
186 }
187 Ok(())
188 }
189
190 pub fn reset(&mut self) {
191 self.low.reset();
192 self.down_head = 0;
193 self.up_head = 0;
194 for history in &mut self.down_history {
195 history.fill(0.0);
196 }
197 for history in &mut self.up_history {
198 history.fill(0.0);
199 }
200 for block in &mut self.low_inputs {
201 block.fill(0.0);
202 }
203 for block in &mut self.low_outputs {
204 block.fill(0.0);
205 }
206 }
207
208 pub fn block_size(&self) -> usize {
209 self.block
210 }
211 pub fn low_block_size(&self) -> usize {
212 self.low.block_size()
213 }
214 pub fn factor(&self) -> usize {
215 self.factor
216 }
217 pub fn input_count(&self) -> usize {
218 self.low.input_count()
219 }
220 pub fn output_count(&self) -> usize {
221 self.low.output_count()
222 }
223 pub fn latency_samples(&self) -> usize {
225 self.prototype.len() - 1
226 }
227 pub fn tail_samples(&self) -> usize {
229 self.tail
230 }
231 pub fn transforms_per_block(&self) -> (usize, usize) {
232 self.low.transforms_per_block()
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 fn direct(x: &[f64], h: &[f64], n: usize) -> Vec<f64> {
241 (0..n)
242 .map(|t| {
243 h.iter()
244 .enumerate()
245 .take(t + 1)
246 .filter(|(k, _)| t - k < x.len())
247 .map(|(k, h)| x[t - k] * h)
248 .sum()
249 })
250 .collect()
251 }
252
253 #[test]
254 fn polyphase_stream_matches_explicit_filter_downsample_upsample_filter() {
255 let (factor, n) = (3, 504);
256 let prototype = [0.05, 0.1, 0.2, 0.3, 0.2, 0.1, 0.05];
257 let routes = vec![
258 FirRoute {
259 input: 0,
260 output: 0,
261 taps: vec![1.0, -0.3, 0.2],
262 },
263 FirRoute {
264 input: 1,
265 output: 0,
266 taps: vec![0.4; 19],
267 },
268 FirRoute {
269 input: 0,
270 output: 1,
271 taps: vec![0.5; 13],
272 },
273 ];
274 let inputs: Vec<Vec<_>> = (0..2)
275 .map(|i| {
276 (0..n)
277 .map(|k| {
278 if k < n / 2 {
279 ((k * 7 + i) as f64).sin()
280 } else {
281 0.0
282 }
283 })
284 .collect()
285 })
286 .collect();
287 let down: Vec<Vec<_>> = inputs
288 .iter()
289 .map(|x| {
290 direct(x, &prototype, n)
291 .into_iter()
292 .step_by(factor)
293 .collect()
294 })
295 .collect();
296 let mut low = vec![vec![0.0; n / factor]; 2];
297 for route in &routes {
298 let filtered = direct(&down[route.input], &route.taps, n / factor);
299 for (a, b) in low[route.output].iter_mut().zip(filtered) {
300 *a += b;
301 }
302 }
303 let expected: Vec<_> = low
304 .iter()
305 .map(|x| {
306 let mut up = vec![0.0; n];
307 for (t, x) in x.iter().enumerate() {
308 up[t * factor] = *x;
309 }
310 direct(&up, &prototype, n)
311 .into_iter()
312 .map(|y| y * factor as f64)
313 .collect::<Vec<_>>()
314 })
315 .collect();
316 for block in [1, 7, 28] {
317 let mut filter =
318 MultirateMatrixConvolver::with_prototype(2, 2, block, factor, &routes, &prototype)
319 .unwrap();
320 assert_eq!(filter.latency_samples(), 6);
321 for _ in 0..2 {
322 let mut got = vec![vec![0.0; n]; 2];
323 for offset in (0..n).step_by(filter.block_size()) {
324 let end = offset + filter.block_size();
325 let x: Vec<_> = inputs.iter().map(|x| &x[offset..end]).collect();
326 let mut y: Vec<_> = got.iter_mut().map(|y| &mut y[offset..end]).collect();
327 filter.process(&x, &mut y).unwrap();
328 }
329 for (a, b) in got.iter().flatten().zip(expected.iter().flatten()) {
330 assert!((a - b).abs() < 2e-12);
331 }
332 filter.reset();
333 }
334 }
335 }
336
337 #[test]
338 fn default_rate_converter_preserves_dc_rejects_aliases_and_reports_delay() {
339 let route = FirRoute {
340 input: 0,
341 output: 0,
342 taps: vec![1.0],
343 };
344 let (factor, block) = (80, 32);
345 let mut filter = MultirateMatrixConvolver::new(1, 1, block, factor, &[route]).unwrap();
346 assert_eq!(filter.latency_samples(), 5120);
347 let n = filter.block_size();
348 let mut output = vec![0.0; n];
349 for _ in 0..8 {
350 filter
351 .process(&[&vec![1.0; n]], &mut [&mut output])
352 .unwrap();
353 }
354 let dc = output.iter().sum::<f64>() / n as f64;
355 assert!((dc - 1.0).abs() < 2e-4, "DC gain={dc}");
356 assert!(output.iter().all(|y| (y - 1.0).abs() < 1e-3));
357 filter.reset();
358 for b in 0..8 {
359 let input: Vec<_> = (0..n)
360 .map(|t| (2.0 * std::f64::consts::PI * 550.0 * (b * n + t) as f64 / 48000.0).sin())
361 .collect();
362 filter.process(&[&input], &mut [&mut output]).unwrap();
363 }
364 let rms = (output.iter().map(|y| y * y).sum::<f64>() / n as f64).sqrt();
365 assert!(rms < 1e-3, "550 Hz alias RMS={rms}");
366 filter.reset();
367 let mut impulse_response = Vec::new();
368 for b in 0..5 {
369 let mut input = vec![0.0; n];
370 if b == 0 {
371 input[0] = 1.0;
372 }
373 filter.process(&[&input], &mut [&mut output]).unwrap();
374 impulse_response.extend_from_slice(&output);
375 }
376 let peak = impulse_response
377 .iter()
378 .enumerate()
379 .max_by(|(_, a), (_, b)| a.abs().total_cmp(&b.abs()))
380 .unwrap()
381 .0;
382 assert_eq!(peak, filter.latency_samples());
383 }
384
385 #[test]
386 fn invalid_multirate_dimensions_and_prototypes_are_rejected() {
387 assert!(MultirateMatrixConvolver::new(1, 1, 8, 0, &[]).is_err());
388 assert!(MultirateMatrixConvolver::new(1, 1, usize::MAX, 80, &[]).is_err());
389 for prototype in [&[][..], &[1.0, 0.0], &[1.0, 2.0, 0.0], &[f64::NAN]] {
390 assert!(MultirateMatrixConvolver::with_prototype(1, 1, 8, 3, &[], prototype).is_err());
391 }
392 let mut filter = MultirateMatrixConvolver::with_prototype(1, 1, 8, 3, &[], &[1.0]).unwrap();
393 let mut output = [0.0; 24];
394 assert!(filter.process(&[&[0.0; 23]], &mut [&mut output]).is_err());
395 assert!(
396 filter
397 .process(&[&[f64::NAN; 24]], &mut [&mut output])
398 .is_err()
399 );
400 }
401}