mirror of
https://github.com/RustAudio/rodio
synced 2024-12-13 13:42:34 +00:00
merge upstream/master
This commit is contained in:
commit
2f1b3f5309
12 changed files with 123 additions and 290 deletions
|
@ -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 }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
169
src/engine.rs
169
src/engine.rs
|
@ -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)
|
||||
}
|
||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -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"))]
|
||||
|
|
42
src/sink.rs
42
src/sink.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue