I just very hastily put something together that should work. Instead of using sink.sleep_until_end(); we store the OutputStreamHandle which should keep it playing, then drop that handle later
use rodio::{source::Source, Decoder, OutputStream, OutputStreamHandle, Sink};
use serde::Serialize;
use std::fs::File;
use std::io::BufReader;
use std::sync::Mutex;
use tauri::{Manager, State};
#[derive(Serialize)]
pub(crate) struct PlaybackState {
#[serde(skip_serializing)]
stream: Option<OutputStreamHandle>,
}
impl Default for PlaybackState {
fn default() -> Self {
PlaybackState { stream: None }
}
}
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[tauri::command]
async fn play(path: &str, playback_mutex: State<'_, Mutex<PlaybackState>>) -> Result<(), ()> {
let mut state = playback_mutex.lock().unwrap();
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
let file = BufReader::new(File::open(path).unwrap());
let source = Decoder::new(file).unwrap();
sink.append(source);
state.stream = Some(stream_handle);
Ok(())
}
#[tauri::command]
async fn stop(path: &str, playback_mutex: State<'_, Mutex<PlaybackState>>) -> Result<(), ()> {
let mut state = playback_mutex.lock().unwrap();
state.stream = None;
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_window::init())
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet, play, stop])
.manage(Mutex::new(PlaybackState::default()))
.run(tauri::generate_context!())
.expect("error while running tauri application");
}