0

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 !

  • 1
    Iirc you need the `_stream` to hang around for the whole duration you're playing music, but it gets dropped at the end of `MusicStream::new()` there should be something about it in the docs. – cafce25 May 13 '23 at 04:35
  • Yup, thats it, I'll post an updated version. Thanks. – NedNoodleHead May 13 '23 at 19:03

1 Answers1

2

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