use crate::error::SignalError;
use crate::linear_algebra::Vector;
use crate::scalar::Numeric;
use crate::signal_processing::{Biquad, BiquadCoefficients};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BiquadCascade<const SECTIONS: usize, T: Numeric = f64> {
sections: [Biquad<T>; SECTIONS],
}
impl<const SECTIONS: usize, T: Numeric> BiquadCascade<SECTIONS, T> {
#[must_use]
pub fn new(coefficients: [BiquadCoefficients<T>; SECTIONS]) -> Self {
Self {
sections: core::array::from_fn(|index| Biquad::new(coefficients[index])),
}
}
#[inline]
#[must_use]
pub fn filter(&mut self, input: T) -> T {
let mut value = input;
for section in &mut self.sections {
value = section.filter(value);
}
value
}
pub fn set_section(
&mut self,
index: usize,
coefficients: BiquadCoefficients<T>,
) -> Result<(), SignalError> {
if index >= SECTIONS {
return Err(SignalError::SectionIndexOutOfRange);
}
self.sections[index].set_coefficients(coefficients);
Ok(())
}
pub fn settle_to(&mut self, value: T) {
let mut carried = value;
for section in &mut self.sections {
section.settle_to(carried);
carried = section.value();
}
}
pub fn reset(&mut self) {
for section in &mut self.sections {
section.reset();
}
}
#[must_use]
pub fn magnitude_at(&self, frequency_hz: T) -> T {
self.sections.iter().fold(T::ONE, |carried, section| {
carried * section.coefficients().magnitude_at(frequency_hz)
})
}
#[must_use]
pub fn magnitude_in_decibels_at(&self, frequency_hz: T) -> T {
T::from_f64(20.0 / core::f64::consts::LN_10) * self.magnitude_at(frequency_hz).ln()
}
#[must_use]
pub fn phase_at(&self, frequency_hz: T) -> T {
self.sections.iter().fold(T::ZERO, |carried, section| {
carried + section.coefficients().phase_at(frequency_hz)
})
}
#[must_use]
pub fn delay_at(&self, frequency_hz: T) -> T {
if frequency_hz == T::ZERO {
return T::ZERO;
}
-self.phase_at(frequency_hz) / (T::TWO * T::PI * frequency_hz)
}
#[must_use]
pub fn is_stable(&self) -> bool {
self.sections
.iter()
.all(|section| section.coefficients().is_stable())
}
#[must_use]
pub fn value(&self) -> T {
self.sections.last().map_or(T::ZERO, Biquad::value)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MultiChannelBiquad<const CHANNELS: usize, T: Numeric = f64> {
channels: [Biquad<T>; CHANNELS],
}
impl<const CHANNELS: usize, T: Numeric> MultiChannelBiquad<CHANNELS, T> {
#[must_use]
pub fn new(coefficients: BiquadCoefficients<T>) -> Self {
Self {
channels: core::array::from_fn(|_| Biquad::new(coefficients)),
}
}
#[inline]
pub fn filter(&mut self, input: Vector<CHANNELS, T>) -> Vector<CHANNELS, T> {
Vector::from_fn(|channel| self.channels[channel].filter(input[channel]))
}
pub fn set_coefficients(&mut self, coefficients: BiquadCoefficients<T>) {
for channel in &mut self.channels {
channel.set_coefficients(coefficients);
}
}
pub fn settle_to(&mut self, value: Vector<CHANNELS, T>) {
for (channel, filter) in self.channels.iter_mut().enumerate() {
filter.settle_to(value[channel]);
}
}
pub fn reset(&mut self) {
for channel in &mut self.channels {
channel.reset();
}
}
pub fn value(&self) -> Vector<CHANNELS, T> {
Vector::from_fn(|channel| self.channels[channel].value())
}
}
pub fn harmonic_notch_coefficients<const SECTIONS: usize, T: Numeric>(
fundamental_hz: T,
quality_factor: T,
dt: T,
) -> Result<[BiquadCoefficients<T>; SECTIONS], SignalError> {
if SECTIONS == 0 {
return Err(SignalError::WindowTooShort);
}
let fundamental = BiquadCoefficients::notch(fundamental_hz, quality_factor, dt)?;
if fundamental_hz * T::from_usize(SECTIONS) * dt >= T::HALF {
return Err(SignalError::FrequencyOutOfRange);
}
let mut sections = [fundamental; SECTIONS];
for (index, section) in sections.iter_mut().enumerate().skip(1) {
*section = BiquadCoefficients::notch(
fundamental_hz * T::from_usize(index + 1),
quality_factor,
dt,
)?;
}
Ok(sections)
}