use super::super::{Biquad, DummyFilter, Filter, Result, SeriesBiquad};
use super::FilterGenerator;
use crate::*;
use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
#[cfg_attr(
feature = "python-bindings",
gen_stub_pyclass,
pyclass(get_all, set_all, from_py_object)
)]
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct ParametricEqualizer {
pub descriptors: Vec<ParametricFilterDescriptor>,
}
impl ParametricEqualizer {
pub fn new() -> Self {
Self {
descriptors: Vec::new(),
}
}
}
#[cfg(feature = "python-bindings")]
#[cfg_attr(feature = "python-bindings", gen_stub_pymethods, pymethods)]
impl ParametricEqualizer {
#[new]
pub fn new_py() -> Self {
Self::new()
}
}
impl FilterGenerator for ParametricEqualizer {
fn genFilter(&self, fs: StrictlyPositive) -> Result<Filter> {
if self.descriptors.is_empty() {
return Ok(Filter::Dummy(DummyFilter));
}
let mut filters = Vec::with_capacity(self.descriptors.len());
for desc in &self.descriptors {
filters.push(desc.genFilter(fs)?);
}
Ok(SeriesBiquad::newFromBiqs(filters).into())
}
}
#[cfg_attr(
feature = "python-bindings",
gen_stub_pyclass,
pyclass(get_all, set_all, from_py_object)
)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParametricFilterDescriptor {
pub f0: StrictlyPositive,
pub gain: Flt,
pub Q: StrictlyPositive,
pub filter_type: ParametricFilterType,
pub active: bool,
pub metadata: String,
}
#[cfg(feature = "python-bindings")]
#[gen_stub_pymethods]
#[pymethods]
impl ParametricFilterDescriptor {
#[new]
pub fn new() -> Self {
Self::default()
}
#[pyo3(name = "genFilter")]
pub fn genFilter_py(&self, fs: Flt) -> PyResult<Biquad> {
Ok(self.genFilter(fs.try_into()?)?)
}
}
impl Default for ParametricFilterDescriptor {
fn default() -> Self {
Self {
f0: 1e3.try_into().unwrap(),
gain: 1.0,
Q: 1.0.try_into().unwrap(),
filter_type: ParametricFilterType::LowPass,
active: true,
metadata: String::new(),
}
}
}
impl ParametricFilterDescriptor {
pub fn genFilter(&self, fs: StrictlyPositive) -> Result<Biquad> {
let w0 = 2.0 * pi * *self.f0 / *fs;
if !self.active {
return Ok(Biquad::unit());
}
let alpha = Flt::sin(w0) / *self.Q / 2.;
match &self.filter_type {
ParametricFilterType::Bypass => Ok(Biquad::unit()),
ParametricFilterType::Peaking => {
let A = Flt::sqrt(Flt::powf(10., self.gain / 20.));
let b0 = 1. + alpha * A;
let b1 = -2. * Flt::cos(w0);
let b2 = 1. - alpha * A;
let a0 = 1. + alpha / A;
let a1 = -2. * Flt::cos(w0);
let a2 = 1. - alpha / A;
Ok(Biquad::fromNotNormalized(&[b0, b1, b2, a0, a1, a2])?)
}
ParametricFilterType::LowPass => {
let b0 = (1. - Flt::cos(w0)) / 2.;
let b1 = 1. - Flt::cos(w0);
let b2 = (1. - Flt::cos(w0)) / 2.;
let a0 = 1. + alpha;
let a1 = -2. * Flt::cos(w0);
let a2 = 1. - alpha;
Ok(Biquad::fromNotNormalized(&[b0, b1, b2, a0, a1, a2])?)
}
ParametricFilterType::HighPass => {
let b0 = (1. + Flt::cos(w0)) / 2.;
let b1 = -(1. + Flt::cos(w0));
let b2 = (1. + Flt::cos(w0)) / 2.;
let a0 = 1. + alpha;
let a1 = -2. * Flt::cos(w0);
let a2 = 1. - alpha;
Ok(Biquad::fromNotNormalized(&[b0, b1, b2, a0, a1, a2])?)
}
ParametricFilterType::LowShelf => {
let A = Flt::powf(10., self.gain / 40.);
let b0 = A * ((A + 1.) - (A - 1.) * Flt::cos(w0) + 2. * Flt::sqrt(A) * alpha);
let b1 = 2. * A * ((A - 1.) - (A + 1.) * Flt::cos(w0));
let b2 = A * ((A + 1.) - (A - 1.) * Flt::cos(w0) - 2. * Flt::sqrt(A) * alpha);
let a0 = (A + 1.) + (A - 1.) * Flt::cos(w0) + 2. * Flt::sqrt(A) * alpha;
let a1 = -2. * ((A - 1.) + (A + 1.) * Flt::cos(w0));
let a2 = (A + 1.) + (A - 1.) * Flt::cos(w0) - 2. * Flt::sqrt(A) * alpha;
Ok(Biquad::fromNotNormalized(&[b0, b1, b2, a0, a1, a2])?)
}
ParametricFilterType::HighShelf => {
let A = Flt::powf(10., self.gain / 40.);
let b0 = A * ((A + 1.) + (A - 1.) * Flt::cos(w0) + 2. * Flt::sqrt(A) * alpha);
let b1 = -2. * A * ((A - 1.) + (A + 1.) * Flt::cos(w0));
let b2 = A * ((A + 1.) + (A - 1.) * Flt::cos(w0) - 2. * Flt::sqrt(A) * alpha);
let a0 = (A + 1.) - (A - 1.) * Flt::cos(w0) + 2. * Flt::sqrt(A) * alpha;
let a1 = 2. * ((A - 1.) - (A + 1.) * Flt::cos(w0));
let a2 = (A + 1.) - (A - 1.) * Flt::cos(w0) - 2. * Flt::sqrt(A) * alpha;
Ok(Biquad::fromNotNormalized(&[b0, b1, b2, a0, a1, a2])?)
}
ParametricFilterType::Notch => {
let b0 = 1.;
let b1 = -2. * Flt::cos(w0);
let b2 = 1.;
let a0 = 1. + alpha;
let a1 = -2. * Flt::cos(w0);
let a2 = 1. - alpha;
Ok(Biquad::fromNotNormalized(&[b0, b1, b2, a0, a1, a2])?)
}
}
}
}
#[cfg_attr(
feature = "python-bindings",
gen_stub_pyclass_enum,
pyclass(from_py_object)
)]
#[derive(
Debug, Copy, Clone, Serialize, Deserialize, strum_macros::EnumIter, strum_macros::Display,
)]
#[non_exhaustive] #[repr(u32)]
pub enum ParametricFilterType {
LowPass = 0,
HighPass = 1,
LowShelf = 2,
HighShelf = 3,
Peaking = 4,
Notch = 5,
Bypass = 6,
}
#[cfg(feature = "python-bindings")]
#[cfg_attr(feature = "python-bindings", gen_stub_pymethods, pymethods)]
impl ParametricFilterType {
#[staticmethod]
fn all() -> Vec<ParametricFilterType> {
Self::iter().collect()
}
fn __str__(&self) -> String {
format!("{:?}", self)
}
fn type_index(&self) -> u32 {
*self as u32
}
}