1use crate::{Audio, AudioFormat, DecodeOptions, EncodeOptions, Error, ErrorCode, Result};
2use std::io::Read;
3use std::path::Path;
4
5#[derive(Clone, Debug)]
8pub struct ReadAudioOptions {
9 pub max_encoded_bytes: usize,
11 pub decode: DecodeOptions,
13}
14impl Default for ReadAudioOptions {
15 fn default() -> Self {
16 Self {
17 max_encoded_bytes: 512 * 1024 * 1024,
18 decode: DecodeOptions::default(),
19 }
20 }
21}
22
23pub fn sniff(bytes: &[u8]) -> Option<AudioFormat> {
25 if bytes.len() < 4 {
26 return None;
27 }
28 match &bytes[..4] {
29 b"RIFF" | b"RF64" | b"BW64" if bytes.get(8..12) == Some(b"WAVE") => Some(AudioFormat::Wav),
30 b"FORM" if bytes.get(8..12) == Some(b"AIFF") || bytes.get(8..12) == Some(b"AIFC") => {
31 Some(AudioFormat::Aiff)
32 }
33 b"caff" => Some(AudioFormat::Caf),
34 b".snd" => Some(AudioFormat::Au),
35 b"fLaC" => Some(AudioFormat::Flac),
36 b"OggS" => crate::ogg::sniff(bytes),
37 _ if bytes.starts_with(b"ID3") || crate::mp3::has_frame(bytes) => Some(AudioFormat::Mp3),
38 _ => None,
39 }
40}
41pub fn decode(bytes: &[u8]) -> Result<Audio> {
42 decode_with_options(bytes, &DecodeOptions::default())
43}
44pub fn decode_with_options(bytes: &[u8], options: &DecodeOptions) -> Result<Audio> {
45 let format = options.format.or_else(|| sniff(bytes)).ok_or_else(|| {
46 Error::new(
47 ErrorCode::UnsupportedFormat,
48 "Unknown or unsupported audio format",
49 )
50 })?;
51 match format {
52 AudioFormat::Wav => crate::formats::decode_wav(bytes, options),
53 AudioFormat::Aiff => crate::formats::decode_aiff(bytes, options),
54 AudioFormat::Caf => crate::formats::decode_caf(bytes, options),
55 AudioFormat::Au => crate::formats::decode_au(bytes, options),
56 AudioFormat::Flac => crate::flac::decode(bytes, options),
57 AudioFormat::Mp3 => crate::mp3::decode(bytes, options),
58 AudioFormat::Vorbis | AudioFormat::Opus => crate::ogg::decode(bytes, format, options),
59 AudioFormat::Raw => Err(Error::invalid(
60 "Raw PCM requires pcm::decode_pcm with an explicit shape",
61 )),
62 }
63}
64pub fn encode(audio: &Audio, format: AudioFormat) -> Result<Vec<u8>> {
65 encode_with_options(audio, format, &EncodeOptions::default())
66}
67pub fn encode_with_options(
68 audio: &Audio,
69 format: AudioFormat,
70 options: &EncodeOptions,
71) -> Result<Vec<u8>> {
72 if options.dither != crate::Dither::None {
73 if format == AudioFormat::Mp3 {
74 return Err(Error::invalid("Dither is not supported for MP3 output"));
75 }
76 crate::pcm::validate_dither(options.sample_format, options.dither)?;
77 }
78 crate::metadata::validate_options(options, format)?;
79 match format {
80 AudioFormat::Wav => crate::formats::encode_wav(audio, options),
81 AudioFormat::Aiff => crate::formats::encode_aiff(audio, options),
82 AudioFormat::Caf => crate::formats::encode_caf(audio, options),
83 AudioFormat::Au => crate::formats::encode_au(audio, options),
84 AudioFormat::Flac => crate::flac::encode(audio, options),
85 AudioFormat::Mp3 => crate::mp3::encode(audio, options),
86 AudioFormat::Vorbis | AudioFormat::Opus => Err(Error::new(
87 ErrorCode::UnsupportedFormat,
88 "Ogg encoding is not supported",
89 )),
90 AudioFormat::Raw => crate::pcm::encode_pcm_with_dither(
91 audio,
92 options.sample_format,
93 options.endian,
94 options.dither,
95 ),
96 }
97}
98pub fn read_audio_file(path: impl AsRef<Path>) -> Result<Audio> {
101 read_audio_file_with_options(path, &ReadAudioOptions::default())
102}
103pub fn read_audio_file_with_options(
118 path: impl AsRef<Path>,
119 options: &ReadAudioOptions,
120) -> Result<Audio> {
121 let path = path.as_ref();
122 let check = |metadata: std::fs::Metadata| {
123 if !metadata.is_file() {
124 return Err(Error::invalid("Audio input must be a regular file"));
125 }
126 if metadata.len() as u128 > options.max_encoded_bytes as u128 {
127 return Err(Error::limit("Encoded file exceeds byte limit"));
128 }
129 Ok(())
130 };
131 check(std::fs::metadata(path)?)?;
132 let file = std::fs::File::open(path)?;
133 check(file.metadata()?)?;
134 decode_with_options(
135 &read_bounded(file, options.max_encoded_bytes)?,
136 &options.decode,
137 )
138}
139
140fn read_bounded(mut reader: impl Read, limit: usize) -> Result<Vec<u8>> {
141 let mut bytes = Vec::new();
142 let mut chunk = [0u8; 8192];
143 loop {
144 let remaining = limit - bytes.len();
145 let count = remaining.min(chunk.len()).max(1);
146 let n = match reader.read(&mut chunk[..count]) {
147 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
148 value => value?,
149 };
150 if n == 0 {
151 return Ok(bytes);
152 }
153 if n > remaining {
154 return Err(Error::limit("Encoded file exceeds byte limit"));
155 }
156 let next = bytes.len() + n;
157 if next > bytes.capacity() {
158 let target = next.max(
159 bytes
160 .capacity()
161 .saturating_add(bytes.capacity() / 2)
162 .min(limit),
163 );
164 bytes
165 .try_reserve_exact(target - bytes.len())
166 .map_err(|_| Error::limit("Cannot reserve encoded file buffer"))?;
167 }
168 bytes.extend_from_slice(&chunk[..n]);
169 }
170}
171pub fn write_audio_file(
172 path: impl AsRef<Path>,
173 audio: &Audio,
174 options: &EncodeOptions,
175) -> Result<()> {
176 let path = path.as_ref();
177 let format = path
178 .extension()
179 .and_then(|s| s.to_str())
180 .ok_or_else(|| Error::invalid("Output path requires an audio extension"))?
181 .parse()?;
182 std::fs::write(path, encode_with_options(audio, format, options)?)?;
183 Ok(())
184}
185
186#[cfg(test)]
187mod bounded_read_tests {
188 use super::*;
189
190 #[test]
191 fn actual_bytes_are_bounded_even_when_the_source_keeps_growing() {
192 struct Growing {
193 read: usize,
194 }
195 impl Read for Growing {
196 fn read(&mut self, bytes: &mut [u8]) -> std::io::Result<usize> {
197 bytes.fill(7);
198 self.read += bytes.len();
199 Ok(bytes.len())
200 }
201 }
202 for limit in [0, 1, 8191, 8192, 8193, 20000] {
203 let mut source = Growing { read: 0 };
204 assert_eq!(
205 read_bounded(&mut source, limit).unwrap_err().code,
206 ErrorCode::LimitExceeded
207 );
208 assert_eq!(source.read, limit + 1);
209 let bytes = vec![7; limit];
210 assert_eq!(
211 read_bounded(std::io::Cursor::new(&bytes), limit).unwrap(),
212 bytes
213 );
214 }
215 }
216
217 #[test]
218 fn interrupted_reads_are_retried_and_other_errors_are_io() {
219 struct Interrupted(bool);
220 impl Read for Interrupted {
221 fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
222 if !self.0 {
223 self.0 = true;
224 Err(std::io::ErrorKind::Interrupted.into())
225 } else {
226 Ok(0)
227 }
228 }
229 }
230 assert!(read_bounded(Interrupted(false), 0).unwrap().is_empty());
231 struct Failed;
232 impl Read for Failed {
233 fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
234 Err(std::io::ErrorKind::PermissionDenied.into())
235 }
236 }
237 assert_eq!(read_bounded(Failed, 10).unwrap_err().code, ErrorCode::Io);
238 }
239}