rodio icon indicating copy to clipboard operation
rodio copied to clipboard

Music not playing and no errors

Open arthurmelton opened this issue 4 years ago • 9 comments

So it is not playing anything and I just took the code off your docs so idk (and it's not giving me an invalid file or anything not even logging anything)

Kernal: linux Os: arch Rodio v: 0.14.0 Cargo v: 1.53.0 Rust v: 2018

// Get a output stream handle to the default physical sound device
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
// Load a sound from a file, using a path relative to Cargo.toml
let file = BufReader::new(File::open("doom.ogg").unwrap());
// Decode that sound file into a source
let source = Decoder::new(file).unwrap();
// Play the sound directly on the device
stream_handle.play_raw(source.convert_samples());

// The sound plays in a separate audio thread,
// so we need to keep the main thread alive while it's playing.
std::thread::sleep(std::time::Duration::from_secs(5));

arthurmelton avatar Jul 02 '21 20:07 arthurmelton

I had the same issue with this code (not relying on any external files):

fn main() {
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();

    let source = SineWave::new(10_000);

    stream_handle.play_raw(source.convert_samples()).unwrap();

    std::thread::sleep(std::time::Duration::from_secs(3));
}

It turned out that running the code in tmux caused no audio to be produced, but in a regular terminal things worked fine. I'm not sure why that is.

VanillaBrooks avatar Nov 11 '21 21:11 VanillaBrooks

I was able to get both the play_raw() and Sink version working in tmux, however, when I tried to extract the audio playing part into it's own struct, I found that it would exhibit this behavior unless the sink was declared and defined in main(). The source could be defined outside of main() (I created the source and appended it from a struct method). I'm unsure if this info will help pin the problem, but I thought I'd contribute my observations.

AjiBuster499 avatar Apr 02 '22 23:04 AjiBuster499

I can confirm @AjiBuster499 's observation that the Sink must be declared and defined in main(), otherwise no sound is played. This is in the context of a Dioxus app, for what it's worth.

ivakam avatar Sep 14 '22 12:09 ivakam

I can also confirm that it must be declared/defined in main in a GTK4 app

verbose details here

works

use gtk::prelude::*;
use rodio::OutputStream;
use std::io::BufReader;

fn main() {
    let application =
        gtk::Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default());

    // Play beep
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let file = std::fs::File::open("assets/beep.wav").unwrap();
    let beep1 = stream_handle.play_once(BufReader::new(file)).unwrap();
    beep1.set_volume(0.2);

    application.connect_activate(build_ui);
    application.run();
}

fn build_ui(application: &gtk::Application) {
    let window = gtk::ApplicationWindow::new(application);
    window.set_title(Some("First GTK Program"));
    window.set_default_size(350, 70);

    let button = gtk::Button::with_label("Click me!");
    window.set_child(Some(&button));
    window.show();
}

does not work (plays fraction of beep and is cut off)

use gtk::prelude::*;
use rodio::OutputStream;
use std::io::BufReader;

fn main() {
    let application =
        gtk::Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default());

    application.connect_activate(build_ui);
    application.run();
}

fn build_ui(application: &gtk::Application) {
    let window = gtk::ApplicationWindow::new(application);
    window.set_title(Some("First GTK Program"));
    window.set_default_size(350, 70);

    let button = gtk::Button::with_label("Click me!");

    // Play beep
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let file = std::fs::File::open("assets/beep.wav").unwrap();
    let beep1 = stream_handle.play_once(BufReader::new(file)).unwrap();
    beep1.set_volume(0.2);

    window.set_child(Some(&button));
    window.show();
}

cargo.toml for extra detail

[package]
name = "helloworldplayer"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rodio = "0.16.0"
gtk = { version = "0.4", package = "gtk4", features = ["v4_8"] }



cmdcolin avatar Sep 20 '22 03:09 cmdcolin

Just to confirm I had this same issue. I created a function that spun up a thread inside, and used a channel to communicate stopping to the sink. A stop function was returned. No errors, everything compiling fine but no music was planning. I moved the stream handle and sink out into the main thread, passed them into my function and then everything worked fine.

HHogg avatar Feb 17 '23 22:02 HHogg

I initially came to the same conclusion as @AjiBuster499 but wanted to know what caused this. After experimenting and identifying the issue, I was able to find #330 which relates to this issue. In short:

As #330 states, the OutputStreamHandle confusingly is a reference to the OutputStream returned by OutputStream::try_default(), and if you do not capture the stream into a variable, the handle cannot point to the object. In the issue here, when the stream goes out of scope, the handle becomes worthless. The inner field of OutputStreamHandle is of type alloc::sync::Weak which explains this behavior.

Bevy handles using Weak and Strong in the same type very well. https://github.com/bevyengine/bevy/blob/f867319336541acb8ed80743c723effeb4be8896/crates/bevy_asset/src/handle.rs#L119

ryllsiili avatar Mar 01 '23 00:03 ryllsiili

Hey folks, I am also facing a similar issue, not sure if it is related to the above mentioned use case.

thread::spawn(move ||{
    let (_stream, handle) = OutputStream::try_default().expect("speaker broke");
    let sink = Sink::try_new(&handle).expect("speaker on the left broke");
    let source = Decoder::new(...);

    sink.append(source);
    sink.play();
    loop {
        match rx.blocking_recv() {
            ... // This is conditional logic for controlling the channel via different thread using channels
        }
    }
})

So, this above mentioned code doesn't produce any sound. But, here's the fun part, the one mentioned below does work

thread::spawn(move ||{
    let (_stream, handle) = OutputStream::try_default().expect("speaker broke");
    let sink = Sink::try_new(&handle).expect("speaker on the left broke");
    let source = Decoder::new(...);

    sink.append(source);
    sink.sleep_until_end();
    loop {
        match rx.blocking_recv() {
            ... // This is conditional logic for controlling the channel via different thread using channels
        }
    }
})

But, this doesn't fall into my use case where, I need the current thread to handle the incoming message for the channel after starting the sink.

Can anyone please let me know, what might be causing this. Because, for testing I tried adding sleep delay after doing sink.play() in the first code snippet, but it still doesn't seem to work. I am not able to predict the behaviour of the code even after going through the source code of how Sink is written.

NishantJoshi00 avatar May 03 '23 20:05 NishantJoshi00

Yeah I was having a similar problem with no sound playing. After finding issue #330 I realized it might be the fact that after the stream variable is dropped, the handle doesn't work anymore. So now I keep the OutputStream variable and the OutputStreamHandle variable in the same struct so their lifetimes should be the same. And that works.

bamidev avatar Jul 20 '23 21:07 bamidev

I think OutputStream either needs to use an Arc<...> ptr instead of Weak<...>, or OutputStreamHandle needs a lifetime parameter in its type towards the OutputStream variable.

bamidev avatar Jul 20 '23 21:07 bamidev