use super::{Cf64, Stage};
use std::f64::consts::PI;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Biquad {
pub b: [f64; 3],
pub a: [f64; 2],
s1: Cf64,
s2: Cf64,
}
impl Biquad {
pub fn new(b: [f64; 3], a: [f64; 3]) -> Self {
assert!(a[0] != 0.0, "a0 must be non-zero");
Self {
b: [b[0] / a[0], b[1] / a[0], b[2] / a[0]],
a: [a[1] / a[0], a[2] / a[0]],
s1: Cf64::default(),
s2: Cf64::default(),
}
}
fn proto(fs_hz: f64, f0_hz: f64, q: f64) -> (f64, f64) {
assert!(
0.0 < f0_hz && f0_hz < fs_hz / 2.0 && q > 0.0,
"biquad needs 0 < f0 < fs/2 and Q > 0"
);
let w0 = 2.0 * PI * f0_hz / fs_hz;
(w0.cos(), w0.sin() / (2.0 * q))
}
pub fn lowpass(fs_hz: f64, f0_hz: f64, q: f64) -> Self {
let (c, al) = Self::proto(fs_hz, f0_hz, q);
Self::new(
[(1.0 - c) / 2.0, 1.0 - c, (1.0 - c) / 2.0],
[1.0 + al, -2.0 * c, 1.0 - al],
)
}
pub fn highpass(fs_hz: f64, f0_hz: f64, q: f64) -> Self {
let (c, al) = Self::proto(fs_hz, f0_hz, q);
Self::new(
[(1.0 + c) / 2.0, -(1.0 + c), (1.0 + c) / 2.0],
[1.0 + al, -2.0 * c, 1.0 - al],
)
}
pub fn bandpass(fs_hz: f64, f0_hz: f64, q: f64) -> Self {
let (c, al) = Self::proto(fs_hz, f0_hz, q);
Self::new([al, 0.0, -al], [1.0 + al, -2.0 * c, 1.0 - al])
}
pub fn notch(fs_hz: f64, f0_hz: f64, q: f64) -> Self {
let (c, al) = Self::proto(fs_hz, f0_hz, q);
Self::new([1.0, -2.0 * c, 1.0], [1.0 + al, -2.0 * c, 1.0 - al])
}
pub fn response(&self, f_hz: f64, fs_hz: f64) -> Cf64 {
let z1 = super::cis(-2.0 * PI * f_hz / fs_hz);
let z2 = z1 * z1;
let num = Cf64::new(self.b[0], 0.0) + z1 * self.b[1] + z2 * self.b[2];
let den = Cf64::new(1.0, 0.0) + z1 * self.a[0] + z2 * self.a[1];
let d2 = super::norm_sqr(den);
num * super::conj(den) * (1.0 / d2)
}
pub fn is_stable(&self) -> bool {
self.a[1].abs() < 1.0 && self.a[0].abs() < 1.0 + self.a[1]
}
pub fn reset(&mut self) {
self.s1 = Cf64::default();
self.s2 = Cf64::default();
}
}
impl Stage for Biquad {
fn process(&mut self, block: &mut [Cf64]) {
let [b0, b1, b2] = self.b;
let [a1, a2] = self.a;
for x in block.iter_mut() {
let xi = *x;
let y = xi * b0 + self.s1;
self.s1 = xi * b1 + self.s2 + y * (-a1);
self.s2 = xi * b2 + y * (-a2);
*x = y;
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct BiquadCascade {
pub sections: Vec<Biquad>,
}
impl BiquadCascade {
pub fn new(sections: Vec<Biquad>) -> Self {
Self { sections }
}
pub fn response(&self, f_hz: f64, fs_hz: f64) -> Cf64 {
self.sections
.iter()
.fold(Cf64::new(1.0, 0.0), |acc, s| acc * s.response(f_hz, fs_hz))
}
}
impl Stage for BiquadCascade {
fn process(&mut self, block: &mut [Cf64]) {
for s in &mut self.sections {
s.process(block);
}
}
}