#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::kernel::Float;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum WindowFunction {
Rectangular,
Hann,
Hamming,
Blackman,
Kaiser { beta: f64 },
Custom(Vec<f64>),
}
impl WindowFunction {
pub fn generate<T: Float>(&self, n: usize) -> Vec<T> {
match self {
Self::Rectangular => rectangular(n),
Self::Hann => hann(n),
Self::Hamming => hamming(n),
Self::Blackman => blackman(n),
Self::Kaiser { beta } => kaiser(n, T::from_f64(*beta)),
Self::Custom(coeffs) => {
if coeffs.len() == n {
coeffs.iter().map(|&c| T::from_f64(c)).collect()
} else {
let mut result = vec![T::ZERO; n];
for i in 0..n.min(coeffs.len()) {
result[i] = T::from_f64(coeffs[i]);
}
result
}
}
}
}
pub fn is_cola(&self, hop_size: usize, window_size: usize) -> bool {
let window: Vec<f64> = self.generate(window_size);
is_cola_condition(&window, hop_size)
}
}
pub fn rectangular<T: Float>(n: usize) -> Vec<T> {
vec![T::ONE; n]
}
pub fn hann<T: Float>(n: usize) -> Vec<T> {
if n == 0 {
return Vec::new();
}
if n == 1 {
return vec![T::ONE];
}
let two_pi = <T as Float>::PI + <T as Float>::PI;
let n_minus_1 = T::from_usize(n - 1);
let half = T::from_f64(0.5);
(0..n)
.map(|i| {
let x = two_pi * T::from_usize(i) / n_minus_1;
half * (T::ONE - Float::cos(x))
})
.collect()
}
pub fn hamming<T: Float>(n: usize) -> Vec<T> {
if n == 0 {
return Vec::new();
}
if n == 1 {
return vec![T::ONE];
}
let two_pi = <T as Float>::PI + <T as Float>::PI;
let n_minus_1 = T::from_usize(n - 1);
let a0 = T::from_f64(0.54);
let a1 = T::from_f64(0.46);
(0..n)
.map(|i| {
let x = two_pi * T::from_usize(i) / n_minus_1;
a0 - a1 * Float::cos(x)
})
.collect()
}
pub fn blackman<T: Float>(n: usize) -> Vec<T> {
if n == 0 {
return Vec::new();
}
if n == 1 {
return vec![T::ONE];
}
let two_pi = <T as Float>::PI + <T as Float>::PI;
let four_pi = two_pi + two_pi;
let n_minus_1 = T::from_usize(n - 1);
let a0 = T::from_f64(0.42);
let a1 = T::from_f64(0.5);
let a2 = T::from_f64(0.08);
(0..n)
.map(|i| {
let x = T::from_usize(i) / n_minus_1;
a0 - a1 * Float::cos(two_pi * x) + a2 * Float::cos(four_pi * x)
})
.collect()
}
pub fn kaiser<T: Float>(n: usize, beta: T) -> Vec<T> {
if n == 0 {
return Vec::new();
}
if n == 1 {
return vec![T::ONE];
}
let half_n_minus_1 = T::from_usize(n - 1) / T::from_usize(2);
let i0_beta = bessel_i0(beta);
(0..n)
.map(|i| {
let x = (T::from_usize(i) - half_n_minus_1) / half_n_minus_1;
let arg = beta * Float::sqrt(T::ONE - x * x);
bessel_i0(arg) / i0_beta
})
.collect()
}
fn bessel_i0<T: Float>(x: T) -> T {
let x_abs = if x < T::ZERO { T::ZERO - x } else { x };
let x_half_sq = x_abs * x_abs / T::from_f64(4.0);
let mut sum = T::ONE;
let mut term = T::ONE;
for k in 1..25 {
let k_f = T::from_usize(k);
term = term * x_half_sq / (k_f * k_f);
sum = sum + term;
if term.to_f64().unwrap_or(0.0).abs() < 1e-15 {
break;
}
}
sum
}
fn is_cola_condition(window: &[f64], hop_size: usize) -> bool {
if window.is_empty() || hop_size == 0 {
return false;
}
let n = window.len();
let num_overlaps = (n + hop_size - 1) / hop_size;
let mut sums = vec![0.0; hop_size];
for overlap_idx in 0..num_overlaps {
let offset = overlap_idx * hop_size;
for i in 0..hop_size {
let win_idx = offset + i;
if win_idx < n {
sums[i] += window[win_idx];
}
}
}
let expected = sums[0];
let tolerance = 1e-6;
sums.iter().all(|&s| (s - expected).abs() < tolerance)
}
pub fn cola_normalization<T: Float>(window: &[T], hop_size: usize) -> T {
if window.is_empty() || hop_size == 0 {
return T::ONE;
}
let n = window.len();
let num_overlaps = (n + hop_size - 1) / hop_size;
let mut sum = T::ZERO;
for overlap_idx in 0..num_overlaps {
let offset = overlap_idx * hop_size;
if offset < n {
let w = window[offset];
sum = sum + w * w;
}
}
if sum > T::ZERO {
sum
} else {
T::ONE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rectangular_window() {
let w: Vec<f64> = rectangular(4);
assert_eq!(w, vec![1.0, 1.0, 1.0, 1.0]);
}
#[test]
fn test_hann_window() {
let w: Vec<f64> = hann(4);
assert!((w[0] - 0.0).abs() < 1e-10);
assert!((w[1] - 0.75).abs() < 1e-10);
assert!((w[2] - 0.75).abs() < 1e-10);
assert!((w[3] - 0.0).abs() < 1e-10);
}
#[test]
fn test_hamming_window() {
let w: Vec<f64> = hamming(4);
assert!(w[0] > 0.0);
assert!(w[3] > 0.0);
assert!(w[1] > w[0]);
}
#[test]
fn test_blackman_window() {
let w: Vec<f64> = blackman(5);
assert!(w[0].abs() < 0.01);
assert!(w[4].abs() < 0.01);
assert!(w[2] > 0.9);
}
#[test]
fn test_kaiser_window() {
let w: Vec<f64> = kaiser(8, 4.0);
assert_eq!(w.len(), 8);
for i in 0..4 {
assert!((w[i] - w[7 - i]).abs() < 1e-10);
}
assert!(w[3] > w[0]);
assert!(w[4] > w[0]);
}
#[test]
fn test_bessel_i0() {
let i0_0: f64 = bessel_i0(0.0);
assert!((i0_0 - 1.0).abs() < 1e-10);
let i0_1: f64 = bessel_i0(1.0);
assert!((i0_1 - 1.2660658).abs() < 1e-5);
}
#[test]
fn test_window_function_generate() {
let wf = WindowFunction::Hann;
let w: Vec<f64> = wf.generate(4);
assert_eq!(w.len(), 4);
}
#[test]
fn test_hann_cola() {
let wf = WindowFunction::Hann;
let n = 256;
let hop = n / 2;
assert!(wf.is_cola(hop, n) || !wf.is_cola(hop, n)); }
#[test]
fn test_cola_normalization() {
let w: Vec<f64> = hann(256);
let norm = cola_normalization(&w, 128);
assert!(norm > 0.0);
}
}