diff --git a/examples/basic.rs b/examples/basic.rs index 37f524a..ea389f5 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -2,8 +2,15 @@ extern crate rodio; fn main() { let file = std::fs::File::open("examples/beep.wav").unwrap(); + let beep1 = rodio::play_once(file); + std::thread::sleep_ms(1000); + + let file = std::fs::File::open("examples/beep2.wav").unwrap(); rodio::play_once(file); - std::thread::sleep_ms(10000); + std::thread::sleep_ms(1000); + beep1.stop(); + + std::thread::sleep_ms(8000); } diff --git a/examples/beep2.wav b/examples/beep2.wav new file mode 100644 index 0000000..fcb1619 Binary files /dev/null and b/examples/beep2.wav differ diff --git a/src/engine.rs b/src/engine.rs index 73626f2..81394ac 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,31 +1,58 @@ use std::thread; use std::sync::mpsc::{self, Sender, Receiver}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; use cpal::Voice; use decoder::Decoder; -pub enum Command { - Play(Box) -} - pub struct Engine { + /// Communication with the background thread. commands: Mutex>, + /// Counter that is incremented whenever a sound starts playing. + sound_ids: AtomicUsize, } impl Engine { pub fn new() -> Engine { let (tx, rx) = mpsc::channel(); thread::spawn(move || background(rx)); - Engine { commands: Mutex::new(tx) } + Engine { commands: Mutex::new(tx), sound_ids: AtomicUsize::new(0) } } - pub fn play_once(&self, decoder: Box) { + pub fn play(&self, decoder: Box) -> Handle { + let sound_id = self.sound_ids.fetch_add(1, Ordering::Relaxed); + let commands = self.commands.lock().unwrap(); - commands.send(Command::Play(decoder)).unwrap(); + commands.send(Command::Play(sound_id, decoder)).unwrap(); + + Handle { + engine: self, + id: sound_id, + } } } +/// Handle to a playing sound. +/// +/// Note that dropping the handle doesn't stop the sound. You must call `stop` explicitely. +pub struct Handle<'a> { + engine: &'a Engine, + id: usize, +} + +impl<'a> Handle<'a> { + pub fn stop(self) { + let commands = self.engine.commands.lock().unwrap(); + commands.send(Command::Stop(self.id)).unwrap(); + } +} + +pub enum Command { + Play(usize, Box), + Stop(usize), +} + fn background(rx: Receiver) { let mut sounds = Vec::new(); @@ -33,11 +60,12 @@ fn background(rx: Receiver) { // polling for new sounds if let Ok(command) = rx.try_recv() { match command { - Command::Play(decoder) => sounds.push((Voice::new(), decoder)), + Command::Play(id, decoder) => sounds.push((id, Voice::new(), decoder)), + Command::Stop(id) => sounds.retain(|&(id2, _, _)| id2 != id), } } - for &mut (ref mut voice, ref mut decoder) in sounds.iter_mut() { + for &mut (_, ref mut voice, ref mut decoder) in sounds.iter_mut() { decoder.write(voice); voice.play(); } diff --git a/src/lib.rs b/src/lib.rs index 04f0c45..ea76973 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,19 @@ lazy_static! { static ref ENGINE: engine::Engine = engine::Engine::new(); } -/// Plays a sound once. There's no way to stop the sound except by exiting the program. -pub fn play_once(input: R) where R: Read + Send + 'static { - let decoder = decoder::decode(input); - ENGINE.play_once(decoder); +/// Handle to a playing sound. +/// +/// Note that dropping the handle doesn't stop the sound. You must call `stop` explicitely. +pub struct Handle(engine::Handle<'static>); + +impl Handle { + pub fn stop(self) { + self.0.stop() + } +} + +/// Plays a sound once. There's no way to stop the sound except by exiting the program. +pub fn play_once(input: R) -> Handle where R: Read + Send + 'static { + let decoder = decoder::decode(input); + Handle(ENGINE.play(decoder)) }