use arraydeque::{ArrayDeque, Wrapping};
use itertools::izip;
#[derive(Clone,Debug)]
pub struct BiquadFilter {
x: ArrayDeque<f32, 3, Wrapping>,
y: ArrayDeque<f32, 3, Wrapping>,
b: [f32; 3],
a: [f32; 3]
}
impl BiquadFilter {
pub fn new(b: &[f32], a: &[f32]) -> BiquadFilter {
assert_eq!(b.len(), 3);
assert_eq!(a.len(), 3);
assert_ne!(a[0], 0.0);
let mut x: ArrayDeque<f32, 3, Wrapping> = ArrayDeque::new();
let mut y: ArrayDeque<f32, 3, Wrapping> = ArrayDeque::new();
for _ in 0..3 {
x.push_front(0.0);
y.push_front(0.0);
}
let mut b_arr: [f32; 3] = [0.0; 3];
b_arr.clone_from_slice(b);
let mut neg_a_arr: [f32; 3] = [0.0; 3];
neg_a_arr[0] = a[0];
for i in 1..3 {
neg_a_arr[i] = -a[i];
}
BiquadFilter {
x, y,
b: b_arr, a: neg_a_arr
}
}
fn process_one(&mut self, in_samp: f32) -> f32 {
self.x.pop_back();
self.x.push_front(in_samp.clone());
self.y.pop_back();
let mut sum = 0.0;
for (xi, bi) in izip!(self.x.iter(), self.b.iter()) {
sum += *xi * *bi;
}
for (yi, ai) in izip!(self.y.iter(), self.a[1..].iter()) {
sum += *yi * *ai;
}
sum /= self.a[0];
self.y.push_front(sum);
sum
}
pub fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) {
let size = std::cmp::min(input_buffer.len(), output_buffer.len());
(0..size).for_each(|i| output_buffer[i] = self.process_one(input_buffer[i]));
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn test_biquad_bilinear_rc() {
let rc = 1.0;
let t_samp = 0.1;
let mut analog_response = Vec::new();
for i in 0..50 {
let t = (i as f32) * t_samp;
analog_response.push(1.0 - f32::exp(-t / rc));
}
let b = [1.0, 1.0, 0.0];
let a = [1.0+(2.0*rc/t_samp), 1.0-(2.0*rc/t_samp), 0.0];
let mut biquad_rc = BiquadFilter::new(&b, &a);
let dig_unit_step = vec![1.0; 50];
let mut digital_response = vec![0.0; 50];
let _ = biquad_rc.process_buffer(&dig_unit_step, &mut digital_response);
for i in 0..50 {
assert_approx_eq!(analog_response[i], digital_response[i], 0.05);
}
}
}