#[derive(Debug, Clone, Copy)]
pub struct BiquadState {
x1: f64, x2: f64, y1: f64, y2: f64, }
impl BiquadState {
#[must_use]
pub fn new() -> Self {
Self {
x1: 0.0,
x2: 0.0,
y1: 0.0,
y2: 0.0,
}
}
}
impl Default for BiquadState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy)]
pub struct BiquadCoeffs {
pub b0: f64,
pub b1: f64,
pub b2: f64,
pub a1: f64,
pub a2: f64,
}
#[inline]
pub fn apply_biquad(x: f64, coeffs: &BiquadCoeffs, state: &mut BiquadState) -> f64 {
let y = coeffs.b0 * x + coeffs.b1 * state.x1 + coeffs.b2 * state.x2
- coeffs.a1 * state.y1
- coeffs.a2 * state.y2;
state.x2 = state.x1;
state.x1 = x;
state.y2 = state.y1;
state.y1 = y;
y
}
#[must_use]
pub fn k_weighting_coeffs(sample_rate: u32) -> (BiquadCoeffs, BiquadCoeffs) {
const SHELF_GAIN_DB: f64 = 3.999_843_853_973_347;
const SHELF_FC: f64 = 1_681.974_450_955_533;
const SHELF_Q: f64 = 0.707_175_236_955_419_6;
const SHELF_VB_EXP: f64 = 0.499_666_774_154_541_6;
let vh = libm::pow(10.0, SHELF_GAIN_DB / 20.0);
let vb = libm::pow(vh, SHELF_VB_EXP);
let k = libm::tan(core::f64::consts::PI * SHELF_FC / sample_rate as f64);
let a0 = 1.0 + k / SHELF_Q + k * k;
let stage1 = BiquadCoeffs {
b0: (vh + vb * (k / SHELF_Q) + k * k) / a0,
b1: (2.0 * (k * k - vh)) / a0,
b2: (vh - vb * (k / SHELF_Q) + k * k) / a0,
a1: (2.0 * (k * k - 1.0)) / a0,
a2: (1.0 - k / SHELF_Q + k * k) / a0,
};
const HIGH_PASS_FC: f64 = 38.135_470_876_024_44;
const HIGH_PASS_Q: f64 = 0.500_327_037_323_877_3;
let k = libm::tan(core::f64::consts::PI * HIGH_PASS_FC / sample_rate as f64);
let a0 = 1.0 + k / HIGH_PASS_Q + k * k;
let stage2 = BiquadCoeffs {
b0: 1.0,
b1: -2.0,
b2: 1.0,
a1: (2.0 * (k * k - 1.0)) / a0,
a2: (1.0 - k / HIGH_PASS_Q + k * k) / a0,
};
(stage1, stage2)
}