1use crate::RecognitionError;
30use std::sync::{Arc, Mutex};
31use voirs_sdk::AudioBuffer;
32
33pub mod adaptive_algorithms;
35pub mod advanced_spectral;
36pub mod agc;
37pub mod bandwidth_extension;
38pub mod echo_cancellation;
39pub mod noise_suppression;
40pub mod optimizations;
41pub mod optimized_preprocessor;
42pub mod realtime_features;
43
44pub use adaptive_algorithms::*;
46pub use advanced_spectral::*;
47pub use agc::*;
48pub use bandwidth_extension::*;
49pub use echo_cancellation::*;
50pub use noise_suppression::*;
51pub use optimizations::*;
52pub use optimized_preprocessor::*;
53pub use realtime_features::*;
54
55#[derive(Debug, Clone)]
57pub struct AudioPreprocessingConfig {
58 pub noise_suppression: bool,
60 pub agc: bool,
62 pub echo_cancellation: bool,
64 pub bandwidth_extension: bool,
66 pub advanced_spectral: bool,
68 pub adaptive_algorithms: bool,
70 pub sample_rate: u32,
72 pub buffer_size: usize,
74 pub advanced_spectral_config: Option<AdvancedSpectralConfig>,
76 pub adaptive_config: Option<AdaptiveConfig>,
78}
79
80impl Default for AudioPreprocessingConfig {
81 fn default() -> Self {
82 Self {
83 noise_suppression: true,
84 agc: true,
85 echo_cancellation: false,
86 bandwidth_extension: false,
87 advanced_spectral: true,
88 adaptive_algorithms: true,
89 sample_rate: 16000,
90 buffer_size: 1024,
91 advanced_spectral_config: Some(AdvancedSpectralConfig::default()),
92 adaptive_config: Some(AdaptiveConfig::default()),
93 }
94 }
95}
96
97#[derive(Debug, Clone)]
99pub struct AudioPreprocessingResult {
100 pub enhanced_audio: AudioBuffer,
102 pub noise_suppression_stats: Option<NoiseSuppressionStats>,
104 pub agc_stats: Option<AGCStats>,
106 pub echo_cancellation_stats: Option<EchoCancellationStats>,
108 pub bandwidth_extension_stats: Option<BandwidthExtensionStats>,
110 pub advanced_spectral_stats: Option<AdvancedSpectralStats>,
112 pub adaptive_stats: Option<AdaptiveStats>,
114 pub processing_time_ms: f64,
116}
117
118#[derive(Debug)]
120pub struct AudioPreprocessor {
121 config: AudioPreprocessingConfig,
123 noise_suppressor: Option<Arc<Mutex<NoiseSuppressionProcessor>>>,
125 agc_processor: Option<Arc<Mutex<AGCProcessor>>>,
127 echo_canceller: Option<Arc<Mutex<EchoCancellationProcessor>>>,
129 bandwidth_extender: Option<Arc<Mutex<BandwidthExtensionProcessor>>>,
131 feature_extractor: Option<Arc<Mutex<RealTimeFeatureExtractor>>>,
133 advanced_spectral_processor: Option<Arc<Mutex<AdvancedSpectralProcessor>>>,
135 adaptive_processor: Option<Arc<Mutex<AdaptiveProcessor>>>,
137}
138
139impl AudioPreprocessor {
140 pub fn new(config: AudioPreprocessingConfig) -> Result<Self, RecognitionError> {
142 let mut preprocessor = Self {
143 config: config.clone(),
144 noise_suppressor: None,
145 agc_processor: None,
146 echo_canceller: None,
147 bandwidth_extender: None,
148 feature_extractor: None,
149 advanced_spectral_processor: None,
150 adaptive_processor: None,
151 };
152
153 if config.noise_suppression {
155 let noise_config = NoiseSuppressionConfig {
156 sample_rate: config.sample_rate,
157 buffer_size: config.buffer_size,
158 algorithm: NoiseSuppressionAlgorithm::SpectralSubtraction,
159 alpha: 2.0,
160 beta: 0.01,
161 };
162 preprocessor.noise_suppressor = Some(Arc::new(Mutex::new(
163 NoiseSuppressionProcessor::new(noise_config)?,
164 )));
165 }
166
167 if config.agc {
168 let agc_config = AGCConfig {
169 sample_rate: config.sample_rate,
170 target_level: -20.0,
171 max_gain: 30.0,
172 attack_time: 0.001,
173 release_time: 0.1,
174 };
175 preprocessor.agc_processor = Some(Arc::new(Mutex::new(AGCProcessor::new(agc_config)?)));
176 }
177
178 if config.echo_cancellation {
179 let echo_config = EchoCancellationConfig {
180 sample_rate: config.sample_rate,
181 filter_length: 1024,
182 adaptation_rate: 0.01,
183 nlp_threshold: 0.5,
184 };
185 preprocessor.echo_canceller = Some(Arc::new(Mutex::new(
186 EchoCancellationProcessor::new(echo_config)?,
187 )));
188 }
189
190 if config.bandwidth_extension {
191 let bwe_config = BandwidthExtensionConfig {
192 target_bandwidth: 8000.0,
193 method: bandwidth_extension::ExtensionMethod::SpectralReplication,
194 quality: bandwidth_extension::QualityLevel::Medium,
195 spectral_replication: true,
196 hf_emphasis: 1.2,
197 };
198 preprocessor.bandwidth_extender = Some(Arc::new(Mutex::new(
199 BandwidthExtensionProcessor::new(bwe_config)?,
200 )));
201 }
202
203 let features_config = RealTimeFeatureConfig {
205 window_size: 512,
206 hop_length: 256,
207 n_mels: 13,
208 extract_mfcc: true,
209 extract_spectral_centroid: true,
210 extract_zcr: true,
211 extract_spectral_rolloff: true,
212 extract_energy: true,
213 };
214 preprocessor.feature_extractor = Some(Arc::new(Mutex::new(RealTimeFeatureExtractor::new(
215 features_config,
216 )?)));
217
218 if config.advanced_spectral {
220 let spectral_config = config.advanced_spectral_config.unwrap_or_else(|| {
221 let mut cfg = AdvancedSpectralConfig::default();
222 cfg.sample_rate = config.sample_rate;
223 cfg
224 });
225 preprocessor.advanced_spectral_processor = Some(Arc::new(Mutex::new(
226 AdvancedSpectralProcessor::new(spectral_config)?,
227 )));
228 }
229
230 if config.adaptive_algorithms {
232 let adaptive_config = config.adaptive_config.unwrap_or_else(|| {
233 let mut cfg = AdaptiveConfig::default();
234 cfg.sample_rate = config.sample_rate;
235 cfg.analysis_window_size = config.buffer_size * 2;
236 cfg
237 });
238 preprocessor.adaptive_processor = Some(Arc::new(Mutex::new(AdaptiveProcessor::new(
239 adaptive_config,
240 )?)));
241 }
242
243 Ok(preprocessor)
244 }
245
246 pub async fn process(
248 &mut self,
249 audio: &AudioBuffer,
250 ) -> Result<AudioPreprocessingResult, RecognitionError> {
251 let start_time = std::time::Instant::now();
252 let mut enhanced_audio = audio.clone();
253
254 let mut noise_suppression_stats = None;
256 let mut agc_stats = None;
257 let mut echo_cancellation_stats = None;
258 let mut bandwidth_extension_stats = None;
259 let mut advanced_spectral_stats = None;
260 let mut adaptive_stats = None;
261
262 if let Some(ref noise_suppressor) = self.noise_suppressor {
267 let result = {
268 let mut processor = noise_suppressor.lock().map_err(|e| {
269 RecognitionError::AudioProcessingError {
270 message: format!("Failed to lock noise suppressor: {e}"),
271 source: None,
272 }
273 })?;
274 processor.process(&enhanced_audio).await?
275 };
276 enhanced_audio = result.enhanced_audio;
277 noise_suppression_stats = Some(result.stats);
278 }
279
280 if let Some(ref agc_processor) = self.agc_processor {
282 let result = {
283 let mut processor =
284 agc_processor
285 .lock()
286 .map_err(|e| RecognitionError::AudioProcessingError {
287 message: format!("Failed to lock AGC processor: {e}"),
288 source: None,
289 })?;
290 processor.process(&enhanced_audio).await?
291 };
292 enhanced_audio = result.enhanced_audio;
293 agc_stats = Some(result.stats);
294 }
295
296 if let Some(ref echo_canceller) = self.echo_canceller {
298 let result = {
299 let mut processor =
300 echo_canceller
301 .lock()
302 .map_err(|e| RecognitionError::AudioProcessingError {
303 message: format!("Failed to lock echo canceller: {e}"),
304 source: None,
305 })?;
306 processor.process(&enhanced_audio).await?
307 };
308 enhanced_audio = result.enhanced_audio;
309 echo_cancellation_stats = Some(result.stats);
310 }
311
312 if let Some(ref bandwidth_extender) = self.bandwidth_extender {
314 let (processed_audio, stats) = {
315 let mut processor = bandwidth_extender.lock().map_err(|e| {
316 RecognitionError::AudioProcessingError {
317 message: format!("Failed to lock bandwidth extender: {e}"),
318 source: None,
319 }
320 })?;
321 let processed = processor.process(&enhanced_audio)?;
322 let stats = processor.get_stats().clone();
323 (processed, stats)
324 };
325 enhanced_audio = processed_audio;
326 bandwidth_extension_stats = Some(stats);
327 }
328
329 if let Some(ref adaptive_processor) = self.adaptive_processor {
331 let adaptive_result = {
332 let mut processor = adaptive_processor.lock().map_err(|e| {
333 RecognitionError::AudioProcessingError {
334 message: format!("Failed to lock adaptive processor: {e}"),
335 source: None,
336 }
337 })?;
338 processor.analyze_and_adapt(&enhanced_audio)?
339 };
340 adaptive_stats = Some(adaptive_result.stats);
341
342 }
345
346 if let Some(ref advanced_spectral_processor) = self.advanced_spectral_processor {
348 let spectral_result = {
349 let mut processor = advanced_spectral_processor.lock().map_err(|e| {
350 RecognitionError::AudioProcessingError {
351 message: format!("Failed to lock advanced spectral processor: {e}"),
352 source: None,
353 }
354 })?;
355 processor.process(&enhanced_audio)?
356 };
357 enhanced_audio = spectral_result.enhanced_audio;
358 advanced_spectral_stats = Some(spectral_result.stats);
359 }
360
361 let processing_time_ms = start_time.elapsed().as_secs_f64() * 1000.0;
362
363 Ok(AudioPreprocessingResult {
364 enhanced_audio,
365 noise_suppression_stats,
366 agc_stats,
367 echo_cancellation_stats,
368 bandwidth_extension_stats,
369 advanced_spectral_stats,
370 adaptive_stats,
371 processing_time_ms,
372 })
373 }
374
375 pub async fn process_stream(
377 &mut self,
378 audio_chunk: &AudioBuffer,
379 ) -> Result<AudioPreprocessingResult, RecognitionError> {
380 self.process(audio_chunk).await
382 }
383
384 pub async fn process_parallel(
387 &mut self,
388 audio: &AudioBuffer,
389 ) -> Result<AudioPreprocessingResult, RecognitionError> {
390 let start_time = std::time::Instant::now();
391
392 if audio.channels() == 1 {
394 return self.process(audio).await;
395 }
396
397 let channels = audio.channels() as usize;
399 let samples_per_channel = audio.samples().len() / channels;
400 let mut channel_buffers = Vec::with_capacity(channels);
401
402 for ch in 0..channels {
403 let mut channel_samples = Vec::with_capacity(samples_per_channel);
404 for i in 0..samples_per_channel {
405 channel_samples.push(audio.samples()[i * channels + ch]);
406 }
407 channel_buffers.push(AudioBuffer::mono(channel_samples, audio.sample_rate()));
408 }
409
410 let mut tasks = Vec::with_capacity(channels);
412
413 for channel_audio in channel_buffers {
414 let result = self.process(&channel_audio).await?;
418 tasks.push(result);
419 }
420
421 let enhanced_samples = self.merge_channel_results(&tasks, channels)?;
423 let enhanced_audio =
424 AudioBuffer::new(enhanced_samples, audio.sample_rate(), channels as u32);
425
426 let mut noise_suppression_stats = None;
428 let mut agc_stats = None;
429 let mut echo_cancellation_stats = None;
430 let mut bandwidth_extension_stats = None;
431 let mut advanced_spectral_stats = None;
432 let mut adaptive_stats = None;
433
434 if let Some(stats) = tasks
436 .first()
437 .and_then(|t| t.noise_suppression_stats.as_ref())
438 {
439 noise_suppression_stats = Some(stats.clone());
440 }
441 if let Some(stats) = tasks.first().and_then(|t| t.agc_stats.as_ref()) {
442 agc_stats = Some(stats.clone());
443 }
444 if let Some(stats) = tasks
445 .first()
446 .and_then(|t| t.echo_cancellation_stats.as_ref())
447 {
448 echo_cancellation_stats = Some(stats.clone());
449 }
450 if let Some(stats) = tasks
451 .first()
452 .and_then(|t| t.bandwidth_extension_stats.as_ref())
453 {
454 bandwidth_extension_stats = Some(stats.clone());
455 }
456 if let Some(stats) = tasks
457 .first()
458 .and_then(|t| t.advanced_spectral_stats.as_ref())
459 {
460 advanced_spectral_stats = Some(stats.clone());
461 }
462 if let Some(stats) = tasks.first().and_then(|t| t.adaptive_stats.as_ref()) {
463 adaptive_stats = Some(stats.clone());
464 }
465
466 let processing_time_ms = start_time.elapsed().as_secs_f64() * 1000.0;
467
468 Ok(AudioPreprocessingResult {
469 enhanced_audio,
470 noise_suppression_stats,
471 agc_stats,
472 echo_cancellation_stats,
473 bandwidth_extension_stats,
474 advanced_spectral_stats,
475 adaptive_stats,
476 processing_time_ms,
477 })
478 }
479
480 fn merge_channel_results(
482 &self,
483 channel_results: &[AudioPreprocessingResult],
484 channels: usize,
485 ) -> Result<Vec<f32>, RecognitionError> {
486 if channel_results.is_empty() {
487 return Ok(Vec::new());
488 }
489
490 let samples_per_channel = channel_results[0].enhanced_audio.samples().len();
491 let total_samples = samples_per_channel * channels;
492 let mut merged_samples = Vec::with_capacity(total_samples);
493
494 if channels == 2 && samples_per_channel >= 8 {
496 self.interleave_stereo_simd(channel_results, &mut merged_samples)?;
497 } else {
498 for i in 0..samples_per_channel {
500 for ch in 0..channels {
501 if ch < channel_results.len() {
502 let channel_samples = channel_results[ch].enhanced_audio.samples();
503 if i < channel_samples.len() {
504 merged_samples.push(channel_samples[i]);
505 } else {
506 merged_samples.push(0.0); }
508 } else {
509 merged_samples.push(0.0); }
511 }
512 }
513 }
514
515 Ok(merged_samples)
516 }
517
518 #[cfg(target_arch = "x86_64")]
520 fn interleave_stereo_simd(
521 &self,
522 channel_results: &[AudioPreprocessingResult],
523 merged_samples: &mut Vec<f32>,
524 ) -> Result<(), RecognitionError> {
525 if channel_results.len() < 2 {
526 return Err(RecognitionError::AudioProcessingError {
527 message: "Need at least 2 channels for stereo interleaving".to_string(),
528 source: None,
529 });
530 }
531
532 let left_samples = channel_results[0].enhanced_audio.samples();
533 let right_samples = channel_results[1].enhanced_audio.samples();
534 let samples_per_channel = left_samples.len().min(right_samples.len());
535
536 merged_samples.resize(samples_per_channel * 2, 0.0);
537
538 if std::arch::is_x86_feature_detected!("avx2") {
539 unsafe {
540 self.interleave_stereo_avx2(left_samples, right_samples, merged_samples);
541 }
542 } else if std::arch::is_x86_feature_detected!("sse2") {
543 unsafe {
544 self.interleave_stereo_sse2(left_samples, right_samples, merged_samples);
545 }
546 } else {
547 for i in 0..samples_per_channel {
549 merged_samples[i * 2] = left_samples[i];
550 merged_samples[i * 2 + 1] = right_samples[i];
551 }
552 }
553
554 Ok(())
555 }
556
557 #[cfg(target_arch = "x86_64")]
559 #[target_feature(enable = "avx2")]
560 unsafe fn interleave_stereo_avx2(&self, left: &[f32], right: &[f32], output: &mut [f32]) {
561 use std::arch::x86_64::*;
562
563 let len = left.len().min(right.len());
564 let simd_len = len & !7; for i in (0..simd_len).step_by(8) {
567 let left_vec = _mm256_loadu_ps(left.as_ptr().add(i));
569 let right_vec = _mm256_loadu_ps(right.as_ptr().add(i));
570
571 let low_interleaved = _mm256_unpacklo_ps(left_vec, right_vec);
573 let high_interleaved = _mm256_unpackhi_ps(left_vec, right_vec);
574
575 _mm256_storeu_ps(output.as_mut_ptr().add(i * 2), low_interleaved);
577 _mm256_storeu_ps(output.as_mut_ptr().add(i * 2 + 8), high_interleaved);
578 }
579
580 for i in simd_len..len {
582 output[i * 2] = left[i];
583 output[i * 2 + 1] = right[i];
584 }
585 }
586
587 #[cfg(target_arch = "x86_64")]
589 #[target_feature(enable = "sse2")]
590 unsafe fn interleave_stereo_sse2(&self, left: &[f32], right: &[f32], output: &mut [f32]) {
591 use std::arch::x86_64::*;
592
593 let len = left.len().min(right.len());
594 let simd_len = len & !3; for i in (0..simd_len).step_by(4) {
597 let left_vec = _mm_loadu_ps(left.as_ptr().add(i));
598 let right_vec = _mm_loadu_ps(right.as_ptr().add(i));
599
600 let low_interleaved = _mm_unpacklo_ps(left_vec, right_vec);
601 let high_interleaved = _mm_unpackhi_ps(left_vec, right_vec);
602
603 _mm_storeu_ps(output.as_mut_ptr().add(i * 2), low_interleaved);
604 _mm_storeu_ps(output.as_mut_ptr().add(i * 2 + 4), high_interleaved);
605 }
606
607 for i in simd_len..len {
608 output[i * 2] = left[i];
609 output[i * 2 + 1] = right[i];
610 }
611 }
612
613 #[cfg(target_arch = "aarch64")]
615 fn interleave_stereo_simd(
616 &self,
617 channel_results: &[AudioPreprocessingResult],
618 merged_samples: &mut Vec<f32>,
619 ) -> Result<(), RecognitionError> {
620 if channel_results.len() < 2 {
621 return Err(RecognitionError::AudioProcessingError {
622 message: "Need at least 2 channels for stereo interleaving".to_string(),
623 source: None,
624 });
625 }
626
627 let left_samples = channel_results[0].enhanced_audio.samples();
628 let right_samples = channel_results[1].enhanced_audio.samples();
629 let samples_per_channel = left_samples.len().min(right_samples.len());
630
631 merged_samples.resize(samples_per_channel * 2, 0.0);
632
633 if std::arch::is_aarch64_feature_detected!("neon") {
634 unsafe {
635 self.interleave_stereo_neon(left_samples, right_samples, merged_samples);
636 }
637 } else {
638 for i in 0..samples_per_channel {
640 merged_samples[i * 2] = left_samples[i];
641 merged_samples[i * 2 + 1] = right_samples[i];
642 }
643 }
644
645 Ok(())
646 }
647
648 #[cfg(target_arch = "aarch64")]
650 #[target_feature(enable = "neon")]
651 unsafe fn interleave_stereo_neon(&self, left: &[f32], right: &[f32], output: &mut [f32]) {
652 use std::arch::aarch64::{vld1q_f32, vst1q_f32, vzipq_f32};
653
654 let len = left.len().min(right.len());
655 let simd_len = len & !3; for i in (0..simd_len).step_by(4) {
658 let left_vec = vld1q_f32(left.as_ptr().add(i));
659 let right_vec = vld1q_f32(right.as_ptr().add(i));
660
661 let interleaved_low = vzipq_f32(left_vec, right_vec);
663
664 vst1q_f32(output.as_mut_ptr().add(i * 2), interleaved_low.0);
665 vst1q_f32(output.as_mut_ptr().add(i * 2 + 4), interleaved_low.1);
666 }
667
668 for i in simd_len..len {
669 output[i * 2] = left[i];
670 output[i * 2 + 1] = right[i];
671 }
672 }
673
674 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
676 fn interleave_stereo_simd(
677 &self,
678 channel_results: &[AudioPreprocessingResult],
679 merged_samples: &mut Vec<f32>,
680 ) -> Result<(), RecognitionError> {
681 if channel_results.len() < 2 {
682 return Err(RecognitionError::AudioProcessingError {
683 message: "Need at least 2 channels for stereo interleaving".to_string(),
684 source: None,
685 });
686 }
687
688 let left_samples = channel_results[0].enhanced_audio.samples();
689 let right_samples = channel_results[1].enhanced_audio.samples();
690 let samples_per_channel = left_samples.len().min(right_samples.len());
691
692 merged_samples.resize(samples_per_channel * 2, 0.0);
693
694 for i in 0..samples_per_channel {
696 merged_samples[i * 2] = left_samples[i];
697 merged_samples[i * 2 + 1] = right_samples[i];
698 }
699
700 Ok(())
701 }
702
703 pub async fn extract_features(
705 &self,
706 audio: &AudioBuffer,
707 ) -> Result<RealTimeFeatureResult, RecognitionError> {
708 if let Some(ref feature_extractor) = self.feature_extractor {
709 let extractor =
710 feature_extractor
711 .lock()
712 .map_err(|e| RecognitionError::AudioProcessingError {
713 message: format!("Failed to lock feature extractor: {e}"),
714 source: None,
715 })?;
716 extractor.extract_features(audio)
717 } else {
718 Err(RecognitionError::AudioProcessingError {
719 message: "Real-time feature extractor not initialized".to_string(),
720 source: None,
721 })
722 }
723 }
724
725 #[must_use]
727 pub fn config(&self) -> &AudioPreprocessingConfig {
728 &self.config
729 }
730
731 pub fn reset(&mut self) -> Result<(), RecognitionError> {
733 if let Some(ref noise_suppressor) = self.noise_suppressor {
734 let mut processor =
735 noise_suppressor
736 .lock()
737 .map_err(|e| RecognitionError::AudioProcessingError {
738 message: format!("Failed to lock noise suppressor: {e}"),
739 source: None,
740 })?;
741 processor.reset()?;
742 }
743 if let Some(ref agc_processor) = self.agc_processor {
744 let mut processor =
745 agc_processor
746 .lock()
747 .map_err(|e| RecognitionError::AudioProcessingError {
748 message: format!("Failed to lock AGC processor: {e}"),
749 source: None,
750 })?;
751 processor.reset()?;
752 }
753 if let Some(ref echo_canceller) = self.echo_canceller {
754 let mut processor =
755 echo_canceller
756 .lock()
757 .map_err(|e| RecognitionError::AudioProcessingError {
758 message: format!("Failed to lock echo canceller: {e}"),
759 source: None,
760 })?;
761 processor.reset()?;
762 }
763 Ok(())
766 }
767}
768
769#[cfg(test)]
770mod tests {
771 use super::*;
772
773 #[tokio::test]
774 async fn test_audio_preprocessor_creation() {
775 let config = AudioPreprocessingConfig::default();
776 let preprocessor = AudioPreprocessor::new(config);
777 assert!(preprocessor.is_ok());
778 }
779
780 #[tokio::test]
781 async fn test_audio_preprocessing_basic() {
782 let config = AudioPreprocessingConfig {
783 noise_suppression: true,
784 agc: true,
785 echo_cancellation: false,
786 bandwidth_extension: false,
787 advanced_spectral: false,
788 adaptive_algorithms: false,
789 sample_rate: 16000,
790 buffer_size: 1024,
791 advanced_spectral_config: None,
792 adaptive_config: None,
793 };
794
795 let mut preprocessor = AudioPreprocessor::new(config).unwrap();
796
797 let samples = vec![0.1f32; 16000]; let audio = AudioBuffer::mono(samples, 16000);
800
801 let result = preprocessor.process(&audio).await;
802 assert!(result.is_ok());
803
804 let result = result.unwrap();
805 assert!(result.noise_suppression_stats.is_some());
807 assert!(result.agc_stats.is_some());
808 assert!(result.echo_cancellation_stats.is_none());
809 assert!(result.bandwidth_extension_stats.is_none());
810 assert!(result.processing_time_ms > 0.0);
811 }
812
813 #[tokio::test]
814 async fn test_feature_extraction() {
815 let config = AudioPreprocessingConfig::default();
816 let preprocessor = AudioPreprocessor::new(config).unwrap();
817
818 let samples = vec![0.1f32; 16000];
819 let audio = AudioBuffer::mono(samples, 16000);
820
821 let result = preprocessor.extract_features(&audio).await;
822 assert!(result.is_ok());
823 }
824
825 #[tokio::test]
826 async fn test_preprocessor_reset() {
827 let config = AudioPreprocessingConfig::default();
828 let mut preprocessor = AudioPreprocessor::new(config).unwrap();
829
830 let result = preprocessor.reset();
831 assert!(result.is_ok());
832 }
833
834 #[tokio::test]
835 async fn test_simd_stereo_interleaving() {
836 let config = AudioPreprocessingConfig::default();
837 let preprocessor = AudioPreprocessor::new(config).unwrap();
838
839 let left_samples = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
841 let right_samples = vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8];
842
843 let left_audio = AudioBuffer::mono(left_samples.clone(), 16000);
844 let right_audio = AudioBuffer::mono(right_samples.clone(), 16000);
845
846 let channel_results = vec![
847 AudioPreprocessingResult {
848 enhanced_audio: left_audio,
849 noise_suppression_stats: None,
850 agc_stats: None,
851 echo_cancellation_stats: None,
852 bandwidth_extension_stats: None,
853 advanced_spectral_stats: None,
854 adaptive_stats: None,
855 processing_time_ms: 0.0,
856 },
857 AudioPreprocessingResult {
858 enhanced_audio: right_audio,
859 noise_suppression_stats: None,
860 agc_stats: None,
861 echo_cancellation_stats: None,
862 bandwidth_extension_stats: None,
863 advanced_spectral_stats: None,
864 adaptive_stats: None,
865 processing_time_ms: 0.0,
866 },
867 ];
868
869 let result = preprocessor.merge_channel_results(&channel_results, 2);
870 assert!(result.is_ok());
871
872 let merged = result.unwrap();
873 assert_eq!(merged.len(), 16); for i in 0..8 {
877 assert!((merged[i * 2] - left_samples[i]).abs() < f32::EPSILON);
878 assert!((merged[i * 2 + 1] - right_samples[i]).abs() < f32::EPSILON);
879 }
880 }
881
882 #[tokio::test]
883 async fn test_memory_efficient_processing() {
884 let config = AudioPreprocessingConfig {
885 noise_suppression: true,
886 agc: false,
887 echo_cancellation: false,
888 bandwidth_extension: false,
889 advanced_spectral: false,
890 adaptive_algorithms: false,
891 sample_rate: 16000,
892 buffer_size: 512, advanced_spectral_config: None,
894 adaptive_config: None,
895 };
896
897 let mut preprocessor = AudioPreprocessor::new(config).unwrap();
898
899 let large_samples = vec![0.1f32; 48000]; let audio = AudioBuffer::mono(large_samples, 16000);
902
903 let result = preprocessor.process(&audio).await;
904 assert!(result.is_ok());
905
906 let processing_result = result.unwrap();
907 assert!(processing_result.processing_time_ms > 0.0);
908 assert_eq!(processing_result.enhanced_audio.samples().len(), 48000);
909 }
910
911 #[tokio::test]
912 async fn test_parallel_processing_performance() {
913 let config = AudioPreprocessingConfig::default();
914 let mut preprocessor = AudioPreprocessor::new(config).unwrap();
915
916 let samples_per_channel = 16000; let stereo_samples = (0..samples_per_channel * 2)
919 .map(|i| (i as f32 * 0.001) % 1.0)
920 .collect::<Vec<f32>>();
921 let stereo_audio = AudioBuffer::new(stereo_samples, 16000, 2);
922
923 let start_time = std::time::Instant::now();
924 let result = preprocessor.process_parallel(&stereo_audio).await;
925 let parallel_time = start_time.elapsed();
926
927 assert!(result.is_ok());
928 let processing_result = result.unwrap();
929 assert!(processing_result.processing_time_ms > 0.0);
930
931 println!("Parallel processing time: {:?}", parallel_time);
932
933 assert_eq!(processing_result.enhanced_audio.channels(), 2);
935 assert_eq!(
936 processing_result.enhanced_audio.samples().len(),
937 samples_per_channel * 2
938 );
939 }
940}