I am using crate rodio and iced to write a GUI program with sounds. In order to handle the audio with buttons in the interface I used the Box<> pointer to allow different threads to control the audio. However, in this way no sound was produced even though I added the audio to the sink.
The source code is only a slight modification of the example on docs.rs or GitHub.
That works, but I don't want it to block the current thread. The interface pops out after the audio ends. Is there any way to keep the sink from being dropped?
You currently move the sink into play_music. You have choices:
You can make play_music use an &mut rodio::Sink instead of taking ownership of a sink, so the audio keeps playing until you drop the sink some other way.
You could call sink.detach() in play_music, which is Rodio's method for saying "let the audio keep playing after the sink is dropped.
I'm actually surprised that the code with play_music inlined works, since Counter::new doesn't store sink either - it's dropped at the and of new, and this is, well, only a few ticks later.
Maybe I should show the real situation I'm dealing with, because it might be too different from the code used to reproduce the problem.
pub struct Audios {
pub sink: Arc<Mutex<Pin<Box<rodio::Sink>>>>,
pub volume: f32,
}
pub async fn initialize() -> Result<State, crate::Error> {
// fetch data from the web.
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
let sink = Box::pin(Sink::try_new(&stream_handle).unwrap());
let sink_mutex = Arc::new(Mutex::new(sink));
let given_mutex = sink_mutex.clone();
tokio::spawn(async move {
play_music(given_mutex, audio_paths).await;
});
let fetched = img_mutex.lock().unwrap();
Ok(State {
// Some other fields.
aud_module: Audios {
volume: 1.0,
sink: sink_mutex,
},
})
// I am not sure if the sink drops here and causes the problem
}
pub async fn play_music(sink: Arc<Mutex<Pin<Box<rodio::Sink>>>>, mut paths: Vec<String>) {
loop {
for audio_dir in &paths {
let audio_buf = std::fs::File::open(&audio_dir).unwrap();
let file = std::io::BufReader::new(audio_buf);
let source = rodio::Decoder::new(file).unwrap();
let sink = sink.lock().unwrap();
sink.append(source);
}
tokio::time::sleep(Duration::from_secs(10)).await;
}
}
Here I am using tokio because I need to call tokio::time::pause() when user want the audio to stop in the GUI button. After it replays, it should continue to loop to add new audio to the queue so that the sound won't stop. In order to make the temporary variable sink not drop, I also tried to use Box::leak(Box::new(sink)), but it didn't seem to work either.