sound_to_patch/dsp/
window.rs1use std::f64::consts::TAU;
4
5use crate::cast;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub enum Window {
12 Hann,
15 BlackmanHarris,
19 Rectangular,
22}
23
24impl Window {
25 #[must_use]
27 pub fn samples(self, len: usize) -> Vec<f64> {
28 let n = cast::usize_to_f64(len);
29 (0..len)
30 .map(|index| {
31 let phase = TAU * cast::usize_to_f64(index) / n;
32 match self {
33 Self::Hann => 0.5 - 0.5 * phase.cos(),
34 Self::BlackmanHarris => {
35 0.358_75 - 0.488_29 * phase.cos() + 0.141_28 * (2.0 * phase).cos()
36 - 0.011_68 * (3.0 * phase).cos()
37 }
38 Self::Rectangular => 1.0,
39 }
40 })
41 .collect()
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn hann_starts_at_zero_and_peaks_at_the_centre() {
51 let hann = Window::Hann.samples(8);
52 assert_eq!(hann[0], 0.0);
53 assert!((hann[4] - 1.0).abs() < 1e-15);
54 assert!((hann[1] - hann[7]).abs() < 1e-15);
56 }
57
58 #[test]
59 fn blackman_harris_coefficients_sum_to_one_at_the_centre() {
60 let window = Window::BlackmanHarris.samples(16);
61 assert!((window[8] - 1.0).abs() < 1e-12, "{}", window[8]);
62 assert!((window[0] - 6.0e-5).abs() < 1e-12, "{}", window[0]);
63 }
64
65 #[test]
66 fn rectangular_is_all_ones_and_empty_stays_empty() {
67 assert_eq!(Window::Rectangular.samples(3), vec![1.0; 3]);
68 assert_eq!(Window::Hann.samples(0), Vec::<f64>::new());
69 }
70}