use alloc::vec::Vec;
use libm::cosf;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum WindowKind {
Hann,
Hamming,
Blackman,
}
#[must_use]
pub fn make_window(kind: WindowKind, n: usize) -> Vec<f32> {
if n == 0 {
return Vec::new();
}
if n == 1 {
return alloc::vec![1.0];
}
let mut w = Vec::with_capacity(n);
let n_f = n as f32;
let two_pi = 2.0 * core::f32::consts::PI;
let four_pi = 4.0 * core::f32::consts::PI;
for k in 0..n {
let kf = k as f32;
let v = match kind {
WindowKind::Hann => 0.5 - 0.5 * cosf(two_pi * kf / n_f),
WindowKind::Hamming => 0.54 - 0.46 * cosf(two_pi * kf / n_f),
WindowKind::Blackman => {
0.42 - 0.5 * cosf(two_pi * kf / n_f) + 0.08 * cosf(four_pi * kf / n_f)
}
};
w.push(v);
}
w
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hann_starts_at_zero_and_peaks_in_middle() {
let w = make_window(WindowKind::Hann, 1024);
assert!(w[0].abs() < 1e-6);
assert!(w[512] > 0.99);
for k in 1..512 {
let mirror = 1024 - k;
assert!((w[k] - w[mirror]).abs() < 1e-5, "k={k}");
}
}
#[test]
fn hamming_min_is_above_zero() {
let w = make_window(WindowKind::Hamming, 512);
assert!(w[0] >= 0.07);
}
#[test]
fn blackman_endpoints_are_close_to_zero() {
let w = make_window(WindowKind::Blackman, 512);
assert!(w[0].abs() < 1e-5);
}
#[test]
fn empty_and_single_element_edges() {
assert!(make_window(WindowKind::Hann, 0).is_empty());
assert_eq!(make_window(WindowKind::Hann, 1), alloc::vec![1.0]);
}
#[test]
fn periodic_windows_have_zero_at_endpoint_only_for_hann_blackman() {
let h = make_window(WindowKind::Hann, 256);
let b = make_window(WindowKind::Blackman, 256);
assert!(h[0].abs() < 1e-5);
assert!(b[0].abs() < 1e-5);
let hm = make_window(WindowKind::Hamming, 256);
assert!(hm[0] > 0.05, "Hamming floor too low: {}", hm[0]);
}
#[test]
fn window_lengths_match_request() {
for kind in [WindowKind::Hann, WindowKind::Hamming, WindowKind::Blackman] {
for n in [2_usize, 4, 16, 100, 1024, 4096] {
let w = make_window(kind, n);
assert_eq!(w.len(), n, "kind={kind:?} n={n}");
}
}
}
#[test]
fn windows_are_non_negative() {
for kind in [WindowKind::Hann, WindowKind::Hamming, WindowKind::Blackman] {
let w = make_window(kind, 1024);
for (i, &v) in w.iter().enumerate() {
assert!(v >= -1e-6, "kind={kind:?} idx={i} v={v}");
}
}
}
}