1use std::f64::consts::PI;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum WindowType {
12 Hann,
14 Hamming,
16 Sine,
18 Blackman,
20 Kaiser,
22 FlatTop,
24 Dpss,
27}
28
29impl WindowType {
30 pub fn parse(s: &str) -> Option<Self> {
31 Some(match s.to_ascii_lowercase().as_str() {
32 "hann" => WindowType::Hann,
33 "hamming" => WindowType::Hamming,
34 "sine" => WindowType::Sine,
35 "blackman" => WindowType::Blackman,
36 "kaiser" | "kaiser-bessel" => WindowType::Kaiser,
37 "flattop" | "flat-top" | "flat_top" => WindowType::FlatTop,
38 "dpss" | "slepian" => WindowType::Dpss,
39 _ => return None,
40 })
41 }
42}
43
44#[derive(Clone, Copy, Debug)]
46pub struct WindowParams {
47 pub kaiser_beta: f64,
49 pub dpss_bandwidth: f64,
51}
52
53impl Default for WindowParams {
54 fn default() -> Self {
55 WindowParams {
56 kaiser_beta: 8.0,
57 dpss_bandwidth: 3.0,
58 }
59 }
60}
61
62pub fn sample(kind: WindowType, n: usize, n_total: usize) -> f64 {
64 sample_with_params(kind, n, n_total, &WindowParams::default())
65}
66
67pub fn sample_with_params(
69 kind: WindowType,
70 n: usize,
71 n_total: usize,
72 params: &WindowParams,
73) -> f64 {
74 let n = n as f64;
75 let nn = n_total as f64;
76 match kind {
77 WindowType::Hann => 0.5 * (1.0 - (2.0 * PI * n / nn).cos()),
78 WindowType::Hamming => 0.54 - 0.46 * (2.0 * PI * n / nn).cos(),
79 WindowType::Sine => (PI * (n + 0.5) / nn).sin(),
80 WindowType::Blackman => {
81 0.42 - 0.5 * (2.0 * PI * n / nn).cos() + 0.08 * (4.0 * PI * n / nn).cos()
82 }
83 WindowType::Kaiser => kaiser(n, nn, params.kaiser_beta),
84 WindowType::FlatTop => flat_top(n, nn),
85 WindowType::Dpss => dpss_approx(n, nn, params.dpss_bandwidth),
86 }
87}
88
89pub fn make(kind: WindowType, n_total: usize) -> Vec<f64> {
91 make_with_params(kind, n_total, &WindowParams::default())
92}
93
94pub fn make_with_params(kind: WindowType, n_total: usize, params: &WindowParams) -> Vec<f64> {
96 let mut w: Vec<f64> = (0..n_total)
97 .map(|i| sample_with_params(kind, i, n_total, params))
98 .collect();
99 if kind == WindowType::Dpss {
101 let peak = w.iter().cloned().fold(0.0f64, f64::max).max(1e-12);
102 if (peak - 1.0).abs() > 1e-6 {
103 for v in &mut w {
104 *v /= peak;
105 }
106 }
107 }
108 w
109}
110
111fn kaiser(n: f64, n_total: f64, beta: f64) -> f64 {
113 let alpha = 0.5 * (n_total - 1.0);
114 let x = (n - alpha) / alpha;
115 bessel_i0(beta * (1.0 - x * x).max(0.0).sqrt()) / bessel_i0(beta)
116}
117
118fn bessel_i0(x: f64) -> f64 {
120 if x.abs() < 3.75 {
121 let t = x / 3.75;
122 let t2 = t * t;
123 1.0 + 3.5156229 * t2
124 + 3.0899424 * t2.powi(2)
125 + 1.2067492 * t2.powi(3)
126 + 0.2659732 * t2.powi(4)
127 + 0.0360768 * t2.powi(5)
128 + 0.0045813 * t2.powi(6)
129 } else {
130 let t = 3.75 / x.abs();
131 let ax = x.abs().exp() / x.abs().sqrt();
132 let c = 0.39894228 + 0.01328592 * t + 0.00225319 * t * t - 0.00157565 * t.powi(3)
133 + 0.00916281 * t.powi(4)
134 - 0.02057706 * t.powi(5)
135 + 0.02635537 * t.powi(6)
136 - 0.01647633 * t.powi(7)
137 + 0.00392377 * t.powi(8);
138 ax * c
139 }
140}
141
142fn flat_top(n: f64, n_total: f64) -> f64 {
144 let a0 = 0.21557895;
145 let a1 = 0.41663158;
146 let a2 = 0.277263158;
147 let a3 = 0.083578947;
148 let a4 = 0.006947368;
149 let phi = 2.0 * PI * n / n_total;
150 let w = a0 - a1 * phi.cos() + a2 * (2.0 * phi).cos() - a3 * (3.0 * phi).cos()
151 + a4 * (4.0 * phi).cos();
152 w.max(0.0)
153}
154
155fn dpss_approx(n: f64, n_total: f64, nw: f64) -> f64 {
158 let m = (2.0 * nw - 0.5).ceil() as i32;
159 let m = m.clamp(1, 8);
160 let mut w = 0.0;
161 for k in 0..m {
162 let vk = dpss_eigenvector_coeff(k, m, nw);
163 w += vk * (2.0 * PI * k as f64 * n / n_total).cos();
164 }
165 w.max(0.0)
166}
167
168fn dpss_eigenvector_coeff(k: i32, m: i32, nw: f64) -> f64 {
170 let lambda_k = 1.0 - (k as f64) / (2.0 * nw);
172 let mut v = lambda_k.max(0.0);
173 if k == 0 {
174 v = 1.0;
175 } else {
176 v *= 0.5 / (k as f64);
177 }
178 let _ = m; v
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn hann_cola_at_50pct() {
188 let n = 1024;
189 let w = make(WindowType::Hann, n);
190 let hop = n / 2;
191 let len = n + hop * 3;
192 let mut cola = vec![0.0; len];
193 let mut start = 0;
194 while start + n <= len {
195 for i in 0..n {
196 cola[start + i] += w[i];
197 }
198 start += hop;
199 }
200 for &c in cola.iter().take(len - n).skip(n) {
201 assert!((c - 1.0).abs() < 1e-12, "cola={c}");
202 }
203 }
204
205 #[test]
206 fn advanced_windows_bounded() {
207 let n = 512;
208 for kind in [WindowType::Kaiser, WindowType::FlatTop, WindowType::Dpss] {
209 let w = make(kind, n);
210 for &v in &w {
211 assert!(v.is_finite());
212 assert!((-0.01..=1.05).contains(&v), "{kind:?} value {v}");
214 }
215 }
216 }
217}