Update winapi version from 0.2 to 0.3 (#202)
Adds only the necessary cargo features to reduce compile time and reduce the chance of linking errors occurring for unused libraries (e.g. d3d12.dll fails to link on my win10 VM). I thought I'd try and land this before before working on the wasapi backend implementation for #201. Tested both beep.rs and enumerate.rs and they work fine with the update.
This commit is contained in:
parent
3ff9e6fe65
commit
b47e46a4ac
|
@ -12,9 +12,7 @@ keywords = ["audio", "sound"]
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = "0.2.8"
|
winapi = { version = "0.3", features = ["audiosessiontypes", "audioclient", "combaseapi", "debug", "handleapi", "ksmedia", "mmdeviceapi", "objbase", "std", "synchapi", "winuser"] }
|
||||||
ole32-sys = "0.2"
|
|
||||||
kernel32-sys = "0.2"
|
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||||
alsa-sys = { version = "0.1", path = "alsa-sys" }
|
alsa-sys = { version = "0.1", path = "alsa-sys" }
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
//! Handles COM initialization and cleanup.
|
//! Handles COM initialization and cleanup.
|
||||||
|
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use super::ole32;
|
|
||||||
use super::winapi;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
use super::winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||||
|
use super::winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
|
||||||
|
|
||||||
thread_local!(static COM_INITIALIZED: ComInitialized = {
|
thread_local!(static COM_INITIALIZED: ComInitialized = {
|
||||||
unsafe {
|
unsafe {
|
||||||
// this call can fail if another library initialized COM in single-threaded mode
|
// this call can fail if another library initialized COM in single-threaded mode
|
||||||
// handling this situation properly would make the API more annoying, so we just don't care
|
// handling this situation properly would make the API more annoying, so we just don't care
|
||||||
check_result(ole32::CoInitializeEx(ptr::null_mut(), winapi::COINIT_MULTITHREADED)).unwrap();
|
check_result(CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED)).unwrap();
|
||||||
ComInitialized(ptr::null_mut())
|
ComInitialized(ptr::null_mut())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,7 +24,7 @@ struct ComInitialized(*mut ());
|
||||||
impl Drop for ComInitialized {
|
impl Drop for ComInitialized {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { ole32::CoUninitialize() };
|
unsafe { CoUninitialize() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,41 @@ use SupportedFormat;
|
||||||
|
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use super::com;
|
use super::com;
|
||||||
use super::ole32;
|
use super::winapi::Interface;
|
||||||
use super::winapi;
|
use super::winapi::shared::ksmedia;
|
||||||
|
use super::winapi::shared::guiddef::{
|
||||||
|
GUID,
|
||||||
|
};
|
||||||
|
use super::winapi::shared::mmreg::{
|
||||||
|
WAVE_FORMAT_PCM,
|
||||||
|
WAVE_FORMAT_EXTENSIBLE,
|
||||||
|
WAVEFORMATEXTENSIBLE,
|
||||||
|
};
|
||||||
|
use super::winapi::um::audioclient::{
|
||||||
|
IAudioClient,
|
||||||
|
IID_IAudioClient,
|
||||||
|
AUDCLNT_E_DEVICE_INVALIDATED,
|
||||||
|
};
|
||||||
|
use super::winapi::um::combaseapi::{
|
||||||
|
CoCreateInstance,
|
||||||
|
CoTaskMemFree,
|
||||||
|
CLSCTX_ALL,
|
||||||
|
};
|
||||||
|
use super::winapi::um::mmdeviceapi::{
|
||||||
|
eConsole,
|
||||||
|
eRender,
|
||||||
|
CLSID_MMDeviceEnumerator,
|
||||||
|
DEVICE_STATE_ACTIVE,
|
||||||
|
IMMDevice,
|
||||||
|
IMMDeviceCollection,
|
||||||
|
IMMDeviceEnumerator,
|
||||||
|
};
|
||||||
|
|
||||||
pub type SupportedFormatsIterator = OptionIntoIter<SupportedFormat>;
|
pub type SupportedFormatsIterator = OptionIntoIter<SupportedFormat>;
|
||||||
|
|
||||||
/// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers.
|
/// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct IAudioClientWrapper(*mut winapi::IAudioClient);
|
struct IAudioClientWrapper(*mut IAudioClient);
|
||||||
unsafe impl Send for IAudioClientWrapper {
|
unsafe impl Send for IAudioClientWrapper {
|
||||||
}
|
}
|
||||||
unsafe impl Sync for IAudioClientWrapper {
|
unsafe impl Sync for IAudioClientWrapper {
|
||||||
|
@ -30,7 +57,7 @@ unsafe impl Sync for IAudioClientWrapper {
|
||||||
|
|
||||||
/// An opaque type that identifies an end point.
|
/// An opaque type that identifies an end point.
|
||||||
pub struct Endpoint {
|
pub struct Endpoint {
|
||||||
device: *mut winapi::IMMDevice,
|
device: *mut IMMDevice,
|
||||||
|
|
||||||
/// We cache an uninitialized `IAudioClient` so that we can call functions from it without
|
/// We cache an uninitialized `IAudioClient` so that we can call functions from it without
|
||||||
/// having to create/destroy audio clients all the time.
|
/// having to create/destroy audio clients all the time.
|
||||||
|
@ -62,13 +89,13 @@ impl Endpoint {
|
||||||
|
|
||||||
// and turning it into a string
|
// and turning it into a string
|
||||||
let name_string: OsString = OsStringExt::from_wide(name_slice);
|
let name_string: OsString = OsStringExt::from_wide(name_slice);
|
||||||
ole32::CoTaskMemFree(name_ptr as *mut _);
|
CoTaskMemFree(name_ptr as *mut _);
|
||||||
name_string.into_string().unwrap()
|
name_string.into_string().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_immdevice(device: *mut winapi::IMMDevice) -> Endpoint {
|
fn from_immdevice(device: *mut IMMDevice) -> Endpoint {
|
||||||
Endpoint {
|
Endpoint {
|
||||||
device: device,
|
device: device,
|
||||||
future_audio_client: Arc::new(Mutex::new(None)),
|
future_audio_client: Arc::new(Mutex::new(None)),
|
||||||
|
@ -83,10 +110,10 @@ impl Endpoint {
|
||||||
return Ok(lock);
|
return Ok(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
let audio_client: *mut winapi::IAudioClient = unsafe {
|
let audio_client: *mut IAudioClient = unsafe {
|
||||||
let mut audio_client = mem::uninitialized();
|
let mut audio_client = mem::uninitialized();
|
||||||
let hresult = (*self.device).Activate(&winapi::IID_IAudioClient,
|
let hresult = (*self.device).Activate(&IID_IAudioClient,
|
||||||
winapi::CLSCTX_ALL,
|
CLSCTX_ALL,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
&mut audio_client);
|
&mut audio_client);
|
||||||
|
|
||||||
|
@ -103,7 +130,7 @@ impl Endpoint {
|
||||||
|
|
||||||
/// Returns an uninitialized `IAudioClient`.
|
/// Returns an uninitialized `IAudioClient`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn build_audioclient(&self) -> Result<*mut winapi::IAudioClient, IoError> {
|
pub(crate) fn build_audioclient(&self) -> Result<*mut IAudioClient, IoError> {
|
||||||
let mut lock = self.ensure_future_audio_client()?;
|
let mut lock = self.ensure_future_audio_client()?;
|
||||||
let client = lock.unwrap().0;
|
let client = lock.unwrap().0;
|
||||||
*lock = None;
|
*lock = None;
|
||||||
|
@ -121,7 +148,7 @@ impl Endpoint {
|
||||||
com::com_initialized();
|
com::com_initialized();
|
||||||
|
|
||||||
let lock = match 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) =>
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
return Err(FormatsEnumerationError::DeviceNotAvailable),
|
return Err(FormatsEnumerationError::DeviceNotAvailable),
|
||||||
e => e.unwrap(),
|
e => e.unwrap(),
|
||||||
};
|
};
|
||||||
|
@ -130,7 +157,7 @@ impl Endpoint {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut format_ptr = mem::uninitialized();
|
let mut format_ptr = mem::uninitialized();
|
||||||
match check_result((*client).GetMixFormat(&mut format_ptr)) {
|
match check_result((*client).GetMixFormat(&mut format_ptr)) {
|
||||||
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) => {
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
return Err(FormatsEnumerationError::DeviceNotAvailable);
|
return Err(FormatsEnumerationError::DeviceNotAvailable);
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{:?}", e),
|
Err(e) => panic!("{:?}", e),
|
||||||
|
@ -139,28 +166,30 @@ impl Endpoint {
|
||||||
|
|
||||||
let format = {
|
let format = {
|
||||||
let (channels, data_type) = match (*format_ptr).wFormatTag {
|
let (channels, data_type) = match (*format_ptr).wFormatTag {
|
||||||
winapi::WAVE_FORMAT_PCM => {
|
WAVE_FORMAT_PCM => {
|
||||||
(2, SampleFormat::I16)
|
(2, SampleFormat::I16)
|
||||||
},
|
},
|
||||||
winapi::WAVE_FORMAT_EXTENSIBLE => {
|
WAVE_FORMAT_EXTENSIBLE => {
|
||||||
let format_ptr = format_ptr as *const winapi::WAVEFORMATEXTENSIBLE;
|
let format_ptr = format_ptr as *const WAVEFORMATEXTENSIBLE;
|
||||||
let channels = (*format_ptr).Format.nChannels as ChannelCount;
|
let channels = (*format_ptr).Format.nChannels as ChannelCount;
|
||||||
let format = {
|
let format = {
|
||||||
fn cmp_guid(a: &winapi::GUID, b: &winapi::GUID) -> bool {
|
fn cmp_guid(a: &GUID, b: &GUID) -> bool {
|
||||||
a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 &&
|
a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 &&
|
||||||
a.Data4 == b.Data4
|
a.Data4 == b.Data4
|
||||||
}
|
}
|
||||||
if cmp_guid(&(*format_ptr).SubFormat,
|
if cmp_guid(&(*format_ptr).SubFormat,
|
||||||
&winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
|
&ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
|
||||||
{
|
{
|
||||||
SampleFormat::F32
|
SampleFormat::F32
|
||||||
} else if cmp_guid(&(*format_ptr).SubFormat,
|
} else if cmp_guid(&(*format_ptr).SubFormat,
|
||||||
&winapi::KSDATAFORMAT_SUBTYPE_PCM)
|
&ksmedia::KSDATAFORMAT_SUBTYPE_PCM)
|
||||||
{
|
{
|
||||||
SampleFormat::I16
|
SampleFormat::I16
|
||||||
} else {
|
} else {
|
||||||
panic!("Unknown SubFormat GUID returned by GetMixFormat: {:?}",
|
panic!("Unknown SubFormat GUID returned by GetMixFormat");
|
||||||
(*format_ptr).SubFormat)
|
// TODO: Re-add this to end of panic. Getting
|
||||||
|
// `trait Debug is not satisfied` error.
|
||||||
|
//(*format_ptr).SubFormat)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -178,7 +207,7 @@ impl Endpoint {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ole32::CoTaskMemFree(format_ptr as *mut _);
|
CoTaskMemFree(format_ptr as *mut _);
|
||||||
|
|
||||||
Ok(Some(format).into_iter())
|
Ok(Some(format).into_iter())
|
||||||
}
|
}
|
||||||
|
@ -232,13 +261,13 @@ lazy_static! {
|
||||||
|
|
||||||
// building the devices enumerator object
|
// building the devices enumerator object
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut enumerator: *mut winapi::IMMDeviceEnumerator = mem::uninitialized();
|
let mut enumerator: *mut IMMDeviceEnumerator = mem::uninitialized();
|
||||||
|
|
||||||
let hresult = ole32::CoCreateInstance(&winapi::CLSID_MMDeviceEnumerator,
|
let hresult = CoCreateInstance(&CLSID_MMDeviceEnumerator,
|
||||||
ptr::null_mut(), winapi::CLSCTX_ALL,
|
ptr::null_mut(), CLSCTX_ALL,
|
||||||
&winapi::IID_IMMDeviceEnumerator,
|
&IMMDeviceEnumerator::uuidof(),
|
||||||
&mut enumerator
|
&mut enumerator
|
||||||
as *mut *mut winapi::IMMDeviceEnumerator
|
as *mut *mut IMMDeviceEnumerator
|
||||||
as *mut _);
|
as *mut _);
|
||||||
|
|
||||||
check_result(hresult).unwrap();
|
check_result(hresult).unwrap();
|
||||||
|
@ -247,8 +276,8 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RAII object around `winapi::IMMDeviceEnumerator`.
|
/// RAII object around `IMMDeviceEnumerator`.
|
||||||
struct Enumerator(*mut winapi::IMMDeviceEnumerator);
|
struct Enumerator(*mut IMMDeviceEnumerator);
|
||||||
|
|
||||||
unsafe impl Send for Enumerator {
|
unsafe impl Send for Enumerator {
|
||||||
}
|
}
|
||||||
|
@ -266,7 +295,7 @@ impl Drop for Enumerator {
|
||||||
|
|
||||||
/// WASAPI implementation for `EndpointsIterator`.
|
/// WASAPI implementation for `EndpointsIterator`.
|
||||||
pub struct EndpointsIterator {
|
pub struct EndpointsIterator {
|
||||||
collection: *mut winapi::IMMDeviceCollection,
|
collection: *mut IMMDeviceCollection,
|
||||||
total_count: u32,
|
total_count: u32,
|
||||||
next_item: u32,
|
next_item: u32,
|
||||||
}
|
}
|
||||||
|
@ -288,10 +317,10 @@ impl Drop for EndpointsIterator {
|
||||||
impl Default for EndpointsIterator {
|
impl Default for EndpointsIterator {
|
||||||
fn default() -> EndpointsIterator {
|
fn default() -> EndpointsIterator {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut collection: *mut winapi::IMMDeviceCollection = mem::uninitialized();
|
let mut collection: *mut IMMDeviceCollection = mem::uninitialized();
|
||||||
// can fail because of wrong parameters (should never happen) or out of memory
|
// can fail because of wrong parameters (should never happen) or out of memory
|
||||||
check_result((*ENUMERATOR.0).EnumAudioEndpoints(winapi::eRender,
|
check_result((*ENUMERATOR.0).EnumAudioEndpoints(eRender,
|
||||||
winapi::DEVICE_STATE_ACTIVE,
|
DEVICE_STATE_ACTIVE,
|
||||||
&mut collection))
|
&mut collection))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -338,7 +367,7 @@ pub fn default_endpoint() -> Option<Endpoint> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut device = mem::uninitialized();
|
let mut device = mem::uninitialized();
|
||||||
let hres = (*ENUMERATOR.0)
|
let hres = (*ENUMERATOR.0)
|
||||||
.GetDefaultAudioEndpoint(winapi::eRender, winapi::eConsole, &mut device);
|
.GetDefaultAudioEndpoint(eRender, eConsole, &mut device);
|
||||||
|
|
||||||
if let Err(_err) = check_result(hres) {
|
if let Err(_err) = check_result(hres) {
|
||||||
return None; // TODO: check specifically for `E_NOTFOUND`, and panic otherwise
|
return None; // TODO: check specifically for `E_NOTFOUND`, and panic otherwise
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
extern crate ole32;
|
|
||||||
extern crate kernel32;
|
|
||||||
|
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
|
||||||
pub use self::endpoint::{Endpoint, EndpointsIterator, SupportedFormatsIterator, default_endpoint};
|
pub use self::endpoint::{Endpoint, EndpointsIterator, SupportedFormatsIterator, default_endpoint};
|
||||||
pub use self::voice::{Buffer, EventLoop, VoiceId};
|
pub use self::voice::{Buffer, EventLoop, VoiceId};
|
||||||
|
use self::winapi::um::winnt::HRESULT;
|
||||||
|
|
||||||
mod com;
|
mod com;
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
mod voice;
|
mod voice;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn check_result(result: winapi::HRESULT) -> Result<(), IoError> {
|
fn check_result(result: HRESULT) -> Result<(), IoError> {
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
Err(IoError::from_raw_os_error(result))
|
Err(IoError::from_raw_os_error(result))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
use super::Endpoint;
|
use super::Endpoint;
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use super::com;
|
use super::com;
|
||||||
use super::kernel32;
|
use super::winapi::shared::basetsd::UINT32;
|
||||||
use super::ole32;
|
use super::winapi::shared::ksmedia;
|
||||||
use super::winapi;
|
use super::winapi::shared::minwindef::{BYTE, DWORD, FALSE, WORD};
|
||||||
|
use super::winapi::shared::mmreg;
|
||||||
|
use super::winapi::shared::winerror;
|
||||||
|
use super::winapi::um::audioclient::{self, AUDCLNT_E_DEVICE_INVALIDATED};
|
||||||
|
use super::winapi::um::audiosessiontypes::{AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK};
|
||||||
|
use super::winapi::um::combaseapi::CoTaskMemFree;
|
||||||
|
use super::winapi::um::handleapi;
|
||||||
|
use super::winapi::um::synchapi;
|
||||||
|
use super::winapi::um::winbase;
|
||||||
|
use super::winapi::um::winnt;
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -37,7 +46,7 @@ pub struct EventLoop {
|
||||||
|
|
||||||
// This event is signalled after a new entry is added to `commands`, so that the `run()`
|
// This event is signalled after a new entry is added to `commands`, so that the `run()`
|
||||||
// method can be notified.
|
// method can be notified.
|
||||||
pending_scheduled_event: winapi::HANDLE,
|
pending_scheduled_event: winnt::HANDLE,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RunContext {
|
struct RunContext {
|
||||||
|
@ -46,7 +55,7 @@ struct RunContext {
|
||||||
|
|
||||||
// Handles corresponding to the `event` field of each element of `voices`. Must always be in
|
// Handles corresponding to the `event` field of each element of `voices`. Must always be in
|
||||||
// sync with `voices`, except that the first element is always `pending_scheduled_event`.
|
// sync with `voices`, except that the first element is always `pending_scheduled_event`.
|
||||||
handles: Vec<winapi::HANDLE>,
|
handles: Vec<winnt::HANDLE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
|
@ -58,23 +67,23 @@ enum Command {
|
||||||
|
|
||||||
struct VoiceInner {
|
struct VoiceInner {
|
||||||
id: VoiceId,
|
id: VoiceId,
|
||||||
audio_client: *mut winapi::IAudioClient,
|
audio_client: *mut audioclient::IAudioClient,
|
||||||
render_client: *mut winapi::IAudioRenderClient,
|
render_client: *mut audioclient::IAudioRenderClient,
|
||||||
// Event that is signalled by WASAPI whenever audio data must be written.
|
// Event that is signalled by WASAPI whenever audio data must be written.
|
||||||
event: winapi::HANDLE,
|
event: winnt::HANDLE,
|
||||||
// True if the voice is currently playing. False if paused.
|
// True if the voice is currently playing. False if paused.
|
||||||
playing: bool,
|
playing: bool,
|
||||||
|
|
||||||
// Number of frames of audio data in the underlying buffer allocated by WASAPI.
|
// Number of frames of audio data in the underlying buffer allocated by WASAPI.
|
||||||
max_frames_in_buffer: winapi::UINT32,
|
max_frames_in_buffer: UINT32,
|
||||||
// Number of bytes that each frame occupies.
|
// Number of bytes that each frame occupies.
|
||||||
bytes_per_frame: winapi::WORD,
|
bytes_per_frame: WORD,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoop {
|
impl EventLoop {
|
||||||
pub fn new() -> EventLoop {
|
pub fn new() -> EventLoop {
|
||||||
let pending_scheduled_event =
|
let pending_scheduled_event =
|
||||||
unsafe { kernel32::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()) };
|
unsafe { synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()) };
|
||||||
|
|
||||||
EventLoop {
|
EventLoop {
|
||||||
pending_scheduled_event: pending_scheduled_event,
|
pending_scheduled_event: pending_scheduled_event,
|
||||||
|
@ -96,7 +105,7 @@ impl EventLoop {
|
||||||
|
|
||||||
// Obtaining a `IAudioClient`.
|
// Obtaining a `IAudioClient`.
|
||||||
let audio_client = match end_point.build_audioclient() {
|
let audio_client = match end_point.build_audioclient() {
|
||||||
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||||
return Err(CreationError::DeviceNotAvailable),
|
return Err(CreationError::DeviceNotAvailable),
|
||||||
e => e.unwrap(),
|
e => e.unwrap(),
|
||||||
};
|
};
|
||||||
|
@ -104,24 +113,24 @@ impl EventLoop {
|
||||||
// Computing the format and initializing the device.
|
// Computing the format and initializing the device.
|
||||||
let format = {
|
let format = {
|
||||||
let format_attempt = format_to_waveformatextensible(format)?;
|
let format_attempt = format_to_waveformatextensible(format)?;
|
||||||
let share_mode = winapi::AUDCLNT_SHAREMODE_SHARED;
|
let share_mode = AUDCLNT_SHAREMODE_SHARED;
|
||||||
|
|
||||||
// `IsFormatSupported` checks whether the format is supported and fills
|
// `IsFormatSupported` checks whether the format is supported and fills
|
||||||
// a `WAVEFORMATEX`
|
// a `WAVEFORMATEX`
|
||||||
let mut dummy_fmt_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized();
|
let mut dummy_fmt_ptr: *mut mmreg::WAVEFORMATEX = mem::uninitialized();
|
||||||
let hresult =
|
let hresult =
|
||||||
(*audio_client)
|
(*audio_client)
|
||||||
.IsFormatSupported(share_mode, &format_attempt.Format, &mut dummy_fmt_ptr);
|
.IsFormatSupported(share_mode, &format_attempt.Format, &mut dummy_fmt_ptr);
|
||||||
// we free that `WAVEFORMATEX` immediately after because we don't need it
|
// we free that `WAVEFORMATEX` immediately after because we don't need it
|
||||||
if !dummy_fmt_ptr.is_null() {
|
if !dummy_fmt_ptr.is_null() {
|
||||||
ole32::CoTaskMemFree(dummy_fmt_ptr as *mut _);
|
CoTaskMemFree(dummy_fmt_ptr as *mut _);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `IsFormatSupported` can return `S_FALSE` (which means that a compatible format
|
// `IsFormatSupported` can return `S_FALSE` (which means that a compatible format
|
||||||
// has been found) but we also treat this as an error
|
// has been found) but we also treat this as an error
|
||||||
match (hresult, check_result(hresult)) {
|
match (hresult, check_result(hresult)) {
|
||||||
(_, Err(ref e))
|
(_, Err(ref e))
|
||||||
if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) => {
|
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(CreationError::DeviceNotAvailable);
|
return Err(CreationError::DeviceNotAvailable);
|
||||||
},
|
},
|
||||||
|
@ -129,7 +138,7 @@ impl EventLoop {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
panic!("{:?}", e);
|
panic!("{:?}", e);
|
||||||
},
|
},
|
||||||
(winapi::S_FALSE, _) => {
|
(winerror::S_FALSE, _) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(CreationError::FormatNotSupported);
|
return Err(CreationError::FormatNotSupported);
|
||||||
},
|
},
|
||||||
|
@ -138,14 +147,14 @@ impl EventLoop {
|
||||||
|
|
||||||
// finally initializing the audio client
|
// finally initializing the audio client
|
||||||
let hresult = (*audio_client).Initialize(share_mode,
|
let hresult = (*audio_client).Initialize(share_mode,
|
||||||
winapi::AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
&format_attempt.Format,
|
&format_attempt.Format,
|
||||||
ptr::null());
|
ptr::null());
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e)
|
||||||
if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) => {
|
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(CreationError::DeviceNotAvailable);
|
return Err(CreationError::DeviceNotAvailable);
|
||||||
},
|
},
|
||||||
|
@ -161,7 +170,7 @@ impl EventLoop {
|
||||||
|
|
||||||
// Creating the event that will be signalled whenever we need to submit some samples.
|
// Creating the event that will be signalled whenever we need to submit some samples.
|
||||||
let event = {
|
let event = {
|
||||||
let event = kernel32::CreateEventA(ptr::null_mut(), 0, 0, ptr::null());
|
let event = synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null());
|
||||||
if event == ptr::null_mut() {
|
if event == ptr::null_mut() {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
panic!("Failed to create event");
|
panic!("Failed to create event");
|
||||||
|
@ -185,7 +194,7 @@ impl EventLoop {
|
||||||
|
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e)
|
||||||
if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) => {
|
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(CreationError::DeviceNotAvailable);
|
return Err(CreationError::DeviceNotAvailable);
|
||||||
},
|
},
|
||||||
|
@ -201,15 +210,15 @@ impl EventLoop {
|
||||||
|
|
||||||
// Building a `IAudioRenderClient` that will be used to fill the samples buffer.
|
// Building a `IAudioRenderClient` that will be used to fill the samples buffer.
|
||||||
let render_client = {
|
let render_client = {
|
||||||
let mut render_client: *mut winapi::IAudioRenderClient = mem::uninitialized();
|
let mut render_client: *mut audioclient::IAudioRenderClient = mem::uninitialized();
|
||||||
let hresult = (*audio_client).GetService(&winapi::IID_IAudioRenderClient,
|
let hresult = (*audio_client).GetService(&audioclient::IID_IAudioRenderClient,
|
||||||
&mut render_client as
|
&mut render_client as
|
||||||
*mut *mut winapi::IAudioRenderClient as
|
*mut *mut audioclient::IAudioRenderClient as
|
||||||
*mut _);
|
*mut _);
|
||||||
|
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e)
|
||||||
if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) => {
|
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(CreationError::DeviceNotAvailable);
|
return Err(CreationError::DeviceNotAvailable);
|
||||||
},
|
},
|
||||||
|
@ -241,7 +250,7 @@ impl EventLoop {
|
||||||
|
|
||||||
self.commands.lock().unwrap().push(Command::NewVoice(inner));
|
self.commands.lock().unwrap().push(Command::NewVoice(inner));
|
||||||
|
|
||||||
let result = kernel32::SetEvent(self.pending_scheduled_event);
|
let result = synchapi::SetEvent(self.pending_scheduled_event);
|
||||||
assert!(result != 0);
|
assert!(result != 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -256,7 +265,7 @@ impl EventLoop {
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push(Command::DestroyVoice(voice_id));
|
.push(Command::DestroyVoice(voice_id));
|
||||||
let result = kernel32::SetEvent(self.pending_scheduled_event);
|
let result = synchapi::SetEvent(self.pending_scheduled_event);
|
||||||
assert!(result != 0);
|
assert!(result != 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,16 +326,16 @@ impl EventLoop {
|
||||||
|
|
||||||
// Wait for any of the handles to be signalled, which means that the corresponding
|
// Wait for any of the handles to be signalled, which means that the corresponding
|
||||||
// sound needs a buffer.
|
// sound needs a buffer.
|
||||||
debug_assert!(run_context.handles.len() <= winapi::MAXIMUM_WAIT_OBJECTS as usize);
|
debug_assert!(run_context.handles.len() <= winnt::MAXIMUM_WAIT_OBJECTS as usize);
|
||||||
let result = kernel32::WaitForMultipleObjectsEx(run_context.handles.len() as u32,
|
let result = synchapi::WaitForMultipleObjectsEx(run_context.handles.len() as u32,
|
||||||
run_context.handles.as_ptr(),
|
run_context.handles.as_ptr(),
|
||||||
winapi::FALSE,
|
FALSE,
|
||||||
winapi::INFINITE, /* TODO: allow setting a timeout */
|
winbase::INFINITE, /* TODO: allow setting a timeout */
|
||||||
winapi::FALSE /* irrelevant parameter here */);
|
FALSE /* irrelevant parameter here */);
|
||||||
|
|
||||||
// Notifying the corresponding task handler.
|
// Notifying the corresponding task handler.
|
||||||
debug_assert!(result >= winapi::WAIT_OBJECT_0);
|
debug_assert!(result >= winbase::WAIT_OBJECT_0);
|
||||||
let handle_id = (result - winapi::WAIT_OBJECT_0) as usize;
|
let handle_id = (result - winbase::WAIT_OBJECT_0) as usize;
|
||||||
|
|
||||||
// If `handle_id` is 0, then it's `pending_scheduled_event` that was signalled in
|
// If `handle_id` is 0, then it's `pending_scheduled_event` that was signalled in
|
||||||
// order for us to pick up the pending commands.
|
// order for us to pick up the pending commands.
|
||||||
|
@ -350,7 +359,7 @@ impl EventLoop {
|
||||||
|
|
||||||
// Obtaining a pointer to the buffer.
|
// Obtaining a pointer to the buffer.
|
||||||
let (buffer_data, buffer_len) = {
|
let (buffer_data, buffer_len) = {
|
||||||
let mut buffer: *mut winapi::BYTE = mem::uninitialized();
|
let mut buffer: *mut BYTE = mem::uninitialized();
|
||||||
let hresult = (*voice.render_client)
|
let hresult = (*voice.render_client)
|
||||||
.GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
.GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
||||||
check_result(hresult).unwrap(); // FIXME: can return `AUDCLNT_E_DEVICE_INVALIDATED`
|
check_result(hresult).unwrap(); // FIXME: can return `AUDCLNT_E_DEVICE_INVALIDATED`
|
||||||
|
@ -380,7 +389,7 @@ impl EventLoop {
|
||||||
pub fn play(&self, voice: VoiceId) {
|
pub fn play(&self, voice: VoiceId) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.commands.lock().unwrap().push(Command::Play(voice));
|
self.commands.lock().unwrap().push(Command::Play(voice));
|
||||||
let result = kernel32::SetEvent(self.pending_scheduled_event);
|
let result = synchapi::SetEvent(self.pending_scheduled_event);
|
||||||
assert!(result != 0);
|
assert!(result != 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,7 +398,7 @@ impl EventLoop {
|
||||||
pub fn pause(&self, voice: VoiceId) {
|
pub fn pause(&self, voice: VoiceId) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.commands.lock().unwrap().push(Command::Pause(voice));
|
self.commands.lock().unwrap().push(Command::Pause(voice));
|
||||||
let result = kernel32::SetEvent(self.pending_scheduled_event);
|
let result = synchapi::SetEvent(self.pending_scheduled_event);
|
||||||
assert!(result != 0);
|
assert!(result != 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +408,7 @@ impl Drop for EventLoop {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
kernel32::CloseHandle(self.pending_scheduled_event);
|
handleapi::CloseHandle(self.pending_scheduled_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,7 +428,7 @@ impl Drop for VoiceInner {
|
||||||
unsafe {
|
unsafe {
|
||||||
(*self.render_client).Release();
|
(*self.render_client).Release();
|
||||||
(*self.audio_client).Release();
|
(*self.audio_client).Release();
|
||||||
kernel32::CloseHandle(self.event);
|
handleapi::CloseHandle(self.event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,7 +438,7 @@ pub struct Buffer<'a, T: 'a> {
|
||||||
|
|
||||||
buffer_data: *mut T,
|
buffer_data: *mut T,
|
||||||
buffer_len: usize,
|
buffer_len: usize,
|
||||||
frames: winapi::UINT32,
|
frames: UINT32,
|
||||||
|
|
||||||
marker: PhantomData<&'a mut [T]>,
|
marker: PhantomData<&'a mut [T]>,
|
||||||
}
|
}
|
||||||
|
@ -454,7 +463,7 @@ impl<'a, T> Buffer<'a, T> {
|
||||||
let hresult = (*self.voice.render_client).ReleaseBuffer(self.frames as u32, 0);
|
let hresult = (*self.voice.render_client).ReleaseBuffer(self.frames as u32, 0);
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
// Ignoring the error that is produced if the device has been disconnected.
|
// Ignoring the error that is produced if the device has been disconnected.
|
||||||
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) => (),
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => (),
|
||||||
e => e.unwrap(),
|
e => e.unwrap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -463,53 +472,53 @@ impl<'a, T> Buffer<'a, T> {
|
||||||
|
|
||||||
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
||||||
fn format_to_waveformatextensible(format: &Format)
|
fn format_to_waveformatextensible(format: &Format)
|
||||||
-> Result<winapi::WAVEFORMATEXTENSIBLE, CreationError> {
|
-> Result<mmreg::WAVEFORMATEXTENSIBLE, CreationError> {
|
||||||
Ok(winapi::WAVEFORMATEXTENSIBLE {
|
Ok(mmreg::WAVEFORMATEXTENSIBLE {
|
||||||
Format: winapi::WAVEFORMATEX {
|
Format: mmreg::WAVEFORMATEX {
|
||||||
wFormatTag: match format.data_type {
|
wFormatTag: match format.data_type {
|
||||||
SampleFormat::I16 => winapi::WAVE_FORMAT_PCM,
|
SampleFormat::I16 => mmreg::WAVE_FORMAT_PCM,
|
||||||
SampleFormat::F32 => winapi::WAVE_FORMAT_EXTENSIBLE,
|
SampleFormat::F32 => mmreg::WAVE_FORMAT_EXTENSIBLE,
|
||||||
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
||||||
},
|
},
|
||||||
nChannels: format.channels as winapi::WORD,
|
nChannels: format.channels as WORD,
|
||||||
nSamplesPerSec: format.sample_rate.0 as winapi::DWORD,
|
nSamplesPerSec: format.sample_rate.0 as DWORD,
|
||||||
nAvgBytesPerSec: format.channels as winapi::DWORD *
|
nAvgBytesPerSec: format.channels as DWORD *
|
||||||
format.sample_rate.0 as winapi::DWORD *
|
format.sample_rate.0 as DWORD *
|
||||||
format.data_type.sample_size() as winapi::DWORD,
|
format.data_type.sample_size() as DWORD,
|
||||||
nBlockAlign: format.channels as winapi::WORD *
|
nBlockAlign: format.channels as WORD *
|
||||||
format.data_type.sample_size() as winapi::WORD,
|
format.data_type.sample_size() as WORD,
|
||||||
wBitsPerSample: 8 * format.data_type.sample_size() as winapi::WORD,
|
wBitsPerSample: 8 * format.data_type.sample_size() as WORD,
|
||||||
cbSize: match format.data_type {
|
cbSize: match format.data_type {
|
||||||
SampleFormat::I16 => 0,
|
SampleFormat::I16 => 0,
|
||||||
SampleFormat::F32 => (mem::size_of::<winapi::WAVEFORMATEXTENSIBLE>() -
|
SampleFormat::F32 => (mem::size_of::<mmreg::WAVEFORMATEXTENSIBLE>() -
|
||||||
mem::size_of::<winapi::WAVEFORMATEX>()) as
|
mem::size_of::<mmreg::WAVEFORMATEX>()) as
|
||||||
winapi::WORD,
|
WORD,
|
||||||
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Samples: 8 * format.data_type.sample_size() as winapi::WORD,
|
Samples: 8 * format.data_type.sample_size() as WORD,
|
||||||
dwChannelMask: {
|
dwChannelMask: {
|
||||||
let mut mask = 0;
|
let mut mask = 0;
|
||||||
|
|
||||||
const CHANNEL_POSITIONS: &'static [winapi::DWORD] = &[
|
const CHANNEL_POSITIONS: &'static [DWORD] = &[
|
||||||
winapi::SPEAKER_FRONT_LEFT,
|
mmreg::SPEAKER_FRONT_LEFT,
|
||||||
winapi::SPEAKER_FRONT_RIGHT,
|
mmreg::SPEAKER_FRONT_RIGHT,
|
||||||
winapi::SPEAKER_FRONT_CENTER,
|
mmreg::SPEAKER_FRONT_CENTER,
|
||||||
winapi::SPEAKER_LOW_FREQUENCY,
|
mmreg::SPEAKER_LOW_FREQUENCY,
|
||||||
winapi::SPEAKER_BACK_LEFT,
|
mmreg::SPEAKER_BACK_LEFT,
|
||||||
winapi::SPEAKER_BACK_RIGHT,
|
mmreg::SPEAKER_BACK_RIGHT,
|
||||||
winapi::SPEAKER_FRONT_LEFT_OF_CENTER,
|
mmreg::SPEAKER_FRONT_LEFT_OF_CENTER,
|
||||||
winapi::SPEAKER_FRONT_RIGHT_OF_CENTER,
|
mmreg::SPEAKER_FRONT_RIGHT_OF_CENTER,
|
||||||
winapi::SPEAKER_BACK_CENTER,
|
mmreg::SPEAKER_BACK_CENTER,
|
||||||
winapi::SPEAKER_SIDE_LEFT,
|
mmreg::SPEAKER_SIDE_LEFT,
|
||||||
winapi::SPEAKER_SIDE_RIGHT,
|
mmreg::SPEAKER_SIDE_RIGHT,
|
||||||
winapi::SPEAKER_TOP_CENTER,
|
mmreg::SPEAKER_TOP_CENTER,
|
||||||
winapi::SPEAKER_TOP_FRONT_LEFT,
|
mmreg::SPEAKER_TOP_FRONT_LEFT,
|
||||||
winapi::SPEAKER_TOP_FRONT_CENTER,
|
mmreg::SPEAKER_TOP_FRONT_CENTER,
|
||||||
winapi::SPEAKER_TOP_FRONT_RIGHT,
|
mmreg::SPEAKER_TOP_FRONT_RIGHT,
|
||||||
winapi::SPEAKER_TOP_BACK_LEFT,
|
mmreg::SPEAKER_TOP_BACK_LEFT,
|
||||||
winapi::SPEAKER_TOP_BACK_CENTER,
|
mmreg::SPEAKER_TOP_BACK_CENTER,
|
||||||
winapi::SPEAKER_TOP_BACK_RIGHT,
|
mmreg::SPEAKER_TOP_BACK_RIGHT,
|
||||||
];
|
];
|
||||||
|
|
||||||
for i in 0..format.channels {
|
for i in 0..format.channels {
|
||||||
|
@ -520,8 +529,8 @@ fn format_to_waveformatextensible(format: &Format)
|
||||||
mask
|
mask
|
||||||
},
|
},
|
||||||
SubFormat: match format.data_type {
|
SubFormat: match format.data_type {
|
||||||
SampleFormat::I16 => winapi::KSDATAFORMAT_SUBTYPE_PCM,
|
SampleFormat::I16 => ksmedia::KSDATAFORMAT_SUBTYPE_PCM,
|
||||||
SampleFormat::F32 => winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
|
SampleFormat::F32 => ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
|
||||||
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue