1use crate::error::{FFTError, FFTResult};
37use scirs2_core::numeric::Complex;
38use scirs2_core::numeric::Float;
39use std::fmt::Debug;
40use std::sync::{Arc, Mutex};
41
42pub trait RealToComplex<T: Float>: Send + Sync {
47 fn process(&self, input: &[T], output: &mut [Complex<T>]);
54
55 fn len(&self) -> usize;
57
58 fn is_empty(&self) -> bool {
60 self.len() == 0
61 }
62
63 fn output_len(&self) -> usize {
65 self.len() / 2 + 1
66 }
67}
68
69pub trait ComplexToReal<T: Float>: Send + Sync {
74 fn process(&self, input: &[Complex<T>], output: &mut [T]);
81
82 fn len(&self) -> usize;
84
85 fn is_empty(&self) -> bool {
87 self.len() == 0
88 }
89
90 fn input_len(&self) -> usize {
92 self.len() / 2 + 1
93 }
94}
95
96struct RealFftPlanF64 {
98 length: usize,
99 planner: Arc<Mutex<rustfft::FftPlanner<f64>>>,
100}
101
102impl RealFftPlanF64 {
103 fn new(length: usize, planner: Arc<Mutex<rustfft::FftPlanner<f64>>>) -> Self {
104 Self { length, planner }
105 }
106}
107
108impl RealToComplex<f64> for RealFftPlanF64 {
109 fn process(&self, input: &[f64], output: &mut [Complex<f64>]) {
110 assert_eq!(
112 input.len(),
113 self.length,
114 "Input length {} doesn't match plan length {}",
115 input.len(),
116 self.length
117 );
118 assert_eq!(
119 output.len(),
120 self.output_len(),
121 "Output length {} doesn't match expected length {}",
122 output.len(),
123 self.output_len()
124 );
125
126 let mut buffer: Vec<Complex<f64>> = input.iter().map(|&x| Complex::new(x, 0.0)).collect();
128
129 let mut planner = self.planner.lock().unwrap();
131 let fft = planner.plan_fft_forward(self.length);
132 drop(planner); fft.process(&mut buffer);
135
136 output.copy_from_slice(&buffer[..self.output_len()]);
138 }
139
140 fn len(&self) -> usize {
141 self.length
142 }
143}
144
145struct InverseRealFftPlanF64 {
147 length: usize,
148 planner: Arc<Mutex<rustfft::FftPlanner<f64>>>,
149}
150
151impl InverseRealFftPlanF64 {
152 fn new(length: usize, planner: Arc<Mutex<rustfft::FftPlanner<f64>>>) -> Self {
153 Self { length, planner }
154 }
155}
156
157impl ComplexToReal<f64> for InverseRealFftPlanF64 {
158 fn process(&self, input: &[Complex<f64>], output: &mut [f64]) {
159 assert_eq!(
161 input.len(),
162 self.input_len(),
163 "Input length {} doesn't match expected length {}",
164 input.len(),
165 self.input_len()
166 );
167 assert_eq!(
168 output.len(),
169 self.length,
170 "Output length {} doesn't match plan length {}",
171 output.len(),
172 self.length
173 );
174
175 let mut buffer: Vec<Complex<f64>> = Vec::with_capacity(self.length);
177 buffer.extend_from_slice(input);
178
179 let start_idx = if self.length.is_multiple_of(2) {
181 input.len() - 1
182 } else {
183 input.len()
184 };
185
186 for i in (1..start_idx).rev() {
187 buffer.push(input[i].conj());
188 }
189
190 while buffer.len() < self.length {
192 buffer.push(Complex::new(0.0, 0.0));
193 }
194
195 let mut planner = self.planner.lock().unwrap();
197 let ifft = planner.plan_fft_inverse(self.length);
198 drop(planner); ifft.process(&mut buffer);
201
202 let scale = 1.0 / self.length as f64;
204 for (i, &val) in buffer.iter().enumerate() {
205 output[i] = val.re * scale;
206 }
207 }
208
209 fn len(&self) -> usize {
210 self.length
211 }
212}
213
214struct RealFftPlanF32 {
216 length: usize,
217 planner: Arc<Mutex<rustfft::FftPlanner<f32>>>,
218}
219
220impl RealFftPlanF32 {
221 fn new(length: usize, planner: Arc<Mutex<rustfft::FftPlanner<f32>>>) -> Self {
222 Self { length, planner }
223 }
224}
225
226impl RealToComplex<f32> for RealFftPlanF32 {
227 fn process(&self, input: &[f32], output: &mut [Complex<f32>]) {
228 assert_eq!(
230 input.len(),
231 self.length,
232 "Input length {} doesn't match plan length {}",
233 input.len(),
234 self.length
235 );
236 assert_eq!(
237 output.len(),
238 self.output_len(),
239 "Output length {} doesn't match expected length {}",
240 output.len(),
241 self.output_len()
242 );
243
244 let mut buffer: Vec<Complex<f32>> = input.iter().map(|&x| Complex::new(x, 0.0)).collect();
246
247 let mut planner = self.planner.lock().unwrap();
249 let fft = planner.plan_fft_forward(self.length);
250 drop(planner); fft.process(&mut buffer);
253
254 output.copy_from_slice(&buffer[..self.output_len()]);
256 }
257
258 fn len(&self) -> usize {
259 self.length
260 }
261}
262
263struct InverseRealFftPlanF32 {
265 length: usize,
266 planner: Arc<Mutex<rustfft::FftPlanner<f32>>>,
267}
268
269impl InverseRealFftPlanF32 {
270 fn new(length: usize, planner: Arc<Mutex<rustfft::FftPlanner<f32>>>) -> Self {
271 Self { length, planner }
272 }
273}
274
275impl ComplexToReal<f32> for InverseRealFftPlanF32 {
276 fn process(&self, input: &[Complex<f32>], output: &mut [f32]) {
277 assert_eq!(
279 input.len(),
280 self.input_len(),
281 "Input length {} doesn't match expected length {}",
282 input.len(),
283 self.input_len()
284 );
285 assert_eq!(
286 output.len(),
287 self.length,
288 "Output length {} doesn't match plan length {}",
289 output.len(),
290 self.length
291 );
292
293 let mut buffer: Vec<Complex<f32>> = Vec::with_capacity(self.length);
295 buffer.extend_from_slice(input);
296
297 let start_idx = if self.length.is_multiple_of(2) {
299 input.len() - 1
300 } else {
301 input.len()
302 };
303
304 for i in (1..start_idx).rev() {
305 buffer.push(input[i].conj());
306 }
307
308 while buffer.len() < self.length {
310 buffer.push(Complex::new(0.0, 0.0));
311 }
312
313 let mut planner = self.planner.lock().unwrap();
315 let ifft = planner.plan_fft_inverse(self.length);
316 drop(planner); ifft.process(&mut buffer);
319
320 let scale = 1.0 / self.length as f32;
322 for (i, &val) in buffer.iter().enumerate() {
323 output[i] = val.re * scale;
324 }
325 }
326
327 fn len(&self) -> usize {
328 self.length
329 }
330}
331
332pub struct RealFftPlanner<T: Float> {
351 _phantom: std::marker::PhantomData<T>,
352}
353
354impl RealFftPlanner<f64> {
355 pub fn new() -> Self {
357 Self {
358 _phantom: std::marker::PhantomData,
359 }
360 }
361
362 pub fn plan_fft_forward(&mut self, length: usize) -> Arc<dyn RealToComplex<f64>> {
372 let planner = Arc::new(Mutex::new(rustfft::FftPlanner::<f64>::new()));
373 Arc::new(RealFftPlanF64::new(length, planner))
374 }
375
376 pub fn plan_fft_inverse(&mut self, length: usize) -> Arc<dyn ComplexToReal<f64>> {
386 let planner = Arc::new(Mutex::new(rustfft::FftPlanner::<f64>::new()));
387 Arc::new(InverseRealFftPlanF64::new(length, planner))
388 }
389}
390
391impl Default for RealFftPlanner<f64> {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397impl RealFftPlanner<f32> {
398 pub fn new() -> Self {
400 Self {
401 _phantom: std::marker::PhantomData,
402 }
403 }
404
405 pub fn plan_fft_forward(&mut self, length: usize) -> Arc<dyn RealToComplex<f32>> {
415 let planner = Arc::new(Mutex::new(rustfft::FftPlanner::<f32>::new()));
416 Arc::new(RealFftPlanF32::new(length, planner))
417 }
418
419 pub fn plan_fft_inverse(&mut self, length: usize) -> Arc<dyn ComplexToReal<f32>> {
429 let planner = Arc::new(Mutex::new(rustfft::FftPlanner::<f32>::new()));
430 Arc::new(InverseRealFftPlanF32::new(length, planner))
431 }
432}
433
434impl Default for RealFftPlanner<f32> {
435 fn default() -> Self {
436 Self::new()
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443 use scirs2_core::numeric::Complex64;
444 use std::f64::consts::PI;
445
446 #[test]
447 fn test_real_fft_planner_f64() {
448 let mut planner = RealFftPlanner::<f64>::new();
449 let forward = planner.plan_fft_forward(8);
450 let inverse = planner.plan_fft_inverse(8);
451
452 let input = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
454 let mut spectrum = vec![Complex64::new(0.0, 0.0); 5]; forward.process(&input, &mut spectrum);
458
459 let sum: f64 = input.iter().sum();
461 assert!((spectrum[0].re - sum).abs() < 1e-10);
462 assert!(spectrum[0].im.abs() < 1e-10);
463
464 let mut recovered = vec![0.0; 8];
466 inverse.process(&spectrum, &mut recovered);
467
468 for (i, (&orig, &recov)) in input.iter().zip(recovered.iter()).enumerate() {
470 assert!(
471 (orig - recov).abs() < 1e-10,
472 "Mismatch at index {}: {} vs {}",
473 i,
474 orig,
475 recov
476 );
477 }
478 }
479
480 #[test]
481 fn test_real_fft_planner_f32() {
482 let mut planner = RealFftPlanner::<f32>::new();
483 let forward = planner.plan_fft_forward(8);
484 let inverse = planner.plan_fft_inverse(8);
485
486 let input = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
488 let mut spectrum = vec![Complex::new(0.0f32, 0.0); 5]; forward.process(&input, &mut spectrum);
492
493 let mut recovered = vec![0.0f32; 8];
495 inverse.process(&spectrum, &mut recovered);
496
497 for (i, (&orig, &recov)) in input.iter().zip(recovered.iter()).enumerate() {
499 assert!(
500 (orig - recov).abs() < 1e-5,
501 "Mismatch at index {}: {} vs {}",
502 i,
503 orig,
504 recov
505 );
506 }
507 }
508
509 #[test]
510 fn test_sine_wave_fft() {
511 let mut planner = RealFftPlanner::<f64>::new();
512 let length = 128;
513 let forward = planner.plan_fft_forward(length);
514
515 let freq_index = 5;
517 let input: Vec<f64> = (0..length)
518 .map(|i| (2.0 * PI * freq_index as f64 * i as f64 / length as f64).sin())
519 .collect();
520
521 let mut spectrum = vec![Complex64::new(0.0, 0.0); length / 2 + 1];
522 forward.process(&input, &mut spectrum);
523
524 let magnitudes: Vec<f64> = spectrum.iter().map(|c| c.norm()).collect();
526
527 let (peak_idx, &peak_mag) = magnitudes
529 .iter()
530 .enumerate()
531 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
532 .unwrap();
533
534 assert_eq!(
535 peak_idx, freq_index,
536 "Peak should be at frequency index {}",
537 freq_index
538 );
539 assert!(peak_mag > length as f64 / 4.0, "Peak magnitude too small");
540 }
541
542 #[test]
543 fn test_plan_properties() {
544 let mut planner = RealFftPlanner::<f64>::new();
545 let forward = planner.plan_fft_forward(1024);
546
547 assert_eq!(forward.len(), 1024);
548 assert_eq!(forward.output_len(), 513); assert!(!forward.is_empty());
550 }
551
552 #[test]
553 fn test_voirs_usage_pattern() {
554 struct AudioProcessor {
556 forward: Arc<dyn RealToComplex<f64>>,
557 backward: Arc<dyn ComplexToReal<f64>>,
558 }
559
560 impl AudioProcessor {
561 fn new(size: usize) -> Self {
562 let mut planner = RealFftPlanner::<f64>::new();
563 Self {
564 forward: planner.plan_fft_forward(size),
565 backward: planner.plan_fft_inverse(size),
566 }
567 }
568
569 fn process(&self, input: &[f64]) -> Vec<f64> {
570 let mut spectrum = vec![Complex64::new(0.0, 0.0); self.forward.output_len()];
571 self.forward.process(input, &mut spectrum);
572
573 let mut output = vec![0.0; self.backward.len()];
574 self.backward.process(&spectrum, &mut output);
575
576 output
577 }
578 }
579
580 let processor = AudioProcessor::new(16);
581 let input = vec![
582 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
583 ];
584 let output = processor.process(&input);
585
586 for (i, (&orig, &recov)) in input.iter().zip(output.iter()).enumerate() {
588 assert!((orig - recov).abs() < 1e-10, "Mismatch at {}", i);
589 }
590 }
591}