137 lines
3.7 KiB
Rust
137 lines
3.7 KiB
Rust
|
|
/// Series of biquads that filter sequentially on an input signal
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// See (tests)
|
|
///
|
|
use super::*;
|
|
use super::biquad::Biquad;
|
|
use anyhow::{bail, Result};
|
|
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
|
|
|
pub struct SeriesBiquad {
|
|
biqs: Vec<Biquad>,
|
|
}
|
|
|
|
#[cfg(feature = "python-bindings")]
|
|
#[cfg_attr(feature = "python-bindings", pymethods)]
|
|
impl SeriesBiquad {
|
|
#[new]
|
|
/// Create new series filter set. See [SeriesBiquad::new()]
|
|
///
|
|
pub fn new_py<'py>(coefs: PyReadonlyArrayDyn<Flt>) -> PyResult<Self> {
|
|
Ok(SeriesBiquad::new(coefs.as_slice()?)?)
|
|
}
|
|
#[pyo3(name = "unit")]
|
|
#[staticmethod]
|
|
/// See: [Biquad::unit()]
|
|
pub fn unit_py() -> SeriesBiquad {
|
|
SeriesBiquad::unit()
|
|
}
|
|
#[pyo3(name = "filter")]
|
|
/// See: [SeriesBiquad::filter()]
|
|
pub fn filter_py<'py>(
|
|
&mut self,
|
|
py: Python<'py>,
|
|
input: PyArrayLike1<Flt>,
|
|
) -> PyResult<&'py PyArray1<Flt>> {
|
|
Ok(self.filter(input.as_slice()?).into_pyarray(py))
|
|
}
|
|
#[pyo3(name = "reset")]
|
|
/// See: [SeriesBiquad::reset()]
|
|
pub fn reset_py(&mut self) {
|
|
self.reset();
|
|
}
|
|
}
|
|
impl SeriesBiquad {
|
|
/// Create a new series biquad, having an arbitrary number of biquads.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `filter_coefs` - Vector of biquad coefficients, stored in a single array. The first six
|
|
/// for the first biquad, and so on.
|
|
///
|
|
///
|
|
pub fn new(filter_coefs: &[Flt]) -> Result<SeriesBiquad> {
|
|
if filter_coefs.len() % 6 != 0 {
|
|
bail!(
|
|
"filter_coefs should be 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);
|
|
}
|
|
|
|
if biqs.is_empty() {
|
|
bail!("No filter coefficients given!");
|
|
}
|
|
|
|
Ok(SeriesBiquad { biqs })
|
|
}
|
|
|
|
/// Unit impulse response series biquad. Input = output
|
|
pub fn unit() -> SeriesBiquad {
|
|
let filter_coefs = &[1., 0., 0., 1., 0., 0.];
|
|
SeriesBiquad::new(filter_coefs).unwrap()
|
|
}
|
|
fn clone_dyn(&self) -> Box<dyn Filter> {
|
|
Box::new(self.clone())
|
|
}
|
|
}
|
|
|
|
impl Filter for SeriesBiquad {
|
|
//! Filter input by applying all biquad filters in series on each input sample, to obtain the
|
|
//! output samples.
|
|
//!
|
|
fn filter(&mut self, input: &[Flt]) -> Vd {
|
|
let mut inout = input.to_vec();
|
|
for biq in self.biqs.iter_mut() {
|
|
biq.filter_inout(&mut inout);
|
|
}
|
|
inout
|
|
}
|
|
fn reset(&mut self) {
|
|
self.biqs.iter_mut().for_each(|f| f.reset());
|
|
}
|
|
fn clone_dyn(&self) -> Box<dyn Filter> {
|
|
Box::new(self.clone())
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_biquad2() {
|
|
// A a0 coefficient not in the right place, meaning we panic on unwrap
|
|
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 filtered = ser.filter(&inp);
|
|
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 filtered = ser.filter(&inp);
|
|
|
|
// Change input to see match what should come out of output
|
|
inp[0] = 0.5;
|
|
inp[1] = 0.5;
|
|
assert_eq!(&inp, &filtered);
|
|
}
|
|
} |