merge upstream/master

This commit is contained in:
Andriy S. from cobalt 2017-10-29 18:06:20 +02:00
commit 2f1b3f5309
12 changed files with 123 additions and 290 deletions

View file

@ -10,8 +10,7 @@ documentation = "http://docs.rs/rodio"
[dependencies]
claxon = { version = "0.3.0", optional = true }
cpal = "0.4.0"
futures = "0.1.1"
cpal = "0.5.1"
hound = { version = "1.0.0", optional = true }
lazy_static = "0.1.12"
lewton = { version = "0.5", optional = true }

View file

@ -5,7 +5,7 @@ use std::thread;
use std::time::Duration;
fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let endpoint = rodio::default_endpoint().unwrap();
let file = std::fs::File::open("examples/beep.wav").unwrap();
let mut beep1 = rodio::play_once(&endpoint, BufReader::new(file)).unwrap();

View file

@ -3,7 +3,7 @@ extern crate rodio;
use std::io::BufReader;
fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let endpoint = rodio::default_endpoint().unwrap();
let sink = rodio::Sink::new(&endpoint);
let file = std::fs::File::open("examples/music.flac").unwrap();

View file

@ -3,7 +3,7 @@ extern crate rodio;
use std::io::BufReader;
fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let endpoint = rodio::default_endpoint().unwrap();
let sink = rodio::Sink::new(&endpoint);
let file = std::fs::File::open("examples/music.ogg").unwrap();

View file

@ -3,7 +3,7 @@ extern crate rodio;
use std::io::BufReader;
fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let endpoint = rodio::default_endpoint().unwrap();
let sink = rodio::Sink::new(&endpoint);
let file = std::fs::File::open("examples/music.wav").unwrap();

View file

@ -5,7 +5,7 @@ use std::io::BufReader;
use std::time::Duration;
fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let endpoint = rodio::default_endpoint().unwrap();
let sink = rodio::Sink::new(&endpoint);
let file = std::fs::File::open("examples/music.ogg").unwrap();

View file

@ -5,7 +5,7 @@ use std::thread;
use std::time::Duration;
fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let endpoint = rodio::default_endpoint().unwrap();
let mut sink = rodio::SpatialSink::new(&endpoint,
[-10.0, 0.0, 0.0],
[1.0, 0.0, 0.0],

View file

@ -1,5 +1,5 @@
use cpal;
use cpal::Sample as CpalSample;
use std::marker::PhantomData;
/// Converts the samples data type to `O`.
@ -35,7 +35,7 @@ impl<I, O> Iterator for DataConverter<I, O>
#[inline]
fn next(&mut self) -> Option<O> {
self.input.next().map(|s| Sample::from(&s))
self.input.next().map(|s| CpalSample::from(&s))
}
#[inline]
@ -64,7 +64,7 @@ impl<I, O> ExactSizeIterator for DataConverter<I, O>
///
/// You can implement this trait on your own type as well if you wish so.
///
pub trait Sample: cpal::Sample {
pub trait Sample: CpalSample {
/// Linear interpolation between two samples.
///
/// The result should be equal to
@ -78,17 +78,6 @@ pub trait Sample: cpal::Sample {
/// Returns the value corresponding to the absence of sound.
fn zero_value() -> Self;
/// Converts this sample into a standard i16 sample.
fn to_i16(&self) -> i16;
/// Converts this sample into a standard u16 sample.
fn to_u16(&self) -> u16;
/// Converts this sample into a standard f32 sample.
fn to_f32(&self) -> f32;
/// Converts any sample type to this one by calling `to_i16`, `to_u16` or `to_f32`.
fn from<S>(&S) -> Self
where S: Sample;
}
impl Sample for u16 {
@ -111,32 +100,6 @@ impl Sample for u16 {
fn zero_value() -> u16 {
32768
}
#[inline]
fn to_i16(&self) -> i16 {
if *self >= 32768 {
(*self - 32768) as i16
} else {
(*self as i16) - 32767 - 1
}
}
#[inline]
fn to_u16(&self) -> u16 {
*self
}
#[inline]
fn to_f32(&self) -> f32 {
self.to_i16().to_f32()
}
#[inline]
fn from<S>(sample: &S) -> Self
where S: Sample
{
sample.to_u16()
}
}
impl Sample for i16 {
@ -160,36 +123,6 @@ impl Sample for i16 {
fn zero_value() -> i16 {
0
}
#[inline]
fn to_i16(&self) -> i16 {
*self
}
#[inline]
fn to_u16(&self) -> u16 {
if *self < 0 {
(*self - ::std::i16::MIN) as u16
} else {
(*self as u16) + 32768
}
}
#[inline]
fn to_f32(&self) -> f32 {
if *self < 0 {
*self as f32 / -(::std::i16::MIN as f32)
} else {
*self as f32 / ::std::i16::MAX as f32
}
}
#[inline]
fn from<S>(sample: &S) -> Self
where S: Sample
{
sample.to_i16()
}
}
impl Sample for f32 {
@ -212,104 +145,4 @@ impl Sample for f32 {
fn zero_value() -> f32 {
0.0
}
#[inline]
fn to_i16(&self) -> i16 {
if *self >= 0.0 {
(*self * ::std::i16::MAX as f32) as i16
} else {
(-*self * ::std::i16::MIN as f32) as i16
}
}
#[inline]
fn to_u16(&self) -> u16 {
(((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16
}
#[inline]
fn to_f32(&self) -> f32 {
*self
}
#[inline]
fn from<S>(sample: &S) -> Self
where S: Sample
{
sample.to_f32()
}
}
#[cfg(test)]
mod test {
use super::Sample;
#[test]
fn i16_to_i16() {
assert_eq!(0i16.to_i16(), 0);
assert_eq!((-467i16).to_i16(), -467);
assert_eq!(32767i16.to_i16(), 32767);
assert_eq!((-32768i16).to_i16(), -32768);
}
#[test]
fn i16_to_u16() {
assert_eq!(0i16.to_u16(), 32768);
assert_eq!((-16384i16).to_u16(), 16384);
assert_eq!(32767i16.to_u16(), 65535);
assert_eq!((-32768i16).to_u16(), 0);
}
#[test]
fn i16_to_f32() {
assert_eq!(0i16.to_f32(), 0.0);
assert_eq!((-16384i16).to_f32(), -0.5);
assert_eq!(32767i16.to_f32(), 1.0);
assert_eq!((-32768i16).to_f32(), -1.0);
}
#[test]
fn u16_to_i16() {
assert_eq!(32768u16.to_i16(), 0);
assert_eq!(16384u16.to_i16(), -16384);
assert_eq!(65535u16.to_i16(), 32767);
assert_eq!(0u16.to_i16(), -32768);
}
#[test]
fn u16_to_u16() {
assert_eq!(0u16.to_u16(), 0);
assert_eq!(467u16.to_u16(), 467);
assert_eq!(32767u16.to_u16(), 32767);
assert_eq!(65535u16.to_u16(), 65535);
}
#[test]
fn u16_to_f32() {
assert_eq!(0u16.to_f32(), -1.0);
assert_eq!(32768u16.to_f32(), 0.0);
assert_eq!(65535u16.to_f32(), 1.0);
}
#[test]
fn f32_to_i16() {
assert_eq!(0.0f32.to_i16(), 0);
assert_eq!((-0.5f32).to_i16(), ::std::i16::MIN / 2);
assert_eq!(1.0f32.to_i16(), ::std::i16::MAX);
assert_eq!((-1.0f32).to_i16(), ::std::i16::MIN);
}
#[test]
fn f32_to_u16() {
assert_eq!((-1.0f32).to_u16(), 0);
assert_eq!(0.0f32.to_u16(), 32768);
assert_eq!(1.0f32.to_u16(), 65535);
}
#[test]
fn f32_to_f32() {
assert_eq!(0.1f32.to_f32(), 0.1);
assert_eq!((-0.7f32).to_f32(), -0.7);
assert_eq!(1.0f32.to_f32(), 1.0);
}
}

View file

@ -5,17 +5,12 @@ use std::sync::Mutex;
use std::sync::Weak;
use std::thread::Builder;
use futures::stream::Stream;
use futures::task;
use futures::task::Executor;
use futures::task::Run;
use conversions::Sample;
use cpal;
use cpal::Endpoint;
use cpal::EventLoop;
use cpal::Sample as CpalSample;
use cpal::UnknownTypeBuffer;
use cpal::Voice;
use cpal::VoiceId;
use dynamic_mixer;
use source::Source;
@ -26,28 +21,33 @@ pub fn play_raw<S>(endpoint: &Endpoint, source: S)
where S: Source<Item = f32> + Send + 'static
{
lazy_static! {
static ref ENGINE: Engine = {
let events_loop = Arc::new(EventLoop::new());
static ref ENGINE: Arc<Engine> = {
let engine = Arc::new(Engine {
events_loop: EventLoop::new(),
dynamic_mixers: Mutex::new(HashMap::with_capacity(1)),
end_points: Mutex::new(HashMap::with_capacity(1)),
});
// We ignore errors when creating the background thread.
// The user won't get any audio, but that's better than a panic.
Builder::new()
.name("rodio audio processing".to_string())
.spawn({
let events_loop = events_loop.clone();
move || events_loop.run()
let engine = engine.clone();
move || {
engine.events_loop.run(|voice_id, buffer| {
audio_callback(&engine, voice_id, buffer);
})
}
})
.ok()
.map(|jg| jg.thread().clone());
Engine {
events_loop: events_loop,
end_points: Mutex::new(HashMap::with_capacity(1)),
}
engine
};
}
ENGINE.start(endpoint, source);
start(&ENGINE, endpoint, source);
}
// The internal engine of this library.
@ -55,56 +55,83 @@ pub fn play_raw<S>(endpoint: &Endpoint, source: S)
// Each `Engine` owns a thread that runs in the background and plays the audio.
struct Engine {
// The events loop which the voices are created with.
events_loop: Arc<EventLoop>,
events_loop: EventLoop,
dynamic_mixers: Mutex<HashMap<VoiceId, dynamic_mixer::DynamicMixer<f32>>>,
// TODO: don't use the endpoint name, as it's slow
end_points: Mutex<HashMap<String, Weak<dynamic_mixer::DynamicMixerController<f32>>>>,
}
impl Engine {
// Builds a new sink that targets a given endpoint.
fn start<S>(&self, endpoint: &Endpoint, source: S)
where S: Source<Item = f32> + Send + 'static
{
let mut voice_to_start = None;
fn audio_callback(engine: &Arc<Engine>, voice_id: VoiceId, mut buffer: UnknownTypeBuffer) {
let mut dynamic_mixers = engine.dynamic_mixers.lock().unwrap();
let mixer = {
let mut end_points = self.end_points.lock().unwrap();
let mixer_rx = match dynamic_mixers.get_mut(&voice_id) {
Some(m) => m,
None => return,
};
match end_points.entry(endpoint.get_name()) {
Entry::Vacant(e) => {
let (mixer, voice) = new_voice(endpoint, &self.events_loop);
match buffer {
UnknownTypeBuffer::U16(ref mut buffer) => {
for d in buffer.iter_mut() {
*d = mixer_rx.next().map(|s| s.to_u16()).unwrap_or(0u16);
}
},
UnknownTypeBuffer::I16(ref mut buffer) => {
for d in buffer.iter_mut() {
*d = mixer_rx.next().map(|s| s.to_i16()).unwrap_or(0i16);
}
},
UnknownTypeBuffer::F32(ref mut buffer) => {
for d in buffer.iter_mut() {
*d = mixer_rx.next().unwrap_or(0f32);
}
},
};
}
// Builds a new sink that targets a given endpoint.
fn start<S>(engine: &Arc<Engine>, endpoint: &Endpoint, source: S)
where S: Source<Item = f32> + Send + 'static
{
let mut voice_to_start = None;
let mixer = {
let mut end_points = engine.end_points.lock().unwrap();
match end_points.entry(endpoint.name()) {
Entry::Vacant(e) => {
let (mixer, voice) = new_voice(engine, endpoint);
e.insert(Arc::downgrade(&mixer));
voice_to_start = Some(voice);
mixer
},
Entry::Occupied(mut e) => {
if let Some(m) = e.get().upgrade() {
m.clone()
} else {
let (mixer, voice) = new_voice(engine, endpoint);
e.insert(Arc::downgrade(&mixer));
voice_to_start = Some(voice);
mixer
},
Entry::Occupied(mut e) => {
if let Some(m) = e.get().upgrade() {
m.clone()
} else {
let (mixer, voice) = new_voice(endpoint, &self.events_loop);
e.insert(Arc::downgrade(&mixer));
voice_to_start = Some(voice);
mixer
}
},
}
};
mixer.add(source);
if let Some(mut voice) = voice_to_start {
voice.play();
}
},
}
};
if let Some(voice) = voice_to_start {
engine.events_loop.play(voice);
}
mixer.add(source);
}
// Adds a new voice to the engine.
// TODO: handle possible errors here
fn new_voice(endpoint: &Endpoint, events_loop: &Arc<EventLoop>)
-> (Arc<dynamic_mixer::DynamicMixerController<f32>>, Voice) {
fn new_voice(engine: &Arc<Engine>, endpoint: &Endpoint) -> (Arc<dynamic_mixer::DynamicMixerController<f32>>, VoiceId) {
// Determine the format to use for the new voice.
let format = endpoint
.get_supported_formats_list()
.supported_formats()
.unwrap()
.fold(None, |f1, f2| {
if f1.is_none() {
@ -119,7 +146,7 @@ fn new_voice(endpoint: &Endpoint, events_loop: &Arc<EventLoop>)
}
// Do not go below 44100 if possible.
if f1.samples_rate.0 < 44100 {
if f1.min_samples_rate.0 < 44100 {
return Some(f2);
}
@ -130,45 +157,15 @@ fn new_voice(endpoint: &Endpoint, events_loop: &Arc<EventLoop>)
Some(f1)
})
.expect("The endpoint doesn't support any format!?");
.expect("The endpoint doesn't support any format!?")
.with_max_samples_rate();
let (voice, stream) = Voice::new(&endpoint, &format, events_loop).unwrap();
let (mixer_tx, mut mixer_rx) = {
let voice_id = engine.events_loop.build_voice(endpoint, &format).unwrap();
let (mixer_tx, mixer_rx) = {
dynamic_mixer::mixer::<f32>(format.channels.len() as u16, format.samples_rate.0)
};
let future_to_exec = stream.for_each(move |mut buffer| -> Result<_, ()> {
match buffer {
UnknownTypeBuffer::U16(ref mut buffer) => {
for d in buffer.iter_mut() {
*d = mixer_rx.next().map(|s| s.to_u16()).unwrap_or(0u16);
}
},
UnknownTypeBuffer::I16(ref mut buffer) => {
for d in buffer.iter_mut() {
*d = mixer_rx.next().map(|s| s.to_i16()).unwrap_or(0i16);
}
},
UnknownTypeBuffer::F32(ref mut buffer) => {
for d in buffer.iter_mut() {
*d = mixer_rx.next().unwrap_or(0f32);
}
},
};
engine.dynamic_mixers.lock().unwrap().insert(voice_id.clone(), mixer_rx);
Ok(())
});
{
struct MyExecutor;
impl Executor for MyExecutor {
fn execute(&self, r: Run) {
r.run();
}
}
task::spawn(future_to_exec).execute(Arc::new(MyExecutor));
}
(mixer_tx, voice)
(mixer_tx, voice_id)
}

View file

@ -6,8 +6,8 @@
//! - Create an object that represents the streaming sound. It can be a sine wave, a buffer, a
//! [decoder](decoder/index.html), etc. or even your own type that implements
//! [the `Source` trait](source/trait.Source.html).
//! - Choose an output with the [`get_endpoints_list`](fn.get_endpoints_list.html) or
//! [`get_default_endpoint`](fn.get_default_endpoint.html) functions.
//! - Choose an output with the [`endpoints`](fn.endpoints.html) or
//! [`default_endpoint`](fn.default_endpoint.html) functions.
//! - Call [`play_raw(output, source)`](fn.play_raw.html).
//!
//! The `play_raw` function expects the source to produce `f32`s, which may not be the case. If you
@ -20,7 +20,7 @@
//! use std::io::BufReader;
//! use rodio::Source;
//!
//! let endpoint = rodio::get_default_endpoint().unwrap();
//! let endpoint = rodio::default_endpoint().unwrap();
//!
//! let file = File::open("sound.ogg").unwrap();
//! let source = rodio::Decoder::new(BufReader::new(file)).unwrap();
@ -38,7 +38,7 @@
//! ```no_run
//! use rodio::Sink;
//!
//! let endpoint = rodio::get_default_endpoint().unwrap();
//! let endpoint = rodio::default_endpoint().unwrap();
//! let sink = Sink::new(&endpoint);
//!
//! // Add a dummy source of the sake of the example.
@ -86,7 +86,6 @@
#[cfg(feature = "flac")]
extern crate claxon;
extern crate cpal;
extern crate futures;
#[cfg(feature = "wav")]
extern crate hound;
#[macro_use]
@ -95,7 +94,7 @@ extern crate lazy_static;
extern crate lewton;
extern crate cgmath;
pub use cpal::{Endpoint, get_default_endpoint, get_endpoints_list};
pub use cpal::{Endpoint, default_endpoint, endpoints, get_default_endpoint, get_endpoints_list};
pub use conversions::Sample;
#[cfg(any(feature = "wav", feature = "flac", feature = "vorbis"))]

View file

@ -21,14 +21,18 @@ pub struct Sink {
queue_tx: Arc<queue::SourcesQueueInput<f32>>,
sleep_until_end: Mutex<Option<Receiver<()>>>,
pause: Arc<AtomicBool>,
volume: Arc<Mutex<f32>>,
stopped: Arc<AtomicBool>,
controls: Arc<Controls>,
sound_count: Arc<AtomicUsize>,
detached: bool,
}
struct Controls {
pause: AtomicBool,
volume: Mutex<f32>,
stopped: AtomicBool,
}
impl Sink {
/// Builds a new `Sink`.
#[inline]
@ -39,9 +43,11 @@ impl Sink {
Sink {
queue_tx: queue_tx,
sleep_until_end: Mutex::new(None),
pause: Arc::new(AtomicBool::new(false)),
volume: Arc::new(Mutex::new(1.0)),
stopped: Arc::new(AtomicBool::new(false)),
controls: Arc::new(Controls {
pause: AtomicBool::new(false),
volume: Mutex::new(1.0),
stopped: AtomicBool::new(false),
}),
sound_count: Arc::new(AtomicUsize::new(0)),
detached: false,
}
@ -54,22 +60,20 @@ impl Sink {
S::Item: Sample,
S::Item: Send
{
let volume = self.volume.clone();
let pause = self.pause.clone();
let stopped = self.stopped.clone();
let controls = self.controls.clone();
let source = source
.pausable(false)
.amplify(1.0)
.stoppable()
.periodic_access(Duration::from_millis(5), move |src| {
if stopped.load(Ordering::SeqCst) {
if controls.stopped.load(Ordering::SeqCst) {
src.stop();
} else {
src.inner_mut().set_factor(*volume.lock().unwrap());
src.inner_mut().set_factor(*controls.volume.lock().unwrap());
src.inner_mut()
.inner_mut()
.set_paused(pause.load(Ordering::SeqCst));
.set_paused(controls.pause.load(Ordering::SeqCst));
}
})
.convert_samples();
@ -84,7 +88,7 @@ impl Sink {
/// multiply each sample by this value.
#[inline]
pub fn volume(&self) -> f32 {
*self.volume.lock().unwrap()
*self.controls.volume.lock().unwrap()
}
/// Changes the volume of the sound.
@ -93,7 +97,7 @@ impl Sink {
/// multiply each sample by this value.
#[inline]
pub fn set_volume(&mut self, value: f32) {
*self.volume.lock().unwrap() = value;
*self.controls.volume.lock().unwrap() = value;
}
/// Resumes playback of a paused sound.
@ -101,7 +105,7 @@ impl Sink {
/// No effect if not paused.
#[inline]
pub fn play(&self) {
self.pause.store(false, Ordering::SeqCst);
self.controls.pause.store(false, Ordering::SeqCst);
}
/// Pauses playback of this sink.
@ -110,20 +114,20 @@ impl Sink {
///
/// A paused sound can be resumed with `play()`.
pub fn pause(&self) {
self.pause.store(true, Ordering::SeqCst);
self.controls.pause.store(true, Ordering::SeqCst);
}
/// Gets if a sound is paused
///
/// Sounds can be paused and resumed using pause() and play(). This gets if a sound is paused.
pub fn is_paused(&self) -> bool {
self.pause.load(Ordering::SeqCst)
self.controls.pause.load(Ordering::SeqCst)
}
/// Stops the sink by emptying the queue.
#[inline]
pub fn stop(&self) {
self.stopped.store(true, Ordering::SeqCst);
self.controls.stopped.store(true, Ordering::SeqCst);
}
/// Destroys the sink without stopping the sounds that are still playing.
@ -153,7 +157,7 @@ impl Drop for Sink {
self.queue_tx.set_keep_alive_if_empty(false);
if !self.detached {
self.stopped.store(true, Ordering::Relaxed);
self.controls.stopped.store(true, Ordering::Relaxed);
}
}
}

View file

@ -1,6 +1,7 @@
use std::marker::PhantomData;
use std::time::Duration;
use cpal::Sample as CpalSample;
use Sample;
use Source;
@ -42,7 +43,7 @@ impl<I, D> Iterator for SamplesConverter<I, D>
#[inline]
fn next(&mut self) -> Option<D> {
self.inner.next().map(|s| Sample::from(&s))
self.inner.next().map(|s| CpalSample::from(&s))
}
#[inline]