commit
7e413cb660
|
@ -6,6 +6,7 @@ Low-level library for audio input and output in pure Rust.
|
|||
|
||||
This library currently supports the following:
|
||||
|
||||
- Enumerate supported audio hosts.
|
||||
- Enumerate all available audio devices.
|
||||
- Get the current default input and output devices.
|
||||
- Enumerate known supported input and output stream formats for a device.
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
extern crate cpal;
|
||||
extern crate failure;
|
||||
|
||||
use cpal::{Device, EventLoop, Host};
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let device = cpal::default_output_device().expect("failed to find a default output device");
|
||||
let host = cpal::default_host();
|
||||
let device = host.default_output_device().expect("failed to find a default output device");
|
||||
let format = device.default_output_format()?;
|
||||
let event_loop = cpal::EventLoop::new();
|
||||
let event_loop = host.event_loop();
|
||||
let stream_id = event_loop.build_output_stream(&device, &format)?;
|
||||
event_loop.play_stream(stream_id.clone())?;
|
||||
|
||||
|
|
|
@ -1,50 +1,60 @@
|
|||
extern crate cpal;
|
||||
extern crate failure;
|
||||
|
||||
use cpal::{Device, Host};
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let default_in = cpal::default_input_device().map(|e| e.name().unwrap());
|
||||
let default_out = cpal::default_output_device().map(|e| e.name().unwrap());
|
||||
println!("Default Input Device:\n {:?}", default_in);
|
||||
println!("Default Output Device:\n {:?}", default_out);
|
||||
println!("Supported hosts:\n {:?}", cpal::ALL_HOSTS);
|
||||
let available_hosts = cpal::available_hosts();
|
||||
println!("Available hosts:\n {:?}", available_hosts);
|
||||
|
||||
let devices = cpal::devices()?;
|
||||
println!("Devices: ");
|
||||
for (device_index, device) in devices.enumerate() {
|
||||
println!("{}. \"{}\"", device_index + 1, device.name()?);
|
||||
for host_id in available_hosts {
|
||||
println!("{:?}", host_id);
|
||||
let host = cpal::host_from_id(host_id)?;
|
||||
let default_in = host.default_input_device().map(|e| e.name().unwrap());
|
||||
let default_out = host.default_output_device().map(|e| e.name().unwrap());
|
||||
println!(" Default Input Device:\n {:?}", default_in);
|
||||
println!(" Default Output Device:\n {:?}", default_out);
|
||||
|
||||
// Input formats
|
||||
if let Ok(fmt) = device.default_input_format() {
|
||||
println!(" Default input stream format:\n {:?}", fmt);
|
||||
}
|
||||
let mut input_formats = match device.supported_input_formats() {
|
||||
Ok(f) => f.peekable(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
if input_formats.peek().is_some() {
|
||||
println!(" All supported input stream formats:");
|
||||
for (format_index, format) in input_formats.enumerate() {
|
||||
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
|
||||
let devices = host.devices()?;
|
||||
println!(" Devices: ");
|
||||
for (device_index, device) in devices.enumerate() {
|
||||
println!(" {}. \"{}\"", device_index + 1, device.name()?);
|
||||
|
||||
// Input formats
|
||||
if let Ok(fmt) = device.default_input_format() {
|
||||
println!(" Default input stream format:\n {:?}", fmt);
|
||||
}
|
||||
let mut input_formats = match device.supported_input_formats() {
|
||||
Ok(f) => f.peekable(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
if input_formats.peek().is_some() {
|
||||
println!(" All supported input stream formats:");
|
||||
for (format_index, format) in input_formats.enumerate() {
|
||||
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output formats
|
||||
if let Ok(fmt) = device.default_output_format() {
|
||||
println!(" Default output stream format:\n {:?}", fmt);
|
||||
}
|
||||
let mut output_formats = match device.supported_output_formats() {
|
||||
Ok(f) => f.peekable(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
if output_formats.peek().is_some() {
|
||||
println!(" All supported output stream formats:");
|
||||
for (format_index, format) in output_formats.enumerate() {
|
||||
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
|
||||
// Output formats
|
||||
if let Ok(fmt) = device.default_output_format() {
|
||||
println!(" Default output stream format:\n {:?}", fmt);
|
||||
}
|
||||
let mut output_formats = match device.supported_output_formats() {
|
||||
Ok(f) => f.peekable(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
if output_formats.peek().is_some() {
|
||||
println!(" All supported output stream formats:");
|
||||
for (format_index, format) in output_formats.enumerate() {
|
||||
println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,17 @@
|
|||
extern crate cpal;
|
||||
extern crate failure;
|
||||
|
||||
use cpal::{Device, EventLoop, Host};
|
||||
|
||||
const LATENCY_MS: f32 = 150.0;
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let event_loop = cpal::EventLoop::new();
|
||||
let host = cpal::default_host();
|
||||
let event_loop = host.event_loop();
|
||||
|
||||
// Default devices.
|
||||
let input_device = cpal::default_input_device().expect("failed to get default input device");
|
||||
let output_device = cpal::default_output_device().expect("failed to get default output device");
|
||||
let input_device = host.default_input_device().expect("failed to get default input device");
|
||||
let output_device = host.default_output_device().expect("failed to get default output device");
|
||||
println!("Using default input device: \"{}\"", input_device.name()?);
|
||||
println!("Using default output device: \"{}\"", output_device.name()?);
|
||||
|
||||
|
|
|
@ -6,13 +6,18 @@ extern crate cpal;
|
|||
extern crate failure;
|
||||
extern crate hound;
|
||||
|
||||
use cpal::{Device, EventLoop, Host};
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
// Use the default host for working with audio devices.
|
||||
let host = cpal::default_host();
|
||||
|
||||
// Setup the default input device and stream with the default input format.
|
||||
let device = cpal::default_input_device().expect("Failed to get default input device");
|
||||
let device = host.default_input_device().expect("Failed to get default input device");
|
||||
println!("Default input device: {}", device.name()?);
|
||||
let format = device.default_input_format().expect("Failed to get default input format");
|
||||
println!("Default input format: {:?}", format);
|
||||
let event_loop = cpal::EventLoop::new();
|
||||
let event_loop = host.event_loop();
|
||||
let stream_id = event_loop.build_input_stream(&device, &format)?;
|
||||
event_loop.play_stream(stream_id)?;
|
||||
|
||||
|
|
|
@ -7,16 +7,21 @@ use ChannelCount;
|
|||
use BackendSpecificError;
|
||||
use BuildStreamError;
|
||||
use DefaultFormatError;
|
||||
use Device as DeviceTrait;
|
||||
use DeviceNameError;
|
||||
use DevicesError;
|
||||
use EventLoop as EventLoopTrait;
|
||||
use Format;
|
||||
use Host as HostTrait;
|
||||
use PauseStreamError;
|
||||
use PlayStreamError;
|
||||
use SupportedFormatsError;
|
||||
use SampleFormat;
|
||||
use SampleRate;
|
||||
use SupportedFormatsError;
|
||||
use StreamData;
|
||||
use StreamDataResult;
|
||||
use StreamError;
|
||||
use StreamId as StreamIdTrait;
|
||||
use SupportedFormat;
|
||||
use UnknownTypeInputBuffer;
|
||||
use UnknownTypeOutputBuffer;
|
||||
|
@ -32,6 +37,109 @@ pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
|
|||
|
||||
mod enumerate;
|
||||
|
||||
/// The default linux and freebsd host type.
|
||||
#[derive(Debug)]
|
||||
pub struct Host;
|
||||
|
||||
impl Host {
|
||||
pub fn new() -> Result<Self, crate::HostUnavailable> {
|
||||
Ok(Host)
|
||||
}
|
||||
}
|
||||
|
||||
impl HostTrait for Host {
|
||||
type Devices = Devices;
|
||||
type Device = Device;
|
||||
type EventLoop = EventLoop;
|
||||
|
||||
fn is_available() -> bool {
|
||||
// Assume ALSA is always available on linux/freebsd.
|
||||
true
|
||||
}
|
||||
|
||||
fn devices(&self) -> Result<Self::Devices, DevicesError> {
|
||||
Devices::new()
|
||||
}
|
||||
|
||||
fn default_input_device(&self) -> Option<Self::Device> {
|
||||
default_input_device()
|
||||
}
|
||||
|
||||
fn default_output_device(&self) -> Option<Self::Device> {
|
||||
default_output_device()
|
||||
}
|
||||
|
||||
fn event_loop(&self) -> Self::EventLoop {
|
||||
EventLoop::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopTrait for EventLoop {
|
||||
type Device = Device;
|
||||
type StreamId = StreamId;
|
||||
|
||||
fn build_input_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError> {
|
||||
EventLoop::build_input_stream(self, device, format)
|
||||
}
|
||||
|
||||
fn build_output_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError> {
|
||||
EventLoop::build_output_stream(self, device, format)
|
||||
}
|
||||
|
||||
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
|
||||
EventLoop::play_stream(self, stream)
|
||||
}
|
||||
|
||||
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
|
||||
EventLoop::pause_stream(self, stream)
|
||||
}
|
||||
|
||||
fn destroy_stream(&self, stream: Self::StreamId) {
|
||||
EventLoop::destroy_stream(self, stream)
|
||||
}
|
||||
|
||||
fn run<F>(&self, callback: F) -> !
|
||||
where
|
||||
F: FnMut(Self::StreamId, StreamDataResult) + Send,
|
||||
{
|
||||
EventLoop::run(self, callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamIdTrait for StreamId {}
|
||||
|
||||
struct Trigger {
|
||||
// [read fd, write fd]
|
||||
|
@ -79,7 +187,7 @@ pub struct Device(String);
|
|||
|
||||
impl Device {
|
||||
#[inline]
|
||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Ok(self.0.clone())
|
||||
}
|
||||
|
||||
|
@ -287,13 +395,13 @@ impl Device {
|
|||
Ok(output.into_iter())
|
||||
}
|
||||
|
||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
unsafe {
|
||||
self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
unsafe {
|
||||
self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK)
|
||||
}
|
||||
|
@ -340,11 +448,11 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(alsa::SND_PCM_STREAM_CAPTURE)
|
||||
}
|
||||
|
||||
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(alsa::SND_PCM_STREAM_PLAYBACK)
|
||||
}
|
||||
}
|
||||
|
@ -434,7 +542,7 @@ enum StreamType { Input, Output }
|
|||
|
||||
impl EventLoop {
|
||||
#[inline]
|
||||
pub fn new() -> EventLoop {
|
||||
fn new() -> EventLoop {
|
||||
let pending_command_trigger = Trigger::new();
|
||||
|
||||
let mut initial_descriptors = vec![];
|
||||
|
@ -460,7 +568,7 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn run<F>(&self, mut callback: F) -> !
|
||||
fn run<F>(&self, mut callback: F) -> !
|
||||
where F: FnMut(StreamId, StreamDataResult)
|
||||
{
|
||||
self.run_inner(&mut callback)
|
||||
|
@ -645,7 +753,7 @@ impl EventLoop {
|
|||
panic!("`cpal::EventLoop::run` API currently disallows returning");
|
||||
}
|
||||
|
||||
pub fn build_input_stream(
|
||||
fn build_input_stream(
|
||||
&self,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
|
@ -724,7 +832,7 @@ impl EventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_output_stream(
|
||||
fn build_output_stream(
|
||||
&self,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
|
@ -805,18 +913,18 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
||||
fn destroy_stream(&self, stream_id: StreamId) {
|
||||
self.push_command(Command::DestroyStream(stream_id));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
||||
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
||||
self.push_command(Command::PlayStream(stream_id));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
||||
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
||||
self.push_command(Command::PauseStream(stream_id));
|
||||
Ok(())
|
||||
}
|
|
@ -5,8 +5,12 @@ use ChannelCount;
|
|||
use BackendSpecificError;
|
||||
use BuildStreamError;
|
||||
use DefaultFormatError;
|
||||
use Device as DeviceTrait;
|
||||
use DeviceNameError;
|
||||
use DevicesError;
|
||||
use EventLoop as EventLoopTrait;
|
||||
use Format;
|
||||
use Host as HostTrait;
|
||||
use PauseStreamError;
|
||||
use PlayStreamError;
|
||||
use SupportedFormatsError;
|
||||
|
@ -14,6 +18,7 @@ use SampleFormat;
|
|||
use SampleRate;
|
||||
use StreamData;
|
||||
use StreamDataResult;
|
||||
use StreamId as StreamIdTrait;
|
||||
use SupportedFormat;
|
||||
use UnknownTypeInputBuffer;
|
||||
use UnknownTypeOutputBuffer;
|
||||
|
@ -72,13 +77,117 @@ mod enumerate;
|
|||
|
||||
pub use self::enumerate::{Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
||||
|
||||
/// Coreaudio host, the default host on macOS and iOS.
|
||||
#[derive(Debug)]
|
||||
pub struct Host;
|
||||
|
||||
impl Host {
|
||||
pub fn new() -> Result<Self, crate::HostUnavailable> {
|
||||
Ok(Host)
|
||||
}
|
||||
}
|
||||
|
||||
impl HostTrait for Host {
|
||||
type Devices = Devices;
|
||||
type Device = Device;
|
||||
type EventLoop = EventLoop;
|
||||
|
||||
fn is_available() -> bool {
|
||||
// Assume coreaudio is always available on macOS and iOS.
|
||||
true
|
||||
}
|
||||
|
||||
fn devices(&self) -> Result<Self::Devices, DevicesError> {
|
||||
Devices::new()
|
||||
}
|
||||
|
||||
fn default_input_device(&self) -> Option<Self::Device> {
|
||||
default_input_device()
|
||||
}
|
||||
|
||||
fn default_output_device(&self) -> Option<Self::Device> {
|
||||
default_output_device()
|
||||
}
|
||||
|
||||
fn event_loop(&self) -> Self::EventLoop {
|
||||
EventLoop::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopTrait for EventLoop {
|
||||
type Device = Device;
|
||||
type StreamId = StreamId;
|
||||
|
||||
fn build_input_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError> {
|
||||
EventLoop::build_input_stream(self, device, format)
|
||||
}
|
||||
|
||||
fn build_output_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError> {
|
||||
EventLoop::build_output_stream(self, device, format)
|
||||
}
|
||||
|
||||
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
|
||||
EventLoop::play_stream(self, stream)
|
||||
}
|
||||
|
||||
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
|
||||
EventLoop::pause_stream(self, stream)
|
||||
}
|
||||
|
||||
fn destroy_stream(&self, stream: Self::StreamId) {
|
||||
EventLoop::destroy_stream(self, stream)
|
||||
}
|
||||
|
||||
fn run<F>(&self, callback: F) -> !
|
||||
where
|
||||
F: FnMut(Self::StreamId, StreamDataResult) + Send,
|
||||
{
|
||||
EventLoop::run(self, callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamIdTrait for StreamId {}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Device {
|
||||
audio_device_id: AudioDeviceID,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
let property_address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioDevicePropertyDeviceNameCFString,
|
||||
mScope: kAudioDevicePropertyScopeOutput,
|
||||
|
@ -212,11 +321,11 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn supported_input_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_input_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
self.supported_formats(kAudioObjectPropertyScopeInput)
|
||||
}
|
||||
|
||||
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
self.supported_formats(kAudioObjectPropertyScopeOutput)
|
||||
}
|
||||
|
||||
|
@ -296,11 +405,11 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(kAudioObjectPropertyScopeInput)
|
||||
}
|
||||
|
||||
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(kAudioObjectPropertyScopeOutput)
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +544,7 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
|
|||
|
||||
impl EventLoop {
|
||||
#[inline]
|
||||
pub fn new() -> EventLoop {
|
||||
fn new() -> EventLoop {
|
||||
EventLoop {
|
||||
user_callback: Arc::new(Mutex::new(UserCallback::Inactive)),
|
||||
streams: Mutex::new(Vec::new()),
|
||||
|
@ -443,7 +552,7 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn run<F>(&self, mut callback: F) -> !
|
||||
fn run<F>(&self, mut callback: F) -> !
|
||||
where F: FnMut(StreamId, StreamDataResult) + Send
|
||||
{
|
||||
{
|
||||
|
@ -490,7 +599,7 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_input_stream(
|
||||
fn build_input_stream(
|
||||
&self,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
|
@ -712,7 +821,7 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_output_stream(
|
||||
fn build_output_stream(
|
||||
&self,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
|
@ -787,14 +896,14 @@ impl EventLoop {
|
|||
Ok(StreamId(stream_id))
|
||||
}
|
||||
|
||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
||||
fn destroy_stream(&self, stream_id: StreamId) {
|
||||
{
|
||||
let mut streams = self.streams.lock().unwrap();
|
||||
streams[stream_id.0] = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
||||
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
||||
let mut streams = self.streams.lock().unwrap();
|
||||
let stream = streams[stream_id.0].as_mut().unwrap();
|
||||
|
||||
|
@ -809,7 +918,7 @@ impl EventLoop {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
||||
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
||||
let mut streams = self.streams.lock().unwrap();
|
||||
let stream = streams[stream_id.0].as_mut().unwrap();
|
||||
|
|
@ -10,17 +10,125 @@ use stdweb::web::set_timeout;
|
|||
|
||||
use BuildStreamError;
|
||||
use DefaultFormatError;
|
||||
use Device as DeviceTrait;
|
||||
use DeviceNameError;
|
||||
use DevicesError;
|
||||
use EventLoop as EventLoopTrait;
|
||||
use Format;
|
||||
use Host as HostTrait;
|
||||
use PauseStreamError;
|
||||
use PlayStreamError;
|
||||
use SupportedFormatsError;
|
||||
use StreamData;
|
||||
use StreamDataResult;
|
||||
use StreamId as StreamIdTrait;
|
||||
use SupportedFormat;
|
||||
use UnknownTypeOutputBuffer;
|
||||
|
||||
/// The default emscripten host type.
|
||||
#[derive(Debug)]
|
||||
pub struct Host;
|
||||
|
||||
impl Host {
|
||||
pub fn new() -> Result<Self, crate::HostUnavailable> {
|
||||
Ok(Host)
|
||||
}
|
||||
}
|
||||
|
||||
impl HostTrait for Host {
|
||||
type Devices = Devices;
|
||||
type Device = Device;
|
||||
type EventLoop = EventLoop;
|
||||
|
||||
fn is_available() -> bool {
|
||||
// Assume this host is always available on emscripten.
|
||||
true
|
||||
}
|
||||
|
||||
fn devices(&self) -> Result<Self::Devices, DevicesError> {
|
||||
Devices::new()
|
||||
}
|
||||
|
||||
fn default_input_device(&self) -> Option<Self::Device> {
|
||||
default_input_device()
|
||||
}
|
||||
|
||||
fn default_output_device(&self) -> Option<Self::Device> {
|
||||
default_output_device()
|
||||
}
|
||||
|
||||
fn event_loop(&self) -> Self::EventLoop {
|
||||
EventLoop::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopTrait for EventLoop {
|
||||
type Device = Device;
|
||||
type StreamId = StreamId;
|
||||
|
||||
fn build_input_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError> {
|
||||
EventLoop::build_input_stream(self, device, format)
|
||||
}
|
||||
|
||||
fn build_output_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError> {
|
||||
EventLoop::build_output_stream(self, device, format)
|
||||
}
|
||||
|
||||
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
|
||||
EventLoop::play_stream(self, stream)
|
||||
}
|
||||
|
||||
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
|
||||
EventLoop::pause_stream(self, stream)
|
||||
}
|
||||
|
||||
fn destroy_stream(&self, stream: Self::StreamId) {
|
||||
EventLoop::destroy_stream(self, stream)
|
||||
}
|
||||
|
||||
fn run<F>(&self, callback: F) -> !
|
||||
where
|
||||
F: FnMut(Self::StreamId, StreamDataResult) + Send,
|
||||
{
|
||||
EventLoop::run(self, callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamIdTrait for StreamId {}
|
||||
|
||||
// The emscripten backend works by having a global variable named `_cpal_audio_contexts`, which
|
||||
// is an array of `AudioContext` objects. A stream ID corresponds to an entry in this array.
|
||||
//
|
||||
|
@ -44,7 +152,7 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn run<F>(&self, callback: F) -> !
|
||||
fn run<F>(&self, callback: F) -> !
|
||||
where F: FnMut(StreamId, StreamDataResult),
|
||||
{
|
||||
// The `run` function uses `set_timeout` to invoke a Rust callback repeatidely. The job
|
||||
|
@ -124,12 +232,12 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_input_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
||||
fn build_input_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_output_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
||||
fn build_output_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
||||
let stream = js!(return new AudioContext()).into_reference().unwrap();
|
||||
|
||||
let mut streams = self.streams.lock().unwrap();
|
||||
|
@ -146,12 +254,12 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
||||
fn destroy_stream(&self, stream_id: StreamId) {
|
||||
self.streams.lock().unwrap()[stream_id.0] = None;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
||||
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
||||
let streams = self.streams.lock().unwrap();
|
||||
let stream = streams
|
||||
.get(stream_id.0)
|
||||
|
@ -162,7 +270,7 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
||||
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
||||
let streams = self.streams.lock().unwrap();
|
||||
let stream = streams
|
||||
.get(stream_id.0)
|
||||
|
@ -193,7 +301,7 @@ fn is_webaudio_available() -> bool {
|
|||
pub struct Devices(bool);
|
||||
|
||||
impl Devices {
|
||||
pub fn new() -> Result<Self, DevicesError> {
|
||||
fn new() -> Result<Self, DevicesError> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
}
|
||||
|
@ -218,12 +326,12 @@ impl Iterator for Devices {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn default_input_device() -> Option<Device> {
|
||||
fn default_input_device() -> Option<Device> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn default_output_device() -> Option<Device> {
|
||||
fn default_output_device() -> Option<Device> {
|
||||
if is_webaudio_available() {
|
||||
Some(Device)
|
||||
} else {
|
||||
|
@ -236,17 +344,17 @@ pub struct Device;
|
|||
|
||||
impl Device {
|
||||
#[inline]
|
||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Ok("Default Device".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
// TODO: right now cpal's API doesn't allow flexibility here
|
||||
// "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if
|
||||
// this ever becomes more flexible, don't forget to change that
|
||||
|
@ -264,11 +372,11 @@ impl Device {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
// TODO: because it is hard coded, see supported_output_formats.
|
||||
Ok(
|
||||
Format {
|
|
@ -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,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
|
@ -276,7 +276,7 @@ impl EventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_output_stream(
|
||||
pub(crate) fn build_output_stream(
|
||||
&self,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
|
@ -439,12 +439,12 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
||||
pub(crate) fn destroy_stream(&self, stream_id: StreamId) {
|
||||
self.push_command(Command::DestroyStream(stream_id));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn run<F>(&self, mut callback: F) -> !
|
||||
pub(crate) fn run<F>(&self, mut callback: F) -> !
|
||||
where F: FnMut(StreamId, StreamDataResult)
|
||||
{
|
||||
self.run_inner(&mut callback);
|
||||
|
@ -617,13 +617,13 @@ impl EventLoop {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> {
|
||||
pub(crate) fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> {
|
||||
self.push_command(Command::PlayStream(stream));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> {
|
||||
pub(crate) fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> {
|
||||
self.push_command(Command::PauseStream(stream));
|
||||
Ok(())
|
||||
}
|
515
src/lib.rs
515
src/lib.rs
|
@ -2,19 +2,25 @@
|
|||
//!
|
||||
//! Here are some concepts cpal exposes:
|
||||
//!
|
||||
//! - A `Device` is an audio device that may have any number of input and output streams.
|
||||
//! - A stream is an open audio channel. Input streams allow you to receive audio data, output
|
||||
//! streams allow you to play audio data. You must choose which `Device` runs your stream before
|
||||
//! you create one.
|
||||
//! - An `EventLoop` is a collection of streams being run by one or more `Device`. Each stream must
|
||||
//! belong to an `EventLoop`, and all the streams that belong to an `EventLoop` are managed
|
||||
//! together.
|
||||
//! - A [**Host**](./trait.Host.html) provides access to the available audio devices on the system.
|
||||
//! Some platforms have more than one host available, but every platform supported by CPAL has at
|
||||
//! least one [**DefaultHost**](./trait.Host.html) that is guaranteed to be available.
|
||||
//! - A [**Device**](./trait.Device.html) is an audio device that may have any number of input and
|
||||
//! output streams.
|
||||
//! - A stream is an open flow of audio data. Input streams allow you to receive audio data, output
|
||||
//! streams allow you to play audio data. You must choose which **Device** will run your stream
|
||||
//! before you can create one. Often, a default device can be retrieved via the **Host**.
|
||||
//! - An [**EventLoop**](./trait.EventLoop.html) is a collection of streams being run by one or
|
||||
//! more **Device**s under a single **Host**. Each stream must belong to an **EventLoop**, and
|
||||
//! all the streams that belong to an **EventLoop** are managed together.
|
||||
//!
|
||||
//! The first step is to create an `EventLoop`:
|
||||
//! The first step is to initialise the `Host` (for accessing audio devices) and create an
|
||||
//! `EventLoop`:
|
||||
//!
|
||||
//! ```
|
||||
//! use cpal::EventLoop;
|
||||
//! let event_loop = EventLoop::new();
|
||||
//! use cpal::Host;
|
||||
//! let host = cpal::default_host();
|
||||
//! let event_loop = host.event_loop();
|
||||
//! ```
|
||||
//!
|
||||
//! Then choose a `Device`. The easiest way is to use the default input or output `Device` via the
|
||||
|
@ -24,7 +30,9 @@
|
|||
//! stream type on the system.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! let device = cpal::default_output_device().expect("no output device available");
|
||||
//! # use cpal::Host;
|
||||
//! # let host = cpal::default_host();
|
||||
//! let device = host.default_output_device().expect("no output device available");
|
||||
//! ```
|
||||
//!
|
||||
//! Before we can create a stream, we must decide what the format of the audio samples is going to
|
||||
|
@ -38,7 +46,9 @@
|
|||
//! > has been disconnected.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # let device = cpal::default_output_device().unwrap();
|
||||
//! use cpal::{Device, Host};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! let mut supported_formats_range = device.supported_output_formats()
|
||||
//! .expect("error while querying formats");
|
||||
//! let format = supported_formats_range.next()
|
||||
|
@ -49,9 +59,11 @@
|
|||
//! Now that we have everything for the stream, we can create it from our event loop:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # let device = cpal::default_output_device().unwrap();
|
||||
//! use cpal::{Device, EventLoop, Host};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let event_loop = host.event_loop();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! # let format = device.supported_output_formats().unwrap().next().unwrap().with_max_sample_rate();
|
||||
//! # let event_loop = cpal::EventLoop::new();
|
||||
//! let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
|
@ -61,15 +73,19 @@
|
|||
//! Now we must start the stream. This is done with the `play_stream()` method on the event loop.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # let event_loop: cpal::EventLoop = return;
|
||||
//! # let stream_id: cpal::StreamId = return;
|
||||
//! # use cpal::{EventLoop, Host};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let event_loop = host.event_loop();
|
||||
//! # let stream_id = unimplemented!();
|
||||
//! event_loop.play_stream(stream_id).expect("failed to play_stream");
|
||||
//! ```
|
||||
//!
|
||||
//! Now everything is ready! We call `run()` on the `event_loop` to begin processing.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # let event_loop = cpal::EventLoop::new();
|
||||
//! # use cpal::{EventLoop, Host};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let event_loop = host.event_loop();
|
||||
//! event_loop.run(move |_stream_id, _stream_result| {
|
||||
//! // react to stream events and read or write stream data here
|
||||
//! });
|
||||
|
@ -87,9 +103,9 @@
|
|||
//! In this example, we simply fill the given output buffer with zeroes.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use cpal::{StreamData, UnknownTypeOutputBuffer};
|
||||
//!
|
||||
//! # let event_loop = cpal::EventLoop::new();
|
||||
//! use cpal::{EventLoop, Host, StreamData, UnknownTypeOutputBuffer};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let event_loop = host.event_loop();
|
||||
//! event_loop.run(move |stream_id, stream_result| {
|
||||
//! let stream_data = match stream_result {
|
||||
//! Ok(data) => data,
|
||||
|
@ -132,51 +148,203 @@ extern crate lazy_static;
|
|||
#[macro_use]
|
||||
extern crate stdweb;
|
||||
|
||||
pub use platform::{ALL_HOSTS, HostId, available_hosts, default_host, host_from_id};
|
||||
pub use samples_formats::{Sample, SampleFormat};
|
||||
|
||||
#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd",
|
||||
target_os = "macos", target_os = "ios", target_os = "emscripten")))]
|
||||
use null as cpal_impl;
|
||||
|
||||
use failure::Fail;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
mod null;
|
||||
mod host;
|
||||
pub mod platform;
|
||||
mod samples_formats;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
#[path = "alsa/mod.rs"]
|
||||
mod cpal_impl;
|
||||
/// A **Host** provides access to the available audio devices on the system.
|
||||
///
|
||||
/// Each platform may have a number of available hosts depending on the system, each with their own
|
||||
/// pros and cons.
|
||||
///
|
||||
/// For example, WASAPI is the standard audio host API that ships with the Windows operating
|
||||
/// system. However, due to historical limitations with respect to performance and flexibility,
|
||||
/// Steinberg created the ASIO API providing better audio device support for pro audio and
|
||||
/// low-latency applications. As a result, it is common for some devices and device capabilities to
|
||||
/// only be available via ASIO, while others are only available via WASAPI.
|
||||
///
|
||||
/// Another great example is the Linux platform. While the ALSA host API is the lowest-level API
|
||||
/// available to almost all distributions of Linux, its flexibility is limited as it requires that
|
||||
/// each process have exclusive access to the devices with which they establish streams. PulseAudio
|
||||
/// is another popular host API that aims to solve this issue by providing user-space mixing,
|
||||
/// however it has its own limitations w.r.t. low-latency and high-performance audio applications.
|
||||
/// JACK is yet another host API that is more suitable to pro-audio applications, however it is
|
||||
/// less readily available by default in many Linux distributions and is known to be tricky to
|
||||
/// setup.
|
||||
pub trait Host {
|
||||
/// The type used for enumerating available devices by the host.
|
||||
type Devices: Iterator<Item = Self::Device>;
|
||||
/// The `Device` type yielded by the host.
|
||||
type Device: Device;
|
||||
/// The event loop type used by the `Host`
|
||||
type EventLoop: EventLoop<Device = Self::Device>;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[path = "wasapi/mod.rs"]
|
||||
mod cpal_impl;
|
||||
/// Whether or not the host is available on the system.
|
||||
fn is_available() -> bool;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[path = "coreaudio/mod.rs"]
|
||||
mod cpal_impl;
|
||||
/// An iterator yielding all `Device`s currently available to the host on the system.
|
||||
///
|
||||
/// Can be empty if the system does not support audio in general.
|
||||
fn devices(&self) -> Result<Self::Devices, DevicesError>;
|
||||
|
||||
#[cfg(target_os = "emscripten")]
|
||||
#[path = "emscripten/mod.rs"]
|
||||
mod cpal_impl;
|
||||
/// The default input audio device on the system.
|
||||
///
|
||||
/// Returns `None` if no input device is available.
|
||||
fn default_input_device(&self) -> Option<Self::Device>;
|
||||
|
||||
/// An opaque type that identifies a device that is capable of either audio input or output.
|
||||
/// The default output audio device on the system.
|
||||
///
|
||||
/// Returns `None` if no output device is available.
|
||||
fn default_output_device(&self) -> Option<Self::Device>;
|
||||
|
||||
/// Initialise the event loop, ready for managing audio streams.
|
||||
fn event_loop(&self) -> Self::EventLoop;
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||
/// input stream formats.
|
||||
///
|
||||
/// Can be empty if the system does not support audio input.
|
||||
fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
|
||||
fn supports_input<D: Device>(device: &D) -> bool {
|
||||
device.supported_input_formats()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
Ok(self.devices()?.filter(supports_input::<Self::Device>))
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||
/// output stream formats.
|
||||
///
|
||||
/// Can be empty if the system does not support audio output.
|
||||
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
|
||||
fn supports_output<D: Device>(device: &D) -> bool {
|
||||
device.supported_output_formats()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
Ok(self.devices()?.filter(supports_output::<Self::Device>))
|
||||
}
|
||||
}
|
||||
|
||||
/// A device that is capable of audio input and/or output.
|
||||
///
|
||||
/// Please note that `Device`s may become invalid if they get disconnected. Therefore all the
|
||||
/// methods that involve a device return a `Result`.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Device(cpal_impl::Device);
|
||||
/// methods that involve a device return a `Result` allowing the user to handle this case.
|
||||
pub trait Device {
|
||||
/// The iterator type yielding supported input stream formats.
|
||||
type SupportedInputFormats: Iterator<Item = SupportedFormat>;
|
||||
/// The iterator type yielding supported output stream formats.
|
||||
type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
|
||||
|
||||
/// The human-readable name of the device.
|
||||
fn name(&self) -> Result<String, DeviceNameError>;
|
||||
|
||||
/// An iterator yielding formats that are supported by the backend.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError>;
|
||||
|
||||
/// An iterator yielding output stream formats that are supported by the device.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>;
|
||||
|
||||
/// The default input stream format for the device.
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError>;
|
||||
|
||||
/// The default output stream format for the device.
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
|
||||
}
|
||||
|
||||
/// Collection of streams managed together.
|
||||
///
|
||||
/// Created with the [`new`](struct.EventLoop.html#method.new) method.
|
||||
pub struct EventLoop(cpal_impl::EventLoop);
|
||||
/// Created with the `Host::event_loop` method.
|
||||
pub trait EventLoop {
|
||||
/// The `Device` type yielded by the host.
|
||||
type Device: Device;
|
||||
/// The type used to uniquely distinguish between streams.
|
||||
type StreamId: StreamId;
|
||||
|
||||
/// Identifier of a stream within the `EventLoop`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct StreamId(cpal_impl::StreamId);
|
||||
/// Creates a new input stream that will run from the given device and with the given format.
|
||||
///
|
||||
/// On success, returns an identifier for the stream.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid, or if the input stream format is not
|
||||
/// supported by the device.
|
||||
fn build_input_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError>;
|
||||
|
||||
/// Creates a new output stream that will play on the given device and with the given format.
|
||||
///
|
||||
/// On success, returns an identifier for the stream.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid, or if the output stream format is not
|
||||
/// supported by the device.
|
||||
fn build_output_stream(
|
||||
&self,
|
||||
device: &Self::Device,
|
||||
format: &Format,
|
||||
) -> Result<Self::StreamId, BuildStreamError>;
|
||||
|
||||
/// Instructs the audio device that it should start playing the stream with the given ID.
|
||||
///
|
||||
/// Has no effect is the stream was already playing.
|
||||
///
|
||||
/// Only call this after you have submitted some data, otherwise you may hear some glitches.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the stream does not exist, this function can either panic or be a no-op.
|
||||
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError>;
|
||||
|
||||
/// Instructs the audio device that it should stop playing the stream with the given ID.
|
||||
///
|
||||
/// Has no effect is the stream was already paused.
|
||||
///
|
||||
/// If you call `play` afterwards, the playback will resume where it was.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the stream does not exist, this function can either panic or be a no-op.
|
||||
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError>;
|
||||
|
||||
/// Destroys an existing stream.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the stream does not exist, this function can either panic or be a no-op.
|
||||
fn destroy_stream(&self, stream: Self::StreamId);
|
||||
|
||||
/// Takes control of the current thread and begins the stream processing.
|
||||
///
|
||||
/// > **Note**: Since it takes control of the thread, this method is best called on a separate
|
||||
/// > thread.
|
||||
///
|
||||
/// Whenever a stream needs to be fed some data, the closure passed as parameter is called.
|
||||
/// You can call the other methods of `EventLoop` without getting a deadlock.
|
||||
fn run<F>(&self, callback: F) -> !
|
||||
where
|
||||
F: FnMut(Self::StreamId, StreamDataResult) + Send;
|
||||
}
|
||||
|
||||
/// The set of required bounds for host `StreamId` types.
|
||||
pub trait StreamId: Clone + std::fmt::Debug + PartialEq + Eq {}
|
||||
|
||||
/// A host's device iterator yielding only *input* devices.
|
||||
pub type InputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
|
||||
|
||||
/// A host's device iterator yielding only *output* devices.
|
||||
pub type OutputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
|
||||
|
||||
/// Number of channels.
|
||||
pub type ChannelCount = u16;
|
||||
|
@ -271,26 +439,10 @@ pub enum UnknownTypeOutputBuffer<'a> {
|
|||
F32(OutputBuffer<'a, f32>),
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system.
|
||||
///
|
||||
/// See [`devices()`](fn.devices.html).
|
||||
pub struct Devices(cpal_impl::Devices);
|
||||
|
||||
/// A `Devices` yielding only *input* devices.
|
||||
pub type InputDevices = iter::Filter<Devices, fn(&Device) -> bool>;
|
||||
|
||||
/// A `Devices` yielding only *output* devices.
|
||||
pub type OutputDevices = iter::Filter<Devices, fn(&Device) -> bool>;
|
||||
|
||||
/// An iterator that produces a list of input stream formats supported by the device.
|
||||
///
|
||||
/// See [`Device::supported_input_formats()`](struct.Device.html#method.supported_input_formats).
|
||||
pub struct SupportedInputFormats(cpal_impl::SupportedInputFormats);
|
||||
|
||||
/// An iterator that produces a list of output stream formats supported by the device.
|
||||
///
|
||||
/// See [`Device::supported_output_formats()`](struct.Device.html#method.supported_output_formats).
|
||||
pub struct SupportedOutputFormats(cpal_impl::SupportedOutputFormats);
|
||||
/// The requested host, although supported on this platform, is unavailable.
|
||||
#[derive(Clone, Debug, Fail)]
|
||||
#[fail(display = "the requested host is unavailable")]
|
||||
pub struct HostUnavailable;
|
||||
|
||||
/// Some error has occurred that is specific to the backend from which it was produced.
|
||||
///
|
||||
|
@ -440,191 +592,6 @@ pub enum StreamError {
|
|||
}
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system.
|
||||
///
|
||||
/// Can be empty if the system does not support audio in general.
|
||||
#[inline]
|
||||
pub fn devices() -> Result<Devices, DevicesError> {
|
||||
Ok(Devices(cpal_impl::Devices::new()?))
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||
/// input stream formats.
|
||||
///
|
||||
/// Can be empty if the system does not support audio input.
|
||||
pub fn input_devices() -> Result<InputDevices, DevicesError> {
|
||||
fn supports_input(device: &Device) -> bool {
|
||||
device.supported_input_formats()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
Ok(devices()?.filter(supports_input))
|
||||
}
|
||||
|
||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||
/// output stream formats.
|
||||
///
|
||||
/// Can be empty if the system does not support audio output.
|
||||
pub fn output_devices() -> Result<OutputDevices, DevicesError> {
|
||||
fn supports_output(device: &Device) -> bool {
|
||||
device.supported_output_formats()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
Ok(devices()?.filter(supports_output))
|
||||
}
|
||||
|
||||
/// The default input audio device on the system.
|
||||
///
|
||||
/// Returns `None` if no input device is available.
|
||||
pub fn default_input_device() -> Option<Device> {
|
||||
cpal_impl::default_input_device().map(Device)
|
||||
}
|
||||
|
||||
/// The default output audio device on the system.
|
||||
///
|
||||
/// Returns `None` if no output device is available.
|
||||
pub fn default_output_device() -> Option<Device> {
|
||||
cpal_impl::default_output_device().map(Device)
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// The human-readable name of the device.
|
||||
#[inline]
|
||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
/// An iterator yielding formats that are supported by the backend.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
#[inline]
|
||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
Ok(SupportedInputFormats(self.0.supported_input_formats()?))
|
||||
}
|
||||
|
||||
/// An iterator yielding output stream formats that are supported by the device.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
#[inline]
|
||||
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
Ok(SupportedOutputFormats(self.0.supported_output_formats()?))
|
||||
}
|
||||
|
||||
/// The default input stream format for the device.
|
||||
#[inline]
|
||||
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.0.default_input_format()
|
||||
}
|
||||
|
||||
/// The default output stream format for the device.
|
||||
#[inline]
|
||||
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.0.default_output_format()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Device {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
/// Initializes a new events loop.
|
||||
#[inline]
|
||||
pub fn new() -> EventLoop {
|
||||
EventLoop(cpal_impl::EventLoop::new())
|
||||
}
|
||||
|
||||
/// Creates a new input stream that will run from the given device and with the given format.
|
||||
///
|
||||
/// On success, returns an identifier for the stream.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid, or if the input stream format is not
|
||||
/// supported by the device.
|
||||
#[inline]
|
||||
pub fn build_input_stream(
|
||||
&self,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
) -> Result<StreamId, BuildStreamError>
|
||||
{
|
||||
self.0.build_input_stream(&device.0, format).map(StreamId)
|
||||
}
|
||||
|
||||
/// Creates a new output stream that will play on the given device and with the given format.
|
||||
///
|
||||
/// On success, returns an identifier for the stream.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid, or if the output stream format is not
|
||||
/// supported by the device.
|
||||
#[inline]
|
||||
pub fn build_output_stream(
|
||||
&self,
|
||||
device: &Device,
|
||||
format: &Format,
|
||||
) -> Result<StreamId, BuildStreamError>
|
||||
{
|
||||
self.0.build_output_stream(&device.0, format).map(StreamId)
|
||||
}
|
||||
|
||||
/// Instructs the audio device that it should start playing the stream with the given ID.
|
||||
///
|
||||
/// Has no effect is the stream was already playing.
|
||||
///
|
||||
/// Only call this after you have submitted some data, otherwise you may hear some glitches.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the stream does not exist, this function can either panic or be a no-op.
|
||||
///
|
||||
#[inline]
|
||||
pub fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> {
|
||||
self.0.play_stream(stream.0)
|
||||
}
|
||||
|
||||
/// Instructs the audio device that it should stop playing the stream with the given ID.
|
||||
///
|
||||
/// Has no effect is the stream was already paused.
|
||||
///
|
||||
/// If you call `play` afterwards, the playback will resume where it was.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the stream does not exist, this function can either panic or be a no-op.
|
||||
///
|
||||
#[inline]
|
||||
pub fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> {
|
||||
self.0.pause_stream(stream.0)
|
||||
}
|
||||
|
||||
/// Destroys an existing stream.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the stream does not exist, this function can either panic or be a no-op.
|
||||
///
|
||||
#[inline]
|
||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
||||
self.0.destroy_stream(stream_id.0)
|
||||
}
|
||||
|
||||
/// Takes control of the current thread and begins the stream processing.
|
||||
///
|
||||
/// > **Note**: Since it takes control of the thread, this method is best called on a separate
|
||||
/// > thread.
|
||||
///
|
||||
/// Whenever a stream needs to be fed some data, the closure passed as parameter is called.
|
||||
/// You can call the other methods of `EventLoop` without getting a deadlock.
|
||||
#[inline]
|
||||
pub fn run<F>(&self, mut callback: F) -> !
|
||||
where F: FnMut(StreamId, StreamDataResult) + Send
|
||||
{
|
||||
self.0.run(move |id, data| callback(StreamId(id), data))
|
||||
}
|
||||
}
|
||||
|
||||
impl SupportedFormat {
|
||||
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
|
||||
#[inline]
|
||||
|
@ -775,48 +742,6 @@ impl From<Format> for SupportedFormat {
|
|||
}
|
||||
}
|
||||
|
||||
impl Iterator for Devices {
|
||||
type Item = Device;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next().map(Device)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedInputFormats {
|
||||
type Item = SupportedFormat;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SupportedFormat> {
|
||||
self.0.next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedOutputFormats {
|
||||
type Item = SupportedFormat;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SupportedFormat> {
|
||||
self.0.next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BackendSpecificError> for DevicesError {
|
||||
fn from(err: BackendSpecificError) -> Self {
|
||||
DevicesError::BackendSpecific { err }
|
||||
|
|
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