use std::{ f64::{self, consts::SQRT_2}, fmt::Display, sync::Arc, }; use crate::{ decibel::Decibel, SampleRate, traits::{IntFormatter, Processor}, }; #[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Slope { #[default] DB12, DB24, DB36, DB48, } #[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum FilterMode { Peak, HighPass, HighShelf, LowPass, LowShelf, AllPass, BandPass, BandReject, #[default] None, } impl TryFrom for FilterMode { type Error = (); fn try_from(value: i32) -> Result { match value { 0 => Ok(FilterMode::Peak), 1 => Ok(FilterMode::HighPass), 2 => Ok(FilterMode::HighShelf), 3 => Ok(FilterMode::LowPass), 4 => Ok(FilterMode::LowShelf), 5 => Ok(FilterMode::AllPass), 6 => Ok(FilterMode::BandPass), 7 => Ok(FilterMode::BandReject), _ => Err(()), } } } impl From for i32 { fn from(value: FilterMode) -> Self { match value { FilterMode::Peak => 0, FilterMode::HighPass => 1, FilterMode::HighShelf => 2, FilterMode::LowPass => 3, FilterMode::LowShelf => 4, FilterMode::AllPass => 5, FilterMode::BandPass => 6, FilterMode::BandReject => 7, FilterMode::None => 8, } } } impl TryFrom<&str> for FilterMode { type Error = (); fn try_from(value: &str) -> Result { match value { "Peak" => Ok(FilterMode::Peak), "Highpass" => Ok(FilterMode::HighPass), "High Shelf" => Ok(FilterMode::HighShelf), "Lowpass" => Ok(FilterMode::LowPass), "Low Shelf" => Ok(FilterMode::LowShelf), "Allpass" => Ok(FilterMode::AllPass), "Bandpass" => Ok(FilterMode::BandPass), "Notch" => Ok(FilterMode::BandReject), _ => Err(()), } } } impl Display for FilterMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { FilterMode::Peak => "Peak", FilterMode::HighPass => "Highpass", FilterMode::HighShelf => "High Shelf", FilterMode::LowPass => "Lowpass", FilterMode::LowShelf => "Low Shelf", FilterMode::AllPass => "Allpass", FilterMode::BandPass => "Bandpass", FilterMode::BandReject => "Notch", FilterMode::None => "None", }) } } impl IntFormatter for FilterMode { fn v2s() -> Arc String + Send + Sync> where Self: Sized, { Arc::new(move |value| FilterMode::try_from(value).unwrap_or_default().to_string()) } fn s2v() -> Arc Option + Send + Sync> where Self: Sized, { Arc::new(move |value| FilterMode::try_from(value).map(i32::from).ok()) } } #[derive(Default, Debug, Clone, Copy)] struct FilterCoefficients { a1: f64, a2: f64, b0: f64, b1: f64, b2: f64, } impl FilterCoefficients { fn calculate( &mut self, mode: FilterMode, sample_rate: SampleRate, freq: f64, q: f64, gain_db: Decibel, ) { let v = 10f64.powf(gain_db.value().abs() / 20.0); let k = (f64::consts::PI * freq / sample_rate.0).tan(); match mode { FilterMode::Peak => { if gain_db.value() >= 0.0 { let norm = 1.0 / (1.0 + 1.0 / q * k + k * k); self.b0 = (1.0 + v / q * k + k * k) * norm; self.b1 = 2.0 * (k * k - 1.0) * norm; self.b2 = (1.0 - v / q * k + k * k) * norm; self.a1 = self.b1; self.a2 = (1.0 - 1.0 / q * k + k * k) * norm; } else { let norm = 1.0 / (1.0 + v / q * k + k * k); self.b0 = (1.0 + 1.0 / q * k + k * k) * norm; self.b1 = 2.0 * (k * k - 1.0) * norm; self.b2 = (1.0 - 1.0 / q * k + k * k) * norm; self.a1 = self.b1; self.a2 = (1.0 - v / q * k + k * k) * norm; } } FilterMode::HighPass => { let norm = 1.0 / (1.0 + k / q + k * k); self.b0 = 1.0 * norm; self.b1 = -2.0 * self.b0; self.b2 = self.b0; self.a1 = 2.0 * (k * k - 1.0) * norm; self.a2 = (1.0 - k / q + k * k) * norm; } FilterMode::HighShelf => { if gain_db.value() >= 0.0 { let norm = 1.0 / (1.0 + SQRT_2 * k + k * k); self.b0 = (v + (2.0 * v).sqrt() * k + k * k) * norm; self.b1 = 2.0 * (k * k - v) * norm; self.b2 = (v - (2.0 * v).sqrt() * k + k * k) * norm; self.a1 = 2.0 * (k * k - 1.0) * norm; self.a2 = (1.0 - SQRT_2 * k + k * k) * norm; } else { let norm = 1.0 / (v + (2.0 * v).sqrt() * k + k * k); self.b0 = (1.0 + SQRT_2 * k + k * k) * norm; self.b1 = 2.0 * (k * k - 1.0) * norm; self.b2 = (1.0 - SQRT_2 * k + k * k) * norm; self.a1 = 2.0 * (k * k - v) * norm; self.a2 = (v - (2.0 * v).sqrt() * k + k * k) * norm; } } FilterMode::LowPass => { let norm = 1.0 / (1.0 + k / q + k * k); self.b0 = k * k * norm; self.b1 = 2.0 * self.b0; self.b2 = self.b0; self.a1 = 2.0 * (k * k - 1.0) * norm; self.a2 = (1.0 - k / q + k * k) * norm; } FilterMode::LowShelf => { if gain_db.value() >= 0.0 { let norm = 1.0 / (1.0 + SQRT_2 * k + k * k); self.b0 = (1.0 + (2.0 * v).sqrt() * k + v * k * k) * norm; self.b1 = 2.0 * (v * k * k - 1.0) * norm; self.b2 = (1.0 - (2.0 * v).sqrt() * k + v * k * k) * norm; self.a1 = 2.0 * (k * k - 1.0) * norm; self.a2 = (1.0 - SQRT_2 * k + k * k) * norm; } else { let norm = 1.0 / (1.0 + (2.0 * v).sqrt() * k + v * k * k); self.b0 = (1.0 + SQRT_2 * k + k * k) * norm; self.b1 = 2.0 * (k * k - 1.0) * norm; self.b2 = (1.0 - SQRT_2 * k + k * k) * norm; self.a1 = 2.0 * (v * k * k - 1.0) * norm; self.a2 = (1.0 - (2.0 * v).sqrt() * k + v * k * k) * norm; } } FilterMode::AllPass => { let norm = 1.0 / (1.0 + k / q + k * k); self.b0 = (1.0 - k / q + k * k) * norm; self.b1 = 2.0 * (k * k - 1.0) * norm; self.b2 = 1.0; self.a1 = self.b1; self.a2 = self.b0; } FilterMode::BandPass => { let norm = 1.0 / (1.0 + k / q + k * k); self.b0 = k / q * norm; self.b1 = 0.0; self.b2 = -self.b0; self.a1 = 2.0 * (k * k - 1.0) * norm; self.a2 = (1.0 - k / q + k * k) * norm; } FilterMode::BandReject => { let norm = 1.0 / (1.0 + k / q + k * k); self.b0 = (1.0 + k * k) * norm; self.b1 = 2.0 * (k * k - 1.0) * norm; self.b2 = self.b0; self.a1 = self.b1; self.a2 = (1.0 - k / q + k * k) * norm; } FilterMode::None => {} } } } #[derive(Debug, Default, Clone, Copy)] struct State { s1: f64, s2: f64, } #[derive(Debug, Clone, Copy)] pub struct BiquadFilterState { pub mode: FilterMode, pub cutoff: f64, pub q: f64, pub gain_db: Decibel, pub slope: Slope, } impl Default for BiquadFilterState { fn default() -> Self { Self { mode: FilterMode::None, cutoff: 2000.0, q: 0.707, gain_db: Decibel::from(0.0), slope: Slope::DB12, } } } #[derive(Debug, Clone)] pub struct BiquadFilter { pub state: BiquadFilterState, sample_rate: SampleRate, coefficients: FilterCoefficients, slope_states: [State; 4], dirty: bool, } impl BiquadFilter { pub fn new(sample_rate: SampleRate) -> Self { Self::from_state(sample_rate, BiquadFilterState::default()) } pub fn from_state(sample_rate: SampleRate, state: BiquadFilterState) -> Self { Self { sample_rate, coefficients: FilterCoefficients::default(), slope_states: [State::default(); 4], dirty: true, state } } pub fn set_mode(&mut self, mode: FilterMode) { self.dirty = true; self.state.mode = mode; } pub fn set_cutoff(&mut self, cutoff: f64) { self.dirty = true; self.state.cutoff = cutoff; } pub fn set_slope(&mut self, slope: Slope) { self.dirty = true; self.state.slope = slope; } pub fn set_q(&mut self, q: f64) { self.dirty = true; self.state.q = q; } pub fn set_gain(&mut self, gain_db: T) where T: Into, { self.dirty = true; self.state.gain_db = gain_db.into(); } fn update_coefficients(&mut self) { if !self.dirty { return; } self.dirty = false; self.coefficients.calculate( self.state.mode, self.sample_rate, self.state.cutoff, self.state.q, self.state.gain_db, ); } } impl Processor for BiquadFilter { fn process(&mut self, sample: f64) -> f64 { self.update_coefficients(); let iterations = match self.state.slope { Slope::DB12 => 1, Slope::DB24 => 2, Slope::DB36 => 3, Slope::DB48 => 4, }; let mut current = sample; for i in 0..iterations { let result = self.coefficients.b0 * current + self.slope_states[i].s1; let s1 = self.coefficients.b1 * current - self.coefficients.a1 * result + self.slope_states[i].s2; let s2 = self.coefficients.b2 * current - self.coefficients.a2 * result; self.slope_states[i].s1 = s1; self.slope_states[i].s2 = s2; current = result; } if current.is_nan() || current.is_infinite() { 0.0 } else { current } } }