1use std::fs::File;
32use std::io::BufReader;
33use std::path::{Path, PathBuf};
34
35use audio_core_bsd::AudioFrame;
36use hound::{SampleFormat, WavReader, WavSpec};
37
38use crate::decoder::{ContainerDecoder, FormatKind, StreamInfo};
39use crate::error::{CodecError, Result};
40
41const FRAMES_PER_CHUNK: usize = 1024;
45
46pub struct WavDecoder {
52 path: PathBuf,
55 reader: WavReader<BufReader<File>>,
57 spec: WavSpec,
59}
60
61impl WavDecoder {
62 pub fn open(path: &Path) -> Result<Self> {
74 let reader = WavReader::open(path).map_err(|e| CodecError::Io(e.to_string()))?;
75 let spec = reader.spec();
76 Ok(Self {
77 path: path.to_path_buf(),
78 reader,
79 spec,
80 })
81 }
82
83 fn reopen(&mut self, path: &Path) -> Result<()> {
85 let reader = WavReader::open(path).map_err(|e| CodecError::Io(e.to_string()))?;
86 self.spec = reader.spec();
87 self.reader = reader;
88 self.path = path.to_path_buf();
89 Ok(())
90 }
91
92 fn stream_info(&self) -> Result<StreamInfo> {
94 if self.spec.channels == 0 {
95 return Err(CodecError::InvalidChannelCount(0));
96 }
97 if self.spec.sample_rate == 0 {
98 return Err(CodecError::InvalidSampleRate(0));
99 }
100 let total_frames = u64::from(self.reader.duration());
103 Ok(StreamInfo {
104 format: FormatKind::Wav,
105 sample_rate: self.spec.sample_rate,
106 channels: self.spec.channels,
107 bits_per_sample: u32::from(self.spec.bits_per_sample),
108 total_frames: Some(total_frames),
109 })
110 }
111
112 #[allow(clippy::cast_precision_loss)]
116 fn norm_int(sample: i32, bits: u16) -> f32 {
117 let denom = (1u64 << (bits - 1)) as f32;
119 let v = sample as f32 / denom;
120 if v.is_finite() {
121 v
122 } else {
123 0.0
124 }
125 }
126
127 fn norm_float(sample: f32) -> f32 {
129 if sample.is_finite() {
130 sample
131 } else {
132 0.0
133 }
134 }
135
136 fn read_chunk(&mut self) -> Result<Option<AudioFrame>> {
140 let channels = usize::from(self.spec.channels);
141 let bits = self.spec.bits_per_sample;
142 let want = FRAMES_PER_CHUNK.checked_mul(channels).unwrap_or(0);
143
144 let mut inter: Vec<f32> = Vec::with_capacity(want);
145 match self.spec.sample_format {
146 SampleFormat::Int => {
147 let mut it = self.reader.samples::<i32>();
148 for _ in 0..want {
149 match it.next() {
150 Some(Ok(s)) => inter.push(Self::norm_int(s, bits)),
151 Some(Err(e)) => return Err(CodecError::Decode(e.to_string())),
152 None => break,
153 }
154 }
155 }
156 SampleFormat::Float => {
157 let mut it = self.reader.samples::<f32>();
158 for _ in 0..want {
159 match it.next() {
160 Some(Ok(s)) => inter.push(Self::norm_float(s)),
161 Some(Err(e)) => return Err(CodecError::Decode(e.to_string())),
162 None => break,
163 }
164 }
165 }
166 }
167
168 if inter.is_empty() {
169 return Ok(None);
170 }
171 if channels == 0 {
172 return Err(CodecError::InvalidChannelCount(0));
173 }
174
175 let frames = inter.len() / channels;
176 let mut planar = vec![0.0_f32; channels * frames];
178 for i in 0..frames {
179 for c in 0..channels {
180 planar[c * frames + i] = inter[i * channels + c];
181 }
182 }
183
184 Ok(Some(AudioFrame::from_planar(
185 self.spec.channels,
186 self.spec.sample_rate,
187 planar,
188 )))
189 }
190}
191
192impl ContainerDecoder for WavDecoder {
193 fn open(&mut self, path: &Path) -> Result<StreamInfo> {
194 if path != self.path {
195 self.reopen(path)?;
196 }
197 self.stream_info()
198 }
199
200 fn next_frame(&mut self) -> Result<Option<AudioFrame>> {
201 self.read_chunk()
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use hound::{SampleFormat, WavSpec, WavWriter};
209 use std::path::PathBuf;
210
211 fn approx_eq(a: f32, b: f32) -> bool {
214 (a - b).abs() < 1e-4
215 }
216
217 const SCALE_16BIT: f32 = 1.0 / 32_768.0;
219
220 fn write_wav_16bit(
224 channels: u16,
225 sample_rate: u32,
226 interleaved: &[i16],
227 suffix: &str,
228 ) -> PathBuf {
229 let spec = WavSpec {
230 channels,
231 sample_rate,
232 bits_per_sample: 16,
233 sample_format: SampleFormat::Int,
234 };
235 let mut path = std::env::temp_dir();
236 path.push(format!(
237 "audio_codec_bsd_wav_test_{}_{}.wav",
238 std::process::id(),
239 suffix
240 ));
241 let mut writer = WavWriter::create(&path, spec).expect("create wav");
242 for &v in interleaved {
243 writer.write_sample(v).expect("write sample");
244 }
245 writer.finalize().expect("finalize wav");
246 path
247 }
248
249 #[test]
255 fn stereo_deinterleave_recovers_independent_channels() {
256 const FRAMES: usize = 8;
260 let want_ch0: [i16; FRAMES] = [-3500, -2500, -1500, -500, 500, 1500, 2500, 3500];
261 let want_ch1: [i16; FRAMES] = [16_000; FRAMES];
262
263 let mut interleaved: Vec<i16> = Vec::with_capacity(FRAMES * 2);
264 for i in 0..FRAMES {
265 interleaved.push(want_ch0[i]);
266 interleaved.push(want_ch1[i]);
267 }
268 let path = write_wav_16bit(2, 48_000, &interleaved, "stereo_deinterleave");
269
270 let mut dec = WavDecoder::open(&path).expect("open");
271 let info = dec.open(&path).expect("trait open");
272 assert_eq!(info.format, FormatKind::Wav);
273 assert_eq!(info.channels, 2);
274 assert_eq!(info.sample_rate, 48_000);
275 assert_eq!(info.bits_per_sample, 16);
276
277 let frame = dec.next_frame().expect("decode frame").expect("some frame");
278 assert_eq!(frame.channels, 2);
279 assert_eq!(frame.samples.len(), FRAMES * 2);
281
282 let ch0 = frame.channel_slice(0);
283 let ch1 = frame.channel_slice(1);
284 assert_ne!(ch0, ch1);
286 for (i, &want) in want_ch0.iter().enumerate() {
288 let expected = f32::from(want) * SCALE_16BIT;
289 assert!(
290 approx_eq(ch0[i], expected),
291 "ch0[{i}] = {}, expected {expected}",
292 ch0[i]
293 );
294 }
295 for (i, &want) in want_ch1.iter().enumerate() {
296 let expected = f32::from(want) * SCALE_16BIT;
297 assert!(
298 approx_eq(ch1[i], expected),
299 "ch1[{i}] = {}, expected {expected}",
300 ch1[i]
301 );
302 }
303
304 let _ = std::fs::remove_file(&path);
305 }
306
307 #[test]
310 fn mono_round_trip_values_within_tolerance() {
311 let interleaved: [i16; 6] = [0, 4_096, 8_192, 16_384, -8_192, -16_384];
312 let path = write_wav_16bit(1, 44_100, &interleaved, "mono_roundtrip");
313
314 let mut dec = WavDecoder::open(&path).expect("open");
315 let _info = dec.open(&path).expect("trait open");
316 let frame = dec.next_frame().expect("decode").expect("some");
317 assert_eq!(frame.channels, 1);
318 let ch0 = frame.channel_slice(0);
319 assert_eq!(ch0.len(), interleaved.len());
320 for (i, &want) in interleaved.iter().enumerate() {
321 let expected = f32::from(want) * SCALE_16BIT;
322 assert!(
323 approx_eq(ch0[i], expected),
324 "mono[{i}] = {}, expected {expected}",
325 ch0[i]
326 );
327 }
328 let _ = std::fs::remove_file(&path);
329 }
330
331 #[test]
333 fn eof_returns_none_after_drain() {
334 let interleaved: [i16; 3] = [100, 200, 300];
335 let path = write_wav_16bit(1, 48_000, &interleaved, "eof");
336
337 let mut dec = WavDecoder::open(&path).expect("open");
338 let _info = dec.open(&path).expect("trait open");
339 let first = dec.next_frame().expect("first").expect("some data");
341 assert!(!first.samples.is_empty());
342 let next = dec.next_frame().expect("second call is ok");
344 assert!(next.is_none(), "expected EOF, got {next:?}");
345
346 let _ = std::fs::remove_file(&path);
347 }
348}