`cargo fmt`
This commit is contained in:
parent
c62cb48e19
commit
d4965d3673
|
@ -3,8 +3,8 @@
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use super::winapi::um::objbase::{COINIT_MULTITHREADED};
|
|
||||||
use super::winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
|
use super::winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
|
||||||
|
use super::winapi::um::objbase::COINIT_MULTITHREADED;
|
||||||
|
|
||||||
thread_local!(static COM_INITIALIZED: ComInitialized = {
|
thread_local!(static COM_INITIALIZED: ComInitialized = {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -14,65 +14,47 @@ use DefaultFormatError;
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
use DevicesError;
|
use DevicesError;
|
||||||
use Format;
|
use Format;
|
||||||
use SupportedFormatsError;
|
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
use SampleRate;
|
use SampleRate;
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
|
use SupportedFormatsError;
|
||||||
use COMMON_SAMPLE_RATES;
|
use COMMON_SAMPLE_RATES;
|
||||||
|
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use super::check_result_backend_specific;
|
use super::check_result_backend_specific;
|
||||||
use super::com;
|
use super::com;
|
||||||
use super::winapi::Interface;
|
|
||||||
use super::winapi::ctypes::c_void;
|
use super::winapi::ctypes::c_void;
|
||||||
use super::winapi::shared::devpkey;
|
use super::winapi::shared::devpkey;
|
||||||
|
use super::winapi::shared::guiddef::GUID;
|
||||||
use super::winapi::shared::ksmedia;
|
use super::winapi::shared::ksmedia;
|
||||||
use super::winapi::shared::guiddef::{
|
use super::winapi::shared::minwindef::{DWORD, WORD};
|
||||||
GUID,
|
|
||||||
};
|
|
||||||
use super::winapi::shared::winerror;
|
|
||||||
use super::winapi::shared::minwindef::{
|
|
||||||
DWORD,
|
|
||||||
WORD,
|
|
||||||
};
|
|
||||||
use super::winapi::shared::mmreg;
|
use super::winapi::shared::mmreg;
|
||||||
|
use super::winapi::shared::winerror;
|
||||||
use super::winapi::shared::wtypes;
|
use super::winapi::shared::wtypes;
|
||||||
|
use super::winapi::Interface;
|
||||||
// https://msdn.microsoft.com/en-us/library/cc230355.aspx
|
// https://msdn.microsoft.com/en-us/library/cc230355.aspx
|
||||||
use super::winapi::um::winnt::LPWSTR;
|
|
||||||
use super::winapi::um::winnt::WCHAR;
|
|
||||||
use super::winapi::um::coml2api;
|
|
||||||
use super::winapi::um::audioclient::{
|
use super::winapi::um::audioclient::{
|
||||||
self,
|
self, IAudioClient, IID_IAudioClient, AUDCLNT_E_DEVICE_INVALIDATED,
|
||||||
IAudioClient,
|
|
||||||
IID_IAudioClient,
|
|
||||||
AUDCLNT_E_DEVICE_INVALIDATED,
|
|
||||||
};
|
};
|
||||||
use super::winapi::um::audiosessiontypes::{
|
use super::winapi::um::audiosessiontypes::{
|
||||||
AUDCLNT_SHAREMODE_SHARED,
|
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
|
||||||
};
|
};
|
||||||
use super::winapi::um::combaseapi::{
|
use super::winapi::um::combaseapi::{
|
||||||
CoCreateInstance,
|
CoCreateInstance, CoTaskMemFree, PropVariantClear, CLSCTX_ALL,
|
||||||
CoTaskMemFree,
|
|
||||||
CLSCTX_ALL,
|
|
||||||
PropVariantClear,
|
|
||||||
};
|
};
|
||||||
|
use super::winapi::um::coml2api;
|
||||||
use super::winapi::um::mmdeviceapi::{
|
use super::winapi::um::mmdeviceapi::{
|
||||||
eAll,
|
eAll, eCapture, eConsole, eRender, CLSID_MMDeviceEnumerator, EDataFlow, IMMDevice,
|
||||||
eCapture,
|
IMMDeviceCollection, IMMDeviceEnumerator, IMMEndpoint, DEVICE_STATE_ACTIVE,
|
||||||
eConsole,
|
|
||||||
eRender,
|
|
||||||
CLSID_MMDeviceEnumerator,
|
|
||||||
DEVICE_STATE_ACTIVE,
|
|
||||||
EDataFlow,
|
|
||||||
IMMDevice,
|
|
||||||
IMMDeviceCollection,
|
|
||||||
IMMDeviceEnumerator,
|
|
||||||
IMMEndpoint,
|
|
||||||
};
|
};
|
||||||
|
use super::winapi::um::winnt::LPWSTR;
|
||||||
|
use super::winapi::um::winnt::WCHAR;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
stream::{AudioClientFlow, Stream, StreamInner},
|
||||||
|
winapi::um::synchapi,
|
||||||
|
};
|
||||||
use crate::{traits::DeviceTrait, BuildStreamError, StreamData, StreamError};
|
use crate::{traits::DeviceTrait, BuildStreamError, StreamData, StreamError};
|
||||||
use super::{stream::{Stream, AudioClientFlow, StreamInner}, winapi::um::synchapi};
|
|
||||||
|
|
||||||
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
|
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||||
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
|
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||||
|
@ -80,10 +62,8 @@ pub type SupportedOutputFormats = std::vec::IntoIter<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 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 {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An opaque type that identifies an end point.
|
/// An opaque type that identifies an end point.
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
|
@ -102,11 +82,15 @@ impl DeviceTrait for Device {
|
||||||
Device::name(self)
|
Device::name(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||||
Device::supported_input_formats(self)
|
Device::supported_input_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
fn supported_output_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||||
Device::supported_output_formats(self)
|
Device::supported_output_formats(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +102,16 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError>
|
fn build_input_stream<D, E>(
|
||||||
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
|
&self,
|
||||||
|
format: &Format,
|
||||||
|
data_callback: D,
|
||||||
|
error_callback: E,
|
||||||
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
|
where
|
||||||
|
D: FnMut(StreamData) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
|
{
|
||||||
Ok(Stream::new(
|
Ok(Stream::new(
|
||||||
Arc::new(self.build_input_stream_inner(format)?),
|
Arc::new(self.build_input_stream_inner(format)?),
|
||||||
data_callback,
|
data_callback,
|
||||||
|
@ -127,8 +119,16 @@ impl DeviceTrait for Device {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError>
|
fn build_output_stream<D, E>(
|
||||||
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
|
&self,
|
||||||
|
format: &Format,
|
||||||
|
data_callback: D,
|
||||||
|
error_callback: E,
|
||||||
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
|
where
|
||||||
|
D: FnMut(StreamData) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
|
{
|
||||||
Ok(Stream::new(
|
Ok(Stream::new(
|
||||||
Arc::new(self.build_output_stream_inner(format)?),
|
Arc::new(self.build_output_stream_inner(format)?),
|
||||||
data_callback,
|
data_callback,
|
||||||
|
@ -157,7 +157,6 @@ impl Drop for WaveFormatExPtr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl WaveFormat {
|
impl WaveFormat {
|
||||||
// Given a pointer to some format, returns a valid copy of the format.
|
// Given a pointer to some format, returns a valid copy of the format.
|
||||||
pub fn copy_from_waveformatex_ptr(ptr: *const mmreg::WAVEFORMATEX) -> Option<Self> {
|
pub fn copy_from_waveformatex_ptr(ptr: *const mmreg::WAVEFORMATEX) -> Option<Self> {
|
||||||
|
@ -165,11 +164,11 @@ impl WaveFormat {
|
||||||
match (*ptr).wFormatTag {
|
match (*ptr).wFormatTag {
|
||||||
mmreg::WAVE_FORMAT_PCM | mmreg::WAVE_FORMAT_IEEE_FLOAT => {
|
mmreg::WAVE_FORMAT_PCM | mmreg::WAVE_FORMAT_IEEE_FLOAT => {
|
||||||
Some(WaveFormat::Ex(*ptr))
|
Some(WaveFormat::Ex(*ptr))
|
||||||
},
|
}
|
||||||
mmreg::WAVE_FORMAT_EXTENSIBLE => {
|
mmreg::WAVE_FORMAT_EXTENSIBLE => {
|
||||||
let extensible_ptr = ptr as *const mmreg::WAVEFORMATEXTENSIBLE;
|
let extensible_ptr = ptr as *const mmreg::WAVEFORMATEXTENSIBLE;
|
||||||
Some(WaveFormat::Extensible(*extensible_ptr))
|
Some(WaveFormat::Extensible(*extensible_ptr))
|
||||||
},
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,11 +199,12 @@ impl DerefMut for WaveFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsafe fn immendpoint_from_immdevice(device: *const IMMDevice) -> *mut IMMEndpoint {
|
unsafe fn immendpoint_from_immdevice(device: *const IMMDevice) -> *mut IMMEndpoint {
|
||||||
let mut endpoint: *mut IMMEndpoint = mem::uninitialized();
|
let mut endpoint: *mut IMMEndpoint = mem::uninitialized();
|
||||||
check_result((*device).QueryInterface(&IMMEndpoint::uuidof(), &mut endpoint as *mut _ as *mut _))
|
check_result(
|
||||||
.expect("could not query IMMDevice interface for IMMEndpoint");
|
(*device).QueryInterface(&IMMEndpoint::uuidof(), &mut endpoint as *mut _ as *mut _),
|
||||||
|
)
|
||||||
|
.expect("could not query IMMDevice interface for IMMEndpoint");
|
||||||
endpoint
|
endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,10 +219,7 @@ unsafe fn data_flow_from_immendpoint(endpoint: *const IMMEndpoint) -> EDataFlow
|
||||||
pub unsafe fn is_format_supported(
|
pub unsafe fn is_format_supported(
|
||||||
client: *const IAudioClient,
|
client: *const IAudioClient,
|
||||||
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
||||||
) -> Result<bool, SupportedFormatsError>
|
) -> Result<bool, SupportedFormatsError> {
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// `IsFormatSupported` checks whether the format is supported and fills
|
// `IsFormatSupported` checks whether the format is supported and fills
|
||||||
// a `WAVEFORMATEX`
|
// a `WAVEFORMATEX`
|
||||||
|
@ -255,7 +252,6 @@ pub unsafe fn is_format_supported(
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
// Check if the given format is supported.
|
// Check if the given format is supported.
|
||||||
let is_supported = |waveformatex_ptr, mut closest_waveformatex_ptr| {
|
let is_supported = |waveformatex_ptr, mut closest_waveformatex_ptr| {
|
||||||
let result = (*client).IsFormatSupported(
|
let result = (*client).IsFormatSupported(
|
||||||
|
@ -268,16 +264,10 @@ pub unsafe fn is_format_supported(
|
||||||
match (result, check_result(result)) {
|
match (result, check_result(result)) {
|
||||||
(_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
(_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
Err(SupportedFormatsError::DeviceNotAvailable)
|
Err(SupportedFormatsError::DeviceNotAvailable)
|
||||||
},
|
}
|
||||||
(_, Err(_)) => {
|
(_, Err(_)) => Ok(false),
|
||||||
Ok(false)
|
(winerror::S_FALSE, _) => Ok(false),
|
||||||
},
|
(_, Ok(())) => Ok(true),
|
||||||
(winerror::S_FALSE, _) => {
|
|
||||||
Ok(false)
|
|
||||||
},
|
|
||||||
(_, Ok(())) => {
|
|
||||||
Ok(true)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -290,34 +280,30 @@ pub unsafe fn is_format_supported(
|
||||||
let mut closest_waveformatex = *waveformatex_ptr;
|
let mut closest_waveformatex = *waveformatex_ptr;
|
||||||
let closest_waveformatex_ptr = &mut closest_waveformatex as *mut _;
|
let closest_waveformatex_ptr = &mut closest_waveformatex as *mut _;
|
||||||
is_supported(waveformatex_ptr, closest_waveformatex_ptr)
|
is_supported(waveformatex_ptr, closest_waveformatex_ptr)
|
||||||
},
|
}
|
||||||
mmreg::WAVE_FORMAT_EXTENSIBLE => {
|
mmreg::WAVE_FORMAT_EXTENSIBLE => {
|
||||||
let waveformatextensible_ptr =
|
let waveformatextensible_ptr = waveformatex_ptr as *const mmreg::WAVEFORMATEXTENSIBLE;
|
||||||
waveformatex_ptr as *const mmreg::WAVEFORMATEXTENSIBLE;
|
|
||||||
let mut closest_waveformatextensible = *waveformatextensible_ptr;
|
let mut closest_waveformatextensible = *waveformatextensible_ptr;
|
||||||
let closest_waveformatextensible_ptr =
|
let closest_waveformatextensible_ptr = &mut closest_waveformatextensible as *mut _;
|
||||||
&mut closest_waveformatextensible as *mut _;
|
|
||||||
let closest_waveformatex_ptr =
|
let closest_waveformatex_ptr =
|
||||||
closest_waveformatextensible_ptr as *mut mmreg::WAVEFORMATEX;
|
closest_waveformatextensible_ptr as *mut mmreg::WAVEFORMATEX;
|
||||||
is_supported(waveformatex_ptr, closest_waveformatex_ptr)
|
is_supported(waveformatex_ptr, closest_waveformatex_ptr)
|
||||||
},
|
}
|
||||||
_ => Ok(false),
|
_ => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get a cpal Format from a WAVEFORMATEX.
|
// Get a cpal Format from a WAVEFORMATEX.
|
||||||
unsafe fn format_from_waveformatex_ptr(
|
unsafe fn format_from_waveformatex_ptr(
|
||||||
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
||||||
) -> Option<Format>
|
) -> Option<Format> {
|
||||||
{
|
|
||||||
fn cmp_guid(a: &GUID, b: &GUID) -> bool {
|
fn cmp_guid(a: &GUID, b: &GUID) -> bool {
|
||||||
a.Data1 == b.Data1
|
a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 && a.Data4 == b.Data4
|
||||||
&& a.Data2 == b.Data2
|
|
||||||
&& a.Data3 == b.Data3
|
|
||||||
&& a.Data4 == b.Data4
|
|
||||||
}
|
}
|
||||||
let data_type = match ((*waveformatex_ptr).wBitsPerSample, (*waveformatex_ptr).wFormatTag) {
|
let data_type = match (
|
||||||
|
(*waveformatex_ptr).wBitsPerSample,
|
||||||
|
(*waveformatex_ptr).wFormatTag,
|
||||||
|
) {
|
||||||
(16, mmreg::WAVE_FORMAT_PCM) => SampleFormat::I16,
|
(16, mmreg::WAVE_FORMAT_PCM) => SampleFormat::I16,
|
||||||
(32, mmreg::WAVE_FORMAT_IEEE_FLOAT) => SampleFormat::F32,
|
(32, mmreg::WAVE_FORMAT_IEEE_FLOAT) => SampleFormat::F32,
|
||||||
(n_bits, mmreg::WAVE_FORMAT_EXTENSIBLE) => {
|
(n_bits, mmreg::WAVE_FORMAT_EXTENSIBLE) => {
|
||||||
|
@ -330,7 +316,7 @@ unsafe fn format_from_waveformatex_ptr(
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Unknown data format returned by GetMixFormat.
|
// Unknown data format returned by GetMixFormat.
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
@ -342,10 +328,8 @@ unsafe fn format_from_waveformatex_ptr(
|
||||||
Some(format)
|
Some(format)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Device {
|
unsafe impl Send for Device {}
|
||||||
}
|
unsafe impl Sync for Device {}
|
||||||
unsafe impl Sync for Device {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
pub fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
|
@ -356,12 +340,10 @@ impl Device {
|
||||||
|
|
||||||
// Get the endpoint's friendly-name property.
|
// Get the endpoint's friendly-name property.
|
||||||
let mut property_value = mem::zeroed();
|
let mut property_value = mem::zeroed();
|
||||||
if let Err(err) = check_result(
|
if let Err(err) = check_result((*property_store).GetValue(
|
||||||
(*property_store).GetValue(
|
&devpkey::DEVPKEY_Device_FriendlyName as *const _ as *const _,
|
||||||
&devpkey::DEVPKEY_Device_FriendlyName as *const _ as *const _,
|
&mut property_value,
|
||||||
&mut property_value
|
)) {
|
||||||
)
|
|
||||||
) {
|
|
||||||
let description = format!("failed to retrieve name from property store: {}", err);
|
let description = format!("failed to retrieve name from property store: {}", err);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
|
@ -369,8 +351,10 @@ impl Device {
|
||||||
|
|
||||||
// Read the friendly-name from the union data field, expecting a *const u16.
|
// Read the friendly-name from the union data field, expecting a *const u16.
|
||||||
if property_value.vt != wtypes::VT_LPWSTR as _ {
|
if property_value.vt != wtypes::VT_LPWSTR as _ {
|
||||||
let description =
|
let description = format!(
|
||||||
format!("property store produced invalid data: {:?}", property_value.vt);
|
"property store produced invalid data: {:?}",
|
||||||
|
property_value.vt
|
||||||
|
);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
@ -407,8 +391,9 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it.
|
/// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it.
|
||||||
fn ensure_future_audio_client(&self)
|
fn ensure_future_audio_client(
|
||||||
-> Result<MutexGuard<Option<IAudioClientWrapper>>, IoError> {
|
&self,
|
||||||
|
) -> Result<MutexGuard<Option<IAudioClientWrapper>>, IoError> {
|
||||||
let mut lock = self.future_audio_client.lock().unwrap();
|
let mut lock = self.future_audio_client.lock().unwrap();
|
||||||
if lock.is_some() {
|
if lock.is_some() {
|
||||||
return Ok(lock);
|
return Ok(lock);
|
||||||
|
@ -416,10 +401,12 @@ impl Device {
|
||||||
|
|
||||||
let audio_client: *mut 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(&IID_IAudioClient,
|
let hresult = (*self.device).Activate(
|
||||||
CLSCTX_ALL,
|
&IID_IAudioClient,
|
||||||
ptr::null_mut(),
|
CLSCTX_ALL,
|
||||||
&mut audio_client);
|
ptr::null_mut(),
|
||||||
|
&mut audio_client,
|
||||||
|
);
|
||||||
|
|
||||||
// can fail if the device has been disconnected since we enumerated it, or if
|
// can fail if the device has been disconnected since we enumerated it, or if
|
||||||
// the device doesn't support playback for some reason
|
// the device doesn't support playback for some reason
|
||||||
|
@ -466,7 +453,7 @@ impl Device {
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
let client = lock.unwrap().0;
|
let client = lock.unwrap().0;
|
||||||
|
|
||||||
|
@ -477,16 +464,19 @@ impl Device {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
return Err(SupportedFormatsError::DeviceNotAvailable);
|
return Err(SupportedFormatsError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the default format can't succeed we have no hope of finding other formats.
|
// If the default format can't succeed we have no hope of finding other formats.
|
||||||
assert_eq!(is_format_supported(client, default_waveformatex_ptr.0)?, true);
|
assert_eq!(
|
||||||
|
is_format_supported(client, default_waveformatex_ptr.0)?,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
// Copy the format to use as a test format (as to avoid mutating the original format).
|
// Copy the format to use as a test format (as to avoid mutating the original format).
|
||||||
let mut test_format = {
|
let mut test_format = {
|
||||||
|
@ -553,7 +543,9 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
pub fn supported_output_formats(
|
||||||
|
&self,
|
||||||
|
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||||
if self.data_flow() == eRender {
|
if self.data_flow() == eRender {
|
||||||
self.supported_formats()
|
self.supported_formats()
|
||||||
// If it's an input device, assume no output formats.
|
// If it's an input device, assume no output formats.
|
||||||
|
@ -588,12 +580,12 @@ impl Device {
|
||||||
match check_result((*client).GetMixFormat(&mut format_ptr.0)) {
|
match check_result((*client).GetMixFormat(&mut format_ptr.0)) {
|
||||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
return Err(DefaultFormatError::DeviceNotAvailable);
|
return Err(DefaultFormatError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -623,12 +615,11 @@ impl Device {
|
||||||
Err(DefaultFormatError::StreamTypeNotSupported)
|
Err(DefaultFormatError::StreamTypeNotSupported)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_input_stream_inner(
|
pub(crate) fn build_input_stream_inner(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
) -> Result<StreamInner, BuildStreamError>
|
) -> Result<StreamInner, BuildStreamError> {
|
||||||
{
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Making sure that COM is initialized.
|
// Making sure that COM is initialized.
|
||||||
// It's not actually sure that this is required, but when in doubt do it.
|
// It's not actually sure that this is required, but when in doubt do it.
|
||||||
|
@ -637,8 +628,9 @@ impl Device {
|
||||||
// Obtaining a `IAudioClient`.
|
// Obtaining a `IAudioClient`.
|
||||||
let audio_client = match self.build_audioclient() {
|
let audio_client = match self.build_audioclient() {
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) =>
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
return Err(BuildStreamError::DeviceNotAvailable),
|
return Err(BuildStreamError::DeviceNotAvailable)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
|
@ -669,17 +661,16 @@ impl Device {
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
);
|
);
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(BuildStreamError::DeviceNotAvailable);
|
return Err(BuildStreamError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -692,17 +683,16 @@ impl Device {
|
||||||
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
||||||
|
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(BuildStreamError::DeviceNotAvailable);
|
return Err(BuildStreamError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -731,24 +721,24 @@ impl Device {
|
||||||
|
|
||||||
// Building a `IAudioCaptureClient` that will be used to read captured samples.
|
// Building a `IAudioCaptureClient` that will be used to read captured samples.
|
||||||
let capture_client = {
|
let capture_client = {
|
||||||
let mut capture_client: *mut audioclient::IAudioCaptureClient = mem::uninitialized();
|
let mut capture_client: *mut audioclient::IAudioCaptureClient =
|
||||||
|
mem::uninitialized();
|
||||||
let hresult = (*audio_client).GetService(
|
let hresult = (*audio_client).GetService(
|
||||||
&audioclient::IID_IAudioCaptureClient,
|
&audioclient::IID_IAudioCaptureClient,
|
||||||
&mut capture_client as *mut *mut audioclient::IAudioCaptureClient as *mut _,
|
&mut capture_client as *mut *mut audioclient::IAudioCaptureClient as *mut _,
|
||||||
);
|
);
|
||||||
|
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(BuildStreamError::DeviceNotAvailable);
|
return Err(BuildStreamError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
let description = format!("failed to build capture client: {}", e);
|
let description = format!("failed to build capture client: {}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -757,11 +747,9 @@ impl Device {
|
||||||
|
|
||||||
// Once we built the `StreamInner`, we add a command that will be picked up by the
|
// Once we built the `StreamInner`, we add a command that will be picked up by the
|
||||||
// `run()` method and added to the `RunContext`.
|
// `run()` method and added to the `RunContext`.
|
||||||
let client_flow = AudioClientFlow::Capture {
|
let client_flow = AudioClientFlow::Capture { capture_client };
|
||||||
capture_client,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok( StreamInner {
|
Ok(StreamInner {
|
||||||
audio_client,
|
audio_client,
|
||||||
client_flow,
|
client_flow,
|
||||||
event,
|
event,
|
||||||
|
@ -776,8 +764,7 @@ impl Device {
|
||||||
pub(crate) fn build_output_stream_inner(
|
pub(crate) fn build_output_stream_inner(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
) -> Result<StreamInner, BuildStreamError>
|
) -> Result<StreamInner, BuildStreamError> {
|
||||||
{
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Making sure that COM is initialized.
|
// Making sure that COM is initialized.
|
||||||
// It's not actually sure that this is required, but when in doubt do it.
|
// It's not actually sure that this is required, but when in doubt do it.
|
||||||
|
@ -786,8 +773,9 @@ impl Device {
|
||||||
// Obtaining a `IAudioClient`.
|
// Obtaining a `IAudioClient`.
|
||||||
let audio_client = match self.build_audioclient() {
|
let audio_client = match self.build_audioclient() {
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) =>
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
return Err(BuildStreamError::DeviceNotAvailable),
|
return Err(BuildStreamError::DeviceNotAvailable)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
|
@ -809,24 +797,25 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally initializing the audio client
|
// finally initializing the audio client
|
||||||
let hresult = (*audio_client).Initialize(share_mode,
|
let hresult = (*audio_client).Initialize(
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
share_mode,
|
||||||
0,
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||||
0,
|
0,
|
||||||
&format_attempt.Format,
|
0,
|
||||||
ptr::null());
|
&format_attempt.Format,
|
||||||
|
ptr::null(),
|
||||||
|
);
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(BuildStreamError::DeviceNotAvailable);
|
return Err(BuildStreamError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
let description = format!("{}", e);
|
let description = format!("{}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -849,7 +838,7 @@ impl Device {
|
||||||
let description = format!("failed to call SetEventHandle: {}", e);
|
let description = format!("failed to call SetEventHandle: {}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -862,17 +851,16 @@ impl Device {
|
||||||
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
||||||
|
|
||||||
match check_result(hresult) {
|
match check_result(hresult) {
|
||||||
Err(ref e)
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(BuildStreamError::DeviceNotAvailable);
|
return Err(BuildStreamError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
let description = format!("failed to obtain buffer size: {}", e);
|
let description = format!("failed to obtain buffer size: {}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -882,23 +870,22 @@ impl Device {
|
||||||
// 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 audioclient::IAudioRenderClient = mem::uninitialized();
|
let mut render_client: *mut audioclient::IAudioRenderClient = mem::uninitialized();
|
||||||
let hresult = (*audio_client).GetService(&audioclient::IID_IAudioRenderClient,
|
let hresult = (*audio_client).GetService(
|
||||||
&mut render_client as
|
&audioclient::IID_IAudioRenderClient,
|
||||||
*mut *mut audioclient::IAudioRenderClient as
|
&mut render_client 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(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||||
if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
return Err(BuildStreamError::DeviceNotAvailable);
|
return Err(BuildStreamError::DeviceNotAvailable);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
(*audio_client).Release();
|
(*audio_client).Release();
|
||||||
let description = format!("failed to build render client: {}", e);
|
let description = format!("failed to build render client: {}", e);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
},
|
}
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -907,9 +894,7 @@ impl Device {
|
||||||
|
|
||||||
// Once we built the `StreamInner`, we add a command that will be picked up by the
|
// Once we built the `StreamInner`, we add a command that will be picked up by the
|
||||||
// `run()` method and added to the `RunContext`.
|
// `run()` method and added to the `RunContext`.
|
||||||
let client_flow = AudioClientFlow::Render {
|
let client_flow = AudioClientFlow::Render { render_client };
|
||||||
render_client,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(StreamInner {
|
Ok(StreamInner {
|
||||||
audio_client,
|
audio_client,
|
||||||
|
@ -935,38 +920,45 @@ impl PartialEq for Device {
|
||||||
// In this code section we're trying to use the GetId method for the device comparison, cf.
|
// In this code section we're trying to use the GetId method for the device comparison, cf.
|
||||||
// https://docs.microsoft.com/en-us/windows/desktop/api/mmdeviceapi/nf-mmdeviceapi-immdevice-getid
|
// https://docs.microsoft.com/en-us/windows/desktop/api/mmdeviceapi/nf-mmdeviceapi-immdevice-getid
|
||||||
unsafe {
|
unsafe {
|
||||||
struct IdRAII (LPWSTR);
|
struct IdRAII(LPWSTR);
|
||||||
/// RAII for device IDs.
|
/// RAII for device IDs.
|
||||||
impl Drop for IdRAII {
|
impl Drop for IdRAII {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {CoTaskMemFree(self.0 as *mut c_void)}
|
unsafe { CoTaskMemFree(self.0 as *mut c_void) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut id1: LPWSTR = ptr::null_mut();
|
let mut id1: LPWSTR = ptr::null_mut();
|
||||||
let rc1 = (*self.device).GetId(&mut id1);
|
let rc1 = (*self.device).GetId(&mut id1);
|
||||||
// GetId only fails with E_OUTOFMEMORY and if it does, we're probably dead already.
|
// GetId only fails with E_OUTOFMEMORY and if it does, we're probably dead already.
|
||||||
// Plus it won't do to change the device comparison logic unexpectedly.
|
// Plus it won't do to change the device comparison logic unexpectedly.
|
||||||
if rc1 != winerror::S_OK {panic! ("cpal: GetId failure: {}", rc1)}
|
if rc1 != winerror::S_OK {
|
||||||
|
panic!("cpal: GetId failure: {}", rc1)
|
||||||
|
}
|
||||||
let id1 = IdRAII(id1);
|
let id1 = IdRAII(id1);
|
||||||
let mut id2: LPWSTR = ptr::null_mut();
|
let mut id2: LPWSTR = ptr::null_mut();
|
||||||
let rc2 = (*other.device).GetId(&mut id2);
|
let rc2 = (*other.device).GetId(&mut id2);
|
||||||
if rc2 != winerror::S_OK {panic! ("cpal: GetId failure: {}", rc1)}
|
if rc2 != winerror::S_OK {
|
||||||
|
panic!("cpal: GetId failure: {}", rc1)
|
||||||
|
}
|
||||||
let id2 = IdRAII(id2);
|
let id2 = IdRAII(id2);
|
||||||
// 16-bit null-terminated comparison.
|
// 16-bit null-terminated comparison.
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
loop {
|
loop {
|
||||||
let w1: WCHAR = *id1.0.offset(offset);
|
let w1: WCHAR = *id1.0.offset(offset);
|
||||||
let w2: WCHAR = *id2.0.offset(offset);
|
let w2: WCHAR = *id2.0.offset(offset);
|
||||||
if w1 == 0 && w2 == 0 {return true}
|
if w1 == 0 && w2 == 0 {
|
||||||
if w1 != w2 {return false}
|
return true;
|
||||||
|
}
|
||||||
|
if w1 != w2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Device {
|
impl Eq for Device {}
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Device {
|
impl Clone for Device {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1025,9 +1017,7 @@ impl From<*const IMMDevice> for Endpoint {
|
||||||
|
|
||||||
impl Endpoint {
|
impl Endpoint {
|
||||||
fn data_flow(&self) -> EDataFlow {
|
fn data_flow(&self) -> EDataFlow {
|
||||||
unsafe {
|
unsafe { data_flow_from_immendpoint(self.endpoint) }
|
||||||
data_flow_from_immendpoint(self.endpoint)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,10 +1048,8 @@ lazy_static! {
|
||||||
/// RAII object around `IMMDeviceEnumerator`.
|
/// RAII object around `IMMDeviceEnumerator`.
|
||||||
struct Enumerator(*mut IMMDeviceEnumerator);
|
struct Enumerator(*mut IMMDeviceEnumerator);
|
||||||
|
|
||||||
unsafe impl Send for Enumerator {
|
unsafe impl Send for Enumerator {}
|
||||||
}
|
unsafe impl Sync for Enumerator {}
|
||||||
unsafe impl Sync for Enumerator {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Enumerator {
|
impl Drop for Enumerator {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1084,13 +1072,11 @@ impl Devices {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut collection: *mut 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_backend_specific(
|
check_result_backend_specific((*ENUMERATOR.0).EnumAudioEndpoints(
|
||||||
(*ENUMERATOR.0).EnumAudioEndpoints(
|
eAll,
|
||||||
eAll,
|
DEVICE_STATE_ACTIVE,
|
||||||
DEVICE_STATE_ACTIVE,
|
&mut collection,
|
||||||
&mut collection,
|
))?;
|
||||||
)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut count = mem::uninitialized();
|
let mut count = mem::uninitialized();
|
||||||
// can fail if the parameter is null, which should never happen
|
// can fail if the parameter is null, which should never happen
|
||||||
|
@ -1105,10 +1091,8 @@ impl Devices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Devices {
|
unsafe impl Send for Devices {}
|
||||||
}
|
unsafe impl Sync for Devices {}
|
||||||
unsafe impl Sync for Devices {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Devices {
|
impl Drop for Devices {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1148,8 +1132,7 @@ impl Iterator for Devices {
|
||||||
fn default_device(data_flow: EDataFlow) -> Option<Device> {
|
fn default_device(data_flow: EDataFlow) -> Option<Device> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut device = mem::uninitialized();
|
let mut device = mem::uninitialized();
|
||||||
let hres = (*ENUMERATOR.0)
|
let hres = (*ENUMERATOR.0).GetDefaultAudioEndpoint(data_flow, eConsole, &mut device);
|
||||||
.GetDefaultAudioEndpoint(data_flow, 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
|
||||||
}
|
}
|
||||||
|
@ -1165,7 +1148,6 @@ pub fn default_output_device() -> Option<Device> {
|
||||||
default_device(eRender)
|
default_device(eRender)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
||||||
//
|
//
|
||||||
// Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format.
|
// Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format.
|
||||||
|
@ -1187,7 +1169,7 @@ fn format_to_waveformatextensible(format: &Format) -> Option<mmreg::WAVEFORMATEX
|
||||||
let extensible_size = mem::size_of::<mmreg::WAVEFORMATEXTENSIBLE>();
|
let extensible_size = mem::size_of::<mmreg::WAVEFORMATEXTENSIBLE>();
|
||||||
let ex_size = mem::size_of::<mmreg::WAVEFORMATEX>();
|
let ex_size = mem::size_of::<mmreg::WAVEFORMATEX>();
|
||||||
(extensible_size - ex_size) as WORD
|
(extensible_size - ex_size) as WORD
|
||||||
},
|
}
|
||||||
SampleFormat::U16 => return None,
|
SampleFormat::U16 => return None,
|
||||||
};
|
};
|
||||||
let waveformatex = mmreg::WAVEFORMATEX {
|
let waveformatex = mmreg::WAVEFORMATEX {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
|
|
||||||
use BackendSpecificError;
|
pub use self::device::{
|
||||||
use DevicesError;
|
default_input_device, default_output_device, Device, Devices, SupportedInputFormats,
|
||||||
|
SupportedOutputFormats,
|
||||||
|
};
|
||||||
|
pub use self::stream::Stream;
|
||||||
use self::winapi::um::winnt::HRESULT;
|
use self::winapi::um::winnt::HRESULT;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
use traits::{HostTrait};
|
use traits::HostTrait;
|
||||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
use BackendSpecificError;
|
||||||
pub use self::stream::Stream;
|
use DevicesError;
|
||||||
|
|
||||||
mod com;
|
mod com;
|
||||||
mod device;
|
mod device;
|
||||||
|
@ -60,10 +63,8 @@ fn check_result(result: HRESULT) -> Result<(), IoError> {
|
||||||
fn check_result_backend_specific(result: HRESULT) -> Result<(), BackendSpecificError> {
|
fn check_result_backend_specific(result: HRESULT) -> Result<(), BackendSpecificError> {
|
||||||
match check_result(result) {
|
match check_result(result) {
|
||||||
Ok(()) => Ok(()),
|
Ok(()) => Ok(()),
|
||||||
Err(err) => {
|
Err(err) => Err(BackendSpecificError {
|
||||||
Err(BackendSpecificError {
|
description: format!("{}", err),
|
||||||
description: format!("{}", err),
|
}),
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,13 @@ use super::winapi::um::winnt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
|
||||||
use std::{sync::{Arc},
|
|
||||||
thread::{self, JoinHandle}};
|
|
||||||
use crate::traits::StreamTrait;
|
use crate::traits::StreamTrait;
|
||||||
|
use std::{
|
||||||
|
sync::Arc,
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
|
@ -22,8 +24,8 @@ use PlayStreamError;
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
use StreamError;
|
use StreamError;
|
||||||
use UnknownTypeOutputBuffer;
|
|
||||||
use UnknownTypeInputBuffer;
|
use UnknownTypeInputBuffer;
|
||||||
|
use UnknownTypeOutputBuffer;
|
||||||
|
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
/// The high-priority audio processing thread calling callbacks.
|
/// The high-priority audio processing thread calling callbacks.
|
||||||
|
@ -51,12 +53,12 @@ struct RunContext {
|
||||||
commands: Receiver<Command>,
|
commands: Receiver<Command>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub (crate) enum Command {
|
pub(crate) enum Command {
|
||||||
PlayStream,
|
PlayStream,
|
||||||
PauseStream,
|
PauseStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub (crate) enum AudioClientFlow {
|
pub(crate) enum AudioClientFlow {
|
||||||
Render {
|
Render {
|
||||||
render_client: *mut audioclient::IAudioRenderClient,
|
render_client: *mut audioclient::IAudioRenderClient,
|
||||||
},
|
},
|
||||||
|
@ -65,7 +67,7 @@ pub (crate) enum AudioClientFlow {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub (crate) struct StreamInner {
|
pub(crate) struct StreamInner {
|
||||||
audio_client: *mut audioclient::IAudioClient,
|
audio_client: *mut audioclient::IAudioClient,
|
||||||
client_flow: AudioClientFlow,
|
client_flow: AudioClientFlow,
|
||||||
// Event that is signalled by WASAPI whenever audio data must be written.
|
// Event that is signalled by WASAPI whenever audio data must be written.
|
||||||
|
@ -81,8 +83,15 @@ pub (crate) struct StreamInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub (crate) fn new<D, E>(stream_inner: Arc<StreamInner>, mut data_callback: D, mut error_callback: E) -> Stream
|
pub(crate) fn new<D, E>(
|
||||||
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
|
stream_inner: Arc<StreamInner>,
|
||||||
|
mut data_callback: D,
|
||||||
|
mut error_callback: E,
|
||||||
|
) -> Stream
|
||||||
|
where
|
||||||
|
D: FnMut(StreamData) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
|
{
|
||||||
let pending_scheduled_event =
|
let pending_scheduled_event =
|
||||||
unsafe { synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()) };
|
unsafe { synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()) };
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
@ -93,9 +102,8 @@ impl Stream {
|
||||||
commands: rx,
|
commands: rx,
|
||||||
};
|
};
|
||||||
|
|
||||||
let thread = thread::spawn(move || {
|
let thread =
|
||||||
run_inner(run_context, &mut data_callback, &mut error_callback)
|
thread::spawn(move || run_inner(run_context, &mut data_callback, &mut error_callback));
|
||||||
});
|
|
||||||
|
|
||||||
Stream {
|
Stream {
|
||||||
thread: Some(thread),
|
thread: Some(thread),
|
||||||
|
@ -121,9 +129,9 @@ impl Drop for Stream {
|
||||||
unsafe {
|
unsafe {
|
||||||
handleapi::CloseHandle(self.pending_scheduled_event);
|
handleapi::CloseHandle(self.pending_scheduled_event);
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let result = synchapi::SetEvent(self.pending_scheduled_event);
|
let result = synchapi::SetEvent(self.pending_scheduled_event);
|
||||||
assert!(result != 0);
|
assert!(result != 0);
|
||||||
}
|
}
|
||||||
self.thread.take().unwrap().join().unwrap();
|
self.thread.take().unwrap().join().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -134,7 +142,7 @@ impl StreamTrait for Stream {
|
||||||
self.push_command(Command::PlayStream);
|
self.push_command(Command::PlayStream);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn pause(&self)-> Result<(), PauseStreamError> {
|
fn pause(&self) -> Result<(), PauseStreamError> {
|
||||||
self.push_command(Command::PauseStream);
|
self.push_command(Command::PauseStream);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -168,27 +176,23 @@ fn process_commands(run_context: &mut RunContext) -> Result<(), StreamError> {
|
||||||
match command {
|
match command {
|
||||||
Command::PlayStream => {
|
Command::PlayStream => {
|
||||||
if !run_context.stream.playing {
|
if !run_context.stream.playing {
|
||||||
let hresult = unsafe {
|
let hresult = unsafe { (*run_context.stream.audio_client).Start() };
|
||||||
(*run_context.stream.audio_client).Start()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
run_context.stream.playing = true;
|
run_context.stream.playing = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Command::PauseStream => {
|
Command::PauseStream => {
|
||||||
if run_context.stream.playing {
|
if run_context.stream.playing {
|
||||||
let hresult = unsafe {
|
let hresult = unsafe { (*run_context.stream.audio_client).Stop() };
|
||||||
(*run_context.stream.audio_client).Stop()
|
|
||||||
};
|
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
run_context.stream.playing = false;
|
run_context.stream.playing = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,15 +212,13 @@ fn wait_for_handle_signal(handles: &[winnt::HANDLE]) -> Result<usize, BackendSpe
|
||||||
synchapi::WaitForMultipleObjectsEx(
|
synchapi::WaitForMultipleObjectsEx(
|
||||||
handles.len() as u32,
|
handles.len() as u32,
|
||||||
handles.as_ptr(),
|
handles.as_ptr(),
|
||||||
FALSE, // Don't wait for all, just wait for the first
|
FALSE, // Don't wait for all, just wait for the first
|
||||||
winbase::INFINITE, // TODO: allow setting a timeout
|
winbase::INFINITE, // TODO: allow setting a timeout
|
||||||
FALSE, // irrelevant parameter here
|
FALSE, // irrelevant parameter here
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if result == winbase::WAIT_FAILED {
|
if result == winbase::WAIT_FAILED {
|
||||||
let err = unsafe {
|
let err = unsafe { winapi::um::errhandlingapi::GetLastError() };
|
||||||
winapi::um::errhandlingapi::GetLastError()
|
|
||||||
};
|
|
||||||
let description = format!("`WaitForMultipleObjectsEx failed: {}", err);
|
let description = format!("`WaitForMultipleObjectsEx failed: {}", err);
|
||||||
let err = BackendSpecificError { description };
|
let err = BackendSpecificError { description };
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
@ -250,7 +252,11 @@ fn stream_error_from_hresult(hresult: winnt::HRESULT) -> Result<(), StreamError>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData), error_callback: &mut dyn FnMut(StreamError)) {
|
fn run_inner(
|
||||||
|
run_context: RunContext,
|
||||||
|
data_callback: &mut dyn FnMut(StreamData),
|
||||||
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
'stream_loop: loop {
|
'stream_loop: loop {
|
||||||
// Process queued commands.
|
// Process queued commands.
|
||||||
|
@ -282,7 +288,6 @@ fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData),
|
||||||
|
|
||||||
// Obtaining a pointer to the buffer.
|
// Obtaining a pointer to the buffer.
|
||||||
match stream.client_flow {
|
match stream.client_flow {
|
||||||
|
|
||||||
AudioClientFlow::Capture { capture_client } => {
|
AudioClientFlow::Capture { capture_client } => {
|
||||||
let mut frames_available = 0;
|
let mut frames_available = 0;
|
||||||
// Get the available data in the shared buffer.
|
// Get the available data in the shared buffer.
|
||||||
|
@ -316,17 +321,21 @@ fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData),
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
|
|
||||||
let buffer_len = frames_available as usize
|
let buffer_len = frames_available as usize
|
||||||
* stream.bytes_per_frame as usize / sample_size;
|
* stream.bytes_per_frame as usize
|
||||||
|
/ sample_size;
|
||||||
|
|
||||||
// Simplify the capture callback sample format branches.
|
// Simplify the capture callback sample format branches.
|
||||||
macro_rules! capture_callback {
|
macro_rules! capture_callback {
|
||||||
($T:ty, $Variant:ident) => {{
|
($T:ty, $Variant:ident) => {{
|
||||||
let buffer_data = buffer as *mut _ as *const $T;
|
let buffer_data = buffer as *mut _ as *const $T;
|
||||||
let slice = slice::from_raw_parts(buffer_data, buffer_len);
|
let slice = slice::from_raw_parts(buffer_data, buffer_len);
|
||||||
let unknown_buffer = UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
let unknown_buffer =
|
||||||
buffer: slice,
|
UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
||||||
});
|
buffer: slice,
|
||||||
let data = StreamData::Input { buffer: unknown_buffer };
|
});
|
||||||
|
let data = StreamData::Input {
|
||||||
|
buffer: unknown_buffer,
|
||||||
|
};
|
||||||
data_callback(data);
|
data_callback(data);
|
||||||
// Release the buffer.
|
// Release the buffer.
|
||||||
let hresult = (*capture_client).ReleaseBuffer(frames_available);
|
let hresult = (*capture_client).ReleaseBuffer(frames_available);
|
||||||
|
@ -343,7 +352,7 @@ fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData),
|
||||||
SampleFormat::U16 => capture_callback!(u16, U16),
|
SampleFormat::U16 => capture_callback!(u16, U16),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
AudioClientFlow::Render { render_client } => {
|
AudioClientFlow::Render { render_client } => {
|
||||||
// The number of frames available for writing.
|
// The number of frames available for writing.
|
||||||
|
@ -357,10 +366,8 @@ fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buffer: *mut BYTE = mem::uninitialized();
|
let mut buffer: *mut BYTE = mem::uninitialized();
|
||||||
let hresult = (*render_client).GetBuffer(
|
let hresult =
|
||||||
frames_available,
|
(*render_client).GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
||||||
&mut buffer as *mut *mut _,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
|
@ -368,26 +375,27 @@ fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData),
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
let buffer_len = frames_available as usize
|
let buffer_len =
|
||||||
* stream.bytes_per_frame as usize / sample_size;
|
frames_available as usize * stream.bytes_per_frame as usize / sample_size;
|
||||||
|
|
||||||
// Simplify the render callback sample format branches.
|
// Simplify the render callback sample format branches.
|
||||||
macro_rules! render_callback {
|
macro_rules! render_callback {
|
||||||
($T:ty, $Variant:ident) => {{
|
($T:ty, $Variant:ident) => {{
|
||||||
let buffer_data = buffer as *mut $T;
|
let buffer_data = buffer as *mut $T;
|
||||||
let slice = slice::from_raw_parts_mut(buffer_data, buffer_len);
|
let slice = slice::from_raw_parts_mut(buffer_data, buffer_len);
|
||||||
let unknown_buffer = UnknownTypeOutputBuffer::$Variant(::OutputBuffer {
|
let unknown_buffer =
|
||||||
buffer: slice
|
UnknownTypeOutputBuffer::$Variant(::OutputBuffer { buffer: slice });
|
||||||
});
|
let data = StreamData::Output {
|
||||||
let data = StreamData::Output { buffer: unknown_buffer };
|
buffer: unknown_buffer,
|
||||||
|
};
|
||||||
data_callback(data);
|
data_callback(data);
|
||||||
let hresult = (*render_client)
|
let hresult =
|
||||||
.ReleaseBuffer(frames_available as u32, 0);
|
(*render_client).ReleaseBuffer(frames_available as u32, 0);
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
break 'stream_loop;
|
break 'stream_loop;
|
||||||
}
|
}
|
||||||
}}
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
match stream.sample_format {
|
match stream.sample_format {
|
||||||
|
@ -395,7 +403,7 @@ fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData),
|
||||||
SampleFormat::I16 => render_callback!(i16, I16),
|
SampleFormat::I16 => render_callback!(i16, I16),
|
||||||
SampleFormat::U16 => render_callback!(u16, U16),
|
SampleFormat::U16 => render_callback!(u16, U16),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue