1use num::complex::Complex;
60use num::traits::{Float, Signed, Zero};
61use rustfft::{Fft, FftDirection, FftNum, FftPlanner};
62use std::str::FromStr;
63use std::sync::Arc;
64use strider::{SliceRing, SliceRingImpl};
65
66#[inline]
74pub fn log10_positive<T: Float + Signed + Zero>(value: T) -> T {
75 let log = value.log10();
79 if log.is_negative() {
80 T::zero()
81 } else {
82 log
83 }
84}
85
86#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
88pub enum WindowType {
89 Hanning,
90 Hamming,
91 Blackman,
92 Nuttall,
93 None,
94}
95
96impl FromStr for WindowType {
97 type Err = &'static str;
98
99 fn from_str(s: &str) -> Result<Self, Self::Err> {
100 let lower = s.to_lowercase();
101 match lower.as_str() {
102 "hanning" => Ok(WindowType::Hanning),
103 "hann" => Ok(WindowType::Hanning),
104 "hamming" => Ok(WindowType::Hamming),
105 "blackman" => Ok(WindowType::Blackman),
106 "nuttall" => Ok(WindowType::Nuttall),
107 "none" => Ok(WindowType::None),
108 _ => Err("no match"),
109 }
110 }
111}
112
113impl std::fmt::Display for WindowType {
115 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
116 write!(formatter, "{:?}", self)
117 }
118}
119
120static WINDOW_TYPES: [WindowType; 5] = [
122 WindowType::Hanning,
123 WindowType::Hamming,
124 WindowType::Blackman,
125 WindowType::Nuttall,
126 WindowType::None,
127];
128
129impl WindowType {
130 pub fn values() -> [WindowType; 5] {
131 WINDOW_TYPES
132 }
133}
134
135pub struct STFT<T>
136where
137 T: FftNum + FromF64 + num::Float,
138{
139 pub window_size: usize,
140 pub fft_size: usize,
141 pub step_size: usize,
142 pub fft: Arc<dyn Fft<T>>,
143 pub window: Option<Vec<T>>,
144 pub sample_ring: SliceRingImpl<T>,
146 pub real_input: Vec<T>,
147 pub complex_input_output: Vec<Complex<T>>,
148 fft_scratch: Vec<Complex<T>>,
149}
150
151impl<T> STFT<T>
152where
153 T: FftNum + FromF64 + num::Float,
154{
155 pub fn window_type_to_window_vec(
156 window_type: WindowType,
157 window_size: usize,
158 ) -> Option<Vec<T>> {
159 match window_type {
160 WindowType::Hanning => Some(
161 apodize::hanning_iter(window_size)
162 .map(FromF64::from_f64)
163 .collect(),
164 ),
165 WindowType::Hamming => Some(
166 apodize::hamming_iter(window_size)
167 .map(FromF64::from_f64)
168 .collect(),
169 ),
170 WindowType::Blackman => Some(
171 apodize::blackman_iter(window_size)
172 .map(FromF64::from_f64)
173 .collect(),
174 ),
175 WindowType::Nuttall => Some(
176 apodize::nuttall_iter(window_size)
177 .map(FromF64::from_f64)
178 .collect(),
179 ),
180 WindowType::None => None,
181 }
182 }
183
184 pub fn new(window_type: WindowType, window_size: usize, step_size: usize) -> Self {
185 let window = Self::window_type_to_window_vec(window_type, window_size);
186 Self::new_with_window_vec(window, window_size, step_size)
187 }
188
189 pub fn new_with_zero_padding(
190 window_type: WindowType,
191 window_size: usize,
192 fft_size: usize,
193 step_size: usize,
194 ) -> Self {
195 let window = Self::window_type_to_window_vec(window_type, window_size);
196 Self::new_with_window_vec_and_zero_padding(window, window_size, fft_size, step_size)
197 }
198
199 pub fn new_with_window_vec_and_zero_padding(
201 window: Option<Vec<T>>,
202 window_size: usize,
203 fft_size: usize,
204 step_size: usize,
205 ) -> Self {
206 assert!(step_size > 0 && step_size < window_size);
207 let fft = FftPlanner::new().plan_fft(fft_size, FftDirection::Forward);
208
209 let scratch_len = fft.get_inplace_scratch_len();
211 let fft_scratch = vec![Complex::<T>::zero(); scratch_len];
212
213 STFT {
214 window_size,
215 fft_size,
216 step_size,
217 fft,
218 fft_scratch,
219 sample_ring: SliceRingImpl::new(),
220 window,
221 real_input: std::iter::repeat(T::zero()).take(window_size).collect(),
222 complex_input_output: std::iter::repeat(Complex::<T>::zero())
224 .take(fft_size)
225 .collect(),
226 }
228 }
229
230 pub fn new_with_window_vec(
231 window: Option<Vec<T>>,
232 window_size: usize,
233 step_size: usize,
234 ) -> Self {
235 Self::new_with_window_vec_and_zero_padding(window, window_size, window_size, step_size)
236 }
237
238 #[inline]
239 pub fn output_size(&self) -> usize {
240 self.fft_size / 2
241 }
242
243 #[inline]
244 pub fn len(&self) -> usize {
245 self.sample_ring.len()
246 }
247
248 #[inline]
249 pub fn is_empty(&self) -> bool {
250 self.len() == 0
251 }
252
253 pub fn append_samples(&mut self, input: &[T]) {
254 self.sample_ring.push_many_back(input);
255 }
256
257 #[inline]
258 pub fn contains_enough_to_compute(&self) -> bool {
259 self.window_size <= self.sample_ring.len()
260 }
261
262 pub fn compute_into_complex_output(&mut self) {
263 assert!(self.contains_enough_to_compute());
264
265 self.sample_ring.read_many_front(&mut self.real_input);
267
268 if let Some(ref window) = self.window {
270 for (dst, src) in self.real_input.iter_mut().zip(window.iter()) {
271 *dst = *dst * *src;
272 }
273 }
274
275 for (src, dst) in self
278 .real_input
279 .iter()
280 .zip(self.complex_input_output.iter_mut())
281 {
282 dst.re = *src;
283 dst.im = T::zero();
284 }
285
286 if self.window_size < self.fft_size {
288 for dst in self.complex_input_output.iter_mut().skip(self.window_size) {
289 dst.re = T::zero();
290 dst.im = T::zero();
291 }
292 }
293
294 self.fft
296 .process_with_scratch(&mut self.complex_input_output, &mut self.fft_scratch)
297 }
298
299 pub fn compute_complex_column(&mut self, output: &mut [Complex<T>]) {
302 assert_eq!(self.output_size(), output.len());
303
304 self.compute_into_complex_output();
305
306 for (dst, src) in output.iter_mut().zip(self.complex_input_output.iter()) {
307 *dst = *src;
308 }
309 }
310
311 pub fn compute_magnitude_column(&mut self, output: &mut [T]) {
314 assert_eq!(self.output_size(), output.len());
315
316 self.compute_into_complex_output();
317
318 for (dst, src) in output.iter_mut().zip(self.complex_input_output.iter()) {
319 *dst = src.norm();
320 }
321 }
322
323 pub fn compute_column(&mut self, output: &mut [T]) {
327 assert_eq!(self.output_size(), output.len());
328
329 self.compute_into_complex_output();
330
331 for (dst, src) in output.iter_mut().zip(self.complex_input_output.iter()) {
332 *dst = log10_positive(src.norm());
333 }
334 }
335
336 pub fn move_to_next_column(&mut self) {
339 self.sample_ring.drop_many_front(self.step_size);
340 }
341
342 pub fn freqs(&self, fs: f64) -> Vec<f64> {
346 let n_freqs = self.output_size();
347 (0..n_freqs)
348 .map(|f| (f as f64) / ((n_freqs - 1) as f64) * (fs / 2.))
349 .collect()
350 }
351
352 pub fn first_time(&self, fs: f64) -> f64 {
354 (self.window_size as f64) / (fs * 2.)
355 }
356
357 pub fn time_interval(&self, fs: f64) -> f64 {
359 (self.step_size as f64) / fs
360 }
361}
362
363pub trait FromF64 {
364 fn from_f64(n: f64) -> Self;
365}
366
367impl FromF64 for f64 {
368 fn from_f64(n: f64) -> Self {
369 n
370 }
371}
372
373impl FromF64 for f32 {
374 fn from_f64(n: f64) -> Self {
375 n as f32
376 }
377}