Merge pull request #289 from mitchmindtree/host

Introduce a `Host` API
This commit is contained in:
mitchmindtree 2019-06-25 17:31:35 +02:00 committed by GitHub
commit 7e413cb660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1546 additions and 561 deletions

View File

@ -6,6 +6,7 @@ Low-level library for audio input and output in pure Rust.
This library currently supports the following:
- Enumerate supported audio hosts.
- Enumerate all available audio devices.
- Get the current default input and output devices.
- Enumerate known supported input and output stream formats for a device.

View File

@ -1,10 +1,13 @@
extern crate cpal;
extern crate failure;
use cpal::{Device, EventLoop, Host};
fn main() -> Result<(), failure::Error> {
let device = cpal::default_output_device().expect("failed to find a default output device");
let host = cpal::default_host();
let device = host.default_output_device().expect("failed to find a default output device");
let format = device.default_output_format()?;
let event_loop = cpal::EventLoop::new();
let event_loop = host.event_loop();
let stream_id = event_loop.build_output_stream(&device, &format)?;
event_loop.play_stream(stream_id.clone())?;

View File

@ -1,16 +1,25 @@
extern crate cpal;
extern crate failure;
fn main() -> Result<(), failure::Error> {
let default_in = cpal::default_input_device().map(|e| e.name().unwrap());
let default_out = cpal::default_output_device().map(|e| e.name().unwrap());
println!("Default Input Device:\n {:?}", default_in);
println!("Default Output Device:\n {:?}", default_out);
use cpal::{Device, Host};
let devices = cpal::devices()?;
println!("Devices: ");
fn main() -> Result<(), failure::Error> {
println!("Supported hosts:\n {:?}", cpal::ALL_HOSTS);
let available_hosts = cpal::available_hosts();
println!("Available hosts:\n {:?}", available_hosts);
for host_id in available_hosts {
println!("{:?}", host_id);
let host = cpal::host_from_id(host_id)?;
let default_in = host.default_input_device().map(|e| e.name().unwrap());
let default_out = host.default_output_device().map(|e| e.name().unwrap());
println!(" Default Input Device:\n {:?}", default_in);
println!(" Default Output Device:\n {:?}", default_out);
let devices = host.devices()?;
println!(" Devices: ");
for (device_index, device) in devices.enumerate() {
println!("{}. \"{}\"", device_index + 1, device.name()?);
println!(" {}. \"{}\"", device_index + 1, device.name()?);
// Input formats
if let Ok(fmt) = device.default_input_format() {
@ -48,6 +57,7 @@ fn main() -> Result<(), failure::Error> {
}
}
}
}
Ok(())
}

View File

@ -9,14 +9,17 @@
extern crate cpal;
extern crate failure;
use cpal::{Device, EventLoop, Host};
const LATENCY_MS: f32 = 150.0;
fn main() -> Result<(), failure::Error> {
let event_loop = cpal::EventLoop::new();
let host = cpal::default_host();
let event_loop = host.event_loop();
// Default devices.
let input_device = cpal::default_input_device().expect("failed to get default input device");
let output_device = cpal::default_output_device().expect("failed to get default output device");
let input_device = host.default_input_device().expect("failed to get default input device");
let output_device = host.default_output_device().expect("failed to get default output device");
println!("Using default input device: \"{}\"", input_device.name()?);
println!("Using default output device: \"{}\"", output_device.name()?);

View File

@ -6,13 +6,18 @@ extern crate cpal;
extern crate failure;
extern crate hound;
use cpal::{Device, EventLoop, Host};
fn main() -> Result<(), failure::Error> {
// Use the default host for working with audio devices.
let host = cpal::default_host();
// Setup the default input device and stream with the default input format.
let device = cpal::default_input_device().expect("Failed to get default input device");
let device = host.default_input_device().expect("Failed to get default input device");
println!("Default input device: {}", device.name()?);
let format = device.default_input_format().expect("Failed to get default input format");
println!("Default input format: {:?}", format);
let event_loop = cpal::EventLoop::new();
let event_loop = host.event_loop();
let stream_id = event_loop.build_input_stream(&device, &format)?;
event_loop.play_stream(stream_id)?;

View File

@ -7,16 +7,21 @@ use ChannelCount;
use BackendSpecificError;
use BuildStreamError;
use DefaultFormatError;
use Device as DeviceTrait;
use DeviceNameError;
use DevicesError;
use EventLoop as EventLoopTrait;
use Format;
use Host as HostTrait;
use PauseStreamError;
use PlayStreamError;
use SupportedFormatsError;
use SampleFormat;
use SampleRate;
use SupportedFormatsError;
use StreamData;
use StreamDataResult;
use StreamError;
use StreamId as StreamIdTrait;
use SupportedFormat;
use UnknownTypeInputBuffer;
use UnknownTypeOutputBuffer;
@ -32,6 +37,109 @@ pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
mod enumerate;
/// The default linux and freebsd host type.
#[derive(Debug)]
pub struct Host;
impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
Ok(Host)
}
}
impl HostTrait for Host {
type Devices = Devices;
type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool {
// Assume ALSA is always available on linux/freebsd.
true
}
fn devices(&self) -> Result<Self::Devices, DevicesError> {
Devices::new()
}
fn default_input_device(&self) -> Option<Self::Device> {
default_input_device()
}
fn default_output_device(&self) -> Option<Self::Device> {
default_output_device()
}
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
}
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
}
}
impl EventLoopTrait for EventLoop {
type Device = Device;
type StreamId = StreamId;
fn build_input_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_input_stream(self, device, format)
}
fn build_output_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_output_stream(self, device, format)
}
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
EventLoop::play_stream(self, stream)
}
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
EventLoop::pause_stream(self, stream)
}
fn destroy_stream(&self, stream: Self::StreamId) {
EventLoop::destroy_stream(self, stream)
}
fn run<F>(&self, callback: F) -> !
where
F: FnMut(Self::StreamId, StreamDataResult) + Send,
{
EventLoop::run(self, callback)
}
}
impl StreamIdTrait for StreamId {}
struct Trigger {
// [read fd, write fd]
@ -79,7 +187,7 @@ pub struct Device(String);
impl Device {
#[inline]
pub fn name(&self) -> Result<String, DeviceNameError> {
fn name(&self) -> Result<String, DeviceNameError> {
Ok(self.0.clone())
}
@ -287,13 +395,13 @@ impl Device {
Ok(output.into_iter())
}
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
unsafe {
self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE)
}
}
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
unsafe {
self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK)
}
@ -340,11 +448,11 @@ impl Device {
}
}
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(alsa::SND_PCM_STREAM_CAPTURE)
}
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(alsa::SND_PCM_STREAM_PLAYBACK)
}
}
@ -434,7 +542,7 @@ enum StreamType { Input, Output }
impl EventLoop {
#[inline]
pub fn new() -> EventLoop {
fn new() -> EventLoop {
let pending_command_trigger = Trigger::new();
let mut initial_descriptors = vec![];
@ -460,7 +568,7 @@ impl EventLoop {
}
#[inline]
pub fn run<F>(&self, mut callback: F) -> !
fn run<F>(&self, mut callback: F) -> !
where F: FnMut(StreamId, StreamDataResult)
{
self.run_inner(&mut callback)
@ -645,7 +753,7 @@ impl EventLoop {
panic!("`cpal::EventLoop::run` API currently disallows returning");
}
pub fn build_input_stream(
fn build_input_stream(
&self,
device: &Device,
format: &Format,
@ -724,7 +832,7 @@ impl EventLoop {
}
}
pub fn build_output_stream(
fn build_output_stream(
&self,
device: &Device,
format: &Format,
@ -805,18 +913,18 @@ impl EventLoop {
}
#[inline]
pub fn destroy_stream(&self, stream_id: StreamId) {
fn destroy_stream(&self, stream_id: StreamId) {
self.push_command(Command::DestroyStream(stream_id));
}
#[inline]
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
self.push_command(Command::PlayStream(stream_id));
Ok(())
}
#[inline]
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
self.push_command(Command::PauseStream(stream_id));
Ok(())
}

View File

@ -5,8 +5,12 @@ use ChannelCount;
use BackendSpecificError;
use BuildStreamError;
use DefaultFormatError;
use Device as DeviceTrait;
use DeviceNameError;
use DevicesError;
use EventLoop as EventLoopTrait;
use Format;
use Host as HostTrait;
use PauseStreamError;
use PlayStreamError;
use SupportedFormatsError;
@ -14,6 +18,7 @@ use SampleFormat;
use SampleRate;
use StreamData;
use StreamDataResult;
use StreamId as StreamIdTrait;
use SupportedFormat;
use UnknownTypeInputBuffer;
use UnknownTypeOutputBuffer;
@ -72,13 +77,117 @@ mod enumerate;
pub use self::enumerate::{Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
/// Coreaudio host, the default host on macOS and iOS.
#[derive(Debug)]
pub struct Host;
impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
Ok(Host)
}
}
impl HostTrait for Host {
type Devices = Devices;
type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool {
// Assume coreaudio is always available on macOS and iOS.
true
}
fn devices(&self) -> Result<Self::Devices, DevicesError> {
Devices::new()
}
fn default_input_device(&self) -> Option<Self::Device> {
default_input_device()
}
fn default_output_device(&self) -> Option<Self::Device> {
default_output_device()
}
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
}
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
}
}
impl EventLoopTrait for EventLoop {
type Device = Device;
type StreamId = StreamId;
fn build_input_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_input_stream(self, device, format)
}
fn build_output_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_output_stream(self, device, format)
}
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
EventLoop::play_stream(self, stream)
}
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
EventLoop::pause_stream(self, stream)
}
fn destroy_stream(&self, stream: Self::StreamId) {
EventLoop::destroy_stream(self, stream)
}
fn run<F>(&self, callback: F) -> !
where
F: FnMut(Self::StreamId, StreamDataResult) + Send,
{
EventLoop::run(self, callback)
}
}
impl StreamIdTrait for StreamId {}
#[derive(Clone, PartialEq, Eq)]
pub struct Device {
audio_device_id: AudioDeviceID,
}
impl Device {
pub fn name(&self) -> Result<String, DeviceNameError> {
fn name(&self) -> Result<String, DeviceNameError> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceNameCFString,
mScope: kAudioDevicePropertyScopeOutput,
@ -212,11 +321,11 @@ impl Device {
}
}
pub fn supported_input_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
fn supported_input_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
self.supported_formats(kAudioObjectPropertyScopeInput)
}
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
self.supported_formats(kAudioObjectPropertyScopeOutput)
}
@ -296,11 +405,11 @@ impl Device {
}
}
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(kAudioObjectPropertyScopeInput)
}
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(kAudioObjectPropertyScopeOutput)
}
}
@ -435,7 +544,7 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
impl EventLoop {
#[inline]
pub fn new() -> EventLoop {
fn new() -> EventLoop {
EventLoop {
user_callback: Arc::new(Mutex::new(UserCallback::Inactive)),
streams: Mutex::new(Vec::new()),
@ -443,7 +552,7 @@ impl EventLoop {
}
#[inline]
pub fn run<F>(&self, mut callback: F) -> !
fn run<F>(&self, mut callback: F) -> !
where F: FnMut(StreamId, StreamDataResult) + Send
{
{
@ -490,7 +599,7 @@ impl EventLoop {
}
#[inline]
pub fn build_input_stream(
fn build_input_stream(
&self,
device: &Device,
format: &Format,
@ -712,7 +821,7 @@ impl EventLoop {
}
#[inline]
pub fn build_output_stream(
fn build_output_stream(
&self,
device: &Device,
format: &Format,
@ -787,14 +896,14 @@ impl EventLoop {
Ok(StreamId(stream_id))
}
pub fn destroy_stream(&self, stream_id: StreamId) {
fn destroy_stream(&self, stream_id: StreamId) {
{
let mut streams = self.streams.lock().unwrap();
streams[stream_id.0] = None;
}
}
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
let mut streams = self.streams.lock().unwrap();
let stream = streams[stream_id.0].as_mut().unwrap();
@ -809,7 +918,7 @@ impl EventLoop {
Ok(())
}
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
let mut streams = self.streams.lock().unwrap();
let stream = streams[stream_id.0].as_mut().unwrap();

View File

@ -10,17 +10,125 @@ use stdweb::web::set_timeout;
use BuildStreamError;
use DefaultFormatError;
use Device as DeviceTrait;
use DeviceNameError;
use DevicesError;
use EventLoop as EventLoopTrait;
use Format;
use Host as HostTrait;
use PauseStreamError;
use PlayStreamError;
use SupportedFormatsError;
use StreamData;
use StreamDataResult;
use StreamId as StreamIdTrait;
use SupportedFormat;
use UnknownTypeOutputBuffer;
/// The default emscripten host type.
#[derive(Debug)]
pub struct Host;
impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
Ok(Host)
}
}
impl HostTrait for Host {
type Devices = Devices;
type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool {
// Assume this host is always available on emscripten.
true
}
fn devices(&self) -> Result<Self::Devices, DevicesError> {
Devices::new()
}
fn default_input_device(&self) -> Option<Self::Device> {
default_input_device()
}
fn default_output_device(&self) -> Option<Self::Device> {
default_output_device()
}
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
}
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
}
}
impl EventLoopTrait for EventLoop {
type Device = Device;
type StreamId = StreamId;
fn build_input_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_input_stream(self, device, format)
}
fn build_output_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_output_stream(self, device, format)
}
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
EventLoop::play_stream(self, stream)
}
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
EventLoop::pause_stream(self, stream)
}
fn destroy_stream(&self, stream: Self::StreamId) {
EventLoop::destroy_stream(self, stream)
}
fn run<F>(&self, callback: F) -> !
where
F: FnMut(Self::StreamId, StreamDataResult) + Send,
{
EventLoop::run(self, callback)
}
}
impl StreamIdTrait for StreamId {}
// The emscripten backend works by having a global variable named `_cpal_audio_contexts`, which
// is an array of `AudioContext` objects. A stream ID corresponds to an entry in this array.
//
@ -44,7 +152,7 @@ impl EventLoop {
}
#[inline]
pub fn run<F>(&self, callback: F) -> !
fn run<F>(&self, callback: F) -> !
where F: FnMut(StreamId, StreamDataResult),
{
// The `run` function uses `set_timeout` to invoke a Rust callback repeatidely. The job
@ -124,12 +232,12 @@ impl EventLoop {
}
#[inline]
pub fn build_input_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
fn build_input_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
unimplemented!();
}
#[inline]
pub fn build_output_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
fn build_output_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
let stream = js!(return new AudioContext()).into_reference().unwrap();
let mut streams = self.streams.lock().unwrap();
@ -146,12 +254,12 @@ impl EventLoop {
}
#[inline]
pub fn destroy_stream(&self, stream_id: StreamId) {
fn destroy_stream(&self, stream_id: StreamId) {
self.streams.lock().unwrap()[stream_id.0] = None;
}
#[inline]
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
let streams = self.streams.lock().unwrap();
let stream = streams
.get(stream_id.0)
@ -162,7 +270,7 @@ impl EventLoop {
}
#[inline]
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
let streams = self.streams.lock().unwrap();
let stream = streams
.get(stream_id.0)
@ -193,7 +301,7 @@ fn is_webaudio_available() -> bool {
pub struct Devices(bool);
impl Devices {
pub fn new() -> Result<Self, DevicesError> {
fn new() -> Result<Self, DevicesError> {
Ok(Self::default())
}
}
@ -218,12 +326,12 @@ impl Iterator for Devices {
}
#[inline]
pub fn default_input_device() -> Option<Device> {
fn default_input_device() -> Option<Device> {
unimplemented!();
}
#[inline]
pub fn default_output_device() -> Option<Device> {
fn default_output_device() -> Option<Device> {
if is_webaudio_available() {
Some(Device)
} else {
@ -236,17 +344,17 @@ pub struct Device;
impl Device {
#[inline]
pub fn name(&self) -> Result<String, DeviceNameError> {
fn name(&self) -> Result<String, DeviceNameError> {
Ok("Default Device".to_owned())
}
#[inline]
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
unimplemented!();
}
#[inline]
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
// TODO: right now cpal's API doesn't allow flexibility here
// "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if
// this ever becomes more flexible, don't forget to change that
@ -264,11 +372,11 @@ impl Device {
)
}
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
unimplemented!();
}
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
// TODO: because it is hard coded, see supported_output_formats.
Ok(
Format {

9
src/host/mod.rs Normal file
View File

@ -0,0 +1,9 @@
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub(crate) mod alsa;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) mod coreaudio;
#[cfg(target_os = "emscripten")]
pub(crate) mod emscripten;
pub(crate) mod null;
#[cfg(windows)]
pub(crate) mod wasapi;

172
src/host/null/mod.rs Normal file
View File

@ -0,0 +1,172 @@
#![allow(dead_code)]
use BuildStreamError;
use DefaultFormatError;
use Device as DeviceTrait;
use DevicesError;
use DeviceNameError;
use EventLoop as EventLoopTrait;
use Format;
use Host as HostTrait;
use PauseStreamError;
use PlayStreamError;
use StreamDataResult;
use SupportedFormatsError;
use StreamId as StreamIdTrait;
use SupportedFormat;
#[derive(Default)]
pub struct Devices;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Device;
pub struct EventLoop;
pub struct Host;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId;
pub struct SupportedInputFormats;
pub struct SupportedOutputFormats;
impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
Ok(Host)
}
}
impl Devices {
pub fn new() -> Result<Self, DevicesError> {
Ok(Devices)
}
}
impl EventLoop {
pub fn new() -> EventLoop {
EventLoop
}
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
#[inline]
fn name(&self) -> Result<String, DeviceNameError> {
Ok("null".to_owned())
}
#[inline]
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
unimplemented!()
}
#[inline]
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
unimplemented!()
}
#[inline]
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
unimplemented!()
}
#[inline]
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
unimplemented!()
}
}
impl EventLoopTrait for EventLoop {
type Device = Device;
type StreamId = StreamId;
#[inline]
fn run<F>(&self, _callback: F) -> !
where F: FnMut(StreamId, StreamDataResult)
{
loop { /* TODO: don't spin */ }
}
#[inline]
fn build_input_stream(&self, _: &Device, _: &Format) -> Result<StreamId, BuildStreamError> {
Err(BuildStreamError::DeviceNotAvailable)
}
#[inline]
fn build_output_stream(&self, _: &Device, _: &Format) -> Result<StreamId, BuildStreamError> {
Err(BuildStreamError::DeviceNotAvailable)
}
#[inline]
fn destroy_stream(&self, _: StreamId) {
unimplemented!()
}
#[inline]
fn play_stream(&self, _: StreamId) -> Result<(), PlayStreamError> {
panic!()
}
#[inline]
fn pause_stream(&self, _: StreamId) -> Result<(), PauseStreamError> {
panic!()
}
}
impl HostTrait for Host {
type Device = Device;
type Devices = Devices;
type EventLoop = EventLoop;
fn is_available() -> bool {
false
}
fn devices(&self) -> Result<Self::Devices, DevicesError> {
Devices::new()
}
fn default_input_device(&self) -> Option<Device> {
None
}
fn default_output_device(&self) -> Option<Device> {
None
}
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
}
impl StreamIdTrait for StreamId {}
impl Iterator for Devices {
type Item = Device;
#[inline]
fn next(&mut self) -> Option<Device> {
None
}
}
impl Iterator for SupportedInputFormats {
type Item = SupportedFormat;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
None
}
}
impl Iterator for SupportedOutputFormats {
type Item = SupportedFormat;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
None
}
}

147
src/host/wasapi/mod.rs Normal file
View File

@ -0,0 +1,147 @@
extern crate winapi;
use BackendSpecificError;
use BuildStreamError;
use DefaultFormatError;
use Device as DeviceTrait;
use DeviceNameError;
use DevicesError;
use EventLoop as EventLoopTrait;
use Format;
use Host as HostTrait;
use PlayStreamError;
use PauseStreamError;
use StreamDataResult;
use StreamId as StreamIdTrait;
use SupportedFormatsError;
use self::winapi::um::winnt::HRESULT;
use std::io::Error as IoError;
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
pub use self::stream::{EventLoop, StreamId};
mod com;
mod device;
mod stream;
/// The WASAPI host, the default windows host type.
#[derive(Debug)]
pub struct Host;
impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
Ok(Host)
}
}
impl HostTrait for Host {
type Devices = Devices;
type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool {
// Assume WASAPI is always available on windows.
true
}
fn devices(&self) -> Result<Self::Devices, DevicesError> {
Devices::new()
}
fn default_input_device(&self) -> Option<Self::Device> {
default_input_device()
}
fn default_output_device(&self) -> Option<Self::Device> {
default_output_device()
}
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
}
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
}
}
impl EventLoopTrait for EventLoop {
type Device = Device;
type StreamId = StreamId;
fn build_input_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_input_stream(self, device, format)
}
fn build_output_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_output_stream(self, device, format)
}
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
EventLoop::play_stream(self, stream)
}
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
EventLoop::pause_stream(self, stream)
}
fn destroy_stream(&self, stream: Self::StreamId) {
EventLoop::destroy_stream(self, stream)
}
fn run<F>(&self, callback: F) -> !
where
F: FnMut(Self::StreamId, StreamDataResult) + Send,
{
EventLoop::run(self, callback)
}
}
impl StreamIdTrait for StreamId {}
#[inline]
fn check_result(result: HRESULT) -> Result<(), IoError> {
if result < 0 {
Err(IoError::from_raw_os_error(result))
} else {
Ok(())
}
}
fn check_result_backend_specific(result: HRESULT) -> Result<(), BackendSpecificError> {
match check_result(result) {
Ok(()) => Ok(()),
Err(err) => {
let description = format!("{}", err);
return Err(BackendSpecificError { description });
}
}
}

View File

@ -115,7 +115,7 @@ impl EventLoop {
}
}
pub fn build_input_stream(
pub(crate) fn build_input_stream(
&self,
device: &Device,
format: &Format,
@ -276,7 +276,7 @@ impl EventLoop {
}
}
pub fn build_output_stream(
pub(crate) fn build_output_stream(
&self,
device: &Device,
format: &Format,
@ -439,12 +439,12 @@ impl EventLoop {
}
#[inline]
pub fn destroy_stream(&self, stream_id: StreamId) {
pub(crate) fn destroy_stream(&self, stream_id: StreamId) {
self.push_command(Command::DestroyStream(stream_id));
}
#[inline]
pub fn run<F>(&self, mut callback: F) -> !
pub(crate) fn run<F>(&self, mut callback: F) -> !
where F: FnMut(StreamId, StreamDataResult)
{
self.run_inner(&mut callback);
@ -617,13 +617,13 @@ impl EventLoop {
}
#[inline]
pub fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> {
pub(crate) fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> {
self.push_command(Command::PlayStream(stream));
Ok(())
}
#[inline]
pub fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> {
pub(crate) fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> {
self.push_command(Command::PauseStream(stream));
Ok(())
}

View File

@ -2,19 +2,25 @@
//!
//! Here are some concepts cpal exposes:
//!
//! - A `Device` is an audio device that may have any number of input and output streams.
//! - A stream is an open audio channel. Input streams allow you to receive audio data, output
//! streams allow you to play audio data. You must choose which `Device` runs your stream before
//! you create one.
//! - An `EventLoop` is a collection of streams being run by one or more `Device`. Each stream must
//! belong to an `EventLoop`, and all the streams that belong to an `EventLoop` are managed
//! together.
//! - A [**Host**](./trait.Host.html) provides access to the available audio devices on the system.
//! Some platforms have more than one host available, but every platform supported by CPAL has at
//! least one [**DefaultHost**](./trait.Host.html) that is guaranteed to be available.
//! - A [**Device**](./trait.Device.html) is an audio device that may have any number of input and
//! output streams.
//! - A stream is an open flow of audio data. Input streams allow you to receive audio data, output
//! streams allow you to play audio data. You must choose which **Device** will run your stream
//! before you can create one. Often, a default device can be retrieved via the **Host**.
//! - An [**EventLoop**](./trait.EventLoop.html) is a collection of streams being run by one or
//! more **Device**s under a single **Host**. Each stream must belong to an **EventLoop**, and
//! all the streams that belong to an **EventLoop** are managed together.
//!
//! The first step is to create an `EventLoop`:
//! The first step is to initialise the `Host` (for accessing audio devices) and create an
//! `EventLoop`:
//!
//! ```
//! use cpal::EventLoop;
//! let event_loop = EventLoop::new();
//! use cpal::Host;
//! let host = cpal::default_host();
//! let event_loop = host.event_loop();
//! ```
//!
//! Then choose a `Device`. The easiest way is to use the default input or output `Device` via the
@ -24,7 +30,9 @@
//! stream type on the system.
//!
//! ```no_run
//! let device = cpal::default_output_device().expect("no output device available");
//! # use cpal::Host;
//! # let host = cpal::default_host();
//! let device = host.default_output_device().expect("no output device available");
//! ```
//!
//! Before we can create a stream, we must decide what the format of the audio samples is going to
@ -38,7 +46,9 @@
//! > has been disconnected.
//!
//! ```no_run
//! # let device = cpal::default_output_device().unwrap();
//! use cpal::{Device, Host};
//! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap();
//! let mut supported_formats_range = device.supported_output_formats()
//! .expect("error while querying formats");
//! let format = supported_formats_range.next()
@ -49,9 +59,11 @@
//! Now that we have everything for the stream, we can create it from our event loop:
//!
//! ```no_run
//! # let device = cpal::default_output_device().unwrap();
//! use cpal::{Device, EventLoop, Host};
//! # let host = cpal::default_host();
//! # let event_loop = host.event_loop();
//! # let device = host.default_output_device().unwrap();
//! # let format = device.supported_output_formats().unwrap().next().unwrap().with_max_sample_rate();
//! # let event_loop = cpal::EventLoop::new();
//! let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
//! ```
//!
@ -61,15 +73,19 @@
//! Now we must start the stream. This is done with the `play_stream()` method on the event loop.
//!
//! ```no_run
//! # let event_loop: cpal::EventLoop = return;
//! # let stream_id: cpal::StreamId = return;
//! # use cpal::{EventLoop, Host};
//! # let host = cpal::default_host();
//! # let event_loop = host.event_loop();
//! # let stream_id = unimplemented!();
//! event_loop.play_stream(stream_id).expect("failed to play_stream");
//! ```
//!
//! Now everything is ready! We call `run()` on the `event_loop` to begin processing.
//!
//! ```no_run
//! # let event_loop = cpal::EventLoop::new();
//! # use cpal::{EventLoop, Host};
//! # let host = cpal::default_host();
//! # let event_loop = host.event_loop();
//! event_loop.run(move |_stream_id, _stream_result| {
//! // react to stream events and read or write stream data here
//! });
@ -87,9 +103,9 @@
//! In this example, we simply fill the given output buffer with zeroes.
//!
//! ```no_run
//! use cpal::{StreamData, UnknownTypeOutputBuffer};
//!
//! # let event_loop = cpal::EventLoop::new();
//! use cpal::{EventLoop, Host, StreamData, UnknownTypeOutputBuffer};
//! # let host = cpal::default_host();
//! # let event_loop = host.event_loop();
//! event_loop.run(move |stream_id, stream_result| {
//! let stream_data = match stream_result {
//! Ok(data) => data,
@ -132,51 +148,203 @@ extern crate lazy_static;
#[macro_use]
extern crate stdweb;
pub use platform::{ALL_HOSTS, HostId, available_hosts, default_host, host_from_id};
pub use samples_formats::{Sample, SampleFormat};
#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd",
target_os = "macos", target_os = "ios", target_os = "emscripten")))]
use null as cpal_impl;
use failure::Fail;
use std::fmt;
use std::iter;
use std::ops::{Deref, DerefMut};
mod null;
mod host;
pub mod platform;
mod samples_formats;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[path = "alsa/mod.rs"]
mod cpal_impl;
/// A **Host** provides access to the available audio devices on the system.
///
/// Each platform may have a number of available hosts depending on the system, each with their own
/// pros and cons.
///
/// For example, WASAPI is the standard audio host API that ships with the Windows operating
/// system. However, due to historical limitations with respect to performance and flexibility,
/// Steinberg created the ASIO API providing better audio device support for pro audio and
/// low-latency applications. As a result, it is common for some devices and device capabilities to
/// only be available via ASIO, while others are only available via WASAPI.
///
/// Another great example is the Linux platform. While the ALSA host API is the lowest-level API
/// available to almost all distributions of Linux, its flexibility is limited as it requires that
/// each process have exclusive access to the devices with which they establish streams. PulseAudio
/// is another popular host API that aims to solve this issue by providing user-space mixing,
/// however it has its own limitations w.r.t. low-latency and high-performance audio applications.
/// JACK is yet another host API that is more suitable to pro-audio applications, however it is
/// less readily available by default in many Linux distributions and is known to be tricky to
/// setup.
pub trait Host {
/// The type used for enumerating available devices by the host.
type Devices: Iterator<Item = Self::Device>;
/// The `Device` type yielded by the host.
type Device: Device;
/// The event loop type used by the `Host`
type EventLoop: EventLoop<Device = Self::Device>;
#[cfg(windows)]
#[path = "wasapi/mod.rs"]
mod cpal_impl;
/// Whether or not the host is available on the system.
fn is_available() -> bool;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[path = "coreaudio/mod.rs"]
mod cpal_impl;
/// 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.
fn devices(&self) -> Result<Self::Devices, DevicesError>;
#[cfg(target_os = "emscripten")]
#[path = "emscripten/mod.rs"]
mod cpal_impl;
/// The default input audio device on the system.
///
/// Returns `None` if no input device is available.
fn default_input_device(&self) -> Option<Self::Device>;
/// An opaque type that identifies a device that is capable of either audio input or output.
/// The default output audio device on the system.
///
/// Returns `None` if no output device is available.
fn default_output_device(&self) -> Option<Self::Device>;
/// Initialise the event loop, ready for managing audio streams.
fn event_loop(&self) -> Self::EventLoop;
/// 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.
fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
fn supports_input<D: Device>(device: &D) -> bool {
device.supported_input_formats()
.map(|mut iter| iter.next().is_some())
.unwrap_or(false)
}
Ok(self.devices()?.filter(supports_input::<Self::Device>))
}
/// 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.
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
fn supports_output<D: Device>(device: &D) -> bool {
device.supported_output_formats()
.map(|mut iter| iter.next().is_some())
.unwrap_or(false)
}
Ok(self.devices()?.filter(supports_output::<Self::Device>))
}
}
/// A device that is capable of audio input and/or output.
///
/// Please note that `Device`s may become invalid if they get disconnected. Therefore all the
/// methods that involve a device return a `Result`.
#[derive(Clone, PartialEq, Eq)]
pub struct Device(cpal_impl::Device);
/// methods that involve a device return a `Result` allowing the user to handle this case.
pub trait Device {
/// The iterator type yielding supported input stream formats.
type SupportedInputFormats: Iterator<Item = SupportedFormat>;
/// The iterator type yielding supported output stream formats.
type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
/// The human-readable name of the device.
fn name(&self) -> Result<String, DeviceNameError>;
/// An iterator yielding formats that are supported by the backend.
///
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError>;
/// An iterator yielding output stream formats that are supported by the device.
///
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>;
/// The default input stream format for the device.
fn default_input_format(&self) -> Result<Format, DefaultFormatError>;
/// The default output stream format for the device.
fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
}
/// Collection of streams managed together.
///
/// Created with the [`new`](struct.EventLoop.html#method.new) method.
pub struct EventLoop(cpal_impl::EventLoop);
/// Created with the `Host::event_loop` method.
pub trait EventLoop {
/// The `Device` type yielded by the host.
type Device: Device;
/// The type used to uniquely distinguish between streams.
type StreamId: StreamId;
/// Identifier of a stream within the `EventLoop`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId(cpal_impl::StreamId);
/// Creates a new input stream that will run from the given device and with the given format.
///
/// On success, returns an identifier for the stream.
///
/// Can return an error if the device is no longer valid, or if the input stream format is not
/// supported by the device.
fn build_input_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError>;
/// Creates a new output stream that will play on the given device and with the given format.
///
/// On success, returns an identifier for the stream.
///
/// Can return an error if the device is no longer valid, or if the output stream format is not
/// supported by the device.
fn build_output_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError>;
/// Instructs the audio device that it should start playing the stream with the given ID.
///
/// Has no effect is the stream was already playing.
///
/// Only call this after you have submitted some data, otherwise you may hear some glitches.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError>;
/// Instructs the audio device that it should stop playing the stream with the given ID.
///
/// Has no effect is the stream was already paused.
///
/// If you call `play` afterwards, the playback will resume where it was.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError>;
/// Destroys an existing stream.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
fn destroy_stream(&self, stream: Self::StreamId);
/// Takes control of the current thread and begins the stream processing.
///
/// > **Note**: Since it takes control of the thread, this method is best called on a separate
/// > thread.
///
/// Whenever a stream needs to be fed some data, the closure passed as parameter is called.
/// You can call the other methods of `EventLoop` without getting a deadlock.
fn run<F>(&self, callback: F) -> !
where
F: FnMut(Self::StreamId, StreamDataResult) + Send;
}
/// The set of required bounds for host `StreamId` types.
pub trait StreamId: Clone + std::fmt::Debug + PartialEq + Eq {}
/// A host's device iterator yielding only *input* devices.
pub type InputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
/// A host's device iterator yielding only *output* devices.
pub type OutputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
/// Number of channels.
pub type ChannelCount = u16;
@ -271,26 +439,10 @@ pub enum UnknownTypeOutputBuffer<'a> {
F32(OutputBuffer<'a, f32>),
}
/// An iterator yielding all `Device`s currently available to the system.
///
/// See [`devices()`](fn.devices.html).
pub struct Devices(cpal_impl::Devices);
/// A `Devices` yielding only *input* devices.
pub type InputDevices = iter::Filter<Devices, fn(&Device) -> bool>;
/// A `Devices` yielding only *output* devices.
pub type OutputDevices = iter::Filter<Devices, fn(&Device) -> bool>;
/// An iterator that produces a list of input stream formats supported by the device.
///
/// See [`Device::supported_input_formats()`](struct.Device.html#method.supported_input_formats).
pub struct SupportedInputFormats(cpal_impl::SupportedInputFormats);
/// An iterator that produces a list of output stream formats supported by the device.
///
/// See [`Device::supported_output_formats()`](struct.Device.html#method.supported_output_formats).
pub struct SupportedOutputFormats(cpal_impl::SupportedOutputFormats);
/// The requested host, although supported on this platform, is unavailable.
#[derive(Clone, Debug, Fail)]
#[fail(display = "the requested host is unavailable")]
pub struct HostUnavailable;
/// Some error has occurred that is specific to the backend from which it was produced.
///
@ -440,191 +592,6 @@ pub enum StreamError {
}
}
/// An iterator yielding all `Device`s currently available to the system.
///
/// Can be empty if the system does not support audio in general.
#[inline]
pub fn devices() -> Result<Devices, DevicesError> {
Ok(Devices(cpal_impl::Devices::new()?))
}
/// 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.
pub fn input_devices() -> Result<InputDevices, DevicesError> {
fn supports_input(device: &Device) -> bool {
device.supported_input_formats()
.map(|mut iter| iter.next().is_some())
.unwrap_or(false)
}
Ok(devices()?.filter(supports_input))
}
/// 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.
pub fn output_devices() -> Result<OutputDevices, DevicesError> {
fn supports_output(device: &Device) -> bool {
device.supported_output_formats()
.map(|mut iter| iter.next().is_some())
.unwrap_or(false)
}
Ok(devices()?.filter(supports_output))
}
/// The default input audio device on the system.
///
/// Returns `None` if no input device is available.
pub fn default_input_device() -> Option<Device> {
cpal_impl::default_input_device().map(Device)
}
/// The default output audio device on the system.
///
/// Returns `None` if no output device is available.
pub fn default_output_device() -> Option<Device> {
cpal_impl::default_output_device().map(Device)
}
impl Device {
/// The human-readable name of the device.
#[inline]
pub fn name(&self) -> Result<String, DeviceNameError> {
self.0.name()
}
/// An iterator yielding formats that are supported by the backend.
///
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
#[inline]
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
Ok(SupportedInputFormats(self.0.supported_input_formats()?))
}
/// An iterator yielding output stream formats that are supported by the device.
///
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
#[inline]
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
Ok(SupportedOutputFormats(self.0.supported_output_formats()?))
}
/// The default input stream format for the device.
#[inline]
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
self.0.default_input_format()
}
/// The default output stream format for the device.
#[inline]
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
self.0.default_output_format()
}
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl EventLoop {
/// Initializes a new events loop.
#[inline]
pub fn new() -> EventLoop {
EventLoop(cpal_impl::EventLoop::new())
}
/// Creates a new input stream that will run from the given device and with the given format.
///
/// On success, returns an identifier for the stream.
///
/// Can return an error if the device is no longer valid, or if the input stream format is not
/// supported by the device.
#[inline]
pub fn build_input_stream(
&self,
device: &Device,
format: &Format,
) -> Result<StreamId, BuildStreamError>
{
self.0.build_input_stream(&device.0, format).map(StreamId)
}
/// Creates a new output stream that will play on the given device and with the given format.
///
/// On success, returns an identifier for the stream.
///
/// Can return an error if the device is no longer valid, or if the output stream format is not
/// supported by the device.
#[inline]
pub fn build_output_stream(
&self,
device: &Device,
format: &Format,
) -> Result<StreamId, BuildStreamError>
{
self.0.build_output_stream(&device.0, format).map(StreamId)
}
/// Instructs the audio device that it should start playing the stream with the given ID.
///
/// Has no effect is the stream was already playing.
///
/// Only call this after you have submitted some data, otherwise you may hear some glitches.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
///
#[inline]
pub fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> {
self.0.play_stream(stream.0)
}
/// Instructs the audio device that it should stop playing the stream with the given ID.
///
/// Has no effect is the stream was already paused.
///
/// If you call `play` afterwards, the playback will resume where it was.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
///
#[inline]
pub fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> {
self.0.pause_stream(stream.0)
}
/// Destroys an existing stream.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
///
#[inline]
pub fn destroy_stream(&self, stream_id: StreamId) {
self.0.destroy_stream(stream_id.0)
}
/// Takes control of the current thread and begins the stream processing.
///
/// > **Note**: Since it takes control of the thread, this method is best called on a separate
/// > thread.
///
/// Whenever a stream needs to be fed some data, the closure passed as parameter is called.
/// You can call the other methods of `EventLoop` without getting a deadlock.
#[inline]
pub fn run<F>(&self, mut callback: F) -> !
where F: FnMut(StreamId, StreamDataResult) + Send
{
self.0.run(move |id, data| callback(StreamId(id), data))
}
}
impl SupportedFormat {
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
#[inline]
@ -775,48 +742,6 @@ impl From<Format> for SupportedFormat {
}
}
impl Iterator for Devices {
type Item = Device;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(Device)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl Iterator for SupportedInputFormats {
type Item = SupportedFormat;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl Iterator for SupportedOutputFormats {
type Item = SupportedFormat;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl From<BackendSpecificError> for DevicesError {
fn from(err: BackendSpecificError) -> Self {
DevicesError::BackendSpecific { err }

View File

@ -1,145 +0,0 @@
#![allow(dead_code)]
use std::marker::PhantomData;
use BuildStreamError;
use DefaultFormatError;
use DevicesError;
use DeviceNameError;
use Format;
use PauseStreamError;
use PlayStreamError;
use StreamDataResult;
use SupportedFormatsError;
use SupportedFormat;
pub struct EventLoop;
impl EventLoop {
#[inline]
pub fn new() -> EventLoop {
EventLoop
}
#[inline]
pub fn run<F>(&self, _callback: F) -> !
where F: FnMut(StreamId, StreamDataResult)
{
loop { /* TODO: don't spin */ }
}
#[inline]
pub fn build_input_stream(&self, _: &Device, _: &Format) -> Result<StreamId, BuildStreamError> {
Err(BuildStreamError::DeviceNotAvailable)
}
#[inline]
pub fn build_output_stream(&self, _: &Device, _: &Format) -> Result<StreamId, BuildStreamError> {
Err(BuildStreamError::DeviceNotAvailable)
}
#[inline]
pub fn destroy_stream(&self, _: StreamId) {
unimplemented!()
}
#[inline]
pub fn play_stream(&self, _: StreamId) -> Result<(), PlayStreamError> {
panic!()
}
#[inline]
pub fn pause_stream(&self, _: StreamId) -> Result<(), PauseStreamError> {
panic!()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId;
#[derive(Default)]
pub struct Devices;
impl Devices {
pub fn new() -> Result<Self, DevicesError> {
Ok(Devices)
}
}
impl Iterator for Devices {
type Item = Device;
#[inline]
fn next(&mut self) -> Option<Device> {
None
}
}
#[inline]
pub fn default_input_device() -> Option<Device> {
None
}
#[inline]
pub fn default_output_device() -> Option<Device> {
None
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Device;
impl Device {
#[inline]
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
unimplemented!()
}
#[inline]
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
unimplemented!()
}
#[inline]
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
unimplemented!()
}
#[inline]
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
unimplemented!()
}
#[inline]
pub fn name(&self) -> Result<String, DeviceNameError> {
Ok("null".to_owned())
}
}
pub struct SupportedInputFormats;
pub struct SupportedOutputFormats;
impl Iterator for SupportedInputFormats {
type Item = SupportedFormat;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
None
}
}
impl Iterator for SupportedOutputFormats {
type Item = SupportedFormat;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
None
}
}
pub struct InputBuffer<'a, T: 'a> {
marker: PhantomData<&'a T>,
}
pub struct OutputBuffer<'a, T: 'a> {
marker: PhantomData<&'a mut T>,
}

560
src/platform/mod.rs Normal file
View File

@ -0,0 +1,560 @@
//! Platform-specific items.
//!
//! This module also contains the implementation of the platform's dynamically dispatched `Host`
//! type and its associated `EventLoop`, `Device`, `StreamId` and other associated types. These
//! types are useful in the case that users require switching between audio host APIs at runtime.
#[doc(inline)]
pub use self::platform_impl::*;
// A macro to assist with implementing a platform's dynamically dispatched `Host` type.
//
// These dynamically dispatched types are necessary to allow for users to switch between hosts at
// runtime.
//
// For example the invocation `impl_platform_host(Wasapi wasapi, Asio asio)`, this macro should
// expand to:
//
// ```
// pub enum HostId {
// Wasapi,
// Asio,
// }
//
// pub enum Host {
// Wasapi(crate::host::wasapi::Host),
// Asio(crate::host::asio::Host),
// }
// ```
//
// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputFormats,
// SupportedOutputFormats and all their necessary trait implementations.
// ```
macro_rules! impl_platform_host {
($($HostVariant:ident $host_mod:ident),*) => {
/// All hosts supported by CPAL on this platform.
pub const ALL_HOSTS: &'static [HostId] = &[
$(
HostId::$HostVariant,
)*
];
/// The platform's dynamically dispatched **Host** type.
///
/// An instance of this **Host** type may represent one of any of the **Host**s available
/// on the platform.
///
/// Use this type if you require switching between available hosts at runtime.
///
/// This type may be constructed via the **host_from_id** function. **HostId**s may
/// be acquired via the **ALL_HOSTS** const and the **available_hosts** function.
pub struct Host(HostInner);
/// The **Device** implementation associated with the platform's dynamically dispatched
/// **Host** type.
pub struct Device(DeviceInner);
/// The **Devices** iterator associated with the platform's dynamically dispatched **Host**
/// type.
pub struct Devices(DevicesInner);
/// The **EventLoop** implementation associated with the platform's dynamically dispatched
/// **Host** type.
pub struct EventLoop(EventLoopInner);
/// The **StreamId** implementation associated with the platform's dynamically dispatched
/// **Host** type.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StreamId(StreamIdInner);
/// The **SupportedInputFormats** iterator associated with the platform's dynamically
/// dispatched **Host** type.
pub struct SupportedInputFormats(SupportedInputFormatsInner);
/// The **SupportedOutputFormats** iterator associated with the platform's dynamically
/// dispatched **Host** type.
pub struct SupportedOutputFormats(SupportedOutputFormatsInner);
/// Unique identifier for available hosts on the platform.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum HostId {
$(
$HostVariant,
)*
}
enum DeviceInner {
$(
$HostVariant(crate::host::$host_mod::Device),
)*
}
enum DevicesInner {
$(
$HostVariant(crate::host::$host_mod::Devices),
)*
}
enum EventLoopInner {
$(
$HostVariant(crate::host::$host_mod::EventLoop),
)*
}
enum HostInner {
$(
$HostVariant(crate::host::$host_mod::Host),
)*
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum StreamIdInner {
$(
$HostVariant(crate::host::$host_mod::StreamId),
)*
}
enum SupportedInputFormatsInner {
$(
$HostVariant(crate::host::$host_mod::SupportedInputFormats),
)*
}
enum SupportedOutputFormatsInner {
$(
$HostVariant(crate::host::$host_mod::SupportedOutputFormats),
)*
}
impl Host {
/// The unique identifier associated with this host.
pub fn id(&self) -> HostId {
match self.0 {
$(
HostInner::$HostVariant(_) => HostId::$HostVariant,
)*
}
}
}
impl Iterator for Devices {
type Item = Device;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
DevicesInner::$HostVariant(ref mut d) => {
d.next().map(DeviceInner::$HostVariant).map(Device)
}
)*
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
DevicesInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl Iterator for SupportedInputFormats {
type Item = crate::SupportedFormat;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
SupportedInputFormatsInner::$HostVariant(ref mut s) => s.next(),
)*
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
SupportedInputFormatsInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl Iterator for SupportedOutputFormats {
type Item = crate::SupportedFormat;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
SupportedOutputFormatsInner::$HostVariant(ref mut s) => s.next(),
)*
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
SupportedOutputFormatsInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl crate::Device for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
fn name(&self) -> Result<String, crate::DeviceNameError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.name(),
)*
}
}
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, crate::SupportedFormatsError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => {
d.supported_input_formats()
.map(SupportedInputFormatsInner::$HostVariant)
.map(SupportedInputFormats)
}
)*
}
}
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, crate::SupportedFormatsError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => {
d.supported_output_formats()
.map(SupportedOutputFormatsInner::$HostVariant)
.map(SupportedOutputFormats)
}
)*
}
}
fn default_input_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.default_input_format(),
)*
}
}
fn default_output_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.default_output_format(),
)*
}
}
}
impl crate::EventLoop for EventLoop {
type StreamId = StreamId;
type Device = Device;
fn build_input_stream(
&self,
device: &Self::Device,
format: &crate::Format,
) -> Result<Self::StreamId, crate::BuildStreamError> {
match (&self.0, &device.0) {
$(
(&EventLoopInner::$HostVariant(ref e), &DeviceInner::$HostVariant(ref d)) => {
e.build_input_stream(d, format)
.map(StreamIdInner::$HostVariant)
.map(StreamId)
}
)*
}
}
fn build_output_stream(
&self,
device: &Self::Device,
format: &crate::Format,
) -> Result<Self::StreamId, crate::BuildStreamError> {
match (&self.0, &device.0) {
$(
(&EventLoopInner::$HostVariant(ref e), &DeviceInner::$HostVariant(ref d)) => {
e.build_output_stream(d, format)
.map(StreamIdInner::$HostVariant)
.map(StreamId)
}
)*
}
}
fn play_stream(&self, stream: Self::StreamId) -> Result<(), crate::PlayStreamError> {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => {
e.play_stream(s.clone())
}
)*
}
}
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), crate::PauseStreamError> {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => {
e.pause_stream(s.clone())
}
)*
}
}
fn destroy_stream(&self, stream: Self::StreamId) {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => {
e.destroy_stream(s.clone())
}
)*
}
}
fn run<F>(&self, mut callback: F) -> !
where
F: FnMut(Self::StreamId, crate::StreamDataResult) + Send
{
match self.0 {
$(
EventLoopInner::$HostVariant(ref e) => {
e.run(|id, result| {
let result = result;
callback(StreamId(StreamIdInner::$HostVariant(id)), result);
});
},
)*
}
}
}
impl crate::Host for Host {
type Devices = Devices;
type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool {
$( crate::host::$host_mod::Host::is_available() ||)* false
}
fn devices(&self) -> Result<Self::Devices, crate::DevicesError> {
match self.0 {
$(
HostInner::$HostVariant(ref h) => {
h.devices().map(DevicesInner::$HostVariant).map(Devices)
}
)*
}
}
fn default_input_device(&self) -> Option<Self::Device> {
match self.0 {
$(
HostInner::$HostVariant(ref h) => {
h.default_input_device().map(DeviceInner::$HostVariant).map(Device)
}
)*
}
}
fn default_output_device(&self) -> Option<Self::Device> {
match self.0 {
$(
HostInner::$HostVariant(ref h) => {
h.default_output_device().map(DeviceInner::$HostVariant).map(Device)
}
)*
}
}
fn event_loop(&self) -> Self::EventLoop {
match self.0 {
$(
HostInner::$HostVariant(ref h) => {
EventLoop(EventLoopInner::$HostVariant(h.event_loop()))
}
)*
}
}
}
impl crate::StreamId for StreamId {}
$(
impl From<crate::host::$host_mod::Device> for Device {
fn from(h: crate::host::$host_mod::Device) -> Self {
Device(DeviceInner::$HostVariant(h))
}
}
impl From<crate::host::$host_mod::Devices> for Devices {
fn from(h: crate::host::$host_mod::Devices) -> Self {
Devices(DevicesInner::$HostVariant(h))
}
}
impl From<crate::host::$host_mod::EventLoop> for EventLoop {
fn from(h: crate::host::$host_mod::EventLoop) -> Self {
EventLoop(EventLoopInner::$HostVariant(h))
}
}
impl From<crate::host::$host_mod::Host> for Host {
fn from(h: crate::host::$host_mod::Host) -> Self {
Host(HostInner::$HostVariant(h))
}
}
impl From<crate::host::$host_mod::StreamId> for StreamId {
fn from(h: crate::host::$host_mod::StreamId) -> Self {
StreamId(StreamIdInner::$HostVariant(h))
}
}
)*
/// Produces a list of hosts that are currently available on the system.
pub fn available_hosts() -> Vec<HostId> {
let mut host_ids = vec![];
$(
if <crate::host::$host_mod::Host as crate::Host>::is_available() {
host_ids.push(HostId::$HostVariant);
}
)*
host_ids
}
/// Given a unique host identifier, initialise and produce the host if it is available.
pub fn host_from_id(id: HostId) -> Result<Host, crate::HostUnavailable> {
match id {
$(
HostId::$HostVariant => {
crate::host::$host_mod::Host::new()
.map(HostInner::$HostVariant)
.map(Host)
}
)*
}
}
};
}
// TODO: Add pulseaudio and jack here eventually.
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
mod platform_impl {
pub use crate::host::alsa::{
Device as AlsaDevice,
Devices as AlsaDevices,
EventLoop as AlsaEventLoop,
Host as AlsaHost,
StreamId as AlsaStreamId,
SupportedInputFormats as AlsaSupportedInputFormats,
SupportedOutputFormats as AlsaSupportedOutputFormats,
};
impl_platform_host!(Alsa alsa);
/// The default host for the current compilation target platform.
pub fn default_host() -> Host {
AlsaHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod platform_impl {
pub use crate::host::coreaudio::{
Device as CoreAudioDevice,
Devices as CoreAudioDevices,
EventLoop as CoreAudioEventLoop,
Host as CoreAudioHost,
StreamId as CoreAudioStreamId,
SupportedInputFormats as CoreAudioSupportedInputFormats,
SupportedOutputFormats as CoreAudioSupportedOutputFormats,
};
impl_platform_host!(CoreAudio coreaudio);
/// The default host for the current compilation target platform.
pub fn default_host() -> Host {
CoreAudioHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(target_os = "emscripten")]
mod platform_impl {
pub use crate::host::emscripten::{
Device as EmscriptenDevice,
Devices as EmscriptenDevices,
EventLoop as EmscriptenEventLoop,
Host as EmscriptenHost,
StreamId as EmscriptenStreamId,
SupportedInputFormats as EmscriptenSupportedInputFormats,
SupportedOutputFormats as EmscriptenSupportedOutputFormats,
};
impl_platform_host!(Emscripten emscripten);
/// The default host for the current compilation target platform.
pub fn default_host() -> Host {
EmscriptenHost::new()
.expect("the default host should always be available")
.into()
}
}
// TODO: Add `Asio asio` once #221 lands.
#[cfg(windows)]
mod platform_impl {
pub use crate::host::wasapi::{
Device as WasapiDevice,
Devices as WasapiDevices,
EventLoop as WasapiEventLoop,
Host as WasapiHost,
StreamId as WasapiStreamId,
SupportedInputFormats as WasapiSupportedInputFormats,
SupportedOutputFormats as WasapiSupportedOutputFormats,
};
impl_platform_host!(Wasapi wasapi);
/// The default host for the current compilation target platform.
pub fn default_host() -> Host {
WasapiHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd", target_os = "macos",
target_os = "ios", target_os = "emscripten")))]
mod platform_impl {
pub use crate::host::null::{
Device as NullDevice,
Devices as NullDevices,
EventLoop as NullEventLoop,
Host as NullHost,
StreamId as NullStreamId,
SupportedInputFormats as NullSupportedInputFormats,
SupportedOutputFormats as NullSupportedOutputFormats,
};
impl_platform_host!(Null null);
/// The default host for the current compilation target platform.
pub fn default_host() -> Host {
NullHost::new()
.expect("the default host should always be available")
.into()
}
}

View File

@ -1,30 +0,0 @@
extern crate winapi;
use BackendSpecificError;
use self::winapi::um::winnt::HRESULT;
use std::io::Error as IoError;
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
pub use self::stream::{EventLoop, StreamId};
mod com;
mod device;
mod stream;
#[inline]
fn check_result(result: HRESULT) -> Result<(), IoError> {
if result < 0 {
Err(IoError::from_raw_os_error(result))
} else {
Ok(())
}
}
fn check_result_backend_specific(result: HRESULT) -> Result<(), BackendSpecificError> {
match check_result(result) {
Ok(()) => Ok(()),
Err(err) => {
let description = format!("{}", err);
return Err(BackendSpecificError { description });
}
}
}