Add proper error handling
This commit is contained in:
parent
1985c346ac
commit
98b931edff
|
@ -2,7 +2,7 @@ extern crate cpal;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let endpoint = cpal::get_default_endpoint().unwrap();
|
let endpoint = cpal::get_default_endpoint().unwrap();
|
||||||
let format = endpoint.get_supported_formats_list().next().unwrap();
|
let format = endpoint.get_supported_formats_list().unwrap().next().unwrap();
|
||||||
let mut channel = cpal::Voice::new(&endpoint, &format).unwrap();
|
let mut channel = cpal::Voice::new(&endpoint, &format).unwrap();
|
||||||
|
|
||||||
// Produce a sinusoid of maximum amplitude.
|
// Produce a sinusoid of maximum amplitude.
|
||||||
|
|
64
src/lib.rs
64
src/lib.rs
|
@ -33,6 +33,7 @@ extern crate lazy_static;
|
||||||
|
|
||||||
pub use samples_formats::{SampleFormat, Sample};
|
pub use samples_formats::{SampleFormat, Sample};
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
@ -87,8 +88,10 @@ pub struct Endpoint(cpal_impl::Endpoint);
|
||||||
|
|
||||||
impl Endpoint {
|
impl Endpoint {
|
||||||
/// Returns an iterator that produces the list of formats that are supported by the backend.
|
/// Returns an iterator that produces the list of formats that are supported by the backend.
|
||||||
pub fn get_supported_formats_list(&self) -> SupportedFormatsIterator {
|
pub fn get_supported_formats_list(&self) -> Result<SupportedFormatsIterator,
|
||||||
SupportedFormatsIterator(self.0.get_supported_formats_list())
|
FormatsEnumerationError>
|
||||||
|
{
|
||||||
|
Ok(SupportedFormatsIterator(try!(self.0.get_supported_formats_list())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,6 +149,61 @@ pub enum UnknownTypeBuffer<'a> {
|
||||||
F32(Buffer<'a, f32>),
|
F32(Buffer<'a, f32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error that can happen when enumerating the list of supported formats.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FormatsEnumerationError {
|
||||||
|
/// The device no longer exists. This can happen if the device is disconnected while the
|
||||||
|
/// program is running.
|
||||||
|
DeviceNotAvailable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FormatsEnumerationError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(fmt, "{}", self.description())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for FormatsEnumerationError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
&FormatsEnumerationError::DeviceNotAvailable => {
|
||||||
|
"The requested device is no longer available (for example, it has been unplugged)."
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error that can happen when creating a `Voice`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CreationError {
|
||||||
|
/// The device no longer exists. This can happen if the device is disconnected while the
|
||||||
|
/// program is running.
|
||||||
|
DeviceNotAvailable,
|
||||||
|
|
||||||
|
/// The required format is not supported.
|
||||||
|
FormatNotSupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CreationError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(fmt, "{}", self.description())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for CreationError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
&CreationError::DeviceNotAvailable => {
|
||||||
|
"The requested device is no longer available (for example, it has been unplugged)."
|
||||||
|
},
|
||||||
|
|
||||||
|
&CreationError::FormatNotSupported => {
|
||||||
|
"The requested samples format is not supported by the device."
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Controls a sound output. A typical application has one `Voice` for each sound
|
/// Controls a sound output. A typical application has one `Voice` for each sound
|
||||||
/// it wants to output.
|
/// it wants to output.
|
||||||
///
|
///
|
||||||
|
@ -163,7 +221,7 @@ pub struct Voice(cpal_impl::Voice);
|
||||||
impl Voice {
|
impl Voice {
|
||||||
/// Builds a new channel.
|
/// Builds a new channel.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(endpoint: &Endpoint, format: &Format) -> Result<Voice, Box<Error>> {
|
pub fn new(endpoint: &Endpoint, format: &Format) -> Result<Voice, CreationError> {
|
||||||
let channel = try!(cpal_impl::Voice::new(&endpoint.0, format));
|
let channel = try!(cpal_impl::Voice::new(&endpoint.0, format));
|
||||||
Ok(Voice(channel))
|
Ok(Voice(channel))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::ptr;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use Format;
|
use Format;
|
||||||
|
use FormatsEnumerationError;
|
||||||
use SamplesRate;
|
use SamplesRate;
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
|
|
||||||
|
@ -58,27 +59,39 @@ impl Endpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it.
|
/// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it.
|
||||||
fn ensure_future_audio_client(&self) -> MutexGuard<Option<IAudioClientWrapper>> {
|
fn ensure_future_audio_client(&self) -> Result<MutexGuard<Option<IAudioClientWrapper>>, IoError> {
|
||||||
let mut lock = self.future_audio_client.lock().unwrap();
|
let mut lock = self.future_audio_client.lock().unwrap();
|
||||||
if lock.is_some() {
|
if lock.is_some() {
|
||||||
return lock;
|
return Ok(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
let audio_client: *mut winapi::IAudioClient = unsafe {
|
let audio_client: *mut winapi::IAudioClient = unsafe {
|
||||||
let mut audio_client = mem::uninitialized();
|
let mut audio_client = mem::uninitialized();
|
||||||
let hresult = (*self.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
let hresult = (*self.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||||
ptr::null_mut(), &mut audio_client);
|
ptr::null_mut(), &mut audio_client);
|
||||||
|
|
||||||
// can fail if the device has been disconnected since we enumerated it, or if
|
// can fail if the device has been disconnected since we enumerated it, or if
|
||||||
// the device doesn't support playback for some reason
|
// the device doesn't support playback for some reason
|
||||||
check_result(hresult).unwrap(); // FIXME: don't unwrap
|
try!(check_result(hresult));
|
||||||
|
assert!(!audio_client.is_null());
|
||||||
audio_client as *mut _
|
audio_client as *mut _
|
||||||
};
|
};
|
||||||
|
|
||||||
*lock = Some(IAudioClientWrapper(audio_client));
|
*lock = Some(IAudioClientWrapper(audio_client));
|
||||||
lock
|
Ok(lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_supported_formats_list(&self) -> SupportedFormatsIterator {
|
/// Returns an uninitialized `IAudioClient`.
|
||||||
|
fn build_audioclient(&self) -> Result<*mut winapi::IAudioClient, IoError> {
|
||||||
|
let mut lock = try!(self.ensure_future_audio_client());
|
||||||
|
let client = lock.unwrap().0;
|
||||||
|
*lock = None;
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_supported_formats_list(&self)
|
||||||
|
-> Result<SupportedFormatsIterator, FormatsEnumerationError>
|
||||||
|
{
|
||||||
// We always create voices in shared mode, therefore all samples go through an audio
|
// We always create voices in shared mode, therefore all samples go through an audio
|
||||||
// processor to mix them together.
|
// processor to mix them together.
|
||||||
// However there is no way to query the list of all formats that are supported by the
|
// However there is no way to query the list of all formats that are supported by the
|
||||||
|
@ -88,7 +101,11 @@ impl Endpoint {
|
||||||
// initializing COM because we call `CoTaskMemFree`
|
// initializing COM because we call `CoTaskMemFree`
|
||||||
com::com_initialized();
|
com::com_initialized();
|
||||||
|
|
||||||
let lock = self.ensure_future_audio_client();
|
let lock = match self.ensure_future_audio_client() {
|
||||||
|
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
|
return Err(FormatsEnumerationError::DeviceNotAvailable),
|
||||||
|
e => e.unwrap(),
|
||||||
|
};
|
||||||
let client = lock.unwrap().0;
|
let client = lock.unwrap().0;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -108,7 +125,7 @@ impl Endpoint {
|
||||||
|
|
||||||
ole32::CoTaskMemFree(format_ptr as *mut _);
|
ole32::CoTaskMemFree(format_ptr as *mut _);
|
||||||
|
|
||||||
Some(format).into_iter()
|
Ok(Some(format).into_iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ use super::winapi;
|
||||||
use super::Endpoint;
|
use super::Endpoint;
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
|
|
||||||
use std::io::Error as IoError;
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use CreationError;
|
||||||
use Format;
|
use Format;
|
||||||
|
|
||||||
pub struct Voice {
|
pub struct Voice {
|
||||||
|
@ -28,22 +28,18 @@ unsafe impl Send for Voice {}
|
||||||
unsafe impl Sync for Voice {}
|
unsafe impl Sync for Voice {}
|
||||||
|
|
||||||
impl Voice {
|
impl Voice {
|
||||||
pub fn new(end_point: &Endpoint, format: &Format) -> Result<Voice, IoError> {
|
pub fn new(end_point: &Endpoint, format: &Format) -> Result<Voice, CreationError> {
|
||||||
// FIXME: release everything
|
// FIXME: release everything
|
||||||
unsafe {
|
unsafe {
|
||||||
// making sure that COM is initialized
|
// making sure that COM is initialized
|
||||||
// it's not actually sure that this is required, but when in doubt do it
|
// it's not actually sure that this is required, but when in doubt do it
|
||||||
com::com_initialized();
|
com::com_initialized();
|
||||||
|
|
||||||
// activating the end point in order to get a `IAudioClient`
|
// obtaining a `IAudioClient`
|
||||||
let audio_client: *mut winapi::IAudioClient = {
|
let audio_client = match end_point.build_audioclient() {
|
||||||
let mut audio_client = mem::uninitialized();
|
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
let hresult = (*end_point.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
return Err(CreationError::DeviceNotAvailable),
|
||||||
ptr::null_mut(), &mut audio_client);
|
e => e.unwrap(),
|
||||||
// can fail if the device has been disconnected since we enumerated it, or if
|
|
||||||
// the device doesn't support playback for some reason
|
|
||||||
try!(check_result(hresult));
|
|
||||||
audio_client as *mut _
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// computing the format and initializing the device
|
// computing the format and initializing the device
|
||||||
|
@ -61,7 +57,12 @@ impl Voice {
|
||||||
let mut format_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized();
|
let mut format_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized();
|
||||||
let hresult = (*audio_client).IsFormatSupported(winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
|
let hresult = (*audio_client).IsFormatSupported(winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
|
||||||
&format_attempt, &mut format_ptr);
|
&format_attempt, &mut format_ptr);
|
||||||
try!(check_result(hresult));
|
|
||||||
|
match check_result(hresult) {
|
||||||
|
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
|
return Err(CreationError::DeviceNotAvailable),
|
||||||
|
e => e.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
let format = if format_ptr.is_null() {
|
let format = if format_ptr.is_null() {
|
||||||
&format_attempt
|
&format_attempt
|
||||||
|
@ -78,7 +79,11 @@ impl Voice {
|
||||||
ole32::CoTaskMemFree(format_ptr as *mut _);
|
ole32::CoTaskMemFree(format_ptr as *mut _);
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(check_result(hresult));
|
match check_result(hresult) {
|
||||||
|
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
|
return Err(CreationError::DeviceNotAvailable),
|
||||||
|
e => e.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
format_copy
|
format_copy
|
||||||
};
|
};
|
||||||
|
@ -87,7 +92,11 @@ impl Voice {
|
||||||
let max_frames_in_buffer = {
|
let max_frames_in_buffer = {
|
||||||
let mut max_frames_in_buffer = mem::uninitialized();
|
let mut max_frames_in_buffer = mem::uninitialized();
|
||||||
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
||||||
try!(check_result(hresult));
|
match check_result(hresult) {
|
||||||
|
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
|
return Err(CreationError::DeviceNotAvailable),
|
||||||
|
e => e.unwrap(),
|
||||||
|
};
|
||||||
max_frames_in_buffer
|
max_frames_in_buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,8 +104,14 @@ impl Voice {
|
||||||
let render_client = {
|
let render_client = {
|
||||||
let mut render_client: *mut winapi::IAudioRenderClient = mem::uninitialized();
|
let mut render_client: *mut winapi::IAudioRenderClient = mem::uninitialized();
|
||||||
let hresult = (*audio_client).GetService(&winapi::IID_IAudioRenderClient,
|
let hresult = (*audio_client).GetService(&winapi::IID_IAudioRenderClient,
|
||||||
mem::transmute(&mut render_client));
|
&mut render_client as *mut *mut winapi::IAudioRenderClient
|
||||||
try!(check_result(hresult));
|
as *mut _);
|
||||||
|
match check_result(hresult) {
|
||||||
|
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
|
return Err(CreationError::DeviceNotAvailable),
|
||||||
|
e => e.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
&mut *render_client
|
&mut *render_client
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue