Search code examples
rustaudiorodio

How to play sound from memory using Rodio


With rodio, Is there anyway to play sound from a Vec<u8> or similar? I want to be able to read data from a zip file (I'm using the zip crate) into memory and then play it back later. I've tried just about everything but I can't figure it out.

I tried

let sound_data = self.sounds.clone();
let cursor = Cursor::new(sound_data.as_slice());
let source = Decoder::new(cursor).unwrap();
// Play the sound directly on the device
self.audio_output.1.play_raw(source.convert_samples());

where sound_data is a Arc<Vec<u8>>. This complains that sound_data does not live long enough, and that play_raw requires the 'static lifetime. I've also tried various similar configurations involving &[u8]s, BufReaders, etc but none of them work. I'm relatively new to Rust (have tried a few small projects before), and this is making me crazy.


Solution

  • The problem is that Cursor::new(sound_data.as_slice()); borrows sound_data in the local scope, while play_raw requires its argument to be 'static (which the local scope is not). The error you get is due to the fact that sound_data as it is can't guarantee to not be destroyed while play_raw is processing it; the original Arc doesn't help.

    Two things:

    • Use Arc<[u8]> instead of Arc<Vec<u8>>; the latter has two pointer indirections where only one is needed, and is smaller. You're not going to modify the Vec<u8> inside the Arc anyway, so a Arc<[u8]> will suffice.
    • Arc<[u8]> is AsRef<[u8]>, and any std::io::Cursor<T> where T: AsRef<[u8]> is std::io::Read. So you can use the Arc to create a Cursor without borrowing. The following should work, although I haven't tried...
      // `sound_data` is an `Arc<[u8]>`, e.g. `Arc::from(vec![1,2,3])`
      let source = Decoder::new(Cursor::new(Arc::clone(&sound_data))).unwrap();
      self.audio_output.1.play_raw(source.convert_samples());