use crate::{
error::{Error, Result},
types::{Band, PMParameters, ParametersBuilder},
};
use num_traits::Float;
pub fn pm_parameters<T: Float>(
num_taps: usize,
band_settings: &[BandSetting<T>],
) -> Result<impl ParametersBuilder<T> + '_> {
if band_settings.is_empty() {
return Err(Error::BandsEmpty);
}
let bands = band_settings.iter().map(|setting| setting.band()).collect();
let desired = move |f| desired_response(band_settings, f);
let weights = move |f| weight(band_settings, f);
PMParameters::new(num_taps, bands, desired, weights)
}
fn desired_response<T: Float>(settings: &[BandSetting<T>], freq: T) -> T {
let setting = closest_setting(settings, freq);
setting.desired_response.evaluate(freq, &setting.band)
}
fn weight<T: Float>(settings: &[BandSetting<T>], freq: T) -> T {
let setting = closest_setting(settings, freq);
setting.weight.evaluate(freq, &setting.band)
}
fn closest_setting<T: Float>(settings: &[BandSetting<T>], freq: T) -> &BandSetting<T> {
settings
.iter()
.min_by(|a, b| {
a.band
.distance(freq)
.partial_cmp(&b.band.distance(freq))
.unwrap()
})
.unwrap()
}
#[derive(Debug)]
pub struct BandSetting<T> {
band: Band<T>,
desired_response: Setting<T>,
weight: Setting<T>,
}
impl<T: Float> BandSetting<T> {
pub fn new(band_begin: T, band_end: T, desired_response: Setting<T>) -> Result<BandSetting<T>> {
let weight = constant(T::one());
BandSetting::with_weight(band_begin, band_end, desired_response, weight)
}
pub fn with_weight(
band_begin: T,
band_end: T,
desired_response: Setting<T>,
weight: Setting<T>,
) -> Result<BandSetting<T>> {
let band = Band::new(band_begin, band_end)?;
Ok(BandSetting {
band,
desired_response,
weight,
})
}
pub fn band(&self) -> Band<T> {
self.band
}
pub fn set_band(&mut self, band: Band<T>) {
self.band = band;
}
pub fn set_desired_response(&mut self, desired_response: Setting<T>) {
self.desired_response = desired_response;
}
pub fn set_weight(&mut self, weight: Setting<T>) {
self.weight = weight
}
}
#[derive(Debug)]
pub struct Setting<T>(SettingData<T>);
impl<T: Float> Setting<T> {
fn evaluate(&self, x: T, band: &Band<T>) -> T {
match &self.0 {
SettingData::Constant { value } => *value,
SettingData::Linear { begin, end } => {
let u = (x - band.begin()) / (band.end() - band.begin());
*begin + u * (*end - *begin)
}
SettingData::Function { f } => (f)(x),
}
}
}
enum SettingData<T> {
Constant { value: T },
Linear { begin: T, end: T },
Function { f: Box<dyn Fn(T) -> T> },
}
pub fn constant<T: Float>(value: T) -> Setting<T> {
Setting(SettingData::Constant { value })
}
pub fn linear<T: Float>(begin: T, end: T) -> Setting<T> {
Setting(SettingData::Linear { begin, end })
}
pub fn function<T: Float>(f: Box<dyn Fn(T) -> T>) -> Setting<T> {
Setting(SettingData::Function { f })
}
impl<T: std::fmt::Debug> std::fmt::Debug for SettingData<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
SettingData::Constant { value } => {
f.debug_struct("Constant").field("value", value).finish()
}
SettingData::Linear { begin, end } => f
.debug_struct("Linear")
.field("begin", begin)
.field("end", end)
.finish(),
SettingData::Function { .. } => f.debug_struct("Function").finish(),
}
}
}