@@ -12,6 +12,8 @@ use crate::param::AudioParamDescriptor;
1212use crate :: periodic_wave:: { PeriodicWave , PeriodicWaveOptions } ;
1313use crate :: { node, AudioListener } ;
1414
15+ use std:: future:: Future ;
16+
1517/// The interface representing an audio-processing graph built from audio modules linked together,
1618/// each represented by an `AudioNode`.
1719///
@@ -29,11 +31,11 @@ pub trait BaseAudioContext {
2931 ///
3032 /// In addition to the official spec, the input parameter can be any byte stream (not just an
3133 /// array). This means you can decode audio data from a file, network stream, or in memory
32- /// buffer, and any other [`std::io::Read`] implementer. The data if buffered internally so you
34+ /// buffer, and any other [`std::io::Read`] implementer. The data is buffered internally so you
3335 /// should not wrap the source in a `BufReader`.
3436 ///
3537 /// This function operates synchronously, which may be undesirable on the control thread. The
36- /// example shows how to avoid this. An async version is currently not implemented .
38+ /// example shows how to avoid this. See also the async method [`Self::decode_audio_data`] .
3739 ///
3840 /// # Errors
3941 ///
@@ -82,6 +84,48 @@ pub trait BaseAudioContext {
8284 Ok ( buffer)
8385 }
8486
87+ /// Decode an [`AudioBuffer`] from a given input stream.
88+ ///
89+ /// The current implementation can decode FLAC, Opus, PCM, Vorbis, and Wav.
90+ ///
91+ /// In addition to the official spec, the input parameter can be any byte stream (not just an
92+ /// array). This means you can decode audio data from a file, network stream, or in memory
93+ /// buffer, and any other [`std::io::Read`] implementer. The data is buffered internally so you
94+ /// should not wrap the source in a `BufReader`.
95+ ///
96+ /// Warning, the current implementation still uses blocking IO so it's best to use Tokio's
97+ /// `spawn_blocking` to run the decoding on a thread dedicated to blocking operations. See also
98+ /// the async method [`Self::decode_audio_data_sync`].
99+ ///
100+ /// # Errors
101+ ///
102+ /// This method returns an Error in various cases (IO, mime sniffing, decoding).
103+ fn decode_audio_data < R : std:: io:: Read + Send + Sync + ' static > (
104+ & self ,
105+ input : R ,
106+ ) -> impl Future < Output = Result < AudioBuffer , Box < dyn std:: error:: Error + Send + Sync > > >
107+ + Send
108+ + ' static {
109+ let sample_rate = self . sample_rate ( ) ;
110+ async move {
111+ // Set up a media decoder, consume the stream in full and construct a single buffer out of it
112+ let mut buffer = MediaDecoder :: try_new ( input) ?
113+ . collect :: < Result < Vec < _ > , _ > > ( ) ?
114+ . into_iter ( )
115+ . reduce ( |mut accum, item| {
116+ accum. extend ( & item) ;
117+ accum
118+ } )
119+ // if there are no samples decoded, return an empty buffer
120+ . unwrap_or_else ( || AudioBuffer :: from ( vec ! [ vec![ ] ] , sample_rate) ) ;
121+
122+ // resample to desired rate (no-op if already matching)
123+ buffer. resample ( sample_rate) ;
124+
125+ Ok ( buffer)
126+ }
127+ }
128+
85129 /// Create an new "in-memory" `AudioBuffer` with the given number of channels,
86130 /// length (i.e. number of samples per channel) and sample rate.
87131 ///
@@ -347,6 +391,8 @@ mod tests {
347391
348392 use float_eq:: assert_float_eq;
349393
394+ fn require_send_sync_static < T : Send + Sync + ' static > ( _: T ) { }
395+
350396 #[ test]
351397 fn test_decode_audio_data_sync ( ) {
352398 let context = OfflineAudioContext :: new ( 1 , 1 , 44100. ) ;
@@ -364,6 +410,33 @@ mod tests {
364410 assert ! ( left_start != right_start) ;
365411 }
366412
413+ #[ test]
414+ fn test_decode_audio_data_future_send_static ( ) {
415+ let context = OfflineAudioContext :: new ( 1 , 1 , 44100. ) ;
416+ let file = std:: fs:: File :: open ( "samples/sample.wav" ) . unwrap ( ) ;
417+ let future = context. decode_audio_data ( file) ;
418+ require_send_sync_static ( future) ;
419+ }
420+
421+ #[ test]
422+ fn test_decode_audio_data_async ( ) {
423+ use futures:: executor;
424+ let context = OfflineAudioContext :: new ( 1 , 1 , 44100. ) ;
425+ let file = std:: fs:: File :: open ( "samples/sample.wav" ) . unwrap ( ) ;
426+ let future = context. decode_audio_data ( file) ;
427+ let audio_buffer = executor:: block_on ( future) . unwrap ( ) ;
428+
429+ assert_eq ! ( audio_buffer. sample_rate( ) , 44100. ) ;
430+ assert_eq ! ( audio_buffer. length( ) , 142_187 ) ;
431+ assert_eq ! ( audio_buffer. number_of_channels( ) , 2 ) ;
432+ assert_float_eq ! ( audio_buffer. duration( ) , 3.224 , abs_all <= 0.001 ) ;
433+
434+ let left_start = & audio_buffer. get_channel_data ( 0 ) [ 0 ..100 ] ;
435+ let right_start = & audio_buffer. get_channel_data ( 1 ) [ 0 ..100 ] ;
436+ // assert distinct two channel data
437+ assert ! ( left_start != right_start) ;
438+ }
439+
367440 // #[test]
368441 // disabled: symphonia cannot handle empty WAV-files
369442 #[ allow( dead_code) ]
0 commit comments