mirror of
https://github.com/RustAudio/rodio
synced 2025-03-04 23:17:22 +00:00
Add Handle::sleep_until_end and rework decoder handling
This commit is contained in:
parent
338f8bb7c8
commit
5c68d37055
6 changed files with 75 additions and 34 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
13
src/lib.rs
13
src/lib.rs
|
@ -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.
|
||||||
|
|
Loading…
Add table
Reference in a new issue