Refactor `Host` and related traits into a new `traits` module
This is a draft implementation of #294. I'll leave this open for feedback and potentially better trait naming suggestions or better solutions in general! cc @ishitatsuyuki
This commit is contained in:
parent
36dee482bd
commit
5e4f384992
|
@ -1,7 +1,7 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
use cpal::{Device, EventLoop, Host};
|
use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait};
|
||||||
|
|
||||||
fn main() -> Result<(), failure::Error> {
|
fn main() -> Result<(), failure::Error> {
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
use cpal::{Device, Host};
|
use cpal::traits::{DeviceTrait, HostTrait};
|
||||||
|
|
||||||
fn main() -> Result<(), failure::Error> {
|
fn main() -> Result<(), failure::Error> {
|
||||||
println!("Supported hosts:\n {:?}", cpal::ALL_HOSTS);
|
println!("Supported hosts:\n {:?}", cpal::ALL_HOSTS);
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
use cpal::{Device, EventLoop, Host};
|
use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait};
|
||||||
|
|
||||||
const LATENCY_MS: f32 = 150.0;
|
const LATENCY_MS: f32 = 150.0;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
extern crate hound;
|
extern crate hound;
|
||||||
|
|
||||||
use cpal::{Device, EventLoop, Host};
|
use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait};
|
||||||
|
|
||||||
fn main() -> Result<(), failure::Error> {
|
fn main() -> Result<(), failure::Error> {
|
||||||
// Use the default host for working with audio devices.
|
// Use the default host for working with audio devices.
|
||||||
|
|
|
@ -7,12 +7,9 @@ use ChannelCount;
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use Device as DeviceTrait;
|
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
use DevicesError;
|
use DevicesError;
|
||||||
use EventLoop as EventLoopTrait;
|
|
||||||
use Format;
|
use Format;
|
||||||
use Host as HostTrait;
|
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
|
@ -21,10 +18,10 @@ use SupportedFormatsError;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
use StreamDataResult;
|
use StreamDataResult;
|
||||||
use StreamError;
|
use StreamError;
|
||||||
use StreamId as StreamIdTrait;
|
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
use UnknownTypeInputBuffer;
|
use UnknownTypeInputBuffer;
|
||||||
use UnknownTypeOutputBuffer;
|
use UnknownTypeOutputBuffer;
|
||||||
|
use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait};
|
||||||
|
|
||||||
use std::{cmp, ffi, mem, ptr};
|
use std::{cmp, ffi, mem, ptr};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
|
@ -5,12 +5,9 @@ use ChannelCount;
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use Device as DeviceTrait;
|
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
use DevicesError;
|
use DevicesError;
|
||||||
use EventLoop as EventLoopTrait;
|
|
||||||
use Format;
|
use Format;
|
||||||
use Host as HostTrait;
|
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
|
@ -18,10 +15,10 @@ use SampleFormat;
|
||||||
use SampleRate;
|
use SampleRate;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
use StreamDataResult;
|
use StreamDataResult;
|
||||||
use StreamId as StreamIdTrait;
|
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
use UnknownTypeInputBuffer;
|
use UnknownTypeInputBuffer;
|
||||||
use UnknownTypeOutputBuffer;
|
use UnknownTypeOutputBuffer;
|
||||||
|
use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait};
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
|
@ -10,20 +10,17 @@ use stdweb::web::set_timeout;
|
||||||
|
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use Device as DeviceTrait;
|
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
use DevicesError;
|
use DevicesError;
|
||||||
use EventLoop as EventLoopTrait;
|
|
||||||
use Format;
|
use Format;
|
||||||
use Host as HostTrait;
|
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
use StreamDataResult;
|
use StreamDataResult;
|
||||||
use StreamId as StreamIdTrait;
|
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
use UnknownTypeOutputBuffer;
|
use UnknownTypeOutputBuffer;
|
||||||
|
use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait};
|
||||||
|
|
||||||
/// The default emscripten host type.
|
/// The default emscripten host type.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -2,18 +2,15 @@
|
||||||
|
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use Device as DeviceTrait;
|
|
||||||
use DevicesError;
|
use DevicesError;
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
use EventLoop as EventLoopTrait;
|
|
||||||
use Format;
|
use Format;
|
||||||
use Host as HostTrait;
|
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use StreamDataResult;
|
use StreamDataResult;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use StreamId as StreamIdTrait;
|
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
|
use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Devices;
|
pub struct Devices;
|
||||||
|
|
|
@ -3,19 +3,16 @@ extern crate winapi;
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use Device as DeviceTrait;
|
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
use DevicesError;
|
use DevicesError;
|
||||||
use EventLoop as EventLoopTrait;
|
|
||||||
use Format;
|
use Format;
|
||||||
use Host as HostTrait;
|
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use StreamDataResult;
|
use StreamDataResult;
|
||||||
use StreamId as StreamIdTrait;
|
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use self::winapi::um::winnt::HRESULT;
|
use self::winapi::um::winnt::HRESULT;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait};
|
||||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
||||||
pub use self::stream::{EventLoop, StreamId};
|
pub use self::stream::{EventLoop, StreamId};
|
||||||
|
|
||||||
|
|
203
src/lib.rs
203
src/lib.rs
|
@ -18,7 +18,7 @@
|
||||||
//! `EventLoop`:
|
//! `EventLoop`:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use cpal::Host;
|
//! use cpal::traits::HostTrait;
|
||||||
//! let host = cpal::default_host();
|
//! let host = cpal::default_host();
|
||||||
//! let event_loop = host.event_loop();
|
//! let event_loop = host.event_loop();
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
//! stream type on the system.
|
//! stream type on the system.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use cpal::Host;
|
//! # use cpal::traits::HostTrait;
|
||||||
//! # let host = cpal::default_host();
|
//! # let host = cpal::default_host();
|
||||||
//! let device = host.default_output_device().expect("no output device available");
|
//! let device = host.default_output_device().expect("no output device available");
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
//! > has been disconnected.
|
//! > has been disconnected.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use cpal::{Device, Host};
|
//! use cpal::traits::{DeviceTrait, HostTrait};
|
||||||
//! # let host = cpal::default_host();
|
//! # let host = cpal::default_host();
|
||||||
//! # let device = host.default_output_device().unwrap();
|
//! # let device = host.default_output_device().unwrap();
|
||||||
//! let mut supported_formats_range = device.supported_output_formats()
|
//! let mut supported_formats_range = device.supported_output_formats()
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
//! Now that we have everything for the stream, we can create it from our event loop:
|
//! Now that we have everything for the stream, we can create it from our event loop:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use cpal::{Device, EventLoop, Host};
|
//! use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait};
|
||||||
//! # let host = cpal::default_host();
|
//! # let host = cpal::default_host();
|
||||||
//! # let event_loop = host.event_loop();
|
//! # let event_loop = host.event_loop();
|
||||||
//! # let device = host.default_output_device().unwrap();
|
//! # let device = host.default_output_device().unwrap();
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
//! Now we must start the stream. This is done with the `play_stream()` method on the event loop.
|
//! Now we must start the stream. This is done with the `play_stream()` method on the event loop.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use cpal::{EventLoop, Host};
|
//! # use cpal::traits::{EventLoopTrait, HostTrait};
|
||||||
//! # let host = cpal::default_host();
|
//! # let host = cpal::default_host();
|
||||||
//! # let event_loop = host.event_loop();
|
//! # let event_loop = host.event_loop();
|
||||||
//! # let stream_id = unimplemented!();
|
//! # let stream_id = unimplemented!();
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
//! Now everything is ready! We call `run()` on the `event_loop` to begin processing.
|
//! Now everything is ready! We call `run()` on the `event_loop` to begin processing.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use cpal::{EventLoop, Host};
|
//! # use cpal::traits::{EventLoopTrait, HostTrait};
|
||||||
//! # let host = cpal::default_host();
|
//! # let host = cpal::default_host();
|
||||||
//! # let event_loop = host.event_loop();
|
//! # let event_loop = host.event_loop();
|
||||||
//! event_loop.run(move |_stream_id, _stream_result| {
|
//! event_loop.run(move |_stream_id, _stream_result| {
|
||||||
|
@ -103,7 +103,8 @@
|
||||||
//! In this example, we simply fill the given output buffer with zeroes.
|
//! In this example, we simply fill the given output buffer with zeroes.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use cpal::{EventLoop, Host, StreamData, UnknownTypeOutputBuffer};
|
//! use cpal::{StreamData, UnknownTypeOutputBuffer};
|
||||||
|
//! use cpal::traits::{EventLoopTrait, HostTrait};
|
||||||
//! # let host = cpal::default_host();
|
//! # let host = cpal::default_host();
|
||||||
//! # let event_loop = host.event_loop();
|
//! # let event_loop = host.event_loop();
|
||||||
//! event_loop.run(move |stream_id, stream_result| {
|
//! event_loop.run(move |stream_id, stream_result| {
|
||||||
|
@ -148,7 +149,10 @@ extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate stdweb;
|
extern crate stdweb;
|
||||||
|
|
||||||
pub use platform::{ALL_HOSTS, HostId, available_hosts, default_host, host_from_id};
|
pub use platform::{
|
||||||
|
ALL_HOSTS, Device, EventLoop, Host, HostId, StreamId, available_hosts,
|
||||||
|
default_host, host_from_id,
|
||||||
|
};
|
||||||
pub use samples_formats::{Sample, SampleFormat};
|
pub use samples_formats::{Sample, SampleFormat};
|
||||||
|
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
|
@ -157,188 +161,7 @@ use std::ops::{Deref, DerefMut};
|
||||||
mod host;
|
mod host;
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
mod samples_formats;
|
mod samples_formats;
|
||||||
|
pub mod traits;
|
||||||
/// 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>;
|
|
||||||
|
|
||||||
/// Whether or not the host is available on the system.
|
|
||||||
fn is_available() -> bool;
|
|
||||||
|
|
||||||
/// 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>;
|
|
||||||
|
|
||||||
/// The default input audio device on the system.
|
|
||||||
///
|
|
||||||
/// Returns `None` if no input device is available.
|
|
||||||
fn default_input_device(&self) -> Option<Self::Device>;
|
|
||||||
|
|
||||||
/// 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` 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 `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;
|
|
||||||
|
|
||||||
/// 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.
|
/// A host's device iterator yielding only *input* devices.
|
||||||
pub type InputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
|
pub type InputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
|
||||||
|
|
|
@ -199,7 +199,7 @@ macro_rules! impl_platform_host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Device for Device {
|
impl crate::traits::DeviceTrait for Device {
|
||||||
type SupportedInputFormats = SupportedInputFormats;
|
type SupportedInputFormats = SupportedInputFormats;
|
||||||
type SupportedOutputFormats = SupportedOutputFormats;
|
type SupportedOutputFormats = SupportedOutputFormats;
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ macro_rules! impl_platform_host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::EventLoop for EventLoop {
|
impl crate::traits::EventLoopTrait for EventLoop {
|
||||||
type StreamId = StreamId;
|
type StreamId = StreamId;
|
||||||
type Device = Device;
|
type Device = Device;
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ macro_rules! impl_platform_host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Host for Host {
|
impl crate::traits::HostTrait for Host {
|
||||||
type Devices = Devices;
|
type Devices = Devices;
|
||||||
type Device = Device;
|
type Device = Device;
|
||||||
type EventLoop = EventLoop;
|
type EventLoop = EventLoop;
|
||||||
|
@ -385,7 +385,7 @@ macro_rules! impl_platform_host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::StreamId for StreamId {}
|
impl crate::traits::StreamIdTrait for StreamId {}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
impl From<crate::host::$host_mod::Device> for Device {
|
impl From<crate::host::$host_mod::Device> for Device {
|
||||||
|
@ -423,7 +423,7 @@ macro_rules! impl_platform_host {
|
||||||
pub fn available_hosts() -> Vec<HostId> {
|
pub fn available_hosts() -> Vec<HostId> {
|
||||||
let mut host_ids = vec![];
|
let mut host_ids = vec![];
|
||||||
$(
|
$(
|
||||||
if <crate::host::$host_mod::Host as crate::Host>::is_available() {
|
if <crate::host::$host_mod::Host as crate::traits::HostTrait>::is_available() {
|
||||||
host_ids.push(HostId::$HostVariant);
|
host_ids.push(HostId::$HostVariant);
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
|
||||||
|
|
||||||
|
use {
|
||||||
|
BuildStreamError,
|
||||||
|
DefaultFormatError,
|
||||||
|
DeviceNameError,
|
||||||
|
DevicesError,
|
||||||
|
Format,
|
||||||
|
InputDevices,
|
||||||
|
OutputDevices,
|
||||||
|
PauseStreamError,
|
||||||
|
PlayStreamError,
|
||||||
|
StreamDataResult,
|
||||||
|
SupportedFormat,
|
||||||
|
SupportedFormatsError,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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 HostTrait {
|
||||||
|
/// 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: DeviceTrait;
|
||||||
|
/// The event loop type used by the `Host`
|
||||||
|
type EventLoop: EventLoopTrait<Device = Self::Device>;
|
||||||
|
|
||||||
|
/// Whether or not the host is available on the system.
|
||||||
|
fn is_available() -> bool;
|
||||||
|
|
||||||
|
/// 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>;
|
||||||
|
|
||||||
|
/// The default input audio device on the system.
|
||||||
|
///
|
||||||
|
/// Returns `None` if no input device is available.
|
||||||
|
fn default_input_device(&self) -> Option<Self::Device>;
|
||||||
|
|
||||||
|
/// 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: DeviceTrait>(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: DeviceTrait>(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` allowing the user to handle this case.
|
||||||
|
pub trait DeviceTrait {
|
||||||
|
/// 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 `Host::event_loop` method.
|
||||||
|
pub trait EventLoopTrait {
|
||||||
|
/// The `Device` type yielded by the host.
|
||||||
|
type Device: DeviceTrait;
|
||||||
|
/// The type used to uniquely distinguish between streams.
|
||||||
|
type StreamId: StreamIdTrait;
|
||||||
|
|
||||||
|
/// 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 StreamIdTrait: Clone + std::fmt::Debug + PartialEq + Eq {}
|
Loading…
Reference in New Issue