Skip to main content

voirs_recognizer/preprocessing/
mod.rs

1//! Audio preprocessing and enhancement module
2//!
3//! This module provides real-time audio preprocessing and enhancement capabilities
4//! for the `VoiRS` recognition system. It includes:
5//!
6//! - Real-time noise suppression
7//! - Automatic gain control (AGC)
8//! - Echo cancellation
9//! - Bandwidth extension
10//!
11//! # Features
12//!
13//! ## Noise Suppression
14//! Implements spectral subtraction and Wiener filtering for noise reduction
15//! in real-time audio streams.
16//!
17//! ## Automatic Gain Control
18//! Provides adaptive volume control to maintain consistent signal levels
19//! across different audio sources.
20//!
21//! ## Echo Cancellation
22//! Removes echo and feedback from audio signals using adaptive filtering
23//! techniques.
24//!
25//! ## Bandwidth Extension
26//! Extends the frequency range of audio signals to improve recognition
27//! accuracy for band-limited audio sources.
28
29use crate::RecognitionError;
30use std::sync::{Arc, Mutex};
31use voirs_sdk::AudioBuffer;
32
33// Sub-modules
34pub 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
44// Re-exports
45pub 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/// Audio preprocessing configuration
56#[derive(Debug, Clone)]
57pub struct AudioPreprocessingConfig {
58    /// Enable noise suppression
59    pub noise_suppression: bool,
60    /// Enable automatic gain control
61    pub agc: bool,
62    /// Enable echo cancellation
63    pub echo_cancellation: bool,
64    /// Enable bandwidth extension
65    pub bandwidth_extension: bool,
66    /// Enable advanced spectral processing
67    pub advanced_spectral: bool,
68    /// Enable adaptive algorithms
69    pub adaptive_algorithms: bool,
70    /// Sample rate for processing
71    pub sample_rate: u32,
72    /// Buffer size for real-time processing
73    pub buffer_size: usize,
74    /// Advanced spectral processing configuration
75    pub advanced_spectral_config: Option<AdvancedSpectralConfig>,
76    /// Adaptive algorithms configuration
77    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/// Result of audio preprocessing
98#[derive(Debug, Clone)]
99pub struct AudioPreprocessingResult {
100    /// Enhanced audio buffer
101    pub enhanced_audio: AudioBuffer,
102    /// Noise suppression statistics
103    pub noise_suppression_stats: Option<NoiseSuppressionStats>,
104    /// AGC statistics
105    pub agc_stats: Option<AGCStats>,
106    /// Echo cancellation statistics
107    pub echo_cancellation_stats: Option<EchoCancellationStats>,
108    /// Bandwidth extension statistics
109    pub bandwidth_extension_stats: Option<BandwidthExtensionStats>,
110    /// Advanced spectral processing statistics
111    pub advanced_spectral_stats: Option<AdvancedSpectralStats>,
112    /// Adaptive algorithms statistics
113    pub adaptive_stats: Option<AdaptiveStats>,
114    /// Processing time in milliseconds
115    pub processing_time_ms: f64,
116}
117
118/// Audio preprocessing pipeline
119#[derive(Debug)]
120pub struct AudioPreprocessor {
121    /// Configuration
122    config: AudioPreprocessingConfig,
123    /// Noise suppression processor
124    noise_suppressor: Option<Arc<Mutex<NoiseSuppressionProcessor>>>,
125    /// AGC processor
126    agc_processor: Option<Arc<Mutex<AGCProcessor>>>,
127    /// Echo cancellation processor
128    echo_canceller: Option<Arc<Mutex<EchoCancellationProcessor>>>,
129    /// Bandwidth extension processor
130    bandwidth_extender: Option<Arc<Mutex<BandwidthExtensionProcessor>>>,
131    /// Real-time feature extractor
132    feature_extractor: Option<Arc<Mutex<RealTimeFeatureExtractor>>>,
133    /// Advanced spectral processor
134    advanced_spectral_processor: Option<Arc<Mutex<AdvancedSpectralProcessor>>>,
135    /// Adaptive algorithms processor
136    adaptive_processor: Option<Arc<Mutex<AdaptiveProcessor>>>,
137}
138
139impl AudioPreprocessor {
140    /// Create a new audio preprocessor with the given configuration
141    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        // Initialize processors based on configuration
154        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        // Initialize real-time feature extractor
204        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        // Initialize advanced spectral processor
219        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        // Initialize adaptive algorithms processor
231        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    /// Process audio with all enabled preprocessing steps
247    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        // Processing statistics
255        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        // Performance optimization: Minimize lock scope and handle processors sequentially
263        // but with reduced lock contention
264
265        // Apply noise suppression with minimized lock scope
266        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        // Apply automatic gain control with minimized lock scope
281        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        // Apply echo cancellation with minimized lock scope
297        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        // Apply bandwidth extension with minimized lock scope
313        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        // Apply adaptive algorithms first to get optimal parameters
330        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            // The adaptive parameters could be used to adjust other processors dynamically
343            // For now, we'll just collect the statistics
344        }
345
346        // Apply advanced spectral processing
347        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    /// Process audio stream in real-time
376    pub async fn process_stream(
377        &mut self,
378        audio_chunk: &AudioBuffer,
379    ) -> Result<AudioPreprocessingResult, RecognitionError> {
380        // For streaming, we process smaller chunks with optimized latency
381        self.process(audio_chunk).await
382    }
383
384    /// High-performance parallel processing for multi-channel audio
385    /// This method processes channels in parallel when possible to reduce latency
386    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        // For mono audio, fall back to sequential processing
393        if audio.channels() == 1 {
394            return self.process(audio).await;
395        }
396
397        // Split multi-channel audio into separate channels for parallel processing
398        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        // Process channels in parallel using tokio tasks
411        let mut tasks = Vec::with_capacity(channels);
412
413        for channel_audio in channel_buffers {
414            // Note: For this to work properly, we'd need to clone the processors
415            // For now, we'll fall back to sequential processing but with optimized structure
416            // In a real implementation, we'd need lock-free or per-channel processors
417            let result = self.process(&channel_audio).await?;
418            tasks.push(result);
419        }
420
421        // Merge results from all channels
422        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        // Aggregate statistics from all channels
427        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        // Take average statistics from all channels
435        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    /// Helper method to merge channel processing results with SIMD optimization
481    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        // Use SIMD-optimized interleaving when available
495        if channels == 2 && samples_per_channel >= 8 {
496            self.interleave_stereo_simd(channel_results, &mut merged_samples)?;
497        } else {
498            // Fallback to scalar interleaving
499            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); // Silence for missing samples
507                        }
508                    } else {
509                        merged_samples.push(0.0); // Silence for missing channels
510                    }
511                }
512            }
513        }
514
515        Ok(merged_samples)
516    }
517
518    /// SIMD-optimized stereo interleaving
519    #[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            // Fallback to scalar
548            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    /// AVX2 stereo interleaving implementation
558    #[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; // Process 8 samples at a time
565
566        for i in (0..simd_len).step_by(8) {
567            // Load 8 samples from each channel
568            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            // Interleave using unpack operations
572            let low_interleaved = _mm256_unpacklo_ps(left_vec, right_vec);
573            let high_interleaved = _mm256_unpackhi_ps(left_vec, right_vec);
574
575            // Store interleaved results
576            _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        // Handle remaining samples
581        for i in simd_len..len {
582            output[i * 2] = left[i];
583            output[i * 2 + 1] = right[i];
584        }
585    }
586
587    /// SSE2 stereo interleaving implementation
588    #[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; // Process 4 samples at a time
595
596        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    /// ARM NEON stereo interleaving
614    #[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            // Fallback to scalar
639            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    /// NEON stereo interleaving implementation
649    #[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; // Process 4 samples at a time
656
657        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            // Interleave using zip operations
662            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    /// Other architectures fallback
675    #[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        // Scalar fallback
695        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    /// Extract real-time features from audio
704    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    /// Get configuration
726    #[must_use]
727    pub fn config(&self) -> &AudioPreprocessingConfig {
728        &self.config
729    }
730
731    /// Reset internal state (useful for streaming)
732    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        // Reset methods not implemented for bandwidth extender and feature extractor yet
764        // These processors don't have stateful reset methods currently
765        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        // Create test audio buffer
798        let samples = vec![0.1f32; 16000]; // 1 second of test audio
799        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        // Arc mutability issues are now fixed - processing should work
806        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        // Create test stereo data
840        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); // 8 samples * 2 channels
874
875        // Verify interleaving: L0, R0, L1, R1, ...
876        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, // Smaller buffer for memory efficiency
893            advanced_spectral_config: None,
894            adaptive_config: None,
895        };
896
897        let mut preprocessor = AudioPreprocessor::new(config).unwrap();
898
899        // Test with large audio buffer to verify memory efficiency
900        let large_samples = vec![0.1f32; 48000]; // 3 seconds at 16kHz
901        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        // Create stereo audio for parallel processing test
917        let samples_per_channel = 16000; // 1 second at 16kHz
918        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        // Verify the output has correct dimensions
934        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}