[WIP] Introduce a `Host` API

This is an implementation of the API described at #204. Please see that
issue for more details on the motivation.

-----

A **Host** provides access to the available audio devices on the system.
Some platforms have more than one host available, e.g.
wasapi/asio/dsound on windows, alsa/pulse/jack on linux and so on. As a
result, some audio devices are only available on certain hosts, while
others are only available on other hosts. Every platform supported by
CPAL has at least one **DefaultHost** that is guaranteed to be available
(alsa, wasapi and coreaudio). Currently, the default hosts are the only
hosts supported by CPAL, however this will change as of landing #221 (cc
@freesig). These changes should also accommodate support for other hosts
such as jack #250 (cc @derekdreery) and pulseaudio (cc @knappador) #259.

This introduces a suite of traits allowing for both compile time and
runtime dispatch of different hosts and their uniquely associated device
and event loop types.

A new private **host** module has been added containing the individual
host implementations, each in their own submodule gated to the platforms
on which they are available.

A new **platform** module has been added containing platform-specific
items, including a dynamically dispatched host type that allows for
easily switching between hosts at runtime.

The **ALL_HOSTS** slice contains a **HostId** for each host supported on
the current platform. The **available_hosts** function produces a
**HostId** for each host that is currently *available* on the platform.
The **host_from_id** function allows for initialising a host from its
associated ID, failing with a **HostUnavailable** error. The
**default_host** function returns the default host and should never
fail.

Please see the examples for a demonstration of the change in usage. For
the most part, things look the same at the surface level, however the
role of device enumeration and creating the event loop have been moved
from global functions to host methods. The enumerate.rs example has been
updated to enumerate all devices for each host, not just the default.

**TODO**

- [x] Add the new **Host** API
- [x] Update examples for the new API.
- [x] ALSA host
- [ ] WASAPI host
- [ ] CoreAudio host
- [ ] Emscripten host **Follow-up PR**
- [ ] ASIO host #221

cc @ishitatsuyuki more to review for you if you're interested, but it
might be easier after #288 lands and this gets rebased.
This commit is contained in:
mitchmindtree 2019-06-23 15:49:48 +02:00
parent 2b9e2e0b2c
commit e8a05379c2
17 changed files with 989 additions and 447 deletions

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,50 +1,60 @@
extern crate cpal;
extern crate failure;
use cpal::{Device, Host};
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);
println!("Supported hosts:\n {:?}", cpal::ALL_HOSTS);
let available_hosts = cpal::available_hosts();
println!("Available hosts:\n {:?}", available_hosts);
let devices = cpal::devices()?;
println!("Devices: ");
for (device_index, device) in devices.enumerate() {
println!("{}. \"{}\"", device_index + 1, device.name()?);
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);
// Input formats
if let Ok(fmt) = device.default_input_format() {
println!(" Default input stream format:\n {:?}", fmt);
}
let mut input_formats = match device.supported_input_formats() {
Ok(f) => f.peekable(),
Err(e) => {
println!("Error: {:?}", e);
continue;
},
};
if input_formats.peek().is_some() {
println!(" All supported input stream formats:");
for (format_index, format) in input_formats.enumerate() {
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
let devices = host.devices()?;
println!(" Devices: ");
for (device_index, device) in devices.enumerate() {
println!(" {}. \"{}\"", device_index + 1, device.name()?);
// Input formats
if let Ok(fmt) = device.default_input_format() {
println!(" Default input stream format:\n {:?}", fmt);
}
let mut input_formats = match device.supported_input_formats() {
Ok(f) => f.peekable(),
Err(e) => {
println!("Error: {:?}", e);
continue;
},
};
if input_formats.peek().is_some() {
println!(" All supported input stream formats:");
for (format_index, format) in input_formats.enumerate() {
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
}
}
}
// Output formats
if let Ok(fmt) = device.default_output_format() {
println!(" Default output stream format:\n {:?}", fmt);
}
let mut output_formats = match device.supported_output_formats() {
Ok(f) => f.peekable(),
Err(e) => {
println!("Error: {:?}", e);
continue;
},
};
if output_formats.peek().is_some() {
println!(" All supported output stream formats:");
for (format_index, format) in output_formats.enumerate() {
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
// Output formats
if let Ok(fmt) = device.default_output_format() {
println!(" Default output stream format:\n {:?}", fmt);
}
let mut output_formats = match device.supported_output_formats() {
Ok(f) => f.peekable(),
Err(e) => {
println!("Error: {:?}", e);
continue;
},
};
if output_formats.peek().is_some() {
println!(" All supported output stream formats:");
for (format_index, format) in output_formats.enumerate() {
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
}
}
}
}

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(())
}

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

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

View File

@ -1,64 +1,35 @@
#![allow(dead_code)]
use std::marker::PhantomData;
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;
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!()
}
}
pub struct Host;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId;
#[derive(Default)]
pub struct Devices;
pub struct SupportedInputFormats;
pub struct SupportedOutputFormats;
impl Devices {
pub fn new() -> Result<Self, DevicesError> {
@ -66,6 +37,107 @@ impl 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;
@ -75,49 +147,6 @@ impl Iterator for Devices {
}
}
#[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;
@ -135,11 +164,3 @@ impl Iterator for SupportedOutputFormats {
None
}
}
pub struct InputBuffer<'a, T: 'a> {
marker: PhantomData<&'a T>,
}
pub struct OutputBuffer<'a, T: 'a> {
marker: PhantomData<&'a mut T>,
}

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, DefaultHost, 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. PortAudio
/// 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 }

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

@ -0,0 +1,457 @@
//! 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.
// 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(s)) => {
e.play_stream(s)
}
)*
}
}
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), crate::PauseStreamError> {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(s)) => {
e.pause_stream(s)
}
)*
}
}
fn destroy_stream(&self, stream: Self::StreamId) {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(s)) => {
e.destroy_stream(s)
}
)*
}
}
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 {}
/// 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"))]
impl_platform_host!(Alsa alsa);
#[cfg(any(target_os = "macos", target_os = "ios"))]
impl_platform_host!(CoreAudio coreaudio);
#[cfg(target_os = "emscripten")]
impl_platform_host!(Emscripten emscripten);
// TODO: Add `Asio asio` once #221 lands.
#[cfg(windows)]
impl_platform_host!(Wasapi wasapi);
#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd", target_os = "macos",
target_os = "ios", target_os = "emscripten")))]
impl_platform_host!(Null null);
/// The default host for the current compilation target platform.
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub type DefaultHost = crate::host::alsa::Host;
/// The default host for the current compilation target platform.
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub type DefaultHost = crate::host::coreaudio::Host;
/// The default host for the current compilation target platform.
#[cfg(target_os = "emscripten")]
pub type DefaultHost = crate::host::emscripten::Host;
#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd", target_os = "macos",
target_os = "ios", target_os = "emscripten")))]
pub type DefaultHost = crate::host::null::Host;
/// The default host for the current compilation target platform.
#[cfg(windows)]
pub type DefaultHost = crate::host::wasapi::Host;
/// Retrieve the default host for the system.
///
/// There should *always* be a default host for each of the supported target platforms, regardless
/// of whether or not there are any available audio devices.
pub fn default_host() -> DefaultHost {
DefaultHost::new().expect("the default host should always be available")
}