voice_engine/media/codecs/
resample.rs

1use crate::media::{PcmBuf, Sample};
2use anyhow::Result;
3use rubato::{FftFixedOut, Resampler};
4
5pub struct LinearResampler {
6    resampler: FftFixedOut<f64>,
7    input_sample_rate: usize,
8    output_sample_rate: usize,
9    input_chunk_size: usize,
10}
11
12impl LinearResampler {
13    pub fn new(input_sample_rate: usize, output_sample_rate: usize) -> Result<Self> {
14        let rate = output_sample_rate as f64 / input_sample_rate as f64;
15        let input_chunk_size = match input_sample_rate {
16            8000 => 160,
17            16000 => 320,
18            44100 => 882,
19            48000 => 960,
20            _ => (input_sample_rate as f64 / 50.0).ceil() as usize,
21        };
22        let output_chunk_size = (input_chunk_size as f64 * rate) as usize;
23        let resampler = FftFixedOut::<f64>::new(
24            input_sample_rate,
25            output_sample_rate,
26            output_chunk_size,
27            1,
28            1,
29        )?;
30        Ok(Self {
31            resampler,
32            input_sample_rate,
33            output_sample_rate,
34            input_chunk_size,
35        })
36    }
37
38    pub fn resample(&mut self, input: &[Sample]) -> PcmBuf {
39        if self.input_sample_rate == self.output_sample_rate {
40            return input.to_vec();
41        }
42
43        let mut input_f64 = Vec::with_capacity(input.len());
44        for sample in input {
45            input_f64.push(*sample as f64 / i16::MAX as f64);
46        }
47
48        let channel_data = input_f64.chunks(self.input_chunk_size).collect::<Vec<_>>();
49        let resampled = match self.resampler.process(&channel_data, None) {
50            Ok(res) => res,
51            Err(_) => {
52                return input.to_vec();
53            }
54        };
55
56        let mut result = Vec::with_capacity(resampled[0].len());
57        for sample in resampled[0].iter() {
58            result.push((sample * i16::MAX as f64) as i16);
59        }
60
61        result
62    }
63}
64
65pub fn resample_mono(input: &[Sample], input_sample_rate: u32, output_sample_rate: u32) -> PcmBuf {
66    if input_sample_rate == output_sample_rate {
67        return input.to_vec();
68    }
69    let mut resampler =
70        match LinearResampler::new(input_sample_rate as usize, output_sample_rate as usize) {
71            Ok(resampler) => resampler,
72            Err(_) => {
73                return input.to_vec();
74            }
75        };
76
77    let mut result =
78        Vec::with_capacity(input.len() * output_sample_rate as usize / input_sample_rate as usize);
79    for chunk in input.chunks(resampler.input_chunk_size) {
80        let resampled = resampler.resample(chunk);
81        result.extend_from_slice(&resampled);
82    }
83    result
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::media::codecs::samples_to_bytes;
90    use crate::media::track::file::read_wav_file;
91    use std::fs::File;
92    use std::io::Write;
93
94    #[test]
95    fn test_resampler() {
96        let (all_samples, samplerate) = read_wav_file("fixtures/sample.wav").unwrap();
97        let frame_samples = all_samples[0..640].to_vec();
98        let frame_resampled = resample_mono(&frame_samples, samplerate, 8000);
99        assert_eq!(frame_resampled.len(), 320);
100
101        let resampled = resample_mono(&all_samples, samplerate, 8000);
102        let mut file = File::create("fixtures/sample.8k.decoded").expect("Failed to create file");
103        let decoded_bytes = samples_to_bytes(&resampled);
104        file.write_all(&decoded_bytes)
105            .expect("Failed to write file");
106        let rate = resampled.len() as f64 / all_samples.len() as f64;
107        println!(
108            "resampled {}->{} samples, rate: {}",
109            all_samples.len(),
110            resampled.len(),
111            rate
112        );
113        println!("ffplay -f s16le -ar 8000 -i fixtures/sample.8k.decoded");
114    }
115}