use super::biquad::Biquad;
use super::*;
use crate::*;
use serde::{Deserialize, Serialize};
use snafu::prelude::*;
type Result<T> = std::result::Result<T, FilterError>;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "python-bindings", gen_stub_pyclass, pyclass(from_py_object))]
pub struct SeriesBiquad {
gain: Flt,
biqs: Vec<Biquad>,
}
#[cfg(feature = "python-bindings")]
#[cfg_attr(feature = "python-bindings", pymethods)]
impl SeriesBiquad {
fn __repr__(&self) -> String {
format!("{self:?}")
}
#[new]
pub fn new_py(coefs: PyReadonlyArrayDyn<Flt>) -> PyResult<Self> {
Ok(SeriesBiquad::new(coefs.as_slice()?)?)
}
#[pyo3(name = "unit")]
#[staticmethod]
pub fn unit_py() -> SeriesBiquad {
SeriesBiquad::unit()
}
#[pyo3(name = "filter")]
pub fn filter_py<'py>(
&mut self,
py: Python<'py>,
input: PyArrayLike1<Flt>,
) -> std::result::Result<PyArr1Flt<'py>, PyErr> {
let mut output = vec![0.0; input.len()?];
self.filter(input.as_slice()?, &mut output);
Ok(output.into_pyarray(py))
}
#[pyo3(name = "reset")]
pub fn reset_py(&mut self) {
self.reset();
}
}
impl SeriesBiquad {
pub fn newFromBiqs(biqs: Vec<Biquad>) -> SeriesBiquad {
assert!(!biqs.is_empty());
SeriesBiquad { biqs, gain: 1.0 }
}
pub fn setGain(&mut self, g: Flt) {
self.gain = g;
}
pub fn getBiquads(&self) -> &Vec<Biquad> {
&self.biqs
}
pub fn new(filter_coefs: &[Flt]) -> Result<SeriesBiquad> {
ensure!(
filter_coefs.len().is_multiple_of(6) && filter_coefs.len() >= 6,
InvalidBiquadInitializationSnafu {
msg: format!(
"filter_coefs length should at least 6 and a multiple of 6, given: {}.",
filter_coefs.len()
)
}
);
let nfilters = filter_coefs.len() / 6;
let mut biqs: Vec<Biquad> = Vec::with_capacity(nfilters);
for coefs in filter_coefs.chunks(6) {
let biq = Biquad::new(coefs)?;
biqs.push(biq);
}
assert!(!biqs.is_empty());
Ok(SeriesBiquad { biqs, gain: 1.0 })
}
pub fn unit() -> SeriesBiquad {
let filter_coefs = &[1., 0., 0., 1., 0., 0.];
SeriesBiquad::new(filter_coefs).unwrap()
}
pub fn filter_inout(&mut self, inout: &mut [Flt]) {
for biq in self.biqs.iter_mut() {
biq.filter_inout(inout);
}
inout.iter_mut().for_each(|io| *io *= self.gain);
}
}
impl FilterMethods for SeriesBiquad {
fn filter(&mut self, input: &[Flt], output: &mut [Flt]) {
debug_assert_eq!(
input.len(),
output.len(),
"Input and output vectors must have the same length"
);
output.copy_from_slice(input);
self.filter_inout(output);
}
fn reset(&mut self) {
self.biqs.iter_mut().for_each(|f| f.reset());
}
}
impl<'a, T: AsArray<'a, Flt>> TransferFunction<'a, T> for SeriesBiquad {
fn tf(&self, fs: StrictlyPositive, freq: T) -> Ccol {
let freq = freq.into();
let mut res = self.biqs.first().unwrap().tf(fs, freq) * self.gain;
for biq in self.biqs.iter().skip(1) {
res = &res * biq.tf(fs, freq);
}
res
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[should_panic]
fn test_biquad2() {
let filter_coefs = vec![1., 0., 0., 0., 0., 0.];
let mut ser = SeriesBiquad::new(&filter_coefs).unwrap();
let inp = vec![1., 0., 0., 0., 0., 0.];
let mut filtered = vec![0.; inp.len()];
ser.filter(&inp, &mut filtered);
assert_eq!(&filtered, &inp);
}
#[test]
fn test_biquad3() {
let filter_coefs = vec![0.5, 0.5, 0., 1., 0., 0.];
let mut ser = SeriesBiquad::new(&filter_coefs).unwrap();
let mut inp = vec![1., 0., 0., 0., 0., 0.];
let mut filtered = vec![0.; inp.len()];
ser.filter(&inp, &mut filtered);
inp[0] = 0.5;
inp[1] = 0.5;
assert_eq!(&inp, &filtered);
}
#[test]
fn test_seriesbiquad_tf1() {
let filter_coefs = vec![1., 0., 0., 1., 0., 0.];
let ser = SeriesBiquad::new(&filter_coefs).unwrap();
let tf = ser.tf(1.0.try_into().unwrap(), &[0., 1.]);
assert_eq!(tf[0].re, 1.0);
assert_eq!(tf[1].im, 0.0);
}
#[test]
fn test_seriesbiquad_tf2() {
let filter_coefs = &[0.5, 0., 0., 1., 0., 0., 0.5, 0., 0., 1., 0., 0.];
let ser = SeriesBiquad::new(filter_coefs).unwrap();
let tf = ser.tf(1.0.try_into().unwrap(), &[0., 1.]);
assert_eq!(tf[0].re, 0.25);
assert_eq!(tf[1].im, 0.0);
}
}