use crate::AudioEffect;
use std::f32::consts::PI;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterMode {
LowPass,
HighPass,
BandPass,
Notch,
}
#[derive(Debug, Clone)]
pub struct StateVariableConfig {
pub frequency: f32,
pub resonance: f32,
pub mode: FilterMode,
}
impl Default for StateVariableConfig {
fn default() -> Self {
Self {
frequency: 1000.0,
resonance: 0.707,
mode: FilterMode::LowPass,
}
}
}
pub struct StateVariableFilter {
low: f32,
band: f32,
f: f32,
q: f32,
config: StateVariableConfig,
sample_rate: f32,
}
impl StateVariableFilter {
#[must_use]
pub fn new(config: StateVariableConfig, sample_rate: f32) -> Self {
let mut filter = Self {
low: 0.0,
band: 0.0,
f: 0.0,
q: 0.0,
config,
sample_rate,
};
filter.update_coefficients();
filter
}
fn update_coefficients(&mut self) {
self.f = 2.0 * (PI * self.config.frequency / self.sample_rate).sin();
self.q = 1.0 / self.config.resonance;
}
pub fn set_frequency(&mut self, frequency: f32) {
self.config.frequency = frequency.clamp(20.0, self.sample_rate * 0.5);
self.update_coefficients();
}
pub fn set_resonance(&mut self, resonance: f32) {
self.config.resonance = resonance.clamp(0.5, 20.0);
self.update_coefficients();
}
pub fn set_mode(&mut self, mode: FilterMode) {
self.config.mode = mode;
}
}
impl AudioEffect for StateVariableFilter {
fn process_sample(&mut self, input: f32) -> f32 {
self.low += self.f * self.band;
let high = input - self.low - self.q * self.band;
self.band += self.f * high;
match self.config.mode {
FilterMode::LowPass => self.low,
FilterMode::HighPass => high,
FilterMode::BandPass => self.band,
FilterMode::Notch => high + self.low,
}
}
fn reset(&mut self) {
self.low = 0.0;
self.band = 0.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_svf() {
let config = StateVariableConfig::default();
let mut filter = StateVariableFilter::new(config, 48000.0);
let output = filter.process_sample(1.0);
assert!(output.is_finite());
}
#[test]
fn test_svf_modes() {
let modes = [
FilterMode::LowPass,
FilterMode::HighPass,
FilterMode::BandPass,
FilterMode::Notch,
];
for mode in modes {
let config = StateVariableConfig {
mode,
..Default::default()
};
let mut filter = StateVariableFilter::new(config, 48000.0);
for _ in 0..100 {
let output = filter.process_sample(0.5);
assert!(output.is_finite());
}
}
}
}