1use super::filters::Filter;
7use super::functions::bessel_i0;
8use super::spectral::{Spectrogram, WindowType};
9use crate::error::{IoError, IoResult};
10use rustfft::{num_complex::Complex, FftPlanner};
11use scirs2_core::ndarray::Array1;
12use std::f32::consts::PI;
13
14pub struct SignalProcessor {
16 pub(super) buffer_size: usize,
17 pub(super) sample_rate: f32,
18 pub(super) fft_planner: FftPlanner<f32>,
19}
20
21impl SignalProcessor {
22 pub fn new(buffer_size: usize) -> Self {
24 Self {
25 buffer_size,
26 sample_rate: 44100.0,
27 fft_planner: FftPlanner::new(),
28 }
29 }
30
31 pub fn with_sample_rate(mut self, rate: f32) -> Self {
33 self.sample_rate = rate;
34 self
35 }
36
37 pub fn buffer_size(&self) -> usize {
39 self.buffer_size
40 }
41
42 pub fn sample_rate(&self) -> f32 {
44 self.sample_rate
45 }
46
47 pub fn fft(&mut self, signal: &Array1<f32>) -> IoResult<Vec<Complex<f32>>> {
49 let n = signal.len();
50 let fft = self.fft_planner.plan_fft_forward(n);
51 let mut buffer: Vec<Complex<f32>> = signal.iter().map(|&x| Complex::new(x, 0.0)).collect();
52 fft.process(&mut buffer);
53 Ok(buffer)
54 }
55
56 pub fn ifft(&mut self, spectrum: &mut [Complex<f32>]) -> IoResult<Array1<f32>> {
58 let n = spectrum.len();
59 let ifft = self.fft_planner.plan_fft_inverse(n);
60 ifft.process(spectrum);
61 let scale = 1.0 / n as f32;
62 let result: Vec<f32> = spectrum.iter().map(|c| c.re * scale).collect();
63 Ok(Array1::from_vec(result))
64 }
65
66 fn is_power_of_2(n: usize) -> bool {
68 n != 0 && (n & (n - 1)) == 0
69 }
70
71 pub fn fft_pow2(&mut self, signal: &Array1<f32>) -> IoResult<Vec<Complex<f32>>> {
76 let n = signal.len();
77 if Self::is_power_of_2(n) {
78 let fft = self.fft_planner.plan_fft_forward(n);
79 let mut buffer = Vec::with_capacity(n);
80 buffer.extend(signal.iter().map(|&x| Complex::new(x, 0.0)));
81 fft.process(&mut buffer);
82 Ok(buffer)
83 } else {
84 self.fft(signal)
85 }
86 }
87
88 pub fn ifft_pow2(&mut self, spectrum: &mut [Complex<f32>]) -> IoResult<Array1<f32>> {
90 let n = spectrum.len();
91 if Self::is_power_of_2(n) {
92 let ifft = self.fft_planner.plan_fft_inverse(n);
93 ifft.process(spectrum);
94 let scale = 1.0 / n as f32;
95 let result: Vec<f32> = spectrum.iter().map(|c| c.re * scale).collect();
96 Ok(Array1::from_vec(result))
97 } else {
98 self.ifft(spectrum)
99 }
100 }
101
102 pub fn power_spectrum_pow2(&mut self, signal: &Array1<f32>) -> IoResult<Vec<f32>> {
106 let spectrum = self.fft_pow2(signal)?;
107 Ok(spectrum.iter().map(|c| c.norm_sqr()).collect())
108 }
109
110 pub fn zero_pad_pow2(signal: &Array1<f32>) -> Array1<f32> {
112 let n = signal.len();
113 if Self::is_power_of_2(n) {
114 return signal.clone();
115 }
116 let next_pow2 = n.next_power_of_two();
117 let mut padded = vec![0.0f32; next_pow2];
118 padded[..n].copy_from_slice(signal.as_slice().unwrap());
119 Array1::from_vec(padded)
120 }
121
122 pub fn apply_filter(&mut self, signal: &Array1<f32>, filter: Filter) -> IoResult<Array1<f32>> {
124 match filter {
125 Filter::MovingAverage { window } => self.moving_average(signal, window),
126 Filter::LowPass { cutoff, .. } => self.lowpass_fft(signal, cutoff),
127 Filter::HighPass { cutoff, .. } => self.highpass_fft(signal, cutoff),
128 Filter::BandPass { low, high, .. } => self.bandpass_fft(signal, low, high),
129 Filter::Iir(mut iir) => Ok(iir.process(signal)),
130 Filter::Fir(mut fir) => Ok(fir.process(signal)),
131 }
132 }
133
134 pub fn power_spectrum(&mut self, signal: &Array1<f32>) -> IoResult<Array1<f32>> {
136 let spectrum = self.fft(signal)?;
137 let power: Vec<f32> = spectrum.iter().map(|c| c.norm_sqr()).collect();
138 Ok(Array1::from_vec(power))
139 }
140
141 pub fn magnitude_spectrum(&mut self, signal: &Array1<f32>) -> IoResult<Array1<f32>> {
143 let spectrum = self.fft(signal)?;
144 let magnitude: Vec<f32> = spectrum.iter().map(|c| c.norm()).collect();
145 Ok(Array1::from_vec(magnitude))
146 }
147
148 pub fn phase_spectrum(&mut self, signal: &Array1<f32>) -> IoResult<Array1<f32>> {
150 let spectrum = self.fft(signal)?;
151 let phase: Vec<f32> = spectrum.iter().map(|c| c.arg()).collect();
152 Ok(Array1::from_vec(phase))
153 }
154
155 pub fn zero_crossings(signal: &Array1<f32>) -> usize {
157 signal
158 .windows(2)
159 .into_iter()
160 .filter(|w| w[0].signum() != w[1].signum())
161 .count()
162 }
163
164 pub fn peak_to_peak(signal: &Array1<f32>) -> f32 {
166 let max = signal.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
167 let min = signal.iter().cloned().fold(f32::INFINITY, f32::min);
168 max - min
169 }
170
171 pub fn remove_dc(signal: &Array1<f32>) -> Array1<f32> {
173 let mean = signal.mean().unwrap_or(0.0);
174 signal.mapv(|x| x - mean)
175 }
176
177 pub fn envelope(&mut self, signal: &Array1<f32>) -> IoResult<Array1<f32>> {
179 let n = signal.len();
180 let mut spectrum = self.fft(signal)?;
181 spectrum[0] = Complex::new(0.0, 0.0);
182 for item in spectrum.iter_mut().take(n / 2).skip(1) {
183 *item *= Complex::new(2.0, 0.0);
184 }
185 for item in spectrum.iter_mut().skip(n / 2 + 1) {
186 *item = Complex::new(0.0, 0.0);
187 }
188 let ifft = self.fft_planner.plan_fft_inverse(n);
189 ifft.process(&mut spectrum);
190 let scale = 1.0 / n as f32;
191 let envelope: Vec<f32> = spectrum.iter().map(|c| c.norm() * scale).collect();
192 Ok(Array1::from_vec(envelope))
193 }
194
195 fn moving_average(&self, signal: &Array1<f32>, window: usize) -> IoResult<Array1<f32>> {
197 if window == 0 || window > signal.len() {
198 return Err(IoError::SignalError("Invalid window size".into()));
199 }
200 let mut result = Array1::zeros(signal.len());
201 let mut sum: f32 = signal.iter().take(window).sum();
202 for i in 0..signal.len() {
203 if i >= window {
204 sum -= signal[i - window];
205 sum += signal[i];
206 }
207 result[i] = sum / window.min(i + 1) as f32;
208 }
209 Ok(result)
210 }
211
212 fn lowpass_fft(&mut self, signal: &Array1<f32>, cutoff: f32) -> IoResult<Array1<f32>> {
214 let mut spectrum = self.fft(signal)?;
215 let n = spectrum.len();
216 let cutoff_bin = (cutoff / self.sample_rate * n as f32) as usize;
217 for item in spectrum.iter_mut().take(n - cutoff_bin).skip(cutoff_bin) {
218 *item = Complex::new(0.0, 0.0);
219 }
220 self.ifft(&mut spectrum)
221 }
222
223 fn highpass_fft(&mut self, signal: &Array1<f32>, cutoff: f32) -> IoResult<Array1<f32>> {
225 let mut spectrum = self.fft(signal)?;
226 let n = spectrum.len();
227 let cutoff_bin = (cutoff / self.sample_rate * n as f32) as usize;
228 for i in 0..cutoff_bin.min(n / 2) {
229 spectrum[i] = Complex::new(0.0, 0.0);
230 if n - 1 - i < n {
231 spectrum[n - 1 - i] = Complex::new(0.0, 0.0);
232 }
233 }
234 self.ifft(&mut spectrum)
235 }
236
237 fn bandpass_fft(&mut self, signal: &Array1<f32>, low: f32, high: f32) -> IoResult<Array1<f32>> {
239 let mut spectrum = self.fft(signal)?;
240 let n = spectrum.len();
241 let low_bin = (low / self.sample_rate * n as f32) as usize;
242 let high_bin = (high / self.sample_rate * n as f32) as usize;
243 for (i, item) in spectrum.iter_mut().enumerate() {
244 let freq_bin = if i <= n / 2 { i } else { n - i };
245 if freq_bin < low_bin || freq_bin > high_bin {
246 *item = Complex::new(0.0, 0.0);
247 }
248 }
249 self.ifft(&mut spectrum)
250 }
251
252 pub fn normalize(signal: &Array1<f32>) -> Array1<f32> {
254 let max_abs = signal.iter().map(|x| x.abs()).fold(0.0f32, f32::max);
255 if max_abs > 0.0 {
256 signal.mapv(|x| x / max_abs)
257 } else {
258 signal.clone()
259 }
260 }
261
262 pub fn rms(signal: &Array1<f32>) -> f32 {
264 let sum_sq: f32 = signal.iter().map(|x| x * x).sum();
265 (sum_sq / signal.len() as f32).sqrt()
266 }
267
268 #[cfg(feature = "simd")]
272 pub fn rms_simd(signal: &Array1<f32>) -> f32 {
273 Self::rms_simd_impl(signal.as_slice().unwrap())
274 }
275
276 #[cfg(feature = "simd")]
278 fn rms_simd_impl(data: &[f32]) -> f32 {
279 const CHUNK_SIZE: usize = 8;
280 let chunks = data.chunks_exact(CHUNK_SIZE);
281 let remainder = chunks.remainder();
282 let mut sum_sq = chunks.fold(0.0f32, |acc, chunk| {
283 let chunk_sum: f32 = chunk.iter().map(|x| x * x).sum();
284 acc + chunk_sum
285 });
286 sum_sq += remainder.iter().map(|x| x * x).sum::<f32>();
287 (sum_sq / data.len() as f32).sqrt()
288 }
289
290 #[cfg(feature = "simd")]
294 pub fn normalize_simd(signal: &Array1<f32>) -> Array1<f32> {
295 let max_abs = Self::max_abs_simd(signal.as_slice().unwrap());
296 if max_abs > 0.0 {
297 signal.mapv(|x| x / max_abs)
298 } else {
299 signal.clone()
300 }
301 }
302
303 #[cfg(feature = "simd")]
305 fn max_abs_simd(data: &[f32]) -> f32 {
306 const CHUNK_SIZE: usize = 8;
307 let chunks = data.chunks_exact(CHUNK_SIZE);
308 let remainder = chunks.remainder();
309 let mut max_val = 0.0f32;
310 for chunk in chunks {
311 for &val in chunk {
312 max_val = max_val.max(val.abs());
313 }
314 }
315 for &val in remainder {
316 max_val = max_val.max(val.abs());
317 }
318 max_val
319 }
320
321 #[cfg(feature = "simd")]
325 pub fn add_simd(a: &Array1<f32>, b: &Array1<f32>) -> IoResult<Array1<f32>> {
326 if a.len() != b.len() {
327 return Err(IoError::SignalError("Signals must have same length".into()));
328 }
329 let a_slice = a.as_slice().unwrap();
330 let b_slice = b.as_slice().unwrap();
331 let mut result = vec![0.0f32; a.len()];
332 const CHUNK_SIZE: usize = 8;
333 let chunks_a = a_slice.chunks_exact(CHUNK_SIZE);
334 let chunks_b = b_slice.chunks_exact(CHUNK_SIZE);
335 let result_chunks = result.chunks_exact_mut(CHUNK_SIZE);
336 for ((chunk_a, chunk_b), result_chunk) in chunks_a.zip(chunks_b).zip(result_chunks) {
337 for i in 0..CHUNK_SIZE {
338 result_chunk[i] = chunk_a[i] + chunk_b[i];
339 }
340 }
341 let remainder_start = (a.len() / CHUNK_SIZE) * CHUNK_SIZE;
342 for i in remainder_start..a.len() {
343 result[i] = a_slice[i] + b_slice[i];
344 }
345 Ok(Array1::from_vec(result))
346 }
347
348 #[cfg(feature = "simd")]
352 pub fn multiply_simd(a: &Array1<f32>, b: &Array1<f32>) -> IoResult<Array1<f32>> {
353 if a.len() != b.len() {
354 return Err(IoError::SignalError("Signals must have same length".into()));
355 }
356 let a_slice = a.as_slice().unwrap();
357 let b_slice = b.as_slice().unwrap();
358 let mut result = vec![0.0f32; a.len()];
359 const CHUNK_SIZE: usize = 8;
360 let chunks_a = a_slice.chunks_exact(CHUNK_SIZE);
361 let chunks_b = b_slice.chunks_exact(CHUNK_SIZE);
362 let result_chunks = result.chunks_exact_mut(CHUNK_SIZE);
363 for ((chunk_a, chunk_b), result_chunk) in chunks_a.zip(chunks_b).zip(result_chunks) {
364 for i in 0..CHUNK_SIZE {
365 result_chunk[i] = chunk_a[i] * chunk_b[i];
366 }
367 }
368 let remainder_start = (a.len() / CHUNK_SIZE) * CHUNK_SIZE;
369 for i in remainder_start..a.len() {
370 result[i] = a_slice[i] * b_slice[i];
371 }
372 Ok(Array1::from_vec(result))
373 }
374
375 pub fn spectrogram(
380 &mut self,
381 signal: &Array1<f32>,
382 n_fft: usize,
383 hop_length: usize,
384 window: WindowType,
385 ) -> IoResult<Spectrogram> {
386 if n_fft == 0 || hop_length == 0 {
387 return Err(IoError::SignalError(
388 "n_fft and hop_length must be > 0".into(),
389 ));
390 }
391 if signal.len() < n_fft {
392 return Err(IoError::SignalError("Signal shorter than n_fft".into()));
393 }
394 let window_coeffs = Self::create_window(window, n_fft);
395 let num_frames = (signal.len() - n_fft) / hop_length + 1;
396 let num_bins = n_fft / 2 + 1;
397 let mut magnitudes = Vec::with_capacity(num_frames * num_bins);
398 let mut phases = Vec::with_capacity(num_frames * num_bins);
399 for frame_idx in 0..num_frames {
400 let start = frame_idx * hop_length;
401 let frame: Vec<f32> = signal
402 .iter()
403 .skip(start)
404 .take(n_fft)
405 .zip(window_coeffs.iter())
406 .map(|(&s, &w)| s * w)
407 .collect();
408 let spectrum = self.fft(&Array1::from_vec(frame))?;
409 for bin in spectrum.iter().take(num_bins) {
410 magnitudes.push(bin.norm());
411 phases.push(bin.arg());
412 }
413 }
414 Ok(Spectrogram {
415 magnitudes,
416 phases,
417 num_frames,
418 num_bins,
419 hop_length,
420 sample_rate: self.sample_rate,
421 })
422 }
423
424 pub fn create_window(window_type: WindowType, size: usize) -> Vec<f32> {
426 match window_type {
427 WindowType::Rectangular => vec![1.0; size],
428 WindowType::Hann => (0..size)
429 .map(|i| 0.5 * (1.0 - (2.0 * PI * i as f32 / (size - 1) as f32).cos()))
430 .collect(),
431 WindowType::Hamming => (0..size)
432 .map(|i| 0.54 - 0.46 * (2.0 * PI * i as f32 / (size - 1) as f32).cos())
433 .collect(),
434 WindowType::Blackman => (0..size)
435 .map(|i| {
436 let n = i as f32 / (size - 1) as f32;
437 0.42 - 0.5 * (2.0 * PI * n).cos() + 0.08 * (4.0 * PI * n).cos()
438 })
439 .collect(),
440 WindowType::Bartlett => (0..size)
441 .map(|i| {
442 let half = (size - 1) as f32 / 2.0;
443 1.0 - ((i as f32 - half) / half).abs()
444 })
445 .collect(),
446 WindowType::Kaiser { beta } => {
447 let i0_beta = bessel_i0(beta);
448 (0..size)
449 .map(|i| {
450 let n = 2.0 * i as f32 / (size - 1) as f32 - 1.0;
451 bessel_i0(beta * (1.0 - n * n).sqrt()) / i0_beta
452 })
453 .collect()
454 }
455 }
456 }
457
458 pub fn apply_window_to_frame(frame: &[f32], window_type: WindowType) -> Vec<f32> {
460 let window = Self::create_window(window_type, frame.len());
461 frame
462 .iter()
463 .zip(window.iter())
464 .map(|(&s, &w)| s * w)
465 .collect()
466 }
467
468 pub fn mel_filterbank(
470 num_filters: usize,
471 n_fft: usize,
472 sample_rate: f32,
473 f_min: f32,
474 f_max: f32,
475 ) -> Vec<Vec<f32>> {
476 let num_bins = n_fft / 2 + 1;
477 let mel_min = Self::hz_to_mel(f_min);
478 let mel_max = Self::hz_to_mel(f_max);
479 let mel_points: Vec<f32> = (0..=num_filters + 1)
480 .map(|i| mel_min + (mel_max - mel_min) * i as f32 / (num_filters + 1) as f32)
481 .collect();
482 let hz_points: Vec<f32> = mel_points.iter().map(|&m| Self::mel_to_hz(m)).collect();
483 let bin_points: Vec<usize> = hz_points
484 .iter()
485 .map(|&f| ((n_fft as f32 + 1.0) * f / sample_rate).floor() as usize)
486 .collect();
487 let mut filterbank = Vec::with_capacity(num_filters);
488 for m in 0..num_filters {
489 let mut filter = vec![0.0; num_bins];
490 let left = bin_points[m];
491 let center = bin_points[m + 1];
492 let right = bin_points[m + 2];
493 let rise_denom = (center - left).max(1) as f32;
494 for (offset, val) in filter[left..center.min(num_bins)].iter_mut().enumerate() {
495 *val = offset as f32 / rise_denom;
496 }
497 let fall_denom = (right - center).max(1) as f32;
498 for (offset, val) in filter[center..right.min(num_bins)].iter_mut().enumerate() {
499 *val = (right - center - offset) as f32 / fall_denom;
500 }
501 filterbank.push(filter);
502 }
503 filterbank
504 }
505
506 pub fn hz_to_mel(hz: f32) -> f32 {
508 2595.0 * (1.0 + hz / 700.0).log10()
509 }
510
511 pub fn mel_to_hz(mel: f32) -> f32 {
513 700.0 * (10.0_f32.powf(mel / 2595.0) - 1.0)
514 }
515
516 pub fn mfcc(
518 &mut self,
519 signal: &Array1<f32>,
520 n_mfcc: usize,
521 n_fft: usize,
522 hop_length: usize,
523 n_mels: usize,
524 ) -> IoResult<Vec<Vec<f32>>> {
525 let f_max = self.sample_rate / 2.0;
526 let filterbank = Self::mel_filterbank(n_mels, n_fft, self.sample_rate, 0.0, f_max);
527 let spec = self.spectrogram(signal, n_fft, hop_length, WindowType::Hann)?;
528 let mut mfccs = Vec::with_capacity(spec.num_frames);
529 for frame_idx in 0..spec.num_frames {
530 let power: Vec<f32> = (0..spec.num_bins)
531 .map(|bin| {
532 let mag = spec.magnitudes[frame_idx * spec.num_bins + bin];
533 mag * mag
534 })
535 .collect();
536 let mel_energies: Vec<f32> = filterbank
537 .iter()
538 .map(|filter| {
539 let energy: f32 = filter.iter().zip(power.iter()).map(|(&f, &p)| f * p).sum();
540 (energy + 1e-10).ln()
541 })
542 .collect();
543 let mfcc_frame = Self::dct(&mel_energies, n_mfcc);
544 mfccs.push(mfcc_frame);
545 }
546 Ok(mfccs)
547 }
548
549 fn dct(input: &[f32], n_coeffs: usize) -> Vec<f32> {
551 let n = input.len();
552 (0..n_coeffs)
553 .map(|k| {
554 let sum: f32 = input
555 .iter()
556 .enumerate()
557 .map(|(i, &x)| {
558 x * (PI * k as f32 * (2.0 * i as f32 + 1.0) / (2.0 * n as f32)).cos()
559 })
560 .sum();
561 sum * (2.0 / n as f32).sqrt()
562 })
563 .collect()
564 }
565}