use crate::{frequency::Hertz, Errors};
#[allow(unused_imports)]
use libm::{F32Ext, F64Ext};
pub const Q_BUTTERWORTH_F32: f32 = core::f32::consts::FRAC_1_SQRT_2;
pub const Q_BUTTERWORTH_F64: f64 = core::f64::consts::FRAC_1_SQRT_2;
#[derive(Clone, Copy, Debug)]
pub enum Type<DBGain> {
SinglePoleLowPass,
LowPass,
HighPass,
BandPass,
Notch,
AllPass,
LowShelf(DBGain),
HighShelf(DBGain),
PeakingEQ(DBGain),
}
#[derive(Clone, Copy, Debug)]
pub struct Coefficients<T> {
pub a1: T,
pub a2: T,
pub b0: T,
pub b1: T,
pub b2: T,
}
impl Coefficients<f32> {
pub fn from_params(
filter: Type<f32>,
fs: Hertz<f32>,
f0: Hertz<f32>,
q_value: f32,
) -> Result<Coefficients<f32>, Errors> {
if 2.0 * f0.hz() > fs.hz() {
return Err(Errors::OutsideNyquist);
}
if q_value < 0.0 {
return Err(Errors::NegativeQ);
}
let omega = 2.0 * core::f32::consts::PI * f0.hz() / fs.hz();
match filter {
Type::SinglePoleLowPass => {
let alpha = omega / (omega + 1.0);
Ok(Coefficients {
a1: alpha - 1.0,
a2: 0.0,
b0: alpha,
b1: 0.0,
b2: 0.0,
})
}
Type::LowPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = (1.0 - omega_c) * 0.5;
let b1 = 1.0 - omega_c;
let b2 = (1.0 - omega_c) * 0.5;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::HighPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = (1.0 + omega_c) * 0.5;
let b1 = -(1.0 + omega_c);
let b2 = (1.0 + omega_c) * 0.5;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::BandPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = omega_s / 2.0;
let b1 = 0.;
let b2 = -(omega_s / 2.0);
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
let div = 1.0 / a0;
Ok(Coefficients {
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div,
})
}
Type::Notch => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = 1.0;
let b1 = -2.0 * omega_c;
let b2 = 1.0;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::AllPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = 1.0 - alpha;
let b1 = -2.0 * omega_c;
let b2 = 1.0 + alpha;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::LowShelf(db_gain) => {
let a = 10.0f32.powf(db_gain / 40.0);
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = a * ((a + 1.0) - (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt());
let b1 = 2.0 * a * ((a - 1.0) - (a + 1.0) * omega_c);
let b2 = a * ((a + 1.0) - (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt());
let a0 = (a + 1.0) + (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt();
let a1 = -2.0 * ((a - 1.0) + (a + 1.0) * omega_c);
let a2 = (a + 1.0) + (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt();
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::HighShelf(db_gain) => {
let a = 10.0f32.powf(db_gain / 40.0);
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = a * ((a + 1.0) + (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt());
let b1 = -2.0 * a * ((a - 1.0) + (a + 1.0) * omega_c);
let b2 = a * ((a + 1.0) + (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt());
let a0 = (a + 1.0) - (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt();
let a1 = 2.0 * ((a - 1.0) - (a + 1.0) * omega_c);
let a2 = (a + 1.0) - (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt();
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::PeakingEQ(db_gain) => {
let a = 10.0f32.powf(db_gain / 40.0);
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = 1.0 + alpha * a;
let b1 = -2.0 * omega_c;
let b2 = 1.0 - alpha * a;
let a0 = 1.0 + alpha / a;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha / a;
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
}
}
}
impl Coefficients<f64> {
pub fn from_params(
filter: Type<f64>,
fs: Hertz<f64>,
f0: Hertz<f64>,
q_value: f64,
) -> Result<Coefficients<f64>, Errors> {
if 2.0 * f0.hz() > fs.hz() {
return Err(Errors::OutsideNyquist);
}
if q_value < 0.0 {
return Err(Errors::NegativeQ);
}
let omega = 2.0 * core::f64::consts::PI * f0.hz() / fs.hz();
match filter {
Type::SinglePoleLowPass => {
let alpha = omega / (omega + 1.0);
Ok(Coefficients {
a1: alpha - 1.0,
a2: 0.0,
b0: alpha,
b1: 0.0,
b2: 0.0,
})
}
Type::LowPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = (1.0 - omega_c) * 0.5;
let b1 = 1.0 - omega_c;
let b2 = (1.0 - omega_c) * 0.5;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
let div = 1.0 / a0;
Ok(Coefficients {
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div,
})
}
Type::HighPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = (1.0 + omega_c) * 0.5;
let b1 = -(1.0 + omega_c);
let b2 = (1.0 + omega_c) * 0.5;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
let div = 1.0 / a0;
Ok(Coefficients {
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div,
})
}
Type::Notch => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = 1.0;
let b1 = -2.0 * omega_c;
let b2 = 1.0;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
let div = 1.0 / a0;
Ok(Coefficients {
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div,
})
}
Type::BandPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = omega_s / 2.0;
let b1 = 0.;
let b2 = -(omega_s / 2.0);
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
let div = 1.0 / a0;
Ok(Coefficients {
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div,
})
}
Type::AllPass => {
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = 1.0 - alpha;
let b1 = -2.0 * omega_c;
let b2 = 1.0 + alpha;
let a0 = 1.0 + alpha;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha;
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::LowShelf(db_gain) => {
let a = 10.0f64.powf(db_gain / 40.0);
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = a * ((a + 1.0) - (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt());
let b1 = 2.0 * a * ((a - 1.0) - (a + 1.0) * omega_c);
let b2 = a * ((a + 1.0) - (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt());
let a0 = (a + 1.0) + (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt();
let a1 = -2.0 * ((a - 1.0) + (a + 1.0) * omega_c);
let a2 = (a + 1.0) + (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt();
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::HighShelf(db_gain) => {
let a = 10.0f64.powf(db_gain / 40.0);
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = a * ((a + 1.0) + (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt());
let b1 = -2.0 * a * ((a - 1.0) + (a + 1.0) * omega_c);
let b2 = a * ((a + 1.0) + (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt());
let a0 = (a + 1.0) - (a - 1.0) * omega_c + 2.0 * alpha * a.sqrt();
let a1 = 2.0 * ((a - 1.0) - (a + 1.0) * omega_c);
let a2 = (a + 1.0) - (a - 1.0) * omega_c - 2.0 * alpha * a.sqrt();
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
Type::PeakingEQ(db_gain) => {
let a = 10.0f64.powf(db_gain / 40.0);
let omega_s = omega.sin();
let omega_c = omega.cos();
let alpha = omega_s / (2.0 * q_value);
let b0 = 1.0 + alpha * a;
let b1 = -2.0 * omega_c;
let b2 = 1.0 - alpha * a;
let a0 = 1.0 + alpha / a;
let a1 = -2.0 * omega_c;
let a2 = 1.0 - alpha / a;
Ok(Coefficients {
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
})
}
}
}
}