1use crate::{core::AudioFeatures, Error, Result};
4use scirs2_core::Complex;
5use scirs2_fft::RealFftPlanner;
6use tracing::{debug, trace};
7
8#[derive(Debug, Clone)]
10pub struct AudioBuffer {
11 pub samples: Vec<f32>,
13 pub sample_rate: u32,
15 pub capacity: usize,
17 write_pos: usize,
19 ring_buffer: bool,
21}
22
23impl AudioBuffer {
24 pub fn new(capacity: usize, sample_rate: u32) -> Self {
26 Self {
27 samples: vec![0.0; capacity],
28 sample_rate,
29 capacity,
30 write_pos: 0,
31 ring_buffer: false,
32 }
33 }
34
35 pub fn new_ring_buffer(capacity: usize, sample_rate: u32) -> Self {
37 let mut buffer = Self::new(capacity, sample_rate);
38 buffer.ring_buffer = true;
39 buffer
40 }
41
42 pub fn push_samples(&mut self, samples: &[f32]) -> Result<()> {
44 if self.ring_buffer {
45 for &sample in samples {
46 self.samples[self.write_pos] = sample;
47 self.write_pos = (self.write_pos + 1) % self.capacity;
48 }
49 } else {
50 if self.samples.len() + samples.len() > self.capacity {
51 return Err(Error::buffer("Buffer overflow".to_string()));
52 }
53 self.samples.extend_from_slice(samples);
54 }
55 Ok(())
56 }
57
58 pub fn drain(&mut self) -> Vec<f32> {
60 if self.ring_buffer {
61 let mut result = Vec::with_capacity(self.capacity);
62 for i in 0..self.capacity {
63 let idx = (self.write_pos + i) % self.capacity;
64 result.push(self.samples[idx]);
65 self.samples[idx] = 0.0;
66 }
67 result
68 } else {
69 std::mem::take(&mut self.samples)
70 }
71 }
72
73 pub fn level(&self) -> f32 {
75 if self.ring_buffer {
76 1.0 } else {
78 self.samples.len() as f32 / self.capacity as f32
79 }
80 }
81
82 pub fn clear(&mut self) {
84 if self.ring_buffer {
85 self.samples.fill(0.0);
86 self.write_pos = 0;
87 } else {
88 self.samples.clear();
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct ProcessingPipeline {
96 pub stages: Vec<ProcessingStage>,
98 pub config: PipelineConfig,
100}
101
102#[derive(Debug, Clone)]
104pub struct PipelineConfig {
105 pub parallel: bool,
107 pub max_concurrent: usize,
109 pub enable_caching: bool,
111}
112
113impl Default for PipelineConfig {
114 fn default() -> Self {
115 Self {
116 parallel: true,
117 max_concurrent: 4,
118 enable_caching: true,
119 }
120 }
121}
122
123impl ProcessingPipeline {
124 pub fn new() -> Self {
126 Self {
127 stages: Vec::new(),
128 config: PipelineConfig::default(),
129 }
130 }
131
132 pub fn with_config(config: PipelineConfig) -> Self {
134 Self {
135 stages: Vec::new(),
136 config,
137 }
138 }
139
140 pub fn add_stage(&mut self, stage: ProcessingStage) {
142 self.stages.push(stage);
143 }
144
145 pub async fn process(&self, input: &[f32]) -> Result<Vec<f32>> {
147 let mut output = input.to_vec();
148
149 if self.config.parallel && self.stages.len() > 1 {
150 for stage in &self.stages {
152 if stage.can_run_parallel() {
153 output = stage.process(&output).await?;
154 }
155 }
156 } else {
157 for stage in &self.stages {
159 output = stage.process(&output).await?;
160 }
161 }
162
163 Ok(output)
164 }
165
166 pub fn estimated_latency_ms(&self, sample_rate: u32) -> f32 {
168 self.stages
169 .iter()
170 .map(|stage| stage.estimated_latency_ms(sample_rate))
171 .sum()
172 }
173}
174
175impl Default for ProcessingPipeline {
176 fn default() -> Self {
177 Self::new()
178 }
179}
180
181#[derive(Debug, Clone)]
183pub struct ProcessingStage {
184 pub name: String,
186 pub stage_type: StageType,
188 pub parameters: std::collections::HashMap<String, f32>,
190 pub parallel_capable: bool,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum StageType {
197 Normalize,
199 NoiseReduction,
201 Filter,
203 Resample,
205 Compression,
207 Custom(String),
209}
210
211impl ProcessingStage {
212 pub fn new(name: String, stage_type: StageType) -> Self {
214 Self {
215 name,
216 stage_type,
217 parameters: std::collections::HashMap::new(),
218 parallel_capable: true,
219 }
220 }
221
222 pub fn with_parameter(mut self, key: String, value: f32) -> Self {
224 self.parameters.insert(key, value);
225 self
226 }
227
228 pub fn with_parallel(mut self, parallel: bool) -> Self {
230 self.parallel_capable = parallel;
231 self
232 }
233
234 pub fn can_run_parallel(&self) -> bool {
236 self.parallel_capable
237 }
238
239 pub async fn process(&self, input: &[f32]) -> Result<Vec<f32>> {
241 trace!(
242 "Processing stage: {} with {} samples",
243 self.name,
244 input.len()
245 );
246
247 match self.stage_type {
248 StageType::Normalize => self.normalize(input),
249 StageType::NoiseReduction => self.noise_reduction(input),
250 StageType::Filter => self.filter(input),
251 StageType::Resample => self.resample(input),
252 StageType::Compression => self.compression(input),
253 StageType::Custom(_) => {
254 Ok(input.to_vec())
256 }
257 }
258 }
259
260 pub fn estimated_latency_ms(&self, _sample_rate: u32) -> f32 {
262 match self.stage_type {
263 StageType::Normalize => 0.1,
264 StageType::NoiseReduction => 2.0,
265 StageType::Filter => 0.5,
266 StageType::Resample => 1.0,
267 StageType::Compression => 0.3,
268 StageType::Custom(_) => 1.0,
269 }
270 }
271
272 fn normalize(&self, input: &[f32]) -> Result<Vec<f32>> {
275 if input.is_empty() {
276 return Ok(input.to_vec());
277 }
278
279 let max_val = input
280 .iter()
281 .map(|x| x.abs())
282 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
283 .unwrap_or(1.0);
284 if max_val == 0.0 {
285 return Ok(input.to_vec());
286 }
287
288 let target_level = self.parameters.get("target_level").copied().unwrap_or(0.9);
289 let scale = target_level / max_val;
290
291 Ok(input.iter().map(|x| x * scale).collect())
292 }
293
294 fn noise_reduction(&self, input: &[f32]) -> Result<Vec<f32>> {
295 let noise_threshold = self
296 .parameters
297 .get("noise_threshold")
298 .copied()
299 .unwrap_or(0.01);
300
301 Ok(input
302 .iter()
303 .map(|&x| {
304 if x.abs() < noise_threshold {
305 x * 0.1 } else {
307 x
308 }
309 })
310 .collect())
311 }
312
313 fn filter(&self, input: &[f32]) -> Result<Vec<f32>> {
314 let cutoff = self.parameters.get("cutoff").copied().unwrap_or(0.5);
315
316 let mut output = Vec::with_capacity(input.len());
318 let mut prev = 0.0;
319
320 for &sample in input {
321 let filtered = prev + cutoff * (sample - prev);
322 output.push(filtered);
323 prev = filtered;
324 }
325
326 Ok(output)
327 }
328
329 fn resample(&self, input: &[f32]) -> Result<Vec<f32>> {
330 let ratio = self.parameters.get("ratio").copied().unwrap_or(1.0);
331
332 if ratio == 1.0 {
333 return Ok(input.to_vec());
334 }
335
336 let output_len = (input.len() as f32 * ratio) as usize;
337 let mut output = Vec::with_capacity(output_len);
338
339 for i in 0..output_len {
340 let src_idx = (i as f32 / ratio) as usize;
341 if src_idx < input.len() {
342 output.push(input[src_idx]);
343 } else {
344 output.push(0.0);
345 }
346 }
347
348 Ok(output)
349 }
350
351 fn compression(&self, input: &[f32]) -> Result<Vec<f32>> {
352 let ratio = self.parameters.get("ratio").copied().unwrap_or(4.0);
353 let threshold = self.parameters.get("threshold").copied().unwrap_or(0.7);
354
355 Ok(input
356 .iter()
357 .map(|&x| {
358 let abs_x = x.abs();
359 if abs_x > threshold {
360 let excess = abs_x - threshold;
361 let compressed_excess = excess / ratio;
362 let sign = if x >= 0.0 { 1.0 } else { -1.0 };
363 sign * (threshold + compressed_excess)
364 } else {
365 x
366 }
367 })
368 .collect())
369 }
370}
371
372pub struct FeatureExtractor {
374 sample_rate: u32,
376 #[allow(dead_code)]
378 fft_planner: RealFftPlanner<f32>,
379 cache: std::collections::HashMap<String, AudioFeatures>,
381}
382
383impl std::fmt::Debug for FeatureExtractor {
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385 f.debug_struct("FeatureExtractor")
386 .field("sample_rate", &self.sample_rate)
387 .field("cache", &self.cache)
388 .finish()
389 }
390}
391
392impl FeatureExtractor {
393 pub fn new(sample_rate: u32) -> Self {
395 Self {
396 sample_rate,
397 fft_planner: RealFftPlanner::<f32>::new(),
398 cache: std::collections::HashMap::new(),
399 }
400 }
401
402 pub async fn extract_features(&self, audio: &[f32], sample_rate: u32) -> Result<AudioFeatures> {
404 debug!(
405 "Extracting features from {} samples at {} Hz",
406 audio.len(),
407 sample_rate
408 );
409
410 let processed_audio = if sample_rate != self.sample_rate {
412 self.resample_audio(audio, sample_rate, self.sample_rate)?
413 } else {
414 audio.to_vec()
415 };
416
417 let spectral = self.extract_spectral_features(&processed_audio)?;
419 let temporal = self.extract_temporal_features(&processed_audio)?;
420 let prosodic = self.extract_prosodic_features(&processed_audio)?;
421 let speaker_embedding = None; Ok(AudioFeatures {
424 spectral,
425 temporal,
426 prosodic,
427 speaker_embedding,
428 quality: Vec::new(), formants: Vec::new(), harmonics: Vec::new(), })
432 }
433
434 fn extract_spectral_features(&self, audio: &[f32]) -> Result<Vec<f32>> {
436 let mut features = Vec::new();
437
438 let window_size = 1024;
440 let hop_size = 512;
441
442 if audio.len() < window_size {
443 return Ok(vec![0.0; 13]); }
445
446 let mut spectral_centroids = Vec::new();
448 let mut spectral_rolloffs = Vec::new();
449 let mut mfccs = Vec::new();
450
451 for window_start in (0..audio.len() - window_size).step_by(hop_size) {
452 let window = &audio[window_start..window_start + window_size];
453
454 let windowed: Vec<f32> = window
456 .iter()
457 .enumerate()
458 .map(|(i, &x)| {
459 let hann = 0.5
460 - 0.5
461 * (2.0 * std::f32::consts::PI * i as f32 / (window_size - 1) as f32)
462 .cos();
463 x * hann
464 })
465 .collect();
466
467 let spectrum = self.compute_fft(&windowed)?;
469
470 spectral_centroids.push(self.compute_spectral_centroid(&spectrum));
472 spectral_rolloffs.push(self.compute_spectral_rolloff(&spectrum, 0.85));
473
474 let mel_spectrum = self.compute_mel_spectrum(&spectrum, 13);
476 mfccs.extend(mel_spectrum);
477 }
478
479 features.push(self.mean(&spectral_centroids)); features.push(self.std(&spectral_centroids)); features.push(self.mean(&spectral_rolloffs)); features.push(self.std(&spectral_rolloffs)); if !mfccs.is_empty() {
487 let chunk_size = 13;
488 for i in 0..chunk_size {
489 let coeff_values: Vec<f32> =
490 mfccs.iter().skip(i).step_by(chunk_size).copied().collect();
491 features.push(self.mean(&coeff_values));
492 }
493 } else {
494 features.extend(vec![0.0; 13]);
495 }
496
497 Ok(features)
498 }
499
500 fn extract_temporal_features(&self, audio: &[f32]) -> Result<Vec<f32>> {
502 let mut features = Vec::new();
503
504 let rms = (audio.iter().map(|x| x * x).sum::<f32>() / audio.len() as f32).sqrt();
506 features.push(rms);
507
508 let zcr = audio
510 .windows(2)
511 .filter(|w| (w[0] > 0.0) != (w[1] > 0.0))
512 .count() as f32
513 / (audio.len() - 1) as f32;
514 features.push(zcr);
515
516 let frame_size = self.sample_rate as usize / 100; let mut energy_contour = Vec::new();
519
520 for chunk in audio.chunks(frame_size) {
521 let energy = chunk.iter().map(|x| x * x).sum::<f32>() / chunk.len() as f32;
522 energy_contour.push(energy.sqrt());
523 }
524
525 features.push(self.mean(&energy_contour));
526 features.push(self.std(&energy_contour));
527
528 let spectral_flux = self.compute_spectral_flux(audio)?;
530 features.push(spectral_flux);
531
532 Ok(features)
533 }
534
535 fn extract_prosodic_features(&self, audio: &[f32]) -> Result<Vec<f32>> {
537 let mut features = Vec::new();
538
539 let f0_values = self.estimate_f0_contour(audio)?;
541
542 if !f0_values.is_empty() {
543 features.push(self.mean(&f0_values)); features.push(self.std(&f0_values)); features.push(f0_values.iter().copied().reduce(f32::max).unwrap_or(0.0)); features.push(f0_values.iter().copied().reduce(f32::min).unwrap_or(0.0));
547 } else {
549 features.extend(vec![0.0; 4]);
550 }
551
552 let intensity_values = self.compute_intensity_contour(audio);
554 features.push(self.mean(&intensity_values));
555 features.push(self.std(&intensity_values));
556
557 let speaking_rate = self.estimate_speaking_rate(audio)?;
559 features.push(speaking_rate);
560
561 Ok(features)
562 }
563
564 fn resample_audio(&self, audio: &[f32], from_rate: u32, to_rate: u32) -> Result<Vec<f32>> {
567 if from_rate == to_rate {
568 return Ok(audio.to_vec());
569 }
570
571 let ratio = to_rate as f32 / from_rate as f32;
572 let output_len = (audio.len() as f32 * ratio) as usize;
573 let mut output = Vec::with_capacity(output_len);
574
575 for i in 0..output_len {
576 let src_idx = i as f32 / ratio;
577 let idx = src_idx as usize;
578
579 if idx + 1 < audio.len() {
580 let frac = src_idx - idx as f32;
582 let sample = audio[idx] * (1.0 - frac) + audio[idx + 1] * frac;
583 output.push(sample);
584 } else if idx < audio.len() {
585 output.push(audio[idx]);
586 } else {
587 output.push(0.0);
588 }
589 }
590
591 Ok(output)
592 }
593
594 fn compute_fft(&self, audio: &[f32]) -> Result<Vec<f32>> {
595 let mut planner = RealFftPlanner::<f32>::new();
596 let fft = planner.plan_fft_forward(audio.len());
597
598 let input = audio.to_vec();
599 let mut output = vec![Complex::new(0.0, 0.0); audio.len() / 2 + 1];
600
601 fft.process(&input, &mut output);
602
603 Ok(output.iter().map(|c| c.norm()).collect())
604 }
605
606 fn compute_spectral_centroid(&self, spectrum: &[f32]) -> f32 {
607 let mut weighted_sum = 0.0;
608 let mut magnitude_sum = 0.0;
609
610 for (i, &magnitude) in spectrum.iter().enumerate() {
611 let freq = i as f32 * self.sample_rate as f32 / (2.0 * spectrum.len() as f32);
612 weighted_sum += freq * magnitude;
613 magnitude_sum += magnitude;
614 }
615
616 if magnitude_sum > 0.0 {
617 weighted_sum / magnitude_sum
618 } else {
619 0.0
620 }
621 }
622
623 fn compute_spectral_rolloff(&self, spectrum: &[f32], rolloff_point: f32) -> f32 {
624 let total_energy: f32 = spectrum.iter().map(|x| x * x).sum();
625 let target_energy = total_energy * rolloff_point;
626
627 let mut cumulative_energy = 0.0;
628 for (i, &magnitude) in spectrum.iter().enumerate() {
629 cumulative_energy += magnitude * magnitude;
630 if cumulative_energy >= target_energy {
631 return i as f32 * self.sample_rate as f32 / (2.0 * spectrum.len() as f32);
632 }
633 }
634
635 (spectrum.len() - 1) as f32 * self.sample_rate as f32 / (2.0 * spectrum.len() as f32)
636 }
637
638 fn compute_mel_spectrum(&self, spectrum: &[f32], num_coeffs: usize) -> Vec<f32> {
639 let mut mel_spectrum = vec![0.0; num_coeffs];
641 let mel_low = self.hz_to_mel(0.0);
642 let mel_high = self.hz_to_mel(self.sample_rate as f32 / 2.0);
643
644 for (i, mel_value) in mel_spectrum.iter_mut().enumerate().take(num_coeffs) {
645 let mel_center = mel_low + (mel_high - mel_low) * i as f32 / (num_coeffs - 1) as f32;
646 let hz_center = self.mel_to_hz(mel_center);
647 let bin_center = hz_center * spectrum.len() as f32 * 2.0 / self.sample_rate as f32;
648
649 let start_bin = (bin_center - 1.0).max(0.0) as usize;
650 let end_bin = ((bin_center + 1.0) as usize).min(spectrum.len() - 1);
651
652 for j in start_bin..=end_bin {
653 if j < spectrum.len() {
654 *mel_value += spectrum[j];
655 }
656 }
657 }
658
659 self.apply_dct(&mel_spectrum)
661 }
662
663 fn hz_to_mel(&self, hz: f32) -> f32 {
664 2595.0 * (1.0 + hz / 700.0).log10()
665 }
666
667 fn mel_to_hz(&self, mel: f32) -> f32 {
668 700.0 * (10.0_f32.powf(mel / 2595.0) - 1.0)
669 }
670
671 fn apply_dct(&self, input: &[f32]) -> Vec<f32> {
672 let n = input.len();
673 let mut output = vec![0.0; n];
674
675 for (k, output_value) in output.iter_mut().enumerate().take(n) {
676 let mut sum = 0.0;
677 for (i, &input_value) in input.iter().enumerate().take(n) {
678 sum += input_value
679 * (std::f32::consts::PI * k as f32 * (i as f32 + 0.5) / n as f32).cos();
680 }
681 *output_value = sum;
682 }
683
684 output
685 }
686
687 fn compute_spectral_flux(&self, audio: &[f32]) -> Result<f32> {
688 let window_size = 1024;
690 let hop_size = 512;
691
692 if audio.len() < window_size * 2 {
693 return Ok(0.0);
694 }
695
696 let mut flux_values = Vec::new();
697
698 for i in (hop_size..audio.len() - window_size).step_by(hop_size) {
699 let window1 = &audio[i - hop_size..i - hop_size + window_size];
700 let window2 = &audio[i..i + window_size];
701
702 let spectrum1 = self.compute_fft(window1)?;
703 let spectrum2 = self.compute_fft(window2)?;
704
705 let flux: f32 = spectrum1
706 .iter()
707 .zip(spectrum2.iter())
708 .map(|(s1, s2)| (s2 - s1).max(0.0))
709 .sum();
710
711 flux_values.push(flux);
712 }
713
714 Ok(self.mean(&flux_values))
715 }
716
717 fn estimate_f0_contour(&self, audio: &[f32]) -> Result<Vec<f32>> {
718 let frame_size = self.sample_rate as usize / 100; let mut f0_values = Vec::new();
720
721 for chunk in audio.chunks(frame_size) {
722 if chunk.len() < frame_size / 2 {
723 continue;
724 }
725
726 let f0 = self.estimate_f0_autocorrelation(chunk);
727 f0_values.push(f0);
728 }
729
730 Ok(f0_values)
731 }
732
733 fn estimate_f0_autocorrelation(&self, frame: &[f32]) -> f32 {
734 let min_period = self.sample_rate / 500; let max_period = self.sample_rate / 50; let mut max_correlation = 0.0;
738 let mut best_period = min_period;
739
740 for period in min_period..max_period.min(frame.len() as u32 / 2) {
741 let mut correlation = 0.0;
742 let period_samples = period as usize;
743
744 for i in 0..(frame.len() - period_samples) {
745 correlation += frame[i] * frame[i + period_samples];
746 }
747
748 if correlation > max_correlation {
749 max_correlation = correlation;
750 best_period = period;
751 }
752 }
753
754 if max_correlation > 0.0 {
755 self.sample_rate as f32 / best_period as f32
756 } else {
757 0.0
758 }
759 }
760
761 fn compute_intensity_contour(&self, audio: &[f32]) -> Vec<f32> {
762 let frame_size = self.sample_rate as usize / 100; let mut intensity_values = Vec::new();
764
765 for chunk in audio.chunks(frame_size) {
766 let intensity = chunk.iter().map(|x| x * x).sum::<f32>() / chunk.len() as f32;
767 intensity_values.push(intensity.sqrt());
768 }
769
770 intensity_values
771 }
772
773 fn estimate_speaking_rate(&self, audio: &[f32]) -> Result<f32> {
774 let intensity = self.compute_intensity_contour(audio);
776 let threshold = self.mean(&intensity) * 1.2;
777
778 let mut peak_count = 0;
779 let mut in_peak = false;
780
781 for &value in &intensity {
782 if value > threshold && !in_peak {
783 peak_count += 1;
784 in_peak = true;
785 } else if value <= threshold {
786 in_peak = false;
787 }
788 }
789
790 let duration_seconds = audio.len() as f32 / self.sample_rate as f32;
791 Ok(peak_count as f32 / duration_seconds * 60.0) }
793
794 fn mean(&self, values: &[f32]) -> f32 {
795 if values.is_empty() {
796 0.0
797 } else {
798 values.iter().sum::<f32>() / values.len() as f32
799 }
800 }
801
802 fn std(&self, values: &[f32]) -> f32 {
803 if values.len() < 2 {
804 return 0.0;
805 }
806
807 let mean = self.mean(values);
808 let variance =
809 values.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / (values.len() - 1) as f32;
810
811 variance.sqrt()
812 }
813}
814
815#[derive(Debug)]
817pub struct SignalProcessor {
818 #[allow(dead_code)]
820 buffer_size: usize,
821 #[allow(dead_code)]
823 cache: std::collections::HashMap<String, Vec<f32>>,
824}
825
826impl SignalProcessor {
827 pub fn new(buffer_size: usize) -> Self {
829 Self {
830 buffer_size,
831 cache: std::collections::HashMap::new(),
832 }
833 }
834
835 pub fn normalize(&self, audio: &[f32]) -> Result<Vec<f32>> {
837 if audio.is_empty() {
838 return Ok(audio.to_vec());
839 }
840
841 let max_val = audio
842 .iter()
843 .map(|x| x.abs())
844 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
845 .unwrap_or(1.0);
846 if max_val == 0.0 {
847 return Ok(audio.to_vec());
848 }
849
850 let scale = 0.95 / max_val;
851 Ok(audio.iter().map(|x| x * scale).collect())
852 }
853
854 pub fn denoise(&self, audio: &[f32], _sample_rate: u32) -> Result<Vec<f32>> {
856 let noise_threshold = 0.02;
858
859 Ok(audio
860 .iter()
861 .map(|&x| {
862 if x.abs() < noise_threshold {
863 x * 0.1
864 } else {
865 x
866 }
867 })
868 .collect())
869 }
870
871 pub fn resample(&self, audio: &[f32], from_rate: u32, to_rate: u32) -> Result<Vec<f32>> {
873 if from_rate == to_rate {
874 return Ok(audio.to_vec());
875 }
876
877 let ratio = to_rate as f32 / from_rate as f32;
878 let output_len = (audio.len() as f32 * ratio) as usize;
879 let mut output = Vec::with_capacity(output_len);
880
881 for i in 0..output_len {
882 let src_idx = i as f32 / ratio;
883 let idx = src_idx as usize;
884
885 if idx + 1 < audio.len() {
886 let frac = src_idx - idx as f32;
887 let sample = audio[idx] * (1.0 - frac) + audio[idx + 1] * frac;
888 output.push(sample);
889 } else if idx < audio.len() {
890 output.push(audio[idx]);
891 } else {
892 output.push(0.0);
893 }
894 }
895
896 Ok(output)
897 }
898
899 pub fn smooth(&self, audio: &[f32]) -> Result<Vec<f32>> {
901 if audio.len() < 3 {
902 return Ok(audio.to_vec());
903 }
904
905 let mut output = Vec::with_capacity(audio.len());
906 output.push(audio[0]);
907
908 for i in 1..audio.len() - 1 {
909 let smoothed = (audio[i - 1] + 2.0 * audio[i] + audio[i + 1]) / 4.0;
910 output.push(smoothed);
911 }
912
913 output.push(audio[audio.len() - 1]);
914 Ok(output)
915 }
916
917 pub fn compress(&self, audio: &[f32], ratio: f32) -> Result<Vec<f32>> {
919 let threshold = 0.7;
920
921 Ok(audio
922 .iter()
923 .map(|&x| {
924 let abs_x = x.abs();
925 if abs_x > threshold {
926 let excess = abs_x - threshold;
927 let compressed_excess = excess / ratio;
928 let sign = if x >= 0.0 { 1.0 } else { -1.0 };
929 sign * (threshold + compressed_excess)
930 } else {
931 x
932 }
933 })
934 .collect())
935 }
936}