Add Handle::sleep_until_end and rework decoder handling

This commit is contained in:
Pierre Krieger 2015-09-23 10:33:45 +02:00
parent 338f8bb7c8
commit 5c68d37055
6 changed files with 75 additions and 34 deletions

View file

@ -6,7 +6,7 @@ fn main() {
let endpoint = rodio::get_default_endpoint().unwrap(); let endpoint = rodio::get_default_endpoint().unwrap();
let file = std::fs::File::open("examples/music.wav").unwrap(); let file = std::fs::File::open("examples/music.wav").unwrap();
let _music = rodio::play_once(&endpoint, BufReader::new(file)); let music = rodio::play_once(&endpoint, BufReader::new(file));
std::thread::sleep_ms(10000); music.sleep_until_end();
} }

View file

@ -1,4 +1,7 @@
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use std::sync::Arc;
use std::sync::Mutex;
use cpal::Endpoint; use cpal::Endpoint;
mod vorbis; mod vorbis;
@ -12,21 +15,24 @@ pub trait Decoder {
/// Changes the volume of the sound. /// Changes the volume of the sound.
fn set_volume(&mut self, f32); fn set_volume(&mut self, f32);
/// Returns the number of milliseconds before the end of the sound.
fn get_remaining_duration_ms(&self) -> u32;
} }
/// Builds a new `Decoder` from a data stream by determining the correct format. /// Builds a new `Decoder` from a data stream by determining the correct format.
pub fn decode<R>(endpoint: &Endpoint, data: R) -> Box<Decoder + Send> pub fn decode<R>(endpoint: &Endpoint, data: R) -> Arc<Mutex<Decoder + Send>>
where R: Read + Seek + Send + 'static where R: Read + Seek + Send + 'static
{ {
let data = match wav::WavDecoder::new(endpoint, data) { let data = match wav::WavDecoder::new(endpoint, data) {
Err(data) => data, Err(data) => data,
Ok(decoder) => { Ok(decoder) => {
return Box::new(decoder); return Arc::new(Mutex::new(decoder));
} }
}; };
if let Ok(decoder) = vorbis::VorbisDecoder::new(endpoint, data) { if let Ok(decoder) = vorbis::VorbisDecoder::new(endpoint, data) {
return Box::new(decoder); return Arc::new(Mutex::new(decoder));
} }
panic!("Invalid format"); panic!("Invalid format");

View file

@ -66,4 +66,8 @@ impl Decoder for VorbisDecoder {
fn set_volume(&mut self, _: f32) { fn set_volume(&mut self, _: f32) {
unimplemented!(); unimplemented!();
} }
fn get_remaining_duration_ms(&self) -> u32 {
unimplemented!();
}
} }

View file

@ -140,4 +140,12 @@ impl Decoder for WavDecoder {
fn set_volume(&mut self, value: f32) { fn set_volume(&mut self, value: f32) {
self.reader.set_amplification(value); self.reader.set_amplification(value);
} }
fn get_remaining_duration_ms(&self) -> u32 {
let (num_samples, _) = self.reader.size_hint();
let num_samples = num_samples + self.voice.get_pending_samples();
num_samples as u32 * 1000 /
(self.voice.get_samples_rate().0 as u32 * self.voice.get_channels() as u32)
}
} }

View file

@ -3,6 +3,7 @@ use std::io::{Read, Seek};
use std::thread::{self, Builder, Thread}; use std::thread::{self, Builder, Thread};
use std::sync::mpsc::{self, Sender, Receiver}; use std::sync::mpsc::{self, Sender, Receiver};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use cpal::Endpoint; use cpal::Endpoint;
@ -17,11 +18,9 @@ use time;
pub struct Engine { pub struct Engine {
/// Communication with the background thread. /// Communication with the background thread.
commands: Mutex<Sender<Command>>, commands: Mutex<Sender<Command>>,
/// The background thread that executes commands. /// The background thread that executes commands.
thread: Option<Thread>, thread: Option<Thread>,
/// Counter that is incremented whenever a sound starts playing and that is used to track each
/// sound invidiually.
next_sound_id: AtomicUsize,
} }
impl Engine { impl Engine {
@ -38,7 +37,6 @@ impl Engine {
Engine { Engine {
commands: Mutex::new(tx), commands: Mutex::new(tx),
thread: thread, thread: thread,
next_sound_id: AtomicUsize::new(1),
} }
} }
@ -48,9 +46,8 @@ impl Engine {
{ {
let decoder = decoder::decode(endpoint, input); let decoder = decoder::decode(endpoint, input);
let sound_id = self.next_sound_id.fetch_add(1, Ordering::Relaxed);
let commands = self.commands.lock().unwrap(); let commands = self.commands.lock().unwrap();
commands.send(Command::Play(sound_id, decoder)).unwrap(); commands.send(Command::Play(decoder.clone())).unwrap();
if let Some(ref thread) = self.thread { if let Some(ref thread) = self.thread {
thread.unpark(); thread.unpark();
@ -58,7 +55,7 @@ impl Engine {
Handle { Handle {
engine: self, engine: self,
id: sound_id, decoder: decoder,
} }
} }
} }
@ -68,57 +65,70 @@ impl Engine {
/// Note that dropping the handle doesn't stop the sound. You must call `stop` explicitely. /// Note that dropping the handle doesn't stop the sound. You must call `stop` explicitely.
pub struct Handle<'a> { pub struct Handle<'a> {
engine: &'a Engine, engine: &'a Engine,
id: usize, decoder: Arc<Mutex<Decoder + Send>>,
} }
impl<'a> Handle<'a> { impl<'a> Handle<'a> {
#[inline] #[inline]
pub fn set_volume(&mut self, value: f32) { pub fn set_volume(&self, value: f32) {
let commands = self.engine.commands.lock().unwrap(); // we try to touch the decoder directly from this thread
commands.send(Command::SetVolume(self.id, value)).unwrap(); if let Ok(mut decoder) = self.decoder.try_lock() {
decoder.set_volume(value);
}
// we do not wake up the commands thread // if `try_lock` failed, that means that the decoder is in use
// the samples with the previous volume have already been submitted, therefore it won't // therefore we use the backup plan of sending a message
// change anything if we wake it up let commands = self.engine.commands.lock().unwrap();
commands.send(Command::SetVolume(self.decoder.clone(), value));
} }
#[inline] #[inline]
pub fn stop(self) { pub fn stop(self) {
let commands = self.engine.commands.lock().unwrap(); let commands = self.engine.commands.lock().unwrap();
commands.send(Command::Stop(self.id)).unwrap(); commands.send(Command::Stop(self.decoder)).unwrap();
if let Some(ref thread) = self.engine.thread { if let Some(ref thread) = self.engine.thread {
thread.unpark(); thread.unpark();
} }
} }
#[inline]
pub fn get_remaining_duration_ms(&self) -> u32 {
let decoder = self.decoder.lock().unwrap();
decoder.get_remaining_duration_ms()
}
} }
pub enum Command { pub enum Command {
Play(usize, Box<Decoder + Send>), Play(Arc<Mutex<Decoder + Send>>),
Stop(usize), Stop(Arc<Mutex<Decoder + Send>>),
SetVolume(usize, f32), SetVolume(Arc<Mutex<Decoder + Send>>, f32),
} }
fn background(rx: Receiver<Command>) { fn background(rx: Receiver<Command>) {
let mut sounds: Vec<(usize, Box<Decoder + Send>)> = Vec::new(); let mut sounds: Vec<Arc<Mutex<Decoder + Send>>> = Vec::new();
loop { loop {
// polling for new sounds // polling for new sounds
if let Ok(command) = rx.try_recv() { if let Ok(command) = rx.try_recv() {
match command { match command {
Command::Play(id, decoder) => { Command::Play(decoder) => {
sounds.push((id, decoder)); sounds.push(decoder);
}, },
Command::Stop(id) => { Command::Stop(decoder) => {
sounds.retain(|&(id2, _)| id2 != id) let decoder = &*decoder as *const _;
sounds.retain(|dec| {
&**dec as *const _ != decoder
})
}, },
Command::SetVolume(id, volume) => { Command::SetVolume(decoder, volume) => {
if let Some(&mut (_, ref mut d)) = sounds.iter_mut() let decoder = &*decoder as *const _;
.find(|&&mut (i, _)| i == id) if let Some(d) = sounds.iter_mut()
.find(|dec| &***dec as *const _ != decoder)
{ {
d.set_volume(volume); d.lock().unwrap().set_volume(volume);
} }
}, },
} }
@ -130,8 +140,8 @@ fn background(rx: Receiver<Command>) {
let mut next_step_ns = before_updates + 1000000000; // 1s let mut next_step_ns = before_updates + 1000000000; // 1s
// updating the existing sounds // updating the existing sounds
for &mut (_, ref mut decoder) in &mut sounds { for decoder in &sounds {
let val = decoder.write(); let val = decoder.lock().unwrap().write();
let val = time::precise_time_ns() + val; let val = time::precise_time_ns() + val;
next_step_ns = cmp::min(next_step_ns, val); // updating next_step_ns next_step_ns = cmp::min(next_step_ns, val); // updating next_step_ns
} }

View file

@ -8,6 +8,7 @@ extern crate vorbis;
pub use cpal::{Endpoint, get_endpoints_list, get_default_endpoint}; pub use cpal::{Endpoint, get_endpoints_list, get_default_endpoint};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use std::thread;
mod conversions; mod conversions;
mod decoder; mod decoder;
@ -37,6 +38,18 @@ impl Handle {
pub fn stop(self) { pub fn stop(self) {
self.0.stop() self.0.stop()
} }
/// Returns the number of milliseconds remaining before the end of the sound.
#[inline]
pub fn get_remaining_duration_ms(&self) -> u32 {
self.0.get_remaining_duration_ms()
}
/// Sleeps the current thread until the sound ends.
#[inline]
pub fn sleep_until_end(&self) {
thread::sleep_ms(self.get_remaining_duration_ms());
}
} }
/// Plays a sound once. Returns a `Handle` that can be used to control the sound. /// Plays a sound once. Returns a `Handle` that can be used to control the sound.