commit
7e413cb660
|
@ -6,6 +6,7 @@ Low-level library for audio input and output in pure Rust.
|
||||||
|
|
||||||
This library currently supports the following:
|
This library currently supports the following:
|
||||||
|
|
||||||
|
- Enumerate supported audio hosts.
|
||||||
- Enumerate all available audio devices.
|
- Enumerate all available audio devices.
|
||||||
- Get the current default input and output devices.
|
- Get the current default input and output devices.
|
||||||
- Enumerate known supported input and output stream formats for a device.
|
- Enumerate known supported input and output stream formats for a device.
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
|
use cpal::{Device, EventLoop, Host};
|
||||||
|
|
||||||
fn main() -> Result<(), failure::Error> {
|
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 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)?;
|
let stream_id = event_loop.build_output_stream(&device, &format)?;
|
||||||
event_loop.play_stream(stream_id.clone())?;
|
event_loop.play_stream(stream_id.clone())?;
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
fn main() -> Result<(), failure::Error> {
|
use cpal::{Device, Host};
|
||||||
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);
|
|
||||||
|
|
||||||
let devices = cpal::devices()?;
|
fn main() -> Result<(), failure::Error> {
|
||||||
println!("Devices: ");
|
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() {
|
for (device_index, device) in devices.enumerate() {
|
||||||
println!("{}. \"{}\"", device_index + 1, device.name()?);
|
println!(" {}. \"{}\"", device_index + 1, device.name()?);
|
||||||
|
|
||||||
// Input formats
|
// Input formats
|
||||||
if let Ok(fmt) = device.default_input_format() {
|
if let Ok(fmt) = device.default_input_format() {
|
||||||
|
@ -48,6 +57,7 @@ fn main() -> Result<(), failure::Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,17 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
|
use cpal::{Device, EventLoop, Host};
|
||||||
|
|
||||||
const LATENCY_MS: f32 = 150.0;
|
const LATENCY_MS: f32 = 150.0;
|
||||||
|
|
||||||
fn main() -> Result<(), failure::Error> {
|
fn main() -> Result<(), failure::Error> {
|
||||||
let event_loop = cpal::EventLoop::new();
|
let host = cpal::default_host();
|
||||||
|
let event_loop = host.event_loop();
|
||||||
|
|
||||||
// Default devices.
|
// Default devices.
|
||||||
let input_device = cpal::default_input_device().expect("failed to get default input device");
|
let input_device = host.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 output_device = host.default_output_device().expect("failed to get default output device");
|
||||||
println!("Using default input device: \"{}\"", input_device.name()?);
|
println!("Using default input device: \"{}\"", input_device.name()?);
|
||||||
println!("Using default output device: \"{}\"", output_device.name()?);
|
println!("Using default output device: \"{}\"", output_device.name()?);
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,18 @@ extern crate cpal;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
extern crate hound;
|
extern crate hound;
|
||||||
|
|
||||||
|
use cpal::{Device, EventLoop, Host};
|
||||||
|
|
||||||
fn main() -> Result<(), failure::Error> {
|
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.
|
// 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()?);
|
println!("Default input device: {}", device.name()?);
|
||||||
let format = device.default_input_format().expect("Failed to get default input format");
|
let format = device.default_input_format().expect("Failed to get default input format");
|
||||||
println!("Default input format: {:?}", 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)?;
|
let stream_id = event_loop.build_input_stream(&device, &format)?;
|
||||||
event_loop.play_stream(stream_id)?;
|
event_loop.play_stream(stream_id)?;
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,21 @@ 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 EventLoop as EventLoopTrait;
|
||||||
use Format;
|
use Format;
|
||||||
|
use Host as HostTrait;
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use SupportedFormatsError;
|
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
use SampleRate;
|
use SampleRate;
|
||||||
|
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;
|
||||||
|
@ -32,6 +37,109 @@ pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
|
||||||
|
|
||||||
mod enumerate;
|
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 {
|
struct Trigger {
|
||||||
// [read fd, write fd]
|
// [read fd, write fd]
|
||||||
|
@ -79,7 +187,7 @@ pub struct Device(String);
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
Ok(self.0.clone())
|
Ok(self.0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,13 +395,13 @@ impl Device {
|
||||||
Ok(output.into_iter())
|
Ok(output.into_iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE)
|
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 {
|
unsafe {
|
||||||
self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK)
|
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)
|
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)
|
self.default_format(alsa::SND_PCM_STREAM_PLAYBACK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,7 +542,7 @@ enum StreamType { Input, Output }
|
||||||
|
|
||||||
impl EventLoop {
|
impl EventLoop {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> EventLoop {
|
fn new() -> EventLoop {
|
||||||
let pending_command_trigger = Trigger::new();
|
let pending_command_trigger = Trigger::new();
|
||||||
|
|
||||||
let mut initial_descriptors = vec![];
|
let mut initial_descriptors = vec![];
|
||||||
|
@ -460,7 +568,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run<F>(&self, mut callback: F) -> !
|
fn run<F>(&self, mut callback: F) -> !
|
||||||
where F: FnMut(StreamId, StreamDataResult)
|
where F: FnMut(StreamId, StreamDataResult)
|
||||||
{
|
{
|
||||||
self.run_inner(&mut callback)
|
self.run_inner(&mut callback)
|
||||||
|
@ -645,7 +753,7 @@ impl EventLoop {
|
||||||
panic!("`cpal::EventLoop::run` API currently disallows returning");
|
panic!("`cpal::EventLoop::run` API currently disallows returning");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_input_stream(
|
fn build_input_stream(
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
|
@ -724,7 +832,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_output_stream(
|
fn build_output_stream(
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
|
@ -805,18 +913,18 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
fn destroy_stream(&self, stream_id: StreamId) {
|
||||||
self.push_command(Command::DestroyStream(stream_id));
|
self.push_command(Command::DestroyStream(stream_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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));
|
self.push_command(Command::PlayStream(stream_id));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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));
|
self.push_command(Command::PauseStream(stream_id));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -5,8 +5,12 @@ 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 EventLoop as EventLoopTrait;
|
||||||
use Format;
|
use Format;
|
||||||
|
use Host as HostTrait;
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
|
@ -14,6 +18,7 @@ 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;
|
||||||
|
@ -72,13 +77,117 @@ mod enumerate;
|
||||||
|
|
||||||
pub use self::enumerate::{Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
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)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
audio_device_id: AudioDeviceID,
|
audio_device_id: AudioDeviceID,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
let property_address = AudioObjectPropertyAddress {
|
let property_address = AudioObjectPropertyAddress {
|
||||||
mSelector: kAudioDevicePropertyDeviceNameCFString,
|
mSelector: kAudioDevicePropertyDeviceNameCFString,
|
||||||
mScope: kAudioDevicePropertyScopeOutput,
|
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)
|
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)
|
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)
|
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)
|
self.default_format(kAudioObjectPropertyScopeOutput)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -435,7 +544,7 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
|
||||||
|
|
||||||
impl EventLoop {
|
impl EventLoop {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> EventLoop {
|
fn new() -> EventLoop {
|
||||||
EventLoop {
|
EventLoop {
|
||||||
user_callback: Arc::new(Mutex::new(UserCallback::Inactive)),
|
user_callback: Arc::new(Mutex::new(UserCallback::Inactive)),
|
||||||
streams: Mutex::new(Vec::new()),
|
streams: Mutex::new(Vec::new()),
|
||||||
|
@ -443,7 +552,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run<F>(&self, mut callback: F) -> !
|
fn run<F>(&self, mut callback: F) -> !
|
||||||
where F: FnMut(StreamId, StreamDataResult) + Send
|
where F: FnMut(StreamId, StreamDataResult) + Send
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -490,7 +599,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build_input_stream(
|
fn build_input_stream(
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
|
@ -712,7 +821,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build_output_stream(
|
fn build_output_stream(
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
|
@ -787,14 +896,14 @@ impl EventLoop {
|
||||||
Ok(StreamId(stream_id))
|
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();
|
let mut streams = self.streams.lock().unwrap();
|
||||||
streams[stream_id.0] = None;
|
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 mut streams = self.streams.lock().unwrap();
|
||||||
let stream = streams[stream_id.0].as_mut().unwrap();
|
let stream = streams[stream_id.0].as_mut().unwrap();
|
||||||
|
|
||||||
|
@ -809,7 +918,7 @@ impl EventLoop {
|
||||||
Ok(())
|
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 mut streams = self.streams.lock().unwrap();
|
||||||
let stream = streams[stream_id.0].as_mut().unwrap();
|
let stream = streams[stream_id.0].as_mut().unwrap();
|
||||||
|
|
|
@ -10,17 +10,125 @@ 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;
|
||||||
|
|
||||||
|
/// 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
|
// 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.
|
// is an array of `AudioContext` objects. A stream ID corresponds to an entry in this array.
|
||||||
//
|
//
|
||||||
|
@ -44,7 +152,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run<F>(&self, callback: F) -> !
|
fn run<F>(&self, callback: F) -> !
|
||||||
where F: FnMut(StreamId, StreamDataResult),
|
where F: FnMut(StreamId, StreamDataResult),
|
||||||
{
|
{
|
||||||
// The `run` function uses `set_timeout` to invoke a Rust callback repeatidely. The job
|
// The `run` function uses `set_timeout` to invoke a Rust callback repeatidely. The job
|
||||||
|
@ -124,12 +232,12 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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 stream = js!(return new AudioContext()).into_reference().unwrap();
|
||||||
|
|
||||||
let mut streams = self.streams.lock().unwrap();
|
let mut streams = self.streams.lock().unwrap();
|
||||||
|
@ -146,12 +254,12 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
fn destroy_stream(&self, stream_id: StreamId) {
|
||||||
self.streams.lock().unwrap()[stream_id.0] = None;
|
self.streams.lock().unwrap()[stream_id.0] = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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 streams = self.streams.lock().unwrap();
|
||||||
let stream = streams
|
let stream = streams
|
||||||
.get(stream_id.0)
|
.get(stream_id.0)
|
||||||
|
@ -162,7 +270,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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 streams = self.streams.lock().unwrap();
|
||||||
let stream = streams
|
let stream = streams
|
||||||
.get(stream_id.0)
|
.get(stream_id.0)
|
||||||
|
@ -193,7 +301,7 @@ fn is_webaudio_available() -> bool {
|
||||||
pub struct Devices(bool);
|
pub struct Devices(bool);
|
||||||
|
|
||||||
impl Devices {
|
impl Devices {
|
||||||
pub fn new() -> Result<Self, DevicesError> {
|
fn new() -> Result<Self, DevicesError> {
|
||||||
Ok(Self::default())
|
Ok(Self::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,12 +326,12 @@ impl Iterator for Devices {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn default_input_device() -> Option<Device> {
|
fn default_input_device() -> Option<Device> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn default_output_device() -> Option<Device> {
|
fn default_output_device() -> Option<Device> {
|
||||||
if is_webaudio_available() {
|
if is_webaudio_available() {
|
||||||
Some(Device)
|
Some(Device)
|
||||||
} else {
|
} else {
|
||||||
|
@ -236,17 +344,17 @@ pub struct Device;
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
Ok("Default Device".to_owned())
|
Ok("Default Device".to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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
|
// 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
|
// "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
|
// 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!();
|
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.
|
// TODO: because it is hard coded, see supported_output_formats.
|
||||||
Ok(
|
Ok(
|
||||||
Format {
|
Format {
|
|
@ -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;
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -115,7 +115,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_input_stream(
|
pub(crate) fn build_input_stream(
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
|
@ -276,7 +276,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_output_stream(
|
pub(crate) fn build_output_stream(
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
|
@ -439,12 +439,12 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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));
|
self.push_command(Command::DestroyStream(stream_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run<F>(&self, mut callback: F) -> !
|
pub(crate) fn run<F>(&self, mut callback: F) -> !
|
||||||
where F: FnMut(StreamId, StreamDataResult)
|
where F: FnMut(StreamId, StreamDataResult)
|
||||||
{
|
{
|
||||||
self.run_inner(&mut callback);
|
self.run_inner(&mut callback);
|
||||||
|
@ -617,13 +617,13 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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));
|
self.push_command(Command::PlayStream(stream));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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));
|
self.push_command(Command::PauseStream(stream));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
515
src/lib.rs
515
src/lib.rs
|
@ -2,19 +2,25 @@
|
||||||
//!
|
//!
|
||||||
//! Here are some concepts cpal exposes:
|
//! Here are some concepts cpal exposes:
|
||||||
//!
|
//!
|
||||||
//! - A `Device` is an audio device that may have any number of input and output streams.
|
//! - A [**Host**](./trait.Host.html) provides access to the available audio devices on the system.
|
||||||
//! - A stream is an open audio channel. Input streams allow you to receive audio data, output
|
//! Some platforms have more than one host available, but every platform supported by CPAL has at
|
||||||
//! streams allow you to play audio data. You must choose which `Device` runs your stream before
|
//! least one [**DefaultHost**](./trait.Host.html) that is guaranteed to be available.
|
||||||
//! you create one.
|
//! - A [**Device**](./trait.Device.html) is an audio device that may have any number of input and
|
||||||
//! - An `EventLoop` is a collection of streams being run by one or more `Device`. Each stream must
|
//! output streams.
|
||||||
//! belong to an `EventLoop`, and all the streams that belong to an `EventLoop` are managed
|
//! - A stream is an open flow of audio data. Input streams allow you to receive audio data, output
|
||||||
//! together.
|
//! 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;
|
//! use cpal::Host;
|
||||||
//! let event_loop = EventLoop::new();
|
//! 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
|
//! 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.
|
//! stream type on the system.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```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
|
//! 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.
|
//! > has been disconnected.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```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()
|
//! let mut supported_formats_range = device.supported_output_formats()
|
||||||
//! .expect("error while querying formats");
|
//! .expect("error while querying formats");
|
||||||
//! let format = supported_formats_range.next()
|
//! 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:
|
//! Now that we have everything for the stream, we can create it from our event loop:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```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 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();
|
//! 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.
|
//! Now we must start the stream. This is done with the `play_stream()` method on the event loop.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # let event_loop: cpal::EventLoop = return;
|
//! # use cpal::{EventLoop, Host};
|
||||||
//! # let stream_id: cpal::StreamId = return;
|
//! # 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");
|
//! 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.
|
//! Now everything is ready! We call `run()` on the `event_loop` to begin processing.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```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| {
|
//! event_loop.run(move |_stream_id, _stream_result| {
|
||||||
//! // react to stream events and read or write stream data here
|
//! // 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.
|
//! In this example, we simply fill the given output buffer with zeroes.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use cpal::{StreamData, UnknownTypeOutputBuffer};
|
//! use cpal::{EventLoop, Host, StreamData, UnknownTypeOutputBuffer};
|
||||||
//!
|
//! # let host = cpal::default_host();
|
||||||
//! # let event_loop = cpal::EventLoop::new();
|
//! # let event_loop = host.event_loop();
|
||||||
//! event_loop.run(move |stream_id, stream_result| {
|
//! event_loop.run(move |stream_id, stream_result| {
|
||||||
//! let stream_data = match stream_result {
|
//! let stream_data = match stream_result {
|
||||||
//! Ok(data) => data,
|
//! Ok(data) => data,
|
||||||
|
@ -132,51 +148,203 @@ 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 samples_formats::{Sample, SampleFormat};
|
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 failure::Fail;
|
||||||
use std::fmt;
|
|
||||||
use std::iter;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
mod null;
|
mod host;
|
||||||
|
pub mod platform;
|
||||||
mod samples_formats;
|
mod samples_formats;
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
/// A **Host** provides access to the available audio devices on the system.
|
||||||
#[path = "alsa/mod.rs"]
|
///
|
||||||
mod cpal_impl;
|
/// 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)]
|
/// Whether or not the host is available on the system.
|
||||||
#[path = "wasapi/mod.rs"]
|
fn is_available() -> bool;
|
||||||
mod cpal_impl;
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
/// An iterator yielding all `Device`s currently available to the host on the system.
|
||||||
#[path = "coreaudio/mod.rs"]
|
///
|
||||||
mod cpal_impl;
|
/// Can be empty if the system does not support audio in general.
|
||||||
|
fn devices(&self) -> Result<Self::Devices, DevicesError>;
|
||||||
|
|
||||||
#[cfg(target_os = "emscripten")]
|
/// The default input audio device on the system.
|
||||||
#[path = "emscripten/mod.rs"]
|
///
|
||||||
mod cpal_impl;
|
/// 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
|
/// Please note that `Device`s may become invalid if they get disconnected. Therefore all the
|
||||||
/// methods that involve a device return a `Result`.
|
/// methods that involve a device return a `Result` allowing the user to handle this case.
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
pub trait Device {
|
||||||
pub struct Device(cpal_impl::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.
|
/// Collection of streams managed together.
|
||||||
///
|
///
|
||||||
/// Created with the [`new`](struct.EventLoop.html#method.new) method.
|
/// Created with the `Host::event_loop` method.
|
||||||
pub struct EventLoop(cpal_impl::EventLoop);
|
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`.
|
/// Creates a new input stream that will run from the given device and with the given format.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
///
|
||||||
pub struct StreamId(cpal_impl::StreamId);
|
/// 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.
|
/// Number of channels.
|
||||||
pub type ChannelCount = u16;
|
pub type ChannelCount = u16;
|
||||||
|
@ -271,26 +439,10 @@ pub enum UnknownTypeOutputBuffer<'a> {
|
||||||
F32(OutputBuffer<'a, f32>),
|
F32(OutputBuffer<'a, f32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator yielding all `Device`s currently available to the system.
|
/// The requested host, although supported on this platform, is unavailable.
|
||||||
///
|
#[derive(Clone, Debug, Fail)]
|
||||||
/// See [`devices()`](fn.devices.html).
|
#[fail(display = "the requested host is unavailable")]
|
||||||
pub struct Devices(cpal_impl::Devices);
|
pub struct HostUnavailable;
|
||||||
|
|
||||||
/// 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);
|
|
||||||
|
|
||||||
/// Some error has occurred that is specific to the backend from which it was produced.
|
/// 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 {
|
impl SupportedFormat {
|
||||||
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
|
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
|
||||||
#[inline]
|
#[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 {
|
impl From<BackendSpecificError> for DevicesError {
|
||||||
fn from(err: BackendSpecificError) -> Self {
|
fn from(err: BackendSpecificError) -> Self {
|
||||||
DevicesError::BackendSpecific { err }
|
DevicesError::BackendSpecific { err }
|
||||||
|
|
145
src/null/mod.rs
145
src/null/mod.rs
|
@ -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>,
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue