mod functions;
use crate::sample::{cast, Sample};
use alloc::vec::Vec;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Symmetry {
#[default]
Periodic,
Symmetric,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum WindowFunction {
Rectangular,
Hann,
Hamming,
Blackman,
BlackmanHarris,
Nuttall,
FlatTop,
Bartlett,
Triangular,
Welch,
Cosine,
Tukey {
alpha: f64,
},
Kaiser {
beta: f64,
},
Gaussian {
std: f64,
},
}
impl WindowFunction {
fn coefficients(self, len: usize, symmetry: Symmetry) -> Vec<f64> {
if len == 0 {
return Vec::new();
}
if len == 1 {
return alloc::vec![1.0];
}
let m = match symmetry {
Symmetry::Symmetric => len,
Symmetry::Periodic => len + 1,
};
let mut coeffs = match self {
Self::Rectangular => functions::rectangular(m),
Self::Hann => functions::hann(m),
Self::Hamming => functions::hamming(m),
Self::Blackman => functions::blackman(m),
Self::BlackmanHarris => functions::blackman_harris(m),
Self::Nuttall => functions::nuttall(m),
Self::FlatTop => functions::flat_top(m),
Self::Bartlett => functions::bartlett(m),
Self::Triangular => functions::triangular(m),
Self::Welch => functions::welch(m),
Self::Cosine => functions::cosine(m),
Self::Tukey { alpha } => functions::tukey(m, alpha),
Self::Kaiser { beta } => functions::kaiser(m, beta),
Self::Gaussian { std } => functions::gaussian(m, std),
};
coeffs.truncate(len);
coeffs
}
#[must_use]
pub fn generate<T: Sample>(self, len: usize, symmetry: Symmetry) -> Window<T> {
let coeffs = self
.coefficients(len, symmetry)
.into_iter()
.map(cast)
.collect();
Window::from_coefficients(coeffs)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Window<T> {
coeffs: Vec<T>,
sum: T,
sum_sq: T,
}
impl<T: Sample> Window<T> {
#[must_use]
pub fn from_coefficients(coeffs: Vec<T>) -> Self {
let mut sum = T::zero();
let mut sum_sq = T::zero();
for &c in &coeffs {
sum = sum + c;
sum_sq = sum_sq + c * c;
}
Self {
coeffs,
sum,
sum_sq,
}
}
#[must_use]
pub fn new(function: WindowFunction, len: usize, symmetry: Symmetry) -> Self {
function.generate(len, symmetry)
}
#[must_use]
pub fn coefficients(&self) -> &[T] {
&self.coeffs
}
#[must_use]
pub fn len(&self) -> usize {
self.coeffs.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.coeffs.is_empty()
}
#[must_use]
pub fn sum(&self) -> T {
self.sum
}
#[must_use]
pub fn sum_squared(&self) -> T {
self.sum_sq
}
}
macro_rules! window_ctor {
($name:ident, $variant:ident, $doc:literal) => {
#[doc = $doc]
#[doc = ""]
#[doc = "Uses [`Symmetry::Periodic`]; use [`Window::new`] for symmetric windows."]
#[must_use]
pub fn $name(len: usize) -> Self {
WindowFunction::$variant.generate(len, Symmetry::Periodic)
}
};
}
impl<T: Sample> Window<T> {
window_ctor!(rectangular, Rectangular, "A rectangular (boxcar) window.");
window_ctor!(hann, Hann, "A periodic Hann window.");
window_ctor!(hamming, Hamming, "A periodic Hamming window.");
window_ctor!(blackman, Blackman, "A periodic Blackman window.");
window_ctor!(
blackman_harris,
BlackmanHarris,
"A periodic Blackman-Harris window."
);
window_ctor!(nuttall, Nuttall, "A periodic Nuttall window.");
window_ctor!(flat_top, FlatTop, "A periodic flat-top window.");
window_ctor!(bartlett, Bartlett, "A Bartlett window (zero endpoints).");
window_ctor!(triangular, Triangular, "A triangular window.");
window_ctor!(welch, Welch, "A Welch (parabolic) window.");
window_ctor!(cosine, Cosine, "A cosine (sine) window.");
#[must_use]
pub fn tukey(len: usize, alpha: f64) -> Self {
WindowFunction::Tukey { alpha }.generate(len, Symmetry::Periodic)
}
#[must_use]
pub fn kaiser(len: usize, beta: f64) -> Self {
WindowFunction::Kaiser { beta }.generate(len, Symmetry::Periodic)
}
#[must_use]
pub fn gaussian(len: usize, std: f64) -> Self {
WindowFunction::Gaussian { std }.generate(len, Symmetry::Periodic)
}
}