use super::{conj, norm_sqr, Cf64, Stage};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AdaptiveNotch {
r: f64,
mu: f64,
power_alpha: f64,
z0: Cf64,
xf_prev: Cf64,
power: f64,
}
impl AdaptiveNotch {
pub fn new(r: f64, mu: f64, z0: Cf64) -> Self {
assert!(0.0 < r && r < 1.0, "r must be in (0, 1)");
assert!(0.0 < mu && mu < 1.0, "mu must be in (0, 1)");
Self {
r,
mu,
power_alpha: mu.min(0.01),
z0,
xf_prev: Cf64::default(),
power: 0.0,
}
}
pub fn zero(&self) -> Cf64 {
self.z0
}
pub fn frequency_hz(&self, fs_hz: f64) -> f64 {
self.z0.im.atan2(self.z0.re) * fs_hz / (2.0 * std::f64::consts::PI)
}
pub fn gain_at(&self, f_hz: f64, fs_hz: f64) -> f64 {
let zi = super::cis(-2.0 * std::f64::consts::PI * f_hz / fs_hz);
let num = super::sub(Cf64::new(1.0, 0.0), self.z0 * zi);
let den = super::sub(Cf64::new(1.0, 0.0), self.z0 * zi * self.r);
num.abs() / den.abs()
}
}
impl Stage for AdaptiveNotch {
fn process(&mut self, block: &mut [Cf64]) {
for x in block.iter_mut() {
let xf = *x + self.z0 * self.xf_prev * self.r;
let y = super::sub(xf, self.z0 * self.xf_prev);
self.power += self.power_alpha * (norm_sqr(self.xf_prev) - self.power);
if self.power > 0.0 {
self.z0 = self.z0 + y * conj(self.xf_prev) * (self.mu / self.power);
let m = self.z0.abs();
if m > 1.0 {
self.z0 = self.z0 * (1.0 / m);
}
}
self.xf_prev = xf;
*x = y;
}
}
}