cpal/src/wasapi/mod.rs

161 lines
4.9 KiB
Rust
Raw Normal View History

2014-12-11 14:22:55 +01:00
extern crate libc;
extern crate winapi;
2015-03-30 11:06:46 +02:00
extern crate ole32;
2014-12-11 14:22:55 +01:00
use std::io::Error as IoError;
2015-09-01 13:53:54 +02:00
use std::sync::{Arc, Mutex, MutexGuard};
use std::ptr;
use std::mem;
2014-12-11 14:22:55 +01:00
2015-09-01 13:53:54 +02:00
use Format;
2015-09-01 14:17:57 +02:00
use FormatsEnumerationError;
2015-09-01 13:53:54 +02:00
use SamplesRate;
use SampleFormat;
pub use std::option::IntoIter as OptionIntoIter;
pub use self::enumerate::{EndpointsIterator, get_default_endpoint};
pub use self::voice::{Voice, Buffer};
2014-12-11 17:23:33 +01:00
2015-09-01 13:53:54 +02:00
pub type SupportedFormatsIterator = OptionIntoIter<Format>;
mod com;
mod enumerate;
mod voice;
2014-12-11 17:23:33 +01:00
2015-09-01 13:53:54 +02:00
fn check_result(result: winapi::HRESULT) -> Result<(), IoError> {
if result < 0 {
Err(IoError::from_raw_os_error(result))
} else {
Ok(())
}
}
/// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers.
#[derive(Copy, Clone)]
#[allow(raw_pointer_derive)]
2015-09-01 13:53:54 +02:00
struct IAudioClientWrapper(*mut winapi::IAudioClient);
unsafe impl Send for IAudioClientWrapper {}
unsafe impl Sync for IAudioClientWrapper {}
/// An opaque type that identifies an end point.
pub struct Endpoint {
device: *mut winapi::IMMDevice,
/// We cache an uninitialized `IAudioClient` so that we can call functions from it without
/// having to create/destroy audio clients all the time.
future_audio_client: Arc<Mutex<Option<IAudioClientWrapper>>>, // TODO: add NonZero around the ptr
}
2014-12-11 17:23:33 +01:00
unsafe impl Send for Endpoint {}
unsafe impl Sync for Endpoint {}
2014-12-11 19:07:58 +01:00
2015-09-01 13:53:54 +02:00
impl Endpoint {
#[inline]
fn from_immdevice(device: *mut winapi::IMMDevice) -> Endpoint {
Endpoint {
device: device,
future_audio_client: Arc::new(Mutex::new(None)),
}
}
/// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it.
2015-09-01 14:17:57 +02:00
fn ensure_future_audio_client(&self) -> Result<MutexGuard<Option<IAudioClientWrapper>>, IoError> {
2015-09-01 13:53:54 +02:00
let mut lock = self.future_audio_client.lock().unwrap();
if lock.is_some() {
2015-09-01 14:17:57 +02:00
return Ok(lock);
2015-09-01 13:53:54 +02:00
}
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);
2015-09-01 14:17:57 +02:00
2015-09-01 13:53:54 +02:00
// can fail if the device has been disconnected since we enumerated it, or if
// the device doesn't support playback for some reason
2015-09-01 14:17:57 +02:00
try!(check_result(hresult));
assert!(!audio_client.is_null());
2015-09-01 13:53:54 +02:00
audio_client as *mut _
};
*lock = Some(IAudioClientWrapper(audio_client));
2015-09-01 14:17:57 +02:00
Ok(lock)
2015-09-01 13:53:54 +02:00
}
2015-09-01 14:17:57 +02:00
/// 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>
{
2015-09-01 13:53:54 +02:00
// 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
// audio processor, but one format is guaranteed to be supported, the one returned by
// `GetMixFormat`.
// initializing COM because we call `CoTaskMemFree`
com::com_initialized();
2015-09-01 14:17:57 +02:00
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(),
};
2015-09-01 13:53:54 +02:00
let client = lock.unwrap().0;
unsafe {
let mut format_ptr = mem::uninitialized();
check_result((*client).GetMixFormat(&mut format_ptr)).unwrap(); // FIXME: don't unwrap
let format = {
assert!((*format_ptr).wFormatTag == winapi::WAVE_FORMAT_EXTENSIBLE);
// FIXME: decode from the format
Format {
channels: 2,
samples_rate: SamplesRate(44100),
data_type: SampleFormat::U16,
}
};
ole32::CoTaskMemFree(format_ptr as *mut _);
2015-09-01 14:17:57 +02:00
Ok(Some(format).into_iter())
2015-09-01 13:53:54 +02:00
}
}
}
impl PartialEq for Endpoint {
fn eq(&self, other: &Endpoint) -> bool {
self.device == other.device
}
}
impl Eq for Endpoint {}
impl Clone for Endpoint {
fn clone(&self) -> Endpoint {
2015-09-01 13:53:54 +02:00
unsafe { (*self.device).AddRef(); }
Endpoint {
device: self.device,
future_audio_client: self.future_audio_client.clone(),
}
2014-12-22 14:16:47 +01:00
}
2014-12-11 17:23:33 +01:00
}
impl Drop for Endpoint {
2014-12-11 19:42:04 +01:00
fn drop(&mut self) {
2015-09-01 13:53:54 +02:00
unsafe { (*self.device).Release(); }
2014-12-11 19:42:04 +01:00
2015-09-01 13:53:54 +02:00
if let Some(client) = self.future_audio_client.lock().unwrap().take() {
unsafe { (*client.0).Release(); }
}
2014-12-11 16:28:26 +01:00
}
2014-12-11 14:22:55 +01:00
}