rustaudiorodio

Why do Rodio's Sink types fail to play when used inside of a struct?


Been trying to create a struct to do some music operations and I found that if you have a struct with a rodio::sink as a field, it will not play music.

Here is my code:


use rodio::{OutputStream, Source, Sink, Decoder};
use std::fs::File;
use std::io::{BufReader, Seek, SeekFrom};

pub struct MusicStruct {
    sink: Sink
}

fn main() {
    // this will not play (using my struct type)
    let mut music = MusicStruct::new();
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let my_sink = Sink::try_new(&stream_handle).unwrap();
    // will sit here and hang forever (i guess bcs of how the sleep_until_end() is written..)
    MusicStruct::test_play(music);

    // this does play (doing it how the docs suggest)
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let sink = Sink::try_new(&stream_handle).unwrap();
    sink.append(read_file_from_beginning("my_mp3.mp3".to_string()));
    sink.play();
    sink.sleep_until_end()
}


// makes a Decoder<BufReader<File>> from String
fn read_file_from_beginning(file: String) -> Decoder<BufReader<File>> {
    let reader = BufReader::new(File::open(file).unwrap());
    let decoder = Decoder::new(reader).unwrap();
    decoder

}

impl MusicStruct {
    // creates a Decoder<BufReader<File>> and appends it to the sink in our struct and plays it
    pub fn test_play(our_sink: MusicStruct) {
        let file = read_file_from_beginning("my_mp3.mp3".to_string());
        our_sink.sink.append(file);
        our_sink.sink.play();
        our_sink.sink.sleep_until_end()
    }


   pub fn new() -> MusicStruct {
        let (_stream, stream_handle) = OutputStream::try_default().unwrap();
        MusicStruct {
            sink: Sink::try_new(&stream_handle).unwrap()
        }
    }
}

I didn't see anything mention this in the docs / other resources. Am I doing something wrong here? I looked into the source code but didn't really see much in sink that would cause this behavior (albeit I'm not great at rust, so..)

Would appreciate any thoughts or answers !


Solution

  • Need to have the OutputStream live for as long as the sink. Simple fix was adding it to the struct:

    pub struct MusicStruct {
        sink: Sink,
        stream: OutputStream
    }
    

    and changing the new function to include it:

       pub fn new() -> MusicStruct {
            let (stream, stream_handle) = OutputStream::try_default().unwrap();
            MusicStruct {
                sink: Sink::try_new(&stream_handle).unwrap(),
                stream
            }
    

    Edit: The much better way to do this (and to have this work easier with threading) is to use

    let (stream, stream_handle) = OutputStream::try_default().unwrap();
    std::mem::forget(stream);
    

    Which is used officially in the bevy game engine.

    This is fine so long as you are not constructing multiple of these