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