cpal/src/wasapi/mod.rs

234 lines
9.4 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-10 11:44:19 +02:00
use ChannelPosition;
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>;
2015-09-10 11:44:19 +02:00
// TODO: these constants should be moved to winapi
const SPEAKER_FRONT_LEFT: winapi::DWORD = 0x1;
const SPEAKER_FRONT_RIGHT: winapi::DWORD = 0x2;
const SPEAKER_FRONT_CENTER: winapi::DWORD = 0x4;
const SPEAKER_LOW_FREQUENCY: winapi::DWORD = 0x8;
const SPEAKER_BACK_LEFT: winapi::DWORD = 0x10;
const SPEAKER_BACK_RIGHT: winapi::DWORD = 0x20;
const SPEAKER_FRONT_LEFT_OF_CENTER: winapi::DWORD = 0x40;
const SPEAKER_FRONT_RIGHT_OF_CENTER: winapi::DWORD = 0x80;
const SPEAKER_BACK_CENTER: winapi::DWORD = 0x100;
const SPEAKER_SIDE_LEFT: winapi::DWORD = 0x200;
const SPEAKER_SIDE_RIGHT: winapi::DWORD = 0x400;
const SPEAKER_TOP_CENTER: winapi::DWORD = 0x800;
const SPEAKER_TOP_FRONT_LEFT: winapi::DWORD = 0x1000;
const SPEAKER_TOP_FRONT_CENTER: winapi::DWORD = 0x2000;
const SPEAKER_TOP_FRONT_RIGHT: winapi::DWORD = 0x4000;
const SPEAKER_TOP_BACK_LEFT: winapi::DWORD = 0x8000;
const SPEAKER_TOP_BACK_CENTER: winapi::DWORD = 0x10000;
const SPEAKER_TOP_BACK_RIGHT: winapi::DWORD = 0x20000;
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();
match check_result((*client).GetMixFormat(&mut format_ptr)) {
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) => {
return Err(FormatsEnumerationError::DeviceNotAvailable);
},
Err(e) => panic!("{:?}", e),
Ok(()) => (),
};
2015-09-01 13:53:54 +02:00
let format = {
2015-09-10 11:44:19 +02:00
let (channels, data_type) = match (*format_ptr).wFormatTag {
winapi::WAVE_FORMAT_PCM => {
(
vec![ChannelPosition::FrontLeft, ChannelPosition::FrontRight],
SampleFormat::I16
)
},
winapi::WAVE_FORMAT_EXTENSIBLE => {
let format_ptr = format_ptr as *const winapi::WAVEFORMATEXTENSIBLE;
2015-09-10 11:44:19 +02:00
let channels = {
let mut channels = Vec::new();
let mask = (*format_ptr).dwChannelMask;
if (mask & SPEAKER_FRONT_LEFT) != 0 { channels.push(ChannelPosition::FrontLeft); }
if (mask & SPEAKER_FRONT_RIGHT) != 0 { channels.push(ChannelPosition::FrontRight); }
if (mask & SPEAKER_FRONT_CENTER) != 0 { channels.push(ChannelPosition::FrontCenter); }
if (mask & SPEAKER_LOW_FREQUENCY) != 0 { channels.push(ChannelPosition::LowFrequency); }
if (mask & SPEAKER_BACK_LEFT) != 0 { channels.push(ChannelPosition::BackLeft); }
if (mask & SPEAKER_BACK_RIGHT) != 0 { channels.push(ChannelPosition::BackRight); }
if (mask & SPEAKER_FRONT_LEFT_OF_CENTER) != 0 { channels.push(ChannelPosition::FrontLeftOfCenter); }
if (mask & SPEAKER_FRONT_RIGHT_OF_CENTER) != 0 { channels.push(ChannelPosition::FrontRightOfCenter); }
if (mask & SPEAKER_BACK_CENTER) != 0 { channels.push(ChannelPosition::BackCenter); }
if (mask & SPEAKER_SIDE_LEFT) != 0 { channels.push(ChannelPosition::SideLeft); }
if (mask & SPEAKER_SIDE_RIGHT) != 0 { channels.push(ChannelPosition::SideRight); }
if (mask & SPEAKER_TOP_CENTER) != 0 { channels.push(ChannelPosition::TopCenter); }
if (mask & SPEAKER_TOP_FRONT_LEFT) != 0 { channels.push(ChannelPosition::TopFrontLeft); }
if (mask & SPEAKER_TOP_FRONT_CENTER) != 0 { channels.push(ChannelPosition::TopFrontCenter); }
if (mask & SPEAKER_TOP_FRONT_RIGHT) != 0 { channels.push(ChannelPosition::TopFrontRight); }
if (mask & SPEAKER_TOP_BACK_LEFT) != 0 { channels.push(ChannelPosition::TopBackLeft); }
if (mask & SPEAKER_TOP_BACK_CENTER) != 0 { channels.push(ChannelPosition::TopBackCenter); }
if (mask & SPEAKER_TOP_BACK_RIGHT) != 0 { channels.push(ChannelPosition::TopBackRight); }
assert_eq!((*format_ptr).Format.nChannels as usize, channels.len());
channels
};
let format = match (*format_ptr).SubFormat {
winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT => SampleFormat::F32,
winapi::KSDATAFORMAT_SUBTYPE_PCM => SampleFormat::I16,
g => panic!("Unknown SubFormat GUID returned by GetMixFormat: {:?}", g)
2015-09-10 11:44:19 +02:00
};
(channels, format)
},
2015-09-10 11:44:19 +02:00
f => panic!("Unknown data format returned by GetMixFormat: {:?}", f)
};
2015-09-01 13:53:54 +02:00
Format {
2015-09-10 11:44:19 +02:00
channels: channels,
samples_rate: SamplesRate((*format_ptr).nSamplesPerSec),
data_type: data_type,
2015-09-01 13:53:54 +02:00
}
};
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
}