use std::fs::File;
use std::io::Read;
use std::path::Path;
use crate::decoder::ContainerDecoder;
use crate::error::{CodecError, Result};
use crate::symphonia_impl::SymphoniaDecoder;
use crate::wav::WavDecoder;
pub fn open(path: &Path) -> Result<Box<dyn ContainerDecoder>> {
let mut file = File::open(path).map_err(|e| CodecError::Io(e.to_string()))?;
let mut magic = [0u8; 12];
let n = file
.read(&mut magic)
.map_err(|e| CodecError::Io(e.to_string()))?;
let m = &magic[..n];
if m.len() >= 4 && &m[..4] == b"fLaC" {
let dec = SymphoniaDecoder::open(path)?;
return Ok(Box::new(dec));
}
if m.len() >= 12 && &m[..4] == b"RIFF" && &m[8..12] == b"WAVE" {
let dec = WavDecoder::open(path)?;
return Ok(Box::new(dec));
}
Err(CodecError::Format(format!(
"unrecognised magic bytes: {m:?}"
)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unknown_magic_yields_format_error() {
let mut path = std::env::temp_dir();
path.push(format!(
"audio_codec_bsd_format_unknown_{id}.bin",
id = std::process::id()
));
std::fs::write(&path, b"XYZW this is not audio").expect("write temp");
let res = open(&path);
let _ = std::fs::remove_file(&path);
assert!(
matches!(res, Err(CodecError::Format(_))),
"expected Format error"
);
}
#[test]
fn missing_file_yields_io_error() {
let path = std::path::Path::new("/no/such/audio/file.flac");
let res = open(path);
assert!(matches!(res, Err(CodecError::Io(_))), "expected Io error");
}
#[test]
fn riff_magic_routes_to_wav_decoder() {
let mut path = std::env::temp_dir();
path.push(format!(
"audio_codec_bsd_format_wav_{id}.wav",
id = std::process::id()
));
let spec = hound::WavSpec {
channels: 1,
sample_rate: 8000,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
let writer = hound::WavWriter::create(&path, spec).expect("create wav");
writer.finalize().expect("finalize empty wav");
let res = open(&path);
let _ = std::fs::remove_file(&path);
assert!(res.is_ok(), "expected routing to WavDecoder");
}
}