Add `BackendSpecificError`. Add new `DevicesError`.
See the documentation for both new errors for details. The new `DevicesError` has been added to allow for returning errors when enumerating devices. This has allowed to remove multiple potential `panic!`s in each of the alsa, coreaudio and wasapi backends.
This commit is contained in:
parent
cf84ab906f
commit
42fc702f53
|
@ -4,7 +4,7 @@ fn main() {
|
||||||
println!("Default Input Device:\n {:?}", cpal::default_input_device().map(|e| e.name()));
|
println!("Default Input Device:\n {:?}", cpal::default_input_device().map(|e| e.name()));
|
||||||
println!("Default Output Device:\n {:?}", cpal::default_output_device().map(|e| e.name()));
|
println!("Default Output Device:\n {:?}", cpal::default_output_device().map(|e| e.name()));
|
||||||
|
|
||||||
let devices = cpal::devices();
|
let devices = cpal::devices().expect("failed to enumerate devices");
|
||||||
println!("Devices: ");
|
println!("Devices: ");
|
||||||
for (device_index, device) in devices.enumerate() {
|
for (device_index, device) in devices.enumerate() {
|
||||||
println!("{}. \"{}\"",
|
println!("{}. \"{}\"",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use {BackendSpecificError, DevicesError};
|
||||||
use super::Device;
|
use super::Device;
|
||||||
use super::alsa;
|
use super::alsa;
|
||||||
use super::check_errors;
|
use super::check_errors;
|
||||||
|
@ -13,6 +14,28 @@ pub struct Devices {
|
||||||
next_str: *const *const u8,
|
next_str: *const *const u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Devices {
|
||||||
|
pub fn new() -> Result<Self, DevicesError> {
|
||||||
|
unsafe {
|
||||||
|
// TODO: check in which situation this can fail.
|
||||||
|
let card = -1; // -1 means all cards.
|
||||||
|
let iface = b"pcm\0"; // Interface identification.
|
||||||
|
let mut hints = mem::uninitialized(); // Array of device name hints.
|
||||||
|
let res = alsa::snd_device_name_hint(card, iface.as_ptr() as *const _, &mut hints);
|
||||||
|
if let Err(description) = check_errors(res) {
|
||||||
|
let err = BackendSpecificError { description };
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
let hints = hints as *const *const u8;
|
||||||
|
let devices = Devices {
|
||||||
|
global_list: hints,
|
||||||
|
next_str: hints,
|
||||||
|
};
|
||||||
|
Ok(devices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Send for Devices {
|
unsafe impl Send for Devices {
|
||||||
}
|
}
|
||||||
unsafe impl Sync for Devices {
|
unsafe impl Sync for Devices {
|
||||||
|
@ -27,24 +50,6 @@ impl Drop for Devices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Devices {
|
|
||||||
fn default() -> Devices {
|
|
||||||
unsafe {
|
|
||||||
// TODO: check in which situation this can fail.
|
|
||||||
let card = -1; // -1 means all cards.
|
|
||||||
let iface = b"pcm\0"; // Interface identification.
|
|
||||||
let mut hints = mem::uninitialized(); // Array of device name hints.
|
|
||||||
let res = alsa::snd_device_name_hint(card, iface.as_ptr() as *const _, &mut hints);
|
|
||||||
check_errors(res).unwrap();
|
|
||||||
let hints = hints as *const *const u8;
|
|
||||||
Devices {
|
|
||||||
global_list: hints,
|
|
||||||
next_str: hints,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Devices {
|
impl Iterator for Devices {
|
||||||
type Item = Device;
|
type Item = Device;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use SupportedFormat;
|
use {BackendSpecificError, DevicesError, SupportedFormat};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr::null;
|
use std::ptr::null;
|
||||||
use std::vec::IntoIter as VecIntoIter;
|
use std::vec::IntoIter as VecIntoIter;
|
||||||
|
@ -64,20 +64,27 @@ unsafe fn audio_devices() -> Result<Vec<AudioDeviceID>, OSStatus> {
|
||||||
|
|
||||||
pub struct Devices(VecIntoIter<AudioDeviceID>);
|
pub struct Devices(VecIntoIter<AudioDeviceID>);
|
||||||
|
|
||||||
|
impl Devices {
|
||||||
|
pub fn new() -> Result<Self, DevicesError> {
|
||||||
|
let devices = unsafe {
|
||||||
|
match audio_devices() {
|
||||||
|
Ok(devices) => devices,
|
||||||
|
Err(os_status) => {
|
||||||
|
let description = format!("{}", os_status);
|
||||||
|
let err = BackendSpecificError { description };
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Devices(devices.into_iter()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Send for Devices {
|
unsafe impl Send for Devices {
|
||||||
}
|
}
|
||||||
unsafe impl Sync for Devices {
|
unsafe impl Sync for Devices {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Devices {
|
|
||||||
fn default() -> Self {
|
|
||||||
let devices = unsafe {
|
|
||||||
audio_devices().expect("failed to get audio output devices")
|
|
||||||
};
|
|
||||||
Devices(devices.into_iter())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Devices {
|
impl Iterator for Devices {
|
||||||
type Item = Device;
|
type Item = Device;
|
||||||
fn next(&mut self) -> Option<Device> {
|
fn next(&mut self) -> Option<Device> {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use stdweb::web::set_timeout;
|
||||||
|
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
|
use DevicesError;
|
||||||
use Format;
|
use Format;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
|
@ -183,6 +184,13 @@ fn is_webaudio_available() -> bool {
|
||||||
|
|
||||||
// Content is false if the iterator is empty.
|
// Content is false if the iterator is empty.
|
||||||
pub struct Devices(bool);
|
pub struct Devices(bool);
|
||||||
|
|
||||||
|
impl Devices {
|
||||||
|
pub fn new() -> Result<Self, DevicesError> {
|
||||||
|
Ok(Self::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Devices {
|
impl Default for Devices {
|
||||||
fn default() -> Devices {
|
fn default() -> Devices {
|
||||||
// We produce an empty iterator if the WebAudio API isn't available.
|
// We produce an empty iterator if the WebAudio API isn't available.
|
||||||
|
|
49
src/lib.rs
49
src/lib.rs
|
@ -279,6 +279,37 @@ pub struct SupportedInputFormats(cpal_impl::SupportedInputFormats);
|
||||||
/// See [`Device::supported_output_formats()`](struct.Device.html#method.supported_output_formats).
|
/// See [`Device::supported_output_formats()`](struct.Device.html#method.supported_output_formats).
|
||||||
pub struct SupportedOutputFormats(cpal_impl::SupportedOutputFormats);
|
pub struct SupportedOutputFormats(cpal_impl::SupportedOutputFormats);
|
||||||
|
|
||||||
|
/// Some error has occurred that is specific to the backend from which it was produced.
|
||||||
|
///
|
||||||
|
/// This error is often used as a catch-all in cases where:
|
||||||
|
///
|
||||||
|
/// - It is unclear exactly what error might be produced by the backend API.
|
||||||
|
/// - It does not make sense to add a variant to the enclosing error type.
|
||||||
|
/// - No error was expected to occur at all, but we return an error to avoid the possibility of a
|
||||||
|
/// `panic!` caused by some unforseen or unknown reason.
|
||||||
|
///
|
||||||
|
/// **Note:** If you notice a `BackendSpecificError` that you believe could be better handled in a
|
||||||
|
/// cross-platform manner, please create an issue or submit a pull request with a patch that adds
|
||||||
|
/// the necessary error variant to the appropriate error enum.
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
#[fail(display = "A backend-specific error has occurred: {}", description)]
|
||||||
|
pub struct BackendSpecificError {
|
||||||
|
pub description: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error that might occur while attempting to enumerate the available devices on a system.
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum DevicesError {
|
||||||
|
/// Some error that is specific to the backend from which it was produced.
|
||||||
|
///
|
||||||
|
/// Note: This error is often used when
|
||||||
|
#[fail(display = "{}", err)]
|
||||||
|
BackendSpecific {
|
||||||
|
#[fail(cause)]
|
||||||
|
err: BackendSpecificError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Error that can happen when enumerating the list of supported formats.
|
/// Error that can happen when enumerating the list of supported formats.
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum SupportedFormatsError {
|
pub enum SupportedFormatsError {
|
||||||
|
@ -331,34 +362,34 @@ pub enum BuildStreamError {
|
||||||
///
|
///
|
||||||
/// Can be empty if the system does not support audio in general.
|
/// Can be empty if the system does not support audio in general.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn devices() -> Devices {
|
pub fn devices() -> Result<Devices, DevicesError> {
|
||||||
Devices(Default::default())
|
Ok(Devices(cpal_impl::Devices::new()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||||
/// input stream formats.
|
/// input stream formats.
|
||||||
///
|
///
|
||||||
/// Can be empty if the system does not support audio input.
|
/// Can be empty if the system does not support audio input.
|
||||||
pub fn input_devices() -> InputDevices {
|
pub fn input_devices() -> Result<InputDevices, DevicesError> {
|
||||||
fn supports_input(device: &Device) -> bool {
|
fn supports_input(device: &Device) -> bool {
|
||||||
device.supported_input_formats()
|
device.supported_input_formats()
|
||||||
.map(|mut iter| iter.next().is_some())
|
.map(|mut iter| iter.next().is_some())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
devices().filter(supports_input)
|
Ok(devices()?.filter(supports_input))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
||||||
/// output stream formats.
|
/// output stream formats.
|
||||||
///
|
///
|
||||||
/// Can be empty if the system does not support audio output.
|
/// Can be empty if the system does not support audio output.
|
||||||
pub fn output_devices() -> OutputDevices {
|
pub fn output_devices() -> Result<OutputDevices, DevicesError> {
|
||||||
fn supports_output(device: &Device) -> bool {
|
fn supports_output(device: &Device) -> bool {
|
||||||
device.supported_output_formats()
|
device.supported_output_formats()
|
||||||
.map(|mut iter| iter.next().is_some())
|
.map(|mut iter| iter.next().is_some())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
devices().filter(supports_output)
|
Ok(devices()?.filter(supports_output))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The default input audio device on the system.
|
/// The default input audio device on the system.
|
||||||
|
@ -704,6 +735,12 @@ impl Iterator for SupportedOutputFormats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<BackendSpecificError> for DevicesError {
|
||||||
|
fn from(err: BackendSpecificError) -> Self {
|
||||||
|
DevicesError::BackendSpecific { err }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If a backend does not provide an API for retrieving supported formats, we query it with a bunch
|
// If a backend does not provide an API for retrieving supported formats, we query it with a bunch
|
||||||
// of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa.
|
// of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa.
|
||||||
//
|
//
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::marker::PhantomData;
|
||||||
|
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
|
use DevicesError;
|
||||||
use Format;
|
use Format;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
|
@ -56,6 +57,12 @@ pub struct StreamId;
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Devices;
|
pub struct Devices;
|
||||||
|
|
||||||
|
impl Devices {
|
||||||
|
pub fn new() -> Result<Self, DevicesError> {
|
||||||
|
Ok(Devices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Iterator for Devices {
|
impl Iterator for Devices {
|
||||||
type Item = Device;
|
type Item = Device;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,9 @@ use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
|
use BackendSpecificError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
|
use DevicesError;
|
||||||
use Format;
|
use Format;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
|
@ -18,6 +20,7 @@ use SupportedFormat;
|
||||||
use COMMON_SAMPLE_RATES;
|
use COMMON_SAMPLE_RATES;
|
||||||
|
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
|
use super::check_result_backend_specific;
|
||||||
use super::com;
|
use super::com;
|
||||||
use super::winapi::Interface;
|
use super::winapi::Interface;
|
||||||
use super::winapi::ctypes::c_void;
|
use super::winapi::ctypes::c_void;
|
||||||
|
@ -688,6 +691,32 @@ pub struct Devices {
|
||||||
next_item: u32,
|
next_item: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Devices {
|
||||||
|
pub fn new() -> Result<Self, DevicesError> {
|
||||||
|
unsafe {
|
||||||
|
let mut collection: *mut IMMDeviceCollection = mem::uninitialized();
|
||||||
|
// can fail because of wrong parameters (should never happen) or out of memory
|
||||||
|
check_result_backend_specific(
|
||||||
|
(*ENUMERATOR.0).EnumAudioEndpoints(
|
||||||
|
eAll,
|
||||||
|
DEVICE_STATE_ACTIVE,
|
||||||
|
&mut collection,
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut count = mem::uninitialized();
|
||||||
|
// can fail if the parameter is null, which should never happen
|
||||||
|
check_result_backend_specific((*collection).GetCount(&mut count))?;
|
||||||
|
|
||||||
|
Devices {
|
||||||
|
collection: collection,
|
||||||
|
total_count: count,
|
||||||
|
next_item: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Send for Devices {
|
unsafe impl Send for Devices {
|
||||||
}
|
}
|
||||||
unsafe impl Sync for Devices {
|
unsafe impl Sync for Devices {
|
||||||
|
@ -702,32 +731,6 @@ impl Drop for Devices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Devices {
|
|
||||||
fn default() -> Devices {
|
|
||||||
unsafe {
|
|
||||||
let mut collection: *mut IMMDeviceCollection = mem::uninitialized();
|
|
||||||
// can fail because of wrong parameters (should never happen) or out of memory
|
|
||||||
check_result(
|
|
||||||
(*ENUMERATOR.0).EnumAudioEndpoints(
|
|
||||||
eAll,
|
|
||||||
DEVICE_STATE_ACTIVE,
|
|
||||||
&mut collection,
|
|
||||||
)
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let mut count = mem::uninitialized();
|
|
||||||
// can fail if the parameter is null, which should never happen
|
|
||||||
check_result((*collection).GetCount(&mut count)).unwrap();
|
|
||||||
|
|
||||||
Devices {
|
|
||||||
collection: collection,
|
|
||||||
total_count: count,
|
|
||||||
next_item: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Devices {
|
impl Iterator for Devices {
|
||||||
type Item = Device;
|
type Item = Device;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
|
|
||||||
|
use BackendSpecificError;
|
||||||
|
use self::winapi::um::winnt::HRESULT;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
|
||||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
||||||
pub use self::stream::{EventLoop, StreamId};
|
pub use self::stream::{EventLoop, StreamId};
|
||||||
use self::winapi::um::winnt::HRESULT;
|
|
||||||
|
|
||||||
mod com;
|
mod com;
|
||||||
mod device;
|
mod device;
|
||||||
|
@ -18,3 +18,13 @@ fn check_result(result: HRESULT) -> Result<(), IoError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_result_backend_specific(result: HRESULT) -> Result<(), BackendSpecificError> {
|
||||||
|
match check_result(result) {
|
||||||
|
Ok(()) => Ok(())
|
||||||
|
Err(err) => {
|
||||||
|
let description = format!("{}", err);
|
||||||
|
return BackendSpecificError { description }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue