use std::f32::consts::*;
#[derive(Copy, Clone, Debug, Default)]
pub struct BiquadCoefs {
pub a1: f32,
pub a2: f32,
pub b0: f32,
pub b1: f32,
pub b2: f32,
}
impl BiquadCoefs {
#[inline]
pub fn new(b0: f32, b1: f32, b2: f32, a1: f32, a2: f32) -> Self {
Self { b0, b1, b2, a1, a2 }
}
#[inline]
pub fn butter_lowpass(sample_rate: f32, cutoff: f32) -> BiquadCoefs {
let f = (cutoff * PI / sample_rate).tan();
let a0r = 1.0 / (1.0 + SQRT_2 * f + f * f);
let a1 = (2.0 * f * f - 2.0) * a0r;
let a2 = (1.0 - SQRT_2 * f + f * f) * a0r;
let b0 = f * f * a0r;
let b1 = 2.0 * b0;
let b2 = b0;
BiquadCoefs { a1, a2, b0, b1, b2 }
}
pub fn calc_cascaded_butter_q(order: usize, casc_idx: usize) -> f32 {
let order = order as f32;
let casc_idx = casc_idx as f32;
let b = -2.0 * ((2.0 * casc_idx + order - 1.0) * PI / (2.0 * order)).cos();
1.0 / b
}
#[inline]
pub fn lowpass(sample_rate: f32, q: f32, cutoff: f32) -> BiquadCoefs {
let f = (cutoff * PI / sample_rate).tan();
let a0r = 1.0 / (1.0 + f / q + f * f);
let b0 = f * f * a0r;
let b1 = 2.0 * b0;
let b2 = b0;
let a1 = 2.0 * (f * f - 1.0) * a0r;
let a2 = (1.0 - f / q + f * f) * a0r;
BiquadCoefs { a1, a2, b0, b1, b2 }
}
pub fn resonator(sample_rate: f32, center: f32, bandwidth: f32) -> BiquadCoefs {
let r = (-PI * bandwidth / sample_rate).exp();
let a1 = -2.0 * r * (TAU * center / sample_rate).cos();
let a2 = r * r;
let b0 = (1.0 - r * r).sqrt() * 0.5;
let b1 = 0.0;
let b2 = -b0;
BiquadCoefs { a1, a2, b0, b1, b2 }
}
}
#[derive(Debug, Copy, Clone, Default)]
pub struct Biquad {
coefs: BiquadCoefs,
x1: f32,
x2: f32,
y1: f32,
y2: f32,
}
impl Biquad {
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn new_with(b0: f32, b1: f32, b2: f32, a1: f32, a2: f32) -> Self {
let mut s = Self::new();
s.set_coefs(BiquadCoefs::new(b0, b1, b2, a1, a2));
s
}
#[inline]
pub fn coefs(&self) -> &BiquadCoefs {
&self.coefs
}
#[inline]
pub fn set_coefs(&mut self, coefs: BiquadCoefs) {
self.coefs = coefs;
}
pub fn reset(&mut self) {
self.x1 = 0.0;
self.x2 = 0.0;
self.y1 = 0.0;
self.y2 = 0.0;
}
#[inline]
pub fn tick(&mut self, input: f32) -> f32 {
let x0 = input;
let y0 = self.coefs.b0 * x0 + self.coefs.b1 * self.x1 + self.coefs.b2 * self.x2
- self.coefs.a1 * self.y1
- self.coefs.a2 * self.y2;
self.x2 = self.x1;
self.x1 = x0;
self.y2 = self.y1;
self.y1 = y0;
y0
}
}
#[derive(Copy, Clone)]
pub struct ButterLowpass {
biquad: Biquad,
sample_rate: f32,
cutoff: f32,
}
#[allow(dead_code)]
impl ButterLowpass {
pub fn new(sample_rate: f32, cutoff: f32) -> Self {
let mut this = ButterLowpass { biquad: Biquad::new(), sample_rate, cutoff: 0.0 };
this.set_cutoff(cutoff);
this
}
pub fn set_cutoff(&mut self, cutoff: f32) {
self.biquad.set_coefs(BiquadCoefs::butter_lowpass(self.sample_rate, cutoff));
self.cutoff = cutoff;
}
fn set_sample_rate(&mut self, srate: f32) {
self.sample_rate = srate;
self.reset();
self.biquad.reset();
self.set_cutoff(self.cutoff);
}
fn reset(&mut self) {
self.biquad.reset();
self.set_cutoff(self.cutoff);
}
#[inline]
fn tick(&mut self, input: f32) -> f32 {
self.biquad.tick(input)
}
}