mirror of
https://github.com/RustAudio/rodio
synced 2024-12-12 13:12:30 +00:00
Add RodioDevice wrapper for cpal::Device
* Remove static/global facilities.
This commit is contained in:
parent
00c627241c
commit
0517ee7216
14 changed files with 108 additions and 141 deletions
|
@ -10,9 +10,8 @@ documentation = "http://docs.rs/rodio"
|
|||
|
||||
[dependencies]
|
||||
claxon = { version = "0.4.2", optional = true }
|
||||
cpal = { git = "https://github.com/RustAudio/cpal/" }
|
||||
cpal = { git = "https://github.com/RustAudio/cpal" }
|
||||
hound = { version = "3.3.1", optional = true }
|
||||
lazy_static = "1.0.0"
|
||||
lewton = { version = "0.10", optional = true }
|
||||
minimp3 = { version = "0.3.2", optional = true }
|
||||
|
||||
|
|
|
@ -5,24 +5,22 @@ use std::thread;
|
|||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
let device = rodio::default_output_device().unwrap();
|
||||
let device = rodio::RodioDevice::default_output().unwrap();
|
||||
|
||||
let file = std::fs::File::open("examples/beep.wav").unwrap();
|
||||
let beep1 = rodio::play_once(&device, BufReader::new(file)).unwrap();
|
||||
let beep1 = device.play_once(BufReader::new(file)).unwrap();
|
||||
beep1.set_volume(0.2);
|
||||
println!("Started beep1");
|
||||
|
||||
thread::sleep(Duration::from_millis(1500));
|
||||
|
||||
let file = std::fs::File::open("examples/beep2.wav").unwrap();
|
||||
rodio::play_once(&device, BufReader::new(file))
|
||||
.unwrap()
|
||||
.detach();
|
||||
device.play_once(BufReader::new(file)).unwrap().detach();
|
||||
println!("Started beep2");
|
||||
|
||||
thread::sleep(Duration::from_millis(1500));
|
||||
let file = std::fs::File::open("examples/beep3.ogg").unwrap();
|
||||
let beep3 = rodio::play_once(&device, file).unwrap();
|
||||
let beep3 = device.play_once(file).unwrap();
|
||||
println!("Started beep3");
|
||||
|
||||
thread::sleep(Duration::from_millis(1500));
|
||||
|
|
|
@ -3,7 +3,7 @@ extern crate rodio;
|
|||
use std::io::BufReader;
|
||||
|
||||
fn main() {
|
||||
let device = rodio::default_output_device().unwrap();
|
||||
let device = rodio::RodioDevice::default_output().unwrap();
|
||||
let sink = rodio::Sink::new(&device);
|
||||
|
||||
let file = std::fs::File::open("examples/music.flac").unwrap();
|
||||
|
|
|
@ -3,7 +3,7 @@ extern crate rodio;
|
|||
use std::io::BufReader;
|
||||
|
||||
fn main() {
|
||||
let device = rodio::default_output_device().unwrap();
|
||||
let device = rodio::RodioDevice::default_output().unwrap();
|
||||
let sink = rodio::Sink::new(&device);
|
||||
|
||||
let file = std::fs::File::open("examples/music.mp3").unwrap();
|
||||
|
|
|
@ -3,7 +3,7 @@ extern crate rodio;
|
|||
use std::io::BufReader;
|
||||
|
||||
fn main() {
|
||||
let device = rodio::default_output_device().unwrap();
|
||||
let device = rodio::RodioDevice::default_output().unwrap();
|
||||
let sink = rodio::Sink::new(&device);
|
||||
|
||||
let file = std::fs::File::open("examples/music.ogg").unwrap();
|
||||
|
|
|
@ -3,7 +3,7 @@ extern crate rodio;
|
|||
use std::io::BufReader;
|
||||
|
||||
fn main() {
|
||||
let device = rodio::default_output_device().unwrap();
|
||||
let device = rodio::RodioDevice::default_output().unwrap();
|
||||
let sink = rodio::Sink::new(&device);
|
||||
|
||||
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 device = rodio::default_output_device().unwrap();
|
||||
let device = rodio::RodioDevice::default_output().unwrap();
|
||||
let sink = rodio::Sink::new(&device);
|
||||
|
||||
let file = std::fs::File::open("examples/music.ogg").unwrap();
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::thread;
|
|||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
let device = rodio::default_output_device().unwrap();
|
||||
let device = rodio::RodioDevice::default_output().unwrap();
|
||||
let sink = rodio::SpatialSink::new(
|
||||
&device,
|
||||
[-10.0, 0.0, 0.0],
|
||||
|
|
128
src/device.rs
128
src/device.rs
|
@ -1,9 +1,58 @@
|
|||
use cpal::{traits::DeviceTrait, Sample};
|
||||
use dynamic_mixer::{self, DynamicMixer, DynamicMixerController};
|
||||
use cpal::{
|
||||
traits::{DeviceTrait, HostTrait},
|
||||
Sample,
|
||||
};
|
||||
use decoder;
|
||||
use device_mixer::DeviceMixer;
|
||||
use dynamic_mixer::{self, DynamicMixerController};
|
||||
use sink::Sink;
|
||||
use source::Source;
|
||||
use std::cell::RefCell;
|
||||
use std::io::{Read, Seek};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct RodioDevice {
|
||||
mixer: RefCell<DeviceMixer>,
|
||||
inner: cpal::Device,
|
||||
}
|
||||
|
||||
impl From<cpal::Device> for RodioDevice {
|
||||
fn from(device: cpal::Device) -> Self {
|
||||
Self {
|
||||
inner: device,
|
||||
mixer: <_>::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RodioDevice {
|
||||
pub fn default_output() -> Option<Self> {
|
||||
Some(cpal::default_host().default_output_device()?.into())
|
||||
}
|
||||
|
||||
/// Plays a source with a device until it ends.
|
||||
pub fn play_raw<S>(&self, source: S)
|
||||
where
|
||||
S: Source<Item = f32> + Send + 'static,
|
||||
{
|
||||
self.mixer.borrow_mut().play(&self.inner, source)
|
||||
}
|
||||
|
||||
/// Plays a sound once. Returns a `Sink` that can be used to control the sound.
|
||||
#[inline]
|
||||
pub fn play_once<R>(&self, input: R) -> Result<Sink, decoder::DecoderError>
|
||||
where
|
||||
R: Read + Seek + Send + 'static,
|
||||
{
|
||||
let input = decoder::Decoder::new(input)?;
|
||||
let sink = Sink::new(&self);
|
||||
sink.append(input);
|
||||
Ok(sink)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions to `cpal::Device`
|
||||
pub(crate) trait RodioDevice {
|
||||
pub(crate) trait CpalDeviceExt {
|
||||
fn new_output_stream_with_format(
|
||||
&self,
|
||||
format: cpal::Format,
|
||||
|
@ -12,7 +61,7 @@ pub(crate) trait RodioDevice {
|
|||
fn new_output_stream(&self) -> (Arc<DynamicMixerController<f32>>, cpal::Stream);
|
||||
}
|
||||
|
||||
impl RodioDevice for cpal::Device {
|
||||
impl CpalDeviceExt for cpal::Device {
|
||||
fn new_output_stream_with_format(
|
||||
&self,
|
||||
format: cpal::Format,
|
||||
|
@ -20,11 +69,38 @@ impl RodioDevice for cpal::Device {
|
|||
let (mixer_tx, mut mixer_rx) =
|
||||
dynamic_mixer::mixer::<f32>(format.channels, format.sample_rate.0);
|
||||
|
||||
self.build_output_stream(
|
||||
&format,
|
||||
move |data| audio_callback(&mut mixer_rx, data),
|
||||
move |err| eprintln!("an error occurred on output stream: {}", err),
|
||||
)
|
||||
let error_callback = |err| eprintln!("an error occurred on output stream: {}", err);
|
||||
|
||||
match format.data_type {
|
||||
cpal::SampleFormat::F32 => self.build_output_stream::<f32, _, _>(
|
||||
&format.shape(),
|
||||
move |data| {
|
||||
data.iter_mut()
|
||||
.for_each(|d| *d = mixer_rx.next().unwrap_or(0f32))
|
||||
},
|
||||
error_callback,
|
||||
),
|
||||
cpal::SampleFormat::I16 => self.build_output_stream::<i16, _, _>(
|
||||
&format.shape(),
|
||||
move |data| {
|
||||
data.iter_mut()
|
||||
.for_each(|d| *d = mixer_rx.next().map(|s| s.to_i16()).unwrap_or(0i16))
|
||||
},
|
||||
error_callback,
|
||||
),
|
||||
cpal::SampleFormat::U16 => self.build_output_stream::<u16, _, _>(
|
||||
&format.shape(),
|
||||
move |data| {
|
||||
data.iter_mut().for_each(|d| {
|
||||
*d = mixer_rx
|
||||
.next()
|
||||
.map(|s| s.to_u16())
|
||||
.unwrap_or(u16::max_value() / 2)
|
||||
})
|
||||
},
|
||||
error_callback,
|
||||
),
|
||||
}
|
||||
.map(|stream| (mixer_tx, stream))
|
||||
}
|
||||
|
||||
|
@ -46,40 +122,6 @@ impl RodioDevice for cpal::Device {
|
|||
}
|
||||
}
|
||||
|
||||
fn audio_callback(mixer: &mut DynamicMixer<f32>, buffer: cpal::StreamData) {
|
||||
use cpal::{StreamData, UnknownTypeOutputBuffer};
|
||||
|
||||
match buffer {
|
||||
StreamData::Output {
|
||||
buffer: UnknownTypeOutputBuffer::U16(mut buffer),
|
||||
} => {
|
||||
for d in buffer.iter_mut() {
|
||||
*d = mixer
|
||||
.next()
|
||||
.map(|s| s.to_u16())
|
||||
.unwrap_or(u16::max_value() / 2);
|
||||
}
|
||||
}
|
||||
StreamData::Output {
|
||||
buffer: UnknownTypeOutputBuffer::I16(mut buffer),
|
||||
} => {
|
||||
for d in buffer.iter_mut() {
|
||||
*d = mixer.next().map(|s| s.to_i16()).unwrap_or(0i16);
|
||||
}
|
||||
}
|
||||
StreamData::Output {
|
||||
buffer: UnknownTypeOutputBuffer::F32(mut buffer),
|
||||
} => {
|
||||
for d in buffer.iter_mut() {
|
||||
*d = mixer.next().unwrap_or(0f32);
|
||||
}
|
||||
}
|
||||
StreamData::Input { .. } => {
|
||||
panic!("Can't play an input stream!");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// All the supported output formats with sample rates
|
||||
fn supported_output_formats(device: &cpal::Device) -> impl Iterator<Item = cpal::Format> {
|
||||
const HZ_44100: cpal::SampleRate = cpal::SampleRate(44_100);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use cpal::traits::{DeviceTrait, StreamTrait};
|
||||
use device::RodioDevice;
|
||||
use device::CpalDeviceExt;
|
||||
use dynamic_mixer::DynamicMixerController;
|
||||
use source::Source;
|
||||
use std::collections::HashMap;
|
||||
|
|
|
@ -26,8 +26,8 @@ where
|
|||
let input = Arc::new(DynamicMixerController {
|
||||
has_pending: AtomicBool::new(false),
|
||||
pending_sources: Mutex::new(Vec::new()),
|
||||
channels: channels,
|
||||
sample_rate: sample_rate,
|
||||
channels,
|
||||
sample_rate,
|
||||
});
|
||||
|
||||
let output = DynamicMixer {
|
||||
|
|
77
src/lib.rs
77
src/lib.rs
|
@ -20,7 +20,7 @@
|
|||
//! use std::io::BufReader;
|
||||
//! use rodio::Source;
|
||||
//!
|
||||
//! let device = rodio::default_output_device().unwrap();
|
||||
//! let device = rodio::RodioDevice::default_output().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 device = rodio::default_output_device().unwrap();
|
||||
//! let device = rodio::RodioDevice::default_output().unwrap();
|
||||
//! let sink = Sink::new(&device);
|
||||
//!
|
||||
//! // Add a dummy source of the sake of the example.
|
||||
|
@ -88,8 +88,6 @@ extern crate claxon;
|
|||
extern crate cpal;
|
||||
#[cfg(feature = "wav")]
|
||||
extern crate hound;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[cfg(feature = "vorbis")]
|
||||
extern crate lewton;
|
||||
#[cfg(feature = "mp3")]
|
||||
|
@ -104,11 +102,7 @@ pub use decoder::Decoder;
|
|||
pub use sink::Sink;
|
||||
pub use source::Source;
|
||||
pub use spatial_sink::SpatialSink;
|
||||
|
||||
use cpal::traits::HostTrait;
|
||||
use device_mixer::DeviceMixer;
|
||||
use std::io::{Read, Seek};
|
||||
use std::sync::Mutex;
|
||||
pub use device::RodioDevice;
|
||||
|
||||
mod conversions;
|
||||
mod sink;
|
||||
|
@ -122,68 +116,3 @@ pub mod dynamic_mixer;
|
|||
pub mod queue;
|
||||
pub mod source;
|
||||
pub mod static_buffer;
|
||||
|
||||
/// Plays a source with a device until it ends.
|
||||
pub fn play_raw<S>(device: &cpal::Device, source: S)
|
||||
where
|
||||
S: Source<Item = f32> + Send + 'static,
|
||||
{
|
||||
lazy_static! {
|
||||
static ref GLOBAL_MIXER: Mutex<DeviceMixer> = <_>::default();
|
||||
}
|
||||
GLOBAL_MIXER.lock().unwrap().play(device, source)
|
||||
}
|
||||
|
||||
/// Plays a sound once. Returns a `Sink` that can be used to control the sound.
|
||||
#[inline]
|
||||
pub fn play_once<R>(device: &Device, input: R) -> Result<Sink, decoder::DecoderError>
|
||||
where
|
||||
R: Read + Seek + Send + 'static,
|
||||
{
|
||||
let input = decoder::Decoder::new(input)?;
|
||||
let sink = Sink::new(device);
|
||||
sink.append(input);
|
||||
Ok(sink)
|
||||
}
|
||||
|
||||
/// The default input audio device on the system.
|
||||
///
|
||||
/// Returns `None` if no input device is available.
|
||||
#[inline]
|
||||
pub fn default_input_device() -> Option<Device> {
|
||||
cpal::default_host().default_input_device()
|
||||
}
|
||||
|
||||
/// The default output audio device on the system.
|
||||
///
|
||||
/// Returns `None` if no output device is available.
|
||||
#[inline]
|
||||
pub fn default_output_device() -> Option<Device> {
|
||||
cpal::default_host().default_output_device()
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the host on the system.
|
||||
///
|
||||
/// Can be empty if the system does not support audio in general.
|
||||
#[inline]
|
||||
pub fn devices() -> Result<Devices, DevicesError> {
|
||||
cpal::default_host().devices()
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||
/// input stream formats.
|
||||
///
|
||||
/// Can be empty if the system does not support audio input.
|
||||
#[inline]
|
||||
pub fn input_devices() -> Result<InputDevices<Devices>, DevicesError> {
|
||||
cpal::default_host().input_devices()
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||
/// output stream formats.
|
||||
///
|
||||
/// Can be empty if the system does not support audio output.
|
||||
#[inline]
|
||||
pub fn output_devices() -> Result<OutputDevices<Devices>, DevicesError> {
|
||||
cpal::default_host().output_devices()
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use device::RodioDevice;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize};
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
@ -5,10 +6,8 @@ use std::sync::Arc;
|
|||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use play_raw;
|
||||
use queue;
|
||||
use source::Done;
|
||||
use Device;
|
||||
use Sample;
|
||||
use Source;
|
||||
|
||||
|
@ -35,9 +34,9 @@ struct Controls {
|
|||
impl Sink {
|
||||
/// Builds a new `Sink`, beginning playback on a Device.
|
||||
#[inline]
|
||||
pub fn new(device: &Device) -> Sink {
|
||||
pub fn new(device: &RodioDevice) -> Sink {
|
||||
let (sink, queue_rx) = Sink::new_idle();
|
||||
play_raw(device, queue_rx);
|
||||
device.play_raw(queue_rx);
|
||||
sink
|
||||
}
|
||||
|
||||
|
@ -47,7 +46,7 @@ impl Sink {
|
|||
let (queue_tx, queue_rx) = queue::queue(true);
|
||||
|
||||
let sink = Sink {
|
||||
queue_tx: queue_tx,
|
||||
queue_tx,
|
||||
sleep_until_end: Mutex::new(None),
|
||||
controls: Arc::new(Controls {
|
||||
pause: AtomicBool::new(false),
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use device::RodioDevice;
|
||||
use source::Spatial;
|
||||
use std::f32;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use Device;
|
||||
use Sample;
|
||||
use Sink;
|
||||
use Source;
|
||||
|
@ -23,7 +23,7 @@ impl SpatialSink {
|
|||
/// Builds a new `SpatialSink`.
|
||||
#[inline]
|
||||
pub fn new(
|
||||
device: &Device, emitter_position: [f32; 3], left_ear: [f32; 3], right_ear: [f32; 3],
|
||||
device: &RodioDevice, emitter_position: [f32; 3], left_ear: [f32; 3], right_ear: [f32; 3],
|
||||
) -> SpatialSink {
|
||||
SpatialSink {
|
||||
sink: Sink::new(device),
|
||||
|
|
Loading…
Reference in a new issue