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
|
|
|
|
2015-09-01 11:23:41 +02: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;
|
2015-09-01 11:23:41 +02:00
|
|
|
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;
|
|
|
|
|
2015-09-01 11:23:41 +02:00
|
|
|
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)]
|
2015-09-01 11:23:41 +02:00
|
|
|
#[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
|
|
|
|
2015-09-01 11:23:41 +02: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();
|
2015-09-01 15:33:44 +02:00
|
|
|
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
|
|
|
|
)
|
|
|
|
},
|
2015-09-01 15:32:03 +02:00
|
|
|
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 {
|
2015-09-01 15:32:03 +02:00
|
|
|
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-01 15:32:03 +02:00
|
|
|
},
|
2015-09-10 11:44:19 +02:00
|
|
|
|
2015-09-01 15:32:03 +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,
|
2015-09-01 15:32:03 +02:00
|
|
|
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 {}
|
|
|
|
|
2015-09-01 11:23:41 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-09-01 11:23:41 +02: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
|
|
|
}
|