#[cfg(all(not(feature = "std"), not(feature = "libm"), feature = "micromath"))]
#[allow(unused_imports)]
use micromath::F32Ext;
#[cfg(all(not(feature = "std"), feature = "libm"))]
#[allow(unused_imports)]
use num_traits::float::Float;
use core::f32::consts::PI;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum FilterMode {
LowPass,
HighPass,
}
#[derive(Clone, Debug)]
pub(crate) struct StateFilter {
rate: f32,
pub enabled: bool,
was_ever_enabled: bool,
mode: FilterMode,
cutoff_reg: u8,
resonance_reg: u8,
b0: f32,
b1: f32,
b2: f32,
a1: f32,
a2: f32,
xl1: f32,
xl2: f32,
yl1: f32,
yl2: f32,
xr1: f32,
xr2: f32,
yr1: f32,
yr2: f32,
}
impl StateFilter {
pub fn new(rate: f32) -> Self {
Self {
rate,
enabled: false,
was_ever_enabled: false,
mode: FilterMode::LowPass,
cutoff_reg: 127,
resonance_reg: 0,
b0: 1.0,
b1: 0.0,
b2: 0.0,
a1: 0.0,
a2: 0.0,
xl1: 0.0,
xl2: 0.0,
yl1: 0.0,
yl2: 0.0,
xr1: 0.0,
xr2: 0.0,
yr1: 0.0,
yr2: 0.0,
}
}
pub fn reset_history(&mut self) {
self.xl1 = 0.0;
self.xl2 = 0.0;
self.yl1 = 0.0;
self.yl2 = 0.0;
self.xr1 = 0.0;
self.xr2 = 0.0;
self.yr1 = 0.0;
self.yr2 = 0.0;
}
pub fn configure_from_it_registers(&mut self, cutoff_reg: u8, resonance_reg: u8) {
let cutoff_enabled = cutoff_reg & 0x80 != 0;
let resonance_enabled = resonance_reg & 0x80 != 0;
self.enabled = cutoff_enabled || resonance_enabled;
if !self.enabled {
return;
}
self.was_ever_enabled = true;
self.cutoff_reg = cutoff_reg & 0x7F;
self.resonance_reg = resonance_reg & 0x7F;
self.recompute_coefficients();
}
pub fn set_cutoff_reg(&mut self, cutoff_reg: u8) {
let new = cutoff_reg.min(127);
if self.cutoff_reg == new && self.enabled {
return;
}
self.cutoff_reg = new;
self.enabled = true;
self.was_ever_enabled = true;
self.recompute_coefficients();
}
pub fn set_resonance_reg(&mut self, resonance_reg: u8) {
let new = resonance_reg.min(127);
if self.resonance_reg == new && self.enabled {
return;
}
self.resonance_reg = new;
self.enabled = true;
self.was_ever_enabled = true;
self.recompute_coefficients();
}
pub fn set_mode_from_macro(&mut self, xx: u8) {
let disable = xx & 0x20 != 0;
let hp = xx & 0x10 != 0;
if disable {
self.enabled = false;
return;
}
let new_mode = if hp {
FilterMode::HighPass
} else {
FilterMode::LowPass
};
if new_mode != self.mode {
self.mode = new_mode;
if self.enabled {
self.recompute_coefficients();
}
}
}
fn recompute_coefficients(&mut self) {
let fc = 110.0 * powf(2.0, 0.25 + (self.cutoff_reg as f32) / 24.0);
let fc = fc.min(self.rate * 0.49).max(20.0);
let d = powf(10.0, -(self.resonance_reg as f32) * 24.0 / (128.0 * 20.0));
let q = (1.0 / (2.0 * d)).max(0.707);
let omega = 2.0 * PI * fc / self.rate;
let sin_w = sinf(omega);
let cos_w = cosf(omega);
let alpha = sin_w / (2.0 * q);
let (b0, b1, b2) = match self.mode {
FilterMode::LowPass => {
let k = (1.0 - cos_w) * 0.5;
(k, 1.0 - cos_w, k)
}
FilterMode::HighPass => {
let k = (1.0 + cos_w) * 0.5;
(k, -(1.0 + cos_w), k)
}
};
let a0 = 1.0 + alpha;
let a1 = -2.0 * cos_w;
let a2 = 1.0 - alpha;
let inv_a0 = 1.0 / a0;
self.b0 = b0 * inv_a0;
self.b1 = b1 * inv_a0;
self.b2 = b2 * inv_a0;
self.a1 = a1 * inv_a0;
self.a2 = a2 * inv_a0;
}
#[inline]
pub fn process(&mut self, l: f32, r: f32) -> (f32, f32) {
if !self.was_ever_enabled {
return (l, r);
}
if !self.enabled {
self.xl2 = self.xl1;
self.xl1 = l;
self.xr2 = self.xr1;
self.xr1 = r;
return (l, r);
}
let yl = self.b0 * l + self.b1 * self.xl1 + self.b2 * self.xl2
- self.a1 * self.yl1
- self.a2 * self.yl2;
self.xl2 = self.xl1;
self.xl1 = l;
self.yl2 = self.yl1;
self.yl1 = yl;
let yr = self.b0 * r + self.b1 * self.xr1 + self.b2 * self.xr2
- self.a1 * self.yr1
- self.a2 * self.yr2;
self.xr2 = self.xr1;
self.xr1 = r;
self.yr2 = self.yr1;
self.yr1 = yr;
(yl, yr)
}
}
#[inline]
fn powf(x: f32, y: f32) -> f32 {
x.powf(y)
}
#[inline]
fn sinf(x: f32) -> f32 {
x.sin()
}
#[inline]
fn cosf(x: f32) -> f32 {
x.cos()
}