voice_engine/media/codecs/
resample.rs1use 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}