Add proper error handling
This commit is contained in:
parent
1985c346ac
commit
98b931edff
|
@ -2,7 +2,7 @@ extern crate cpal;
|
|||
|
||||
fn main() {
|
||||
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();
|
||||
|
||||
// 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};
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
|
@ -87,8 +88,10 @@ pub struct Endpoint(cpal_impl::Endpoint);
|
|||
|
||||
impl Endpoint {
|
||||
/// Returns an iterator that produces the list of formats that are supported by the backend.
|
||||
pub fn get_supported_formats_list(&self) -> SupportedFormatsIterator {
|
||||
SupportedFormatsIterator(self.0.get_supported_formats_list())
|
||||
pub fn get_supported_formats_list(&self) -> Result<SupportedFormatsIterator,
|
||||
FormatsEnumerationError>
|
||||
{
|
||||
Ok(SupportedFormatsIterator(try!(self.0.get_supported_formats_list())))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +149,61 @@ pub enum UnknownTypeBuffer<'a> {
|
|||
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
|
||||
/// it wants to output.
|
||||
///
|
||||
|
@ -163,7 +221,7 @@ pub struct Voice(cpal_impl::Voice);
|
|||
impl Voice {
|
||||
/// Builds a new channel.
|
||||
#[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));
|
||||
Ok(Voice(channel))
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::ptr;
|
|||
use std::mem;
|
||||
|
||||
use Format;
|
||||
use FormatsEnumerationError;
|
||||
use SamplesRate;
|
||||
use SampleFormat;
|
||||
|
||||
|
@ -58,27 +59,39 @@ impl Endpoint {
|
|||
}
|
||||
|
||||
/// 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();
|
||||
if lock.is_some() {
|
||||
return lock;
|
||||
return Ok(lock);
|
||||
}
|
||||
|
||||
let audio_client: *mut winapi::IAudioClient = unsafe {
|
||||
let mut audio_client = mem::uninitialized();
|
||||
let hresult = (*self.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||
ptr::null_mut(), &mut audio_client);
|
||||
|
||||
// can fail if the device has been disconnected since we enumerated it, or if
|
||||
// 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 _
|
||||
};
|
||||
|
||||
*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
|
||||
// processor to mix them together.
|
||||
// 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`
|
||||
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;
|
||||
|
||||
unsafe {
|
||||
|
@ -108,7 +125,7 @@ impl Endpoint {
|
|||
|
||||
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::check_result;
|
||||
|
||||
use std::io::Error as IoError;
|
||||
use std::cmp;
|
||||
use std::slice;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use CreationError;
|
||||
use Format;
|
||||
|
||||
pub struct Voice {
|
||||
|
@ -28,22 +28,18 @@ unsafe impl Send for Voice {}
|
|||
unsafe impl Sync for 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
|
||||
unsafe {
|
||||
// making sure that COM is initialized
|
||||
// it's not actually sure that this is required, but when in doubt do it
|
||||
com::com_initialized();
|
||||
|
||||
// activating the end point in order to get a `IAudioClient`
|
||||
let audio_client: *mut winapi::IAudioClient = {
|
||||
let mut audio_client = mem::uninitialized();
|
||||
let hresult = (*end_point.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||
ptr::null_mut(), &mut audio_client);
|
||||
// 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 _
|
||||
// obtaining a `IAudioClient`
|
||||
let audio_client = match end_point.build_audioclient() {
|
||||
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||
return Err(CreationError::DeviceNotAvailable),
|
||||
e => e.unwrap(),
|
||||
};
|
||||
|
||||
// computing the format and initializing the device
|
||||
|
@ -61,7 +57,12 @@ impl Voice {
|
|||
let mut format_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized();
|
||||
let hresult = (*audio_client).IsFormatSupported(winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
|
||||
&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() {
|
||||
&format_attempt
|
||||
|
@ -78,7 +79,11 @@ impl Voice {
|
|||
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
|
||||
};
|
||||
|
@ -87,7 +92,11 @@ impl Voice {
|
|||
let max_frames_in_buffer = {
|
||||
let mut max_frames_in_buffer = mem::uninitialized();
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -95,8 +104,14 @@ impl Voice {
|
|||
let render_client = {
|
||||
let mut render_client: *mut winapi::IAudioRenderClient = mem::uninitialized();
|
||||
let hresult = (*audio_client).GetService(&winapi::IID_IAudioRenderClient,
|
||||
mem::transmute(&mut render_client));
|
||||
try!(check_result(hresult));
|
||||
&mut render_client as *mut *mut winapi::IAudioRenderClient
|
||||
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
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue