@@ -10,9 +10,21 @@ use std::{
1010 io:: Cursor ,
1111} ;
1212
13+ #[ cfg( feature = "audio-decode-vorbis" ) ]
14+ use symphonia:: core:: codecs:: CODEC_TYPE_VORBIS ;
15+ #[ cfg( feature = "audio-decode-wav" ) ]
16+ use symphonia:: core:: sample:: SampleFormat ;
1317use symphonia:: core:: {
18+ audio:: SampleBuffer ,
19+ codecs:: {
20+ Decoder ,
21+ DecoderOptions ,
22+ } ,
1423 errors:: Error ,
15- formats:: FormatOptions ,
24+ formats:: {
25+ FormatOptions ,
26+ FormatReader ,
27+ } ,
1628 io:: MediaSourceStream ,
1729 meta:: MetadataOptions ,
1830 probe:: Hint ,
@@ -80,11 +92,39 @@ fn map_probe_error(source_description: &str, error: Error) -> AudioDecodeError {
8092 }
8193}
8294
83- fn probe_bytes (
95+ fn map_read_or_decode_error (
96+ source_description : & str ,
97+ error : Error ,
98+ ) -> AudioDecodeError {
99+ match error {
100+ Error :: Unsupported ( _) => {
101+ return AudioDecodeError :: UnsupportedFormat {
102+ details : format ! ( "unsupported {source_description} audio codec" ) ,
103+ } ;
104+ }
105+ Error :: DecodeError ( _) => {
106+ return AudioDecodeError :: InvalidData {
107+ details : format ! ( "{source_description} decode error: {error}" ) ,
108+ } ;
109+ }
110+ Error :: IoError ( _) => {
111+ return AudioDecodeError :: InvalidData {
112+ details : format ! ( "{source_description} read error: {error}" ) ,
113+ } ;
114+ }
115+ other => {
116+ return AudioDecodeError :: DecodeFailed {
117+ details : format ! ( "{source_description} decode failed: {other}" ) ,
118+ } ;
119+ }
120+ }
121+ }
122+
123+ fn probe_format (
84124 bytes : & [ u8 ] ,
85125 source_description : & str ,
86126 extensions : & [ & str ] ,
87- ) -> Result < ( ) , AudioDecodeError > {
127+ ) -> Result < Box < dyn FormatReader > , AudioDecodeError > {
88128 let hint_value = hint_for_decode ( extensions) ;
89129
90130 let cursor = Cursor :: new ( bytes. to_vec ( ) ) ;
@@ -106,27 +146,270 @@ fn probe_bytes(
106146 } ) ;
107147 }
108148
149+ return Ok ( probe_result. format ) ;
150+ }
151+
152+ fn try_reserve_samples (
153+ samples : & mut Vec < f32 > ,
154+ source_description : & str ,
155+ frames : Option < u64 > ,
156+ channels : Option < u16 > ,
157+ ) -> Result < ( ) , AudioDecodeError > {
158+ let ( frames, channels) = match ( frames, channels) {
159+ ( Some ( frames) , Some ( channels) ) => ( frames, channels) ,
160+ _ => {
161+ return Ok ( ( ) ) ;
162+ }
163+ } ;
164+
165+ let total_samples = frames. saturating_mul ( channels as u64 ) ;
166+ if total_samples > usize:: MAX as u64 {
167+ return Ok ( ( ) ) ;
168+ }
169+
170+ samples. try_reserve ( total_samples as usize ) . map_err ( |_| {
171+ return AudioDecodeError :: DecodeFailed {
172+ details : format ! ( "failed to allocate {source_description} sample buffer" ) ,
173+ } ;
174+ } ) ?;
109175 return Ok ( ( ) ) ;
110176}
111177
178+ fn decode_track_to_interleaved_f32 (
179+ format : & mut dyn FormatReader ,
180+ track_id : u32 ,
181+ decoder : & mut dyn Decoder ,
182+ source_description : & str ,
183+ reserve_frames : Option < u64 > ,
184+ reserve_channels : Option < u16 > ,
185+ ) -> Result < DecodedAudio , AudioDecodeError > {
186+ let mut samples: Vec < f32 > = Vec :: new ( ) ;
187+ try_reserve_samples (
188+ & mut samples,
189+ source_description,
190+ reserve_frames,
191+ reserve_channels,
192+ ) ?;
193+
194+ let mut sample_rate: Option < u32 > = None ;
195+ let mut channel_count: Option < u16 > = None ;
196+
197+ loop {
198+ let packet = match format. next_packet ( ) {
199+ Ok ( packet) => packet,
200+ Err ( Error :: IoError ( error) )
201+ if error. kind ( ) == std:: io:: ErrorKind :: UnexpectedEof =>
202+ {
203+ break ;
204+ }
205+ Err ( error) => {
206+ return Err ( map_read_or_decode_error ( source_description, error) ) ;
207+ }
208+ } ;
209+
210+ if packet. track_id ( ) != track_id {
211+ continue ;
212+ }
213+
214+ let decoded = match decoder. decode ( & packet) {
215+ Ok ( decoded) => decoded,
216+ Err ( Error :: ResetRequired ) => {
217+ decoder. reset ( ) ;
218+ continue ;
219+ }
220+ Err ( error) => {
221+ return Err ( map_read_or_decode_error ( source_description, error) ) ;
222+ }
223+ } ;
224+
225+ let rate = decoded. spec ( ) . rate ;
226+ if rate == 0 {
227+ return Err ( AudioDecodeError :: InvalidData {
228+ details : format ! ( "{source_description} decoded sample rate was 0" ) ,
229+ } ) ;
230+ }
231+
232+ let channels = decoded. spec ( ) . channels . count ( ) as u16 ;
233+ if channels == 0 {
234+ return Err ( AudioDecodeError :: InvalidData {
235+ details : format ! ( "{source_description} decoded channel count was 0" ) ,
236+ } ) ;
237+ }
238+
239+ if channels != 1 && channels != 2 {
240+ return Err ( AudioDecodeError :: UnsupportedFormat {
241+ details : format ! (
242+ "unsupported {source_description} channel count: {channels}"
243+ ) ,
244+ } ) ;
245+ }
246+
247+ if let Some ( previous_rate) = sample_rate {
248+ if previous_rate != rate {
249+ return Err ( AudioDecodeError :: InvalidData {
250+ details : format ! (
251+ "{source_description} sample rate changed during decoding"
252+ ) ,
253+ } ) ;
254+ }
255+ } else {
256+ sample_rate = Some ( rate) ;
257+ }
258+
259+ if let Some ( previous_channels) = channel_count {
260+ if previous_channels != channels {
261+ return Err ( AudioDecodeError :: InvalidData {
262+ details : format ! (
263+ "{source_description} channel count changed during decoding"
264+ ) ,
265+ } ) ;
266+ }
267+ } else {
268+ channel_count = Some ( channels) ;
269+ }
270+
271+ let frames = decoded. frames ( ) ;
272+ let mut sample_buffer =
273+ SampleBuffer :: < f32 > :: new ( frames as u64 , * decoded. spec ( ) ) ;
274+ sample_buffer. copy_interleaved_ref ( decoded) ;
275+ samples. extend_from_slice ( sample_buffer. samples ( ) ) ;
276+ }
277+
278+ let sample_rate = sample_rate. ok_or ( AudioDecodeError :: InvalidData {
279+ details : format ! (
280+ "{source_description} contained no decodable audio frames"
281+ ) ,
282+ } ) ?;
283+ let channels = channel_count. ok_or ( AudioDecodeError :: InvalidData {
284+ details : format ! (
285+ "{source_description} contained no decodable channel configuration"
286+ ) ,
287+ } ) ?;
288+
289+ if samples. is_empty ( ) {
290+ return Err ( AudioDecodeError :: InvalidData {
291+ details : format ! ( "{source_description} contained no decoded samples" ) ,
292+ } ) ;
293+ }
294+
295+ return Ok ( DecodedAudio {
296+ samples,
297+ sample_rate,
298+ channels,
299+ } ) ;
300+ }
301+
112302/// Decode WAV bytes into interleaved `f32` samples.
113303#[ cfg( feature = "audio-decode-wav" ) ]
114304pub fn decode_wav_bytes (
115305 bytes : & [ u8 ] ,
116306) -> Result < DecodedAudio , AudioDecodeError > {
117- probe_bytes ( bytes, "WAV" , & [ "wav" ] ) ?;
118- return Err ( AudioDecodeError :: DecodeFailed {
119- details : "WAV decoding not implemented yet" . to_string ( ) ,
120- } ) ;
307+ let mut format = probe_format ( bytes, "WAV" , & [ "wav" ] ) ?;
308+ let ( track_id, codec_params) = match format. default_track ( ) {
309+ Some ( track) => ( track. id , track. codec_params . clone ( ) ) ,
310+ None => {
311+ return Err ( AudioDecodeError :: InvalidData {
312+ details : "no default audio track found" . to_string ( ) ,
313+ } ) ;
314+ }
315+ } ;
316+
317+ let sample_format =
318+ codec_params
319+ . sample_format
320+ . ok_or ( AudioDecodeError :: UnsupportedFormat {
321+ details : "WAV sample format is unspecified" . to_string ( ) ,
322+ } ) ?;
323+
324+ match sample_format {
325+ SampleFormat :: S16 | SampleFormat :: S24 | SampleFormat :: F32 => { }
326+ other => {
327+ return Err ( AudioDecodeError :: UnsupportedFormat {
328+ details : format ! ( "unsupported WAV sample format: {other:?}" ) ,
329+ } ) ;
330+ }
331+ }
332+
333+ let mut decoder = symphonia:: default:: get_codecs ( )
334+ . make ( & codec_params, & DecoderOptions :: default ( ) )
335+ . map_err ( |error| map_read_or_decode_error ( "WAV" , error) ) ?;
336+
337+ return decode_track_to_interleaved_f32 (
338+ & mut * format,
339+ track_id,
340+ & mut * decoder,
341+ "WAV" ,
342+ codec_params. n_frames ,
343+ codec_params
344+ . channels
345+ . map ( |channels| channels. count ( ) as u16 ) ,
346+ ) ;
121347}
122348
123349/// Decode OGG Vorbis bytes into interleaved `f32` samples.
124350#[ cfg( feature = "audio-decode-vorbis" ) ]
125351pub fn decode_ogg_vorbis_bytes (
126352 bytes : & [ u8 ] ,
127353) -> Result < DecodedAudio , AudioDecodeError > {
128- probe_bytes ( bytes, "OGG Vorbis" , & [ "ogg" , "oga" ] ) ?;
129- return Err ( AudioDecodeError :: DecodeFailed {
130- details : "OGG Vorbis decoding not implemented yet" . to_string ( ) ,
131- } ) ;
354+ let mut format = probe_format ( bytes, "OGG Vorbis" , & [ "ogg" , "oga" ] ) ?;
355+ let ( track_id, codec_params) = match format. default_track ( ) {
356+ Some ( track) => ( track. id , track. codec_params . clone ( ) ) ,
357+ None => {
358+ return Err ( AudioDecodeError :: InvalidData {
359+ details : "no default audio track found" . to_string ( ) ,
360+ } ) ;
361+ }
362+ } ;
363+
364+ if codec_params. codec != CODEC_TYPE_VORBIS {
365+ return Err ( AudioDecodeError :: UnsupportedFormat {
366+ details : "OGG stream is not Vorbis" . to_string ( ) ,
367+ } ) ;
368+ }
369+
370+ let mut decoder = symphonia:: default:: get_codecs ( )
371+ . make ( & codec_params, & DecoderOptions :: default ( ) )
372+ . map_err ( |error| map_read_or_decode_error ( "OGG Vorbis" , error) ) ?;
373+
374+ return decode_track_to_interleaved_f32 (
375+ & mut * format,
376+ track_id,
377+ & mut * decoder,
378+ "OGG Vorbis" ,
379+ codec_params. n_frames ,
380+ codec_params
381+ . channels
382+ . map ( |channels| channels. count ( ) as u16 ) ,
383+ ) ;
384+ }
385+
386+ #[ cfg( test) ]
387+ mod tests {
388+ use super :: * ;
389+
390+ #[ cfg( feature = "audio-decode-wav" ) ]
391+ #[ test]
392+ fn wav_decode_rejects_invalid_bytes ( ) {
393+ let result = decode_wav_bytes ( & [ 0u8 , 1u8 , 2u8 , 3u8 ] ) ;
394+ assert ! ( matches!(
395+ result,
396+ Err ( AudioDecodeError :: UnsupportedFormat { .. } )
397+ | Err ( AudioDecodeError :: InvalidData { .. } )
398+ | Err ( AudioDecodeError :: DecodeFailed { .. } )
399+ ) ) ;
400+ return ;
401+ }
402+
403+ #[ cfg( feature = "audio-decode-vorbis" ) ]
404+ #[ test]
405+ fn ogg_vorbis_decode_rejects_invalid_bytes ( ) {
406+ let result = decode_ogg_vorbis_bytes ( & [ 0u8 , 1u8 , 2u8 , 3u8 ] ) ;
407+ assert ! ( matches!(
408+ result,
409+ Err ( AudioDecodeError :: UnsupportedFormat { .. } )
410+ | Err ( AudioDecodeError :: InvalidData { .. } )
411+ | Err ( AudioDecodeError :: DecodeFailed { .. } )
412+ ) ) ;
413+ return ;
414+ }
132415}
0 commit comments