1use anyhow::{anyhow, Result};
2use rubato::audioadapter_buffers::direct::InterleavedSlice;
3use rubato::{
4 Async, FixedAsync, Resampler, SincInterpolationParameters, SincInterpolationType,
5 WindowFunction,
6};
7use std::fs::File;
8use std::path::Path;
9use symphonia::core::codecs::audio::AudioDecoderOptions;
10use symphonia::core::errors::Error;
11use symphonia::core::formats::probe::Hint;
12use symphonia::core::formats::{FormatOptions, TrackType};
13use symphonia::core::io::MediaSourceStream;
14use symphonia::core::meta::MetadataOptions;
15
16pub struct AudioData {
18 pub samples: Vec<f32>,
19 pub sample_rate: u32,
20}
21
22pub fn load_audio(path: &Path, target_sr: u32) -> Result<AudioData> {
28 let (samples, source_sr, channels) = decode(path)?;
29 let mono = to_mono(&samples, channels);
30 let resampled = resample(mono, source_sr, target_sr)?;
31
32 Ok(AudioData {
33 samples: resampled,
34 sample_rate: target_sr,
35 })
36}
37
38fn decode(path: &Path) -> Result<(Vec<f32>, u32, usize)> {
41 let src = File::open(path)?;
42 let mss = MediaSourceStream::new(Box::new(src), Default::default());
43
44 let mut hint = Hint::new();
46 if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
47 hint.with_extension(ext);
48 }
49
50 let mut format = symphonia::default::get_probe().probe(
51 &hint,
52 mss,
53 FormatOptions::default(),
54 MetadataOptions::default(),
55 )?;
56
57 let track = format
58 .first_track_known_codec(TrackType::Audio)
59 .ok_or_else(|| anyhow!("no supported audio tracks"))?;
60 let track_id = track.id;
61 let codec_params = track
62 .codec_params
63 .as_ref()
64 .and_then(|p| p.audio())
65 .ok_or_else(|| anyhow!("audio track missing codec parameters"))?;
66
67 let mut decoder = symphonia::default::get_codecs()
68 .make_audio_decoder(codec_params, &AudioDecoderOptions::default())?;
69
70 let mut samples: Vec<f32> = Vec::new();
71 let mut scratch: Vec<f32> = Vec::new();
72 let mut source_sr = 0u32;
73 let mut channels = 0usize;
74
75 while let Some(packet) = format.next_packet()? {
77 if packet.track_id != track_id {
78 continue;
79 }
80
81 match decoder.decode(&packet) {
82 Ok(decoded) => {
83 let spec = decoded.spec();
84 source_sr = spec.rate();
85 channels = spec.channels().count();
86
87 decoded.copy_to_vec_interleaved(&mut scratch);
90 samples.extend_from_slice(&scratch);
91 }
92 Err(Error::DecodeError(_)) => (), Err(err) => return Err(anyhow!(err)),
94 }
95 }
96
97 if source_sr == 0 {
98 return Err(anyhow!("failed to decode any audio packets"));
99 }
100
101 Ok((samples, source_sr, channels))
102}
103
104fn to_mono(samples: &[f32], channels: usize) -> Vec<f32> {
106 if channels == 1 {
107 return samples.to_vec();
108 }
109 samples
110 .chunks(channels)
111 .map(|frame| frame.iter().sum::<f32>() / channels as f32)
112 .collect()
113}
114
115pub fn resample(samples: Vec<f32>, source_sr: u32, target_sr: u32) -> Result<Vec<f32>> {
118 if source_sr == target_sr {
119 return Ok(samples);
120 }
121
122 let params = SincInterpolationParameters {
123 sinc_len: 256,
124 f_cutoff: 0.95,
125 interpolation: SincInterpolationType::Linear,
126 oversampling_factor: 256,
127 window: WindowFunction::BlackmanHarris2,
128 };
129
130 let frames = samples.len();
134 let mut resampler = Async::<f32>::new_sinc(
135 target_sr as f64 / source_sr as f64,
136 2.0,
137 ¶ms,
138 frames,
139 1, FixedAsync::Input,
141 )?;
142
143 let input = InterleavedSlice::new(&samples, 1, frames)
145 .map_err(|e| anyhow!("resampler input adapter: {e:?}"))?;
146 let output = resampler.process(&input, 0, None)?;
147 Ok(output.take_data())
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_to_mono_passthrough() {
156 let samples = vec![1.0, 2.0, 3.0];
157 let mono = to_mono(&samples, 1);
158 assert_eq!(mono, vec![1.0, 2.0, 3.0]);
159 }
160
161 #[test]
162 fn test_to_mono_stereo() {
163 let samples = vec![0.5, 1.5, 1.0, 3.0];
165 let mono = to_mono(&samples, 2);
166 assert_eq!(mono, vec![1.0, 2.0]);
167 }
168
169 #[test]
170 fn test_resample_identity() {
171 let samples = vec![1.0, 2.0, 3.0, 4.0];
173 let result = resample(samples.clone(), 22050, 22050).unwrap();
174 assert_eq!(result, samples);
175 }
176}