`cargo fmt`
This commit is contained in:
parent
c62cb48e19
commit
d4965d3673
|
@ -3,8 +3,8 @@
|
|||
use super::check_result;
|
||||
use std::ptr;
|
||||
|
||||
use super::winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||
use super::winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
|
||||
use super::winapi::um::objbase::COINIT_MULTITHREADED;
|
||||
|
||||
thread_local!(static COM_INITIALIZED: ComInitialized = {
|
||||
unsafe {
|
||||
|
|
|
@ -14,65 +14,47 @@ use DefaultFormatError;
|
|||
use DeviceNameError;
|
||||
use DevicesError;
|
||||
use Format;
|
||||
use SupportedFormatsError;
|
||||
use SampleFormat;
|
||||
use SampleRate;
|
||||
use SupportedFormat;
|
||||
use SupportedFormatsError;
|
||||
use COMMON_SAMPLE_RATES;
|
||||
|
||||
use super::check_result;
|
||||
use super::check_result_backend_specific;
|
||||
use super::com;
|
||||
use super::winapi::Interface;
|
||||
use super::winapi::ctypes::c_void;
|
||||
use super::winapi::shared::devpkey;
|
||||
use super::winapi::shared::guiddef::GUID;
|
||||
use super::winapi::shared::ksmedia;
|
||||
use super::winapi::shared::guiddef::{
|
||||
GUID,
|
||||
};
|
||||
use super::winapi::shared::winerror;
|
||||
use super::winapi::shared::minwindef::{
|
||||
DWORD,
|
||||
WORD,
|
||||
};
|
||||
use super::winapi::shared::minwindef::{DWORD, WORD};
|
||||
use super::winapi::shared::mmreg;
|
||||
use super::winapi::shared::winerror;
|
||||
use super::winapi::shared::wtypes;
|
||||
use super::winapi::Interface;
|
||||
// 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::{
|
||||
self,
|
||||
IAudioClient,
|
||||
IID_IAudioClient,
|
||||
AUDCLNT_E_DEVICE_INVALIDATED,
|
||||
self, IAudioClient, IID_IAudioClient, AUDCLNT_E_DEVICE_INVALIDATED,
|
||||
};
|
||||
use super::winapi::um::audiosessiontypes::{
|
||||
AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
};
|
||||
use super::winapi::um::combaseapi::{
|
||||
CoCreateInstance,
|
||||
CoTaskMemFree,
|
||||
CLSCTX_ALL,
|
||||
PropVariantClear,
|
||||
CoCreateInstance, CoTaskMemFree, PropVariantClear, CLSCTX_ALL,
|
||||
};
|
||||
use super::winapi::um::coml2api;
|
||||
use super::winapi::um::mmdeviceapi::{
|
||||
eAll,
|
||||
eCapture,
|
||||
eConsole,
|
||||
eRender,
|
||||
CLSID_MMDeviceEnumerator,
|
||||
DEVICE_STATE_ACTIVE,
|
||||
EDataFlow,
|
||||
IMMDevice,
|
||||
IMMDeviceCollection,
|
||||
IMMDeviceEnumerator,
|
||||
IMMEndpoint,
|
||||
eAll, eCapture, eConsole, eRender, CLSID_MMDeviceEnumerator, EDataFlow, IMMDevice,
|
||||
IMMDeviceCollection, IMMDeviceEnumerator, IMMEndpoint, DEVICE_STATE_ACTIVE,
|
||||
};
|
||||
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 super::{stream::{Stream, AudioClientFlow, StreamInner}, winapi::um::synchapi};
|
||||
|
||||
pub type SupportedInputFormats = 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.
|
||||
#[derive(Copy, Clone)]
|
||||
struct IAudioClientWrapper(*mut IAudioClient);
|
||||
unsafe impl Send for IAudioClientWrapper {
|
||||
}
|
||||
unsafe impl Sync for IAudioClientWrapper {
|
||||
}
|
||||
unsafe impl Send for IAudioClientWrapper {}
|
||||
unsafe impl Sync for IAudioClientWrapper {}
|
||||
|
||||
/// An opaque type that identifies an end point.
|
||||
pub struct Device {
|
||||
|
@ -102,11 +82,15 @@ impl DeviceTrait for Device {
|
|||
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)
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_output_formats(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
}
|
||||
|
||||
|
@ -118,8 +102,16 @@ impl DeviceTrait for Device {
|
|||
Device::default_output_format(self)
|
||||
}
|
||||
|
||||
fn build_input_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError>
|
||||
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
|
||||
fn build_input_stream<D, E>(
|
||||
&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(
|
||||
Arc::new(self.build_input_stream_inner(format)?),
|
||||
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>
|
||||
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
|
||||
fn build_output_stream<D, E>(
|
||||
&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(
|
||||
Arc::new(self.build_output_stream_inner(format)?),
|
||||
data_callback,
|
||||
|
@ -157,7 +157,6 @@ impl Drop for WaveFormatExPtr {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl WaveFormat {
|
||||
// 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> {
|
||||
|
@ -165,11 +164,11 @@ impl WaveFormat {
|
|||
match (*ptr).wFormatTag {
|
||||
mmreg::WAVE_FORMAT_PCM | mmreg::WAVE_FORMAT_IEEE_FLOAT => {
|
||||
Some(WaveFormat::Ex(*ptr))
|
||||
},
|
||||
}
|
||||
mmreg::WAVE_FORMAT_EXTENSIBLE => {
|
||||
let extensible_ptr = ptr as *const mmreg::WAVEFORMATEXTENSIBLE;
|
||||
Some(WaveFormat::Extensible(*extensible_ptr))
|
||||
},
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -200,10 +199,11 @@ impl DerefMut for WaveFormat {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
unsafe fn immendpoint_from_immdevice(device: *const IMMDevice) -> *mut IMMEndpoint {
|
||||
let mut endpoint: *mut IMMEndpoint = mem::uninitialized();
|
||||
check_result((*device).QueryInterface(&IMMEndpoint::uuidof(), &mut endpoint as *mut _ as *mut _))
|
||||
check_result(
|
||||
(*device).QueryInterface(&IMMEndpoint::uuidof(), &mut endpoint as *mut _ as *mut _),
|
||||
)
|
||||
.expect("could not query IMMDevice interface for IMMEndpoint");
|
||||
endpoint
|
||||
}
|
||||
|
@ -219,10 +219,7 @@ unsafe fn data_flow_from_immendpoint(endpoint: *const IMMEndpoint) -> EDataFlow
|
|||
pub unsafe fn is_format_supported(
|
||||
client: *const IAudioClient,
|
||||
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
||||
) -> Result<bool, SupportedFormatsError>
|
||||
{
|
||||
|
||||
|
||||
) -> Result<bool, SupportedFormatsError> {
|
||||
/*
|
||||
// `IsFormatSupported` checks whether the format is supported and fills
|
||||
// a `WAVEFORMATEX`
|
||||
|
@ -255,7 +252,6 @@ pub unsafe fn is_format_supported(
|
|||
};
|
||||
*/
|
||||
|
||||
|
||||
// Check if the given format is supported.
|
||||
let is_supported = |waveformatex_ptr, mut closest_waveformatex_ptr| {
|
||||
let result = (*client).IsFormatSupported(
|
||||
|
@ -268,16 +264,10 @@ pub unsafe fn is_format_supported(
|
|||
match (result, check_result(result)) {
|
||||
(_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
Err(SupportedFormatsError::DeviceNotAvailable)
|
||||
},
|
||||
(_, Err(_)) => {
|
||||
Ok(false)
|
||||
},
|
||||
(winerror::S_FALSE, _) => {
|
||||
Ok(false)
|
||||
},
|
||||
(_, Ok(())) => {
|
||||
Ok(true)
|
||||
},
|
||||
}
|
||||
(_, Err(_)) => Ok(false),
|
||||
(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 closest_waveformatex_ptr = &mut closest_waveformatex as *mut _;
|
||||
is_supported(waveformatex_ptr, closest_waveformatex_ptr)
|
||||
},
|
||||
}
|
||||
mmreg::WAVE_FORMAT_EXTENSIBLE => {
|
||||
let waveformatextensible_ptr =
|
||||
waveformatex_ptr as *const mmreg::WAVEFORMATEXTENSIBLE;
|
||||
let waveformatextensible_ptr = waveformatex_ptr as *const mmreg::WAVEFORMATEXTENSIBLE;
|
||||
let mut closest_waveformatextensible = *waveformatextensible_ptr;
|
||||
let closest_waveformatextensible_ptr =
|
||||
&mut closest_waveformatextensible as *mut _;
|
||||
let closest_waveformatextensible_ptr = &mut closest_waveformatextensible as *mut _;
|
||||
let closest_waveformatex_ptr =
|
||||
closest_waveformatextensible_ptr as *mut mmreg::WAVEFORMATEX;
|
||||
is_supported(waveformatex_ptr, closest_waveformatex_ptr)
|
||||
},
|
||||
}
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get a cpal Format from a WAVEFORMATEX.
|
||||
unsafe fn format_from_waveformatex_ptr(
|
||||
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
||||
) -> Option<Format>
|
||||
{
|
||||
) -> Option<Format> {
|
||||
fn cmp_guid(a: &GUID, b: &GUID) -> bool {
|
||||
a.Data1 == b.Data1
|
||||
&& a.Data2 == b.Data2
|
||||
&& a.Data3 == b.Data3
|
||||
&& a.Data4 == b.Data4
|
||||
a.Data1 == b.Data1 && 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,
|
||||
(32, mmreg::WAVE_FORMAT_IEEE_FLOAT) => SampleFormat::F32,
|
||||
(n_bits, mmreg::WAVE_FORMAT_EXTENSIBLE) => {
|
||||
|
@ -330,7 +316,7 @@ unsafe fn format_from_waveformatex_ptr(
|
|||
} else {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
}
|
||||
// Unknown data format returned by GetMixFormat.
|
||||
_ => return None,
|
||||
};
|
||||
|
@ -342,10 +328,8 @@ unsafe fn format_from_waveformatex_ptr(
|
|||
Some(format)
|
||||
}
|
||||
|
||||
unsafe impl Send for Device {
|
||||
}
|
||||
unsafe impl Sync for Device {
|
||||
}
|
||||
unsafe impl Send for Device {}
|
||||
unsafe impl Sync for Device {}
|
||||
|
||||
impl Device {
|
||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
||||
|
@ -356,12 +340,10 @@ impl Device {
|
|||
|
||||
// Get the endpoint's friendly-name property.
|
||||
let mut property_value = mem::zeroed();
|
||||
if let Err(err) = check_result(
|
||||
(*property_store).GetValue(
|
||||
if let Err(err) = check_result((*property_store).GetValue(
|
||||
&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 err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
|
@ -369,8 +351,10 @@ impl Device {
|
|||
|
||||
// Read the friendly-name from the union data field, expecting a *const u16.
|
||||
if property_value.vt != wtypes::VT_LPWSTR as _ {
|
||||
let description =
|
||||
format!("property store produced invalid data: {:?}", property_value.vt);
|
||||
let description = format!(
|
||||
"property store produced invalid data: {:?}",
|
||||
property_value.vt
|
||||
);
|
||||
let err = BackendSpecificError { description };
|
||||
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.
|
||||
fn ensure_future_audio_client(&self)
|
||||
-> Result<MutexGuard<Option<IAudioClientWrapper>>, IoError> {
|
||||
fn ensure_future_audio_client(
|
||||
&self,
|
||||
) -> Result<MutexGuard<Option<IAudioClientWrapper>>, IoError> {
|
||||
let mut lock = self.future_audio_client.lock().unwrap();
|
||||
if lock.is_some() {
|
||||
return Ok(lock);
|
||||
|
@ -416,10 +401,12 @@ impl Device {
|
|||
|
||||
let audio_client: *mut IAudioClient = unsafe {
|
||||
let mut audio_client = mem::uninitialized();
|
||||
let hresult = (*self.device).Activate(&IID_IAudioClient,
|
||||
let hresult = (*self.device).Activate(
|
||||
&IID_IAudioClient,
|
||||
CLSCTX_ALL,
|
||||
ptr::null_mut(),
|
||||
&mut audio_client);
|
||||
&mut audio_client,
|
||||
);
|
||||
|
||||
// can fail if the device has been disconnected since we enumerated it, or if
|
||||
// the device doesn't support playback for some reason
|
||||
|
@ -466,7 +453,7 @@ impl Device {
|
|||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
};
|
||||
let client = lock.unwrap().0;
|
||||
|
||||
|
@ -477,16 +464,19 @@ impl Device {
|
|||
Ok(()) => (),
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(SupportedFormatsError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// 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).
|
||||
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 {
|
||||
self.supported_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)) {
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(DefaultFormatError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(()) => (),
|
||||
};
|
||||
|
||||
|
@ -627,8 +619,7 @@ impl Device {
|
|||
pub(crate) fn build_input_stream_inner(
|
||||
&self,
|
||||
format: &Format,
|
||||
) -> Result<StreamInner, BuildStreamError>
|
||||
{
|
||||
) -> Result<StreamInner, BuildStreamError> {
|
||||
unsafe {
|
||||
// Making sure that COM is initialized.
|
||||
// It's not actually sure that this is required, but when in doubt do it.
|
||||
|
@ -637,8 +628,9 @@ impl Device {
|
|||
// Obtaining a `IAudioClient`.
|
||||
let audio_client = match self.build_audioclient() {
|
||||
Ok(client) => client,
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||
return Err(BuildStreamError::DeviceNotAvailable),
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(BuildStreamError::DeviceNotAvailable)
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
|
@ -669,17 +661,16 @@ impl Device {
|
|||
ptr::null(),
|
||||
);
|
||||
match check_result(hresult) {
|
||||
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) => {
|
||||
(*audio_client).Release();
|
||||
return Err(BuildStreamError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
(*audio_client).Release();
|
||||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(()) => (),
|
||||
};
|
||||
|
||||
|
@ -692,17 +683,16 @@ impl Device {
|
|||
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
||||
|
||||
match check_result(hresult) {
|
||||
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) => {
|
||||
(*audio_client).Release();
|
||||
return Err(BuildStreamError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
(*audio_client).Release();
|
||||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(()) => (),
|
||||
};
|
||||
|
||||
|
@ -731,24 +721,24 @@ impl Device {
|
|||
|
||||
// Building a `IAudioCaptureClient` that will be used to read captured samples.
|
||||
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(
|
||||
&audioclient::IID_IAudioCaptureClient,
|
||||
&mut capture_client as *mut *mut audioclient::IAudioCaptureClient as *mut _,
|
||||
);
|
||||
|
||||
match check_result(hresult) {
|
||||
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) => {
|
||||
(*audio_client).Release();
|
||||
return Err(BuildStreamError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
(*audio_client).Release();
|
||||
let description = format!("failed to build capture client: {}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(()) => (),
|
||||
};
|
||||
|
||||
|
@ -757,9 +747,7 @@ impl Device {
|
|||
|
||||
// Once we built the `StreamInner`, we add a command that will be picked up by the
|
||||
// `run()` method and added to the `RunContext`.
|
||||
let client_flow = AudioClientFlow::Capture {
|
||||
capture_client,
|
||||
};
|
||||
let client_flow = AudioClientFlow::Capture { capture_client };
|
||||
|
||||
Ok(StreamInner {
|
||||
audio_client,
|
||||
|
@ -776,8 +764,7 @@ impl Device {
|
|||
pub(crate) fn build_output_stream_inner(
|
||||
&self,
|
||||
format: &Format,
|
||||
) -> Result<StreamInner, BuildStreamError>
|
||||
{
|
||||
) -> Result<StreamInner, BuildStreamError> {
|
||||
unsafe {
|
||||
// Making sure that COM is initialized.
|
||||
// It's not actually sure that this is required, but when in doubt do it.
|
||||
|
@ -786,8 +773,9 @@ impl Device {
|
|||
// Obtaining a `IAudioClient`.
|
||||
let audio_client = match self.build_audioclient() {
|
||||
Ok(client) => client,
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) =>
|
||||
return Err(BuildStreamError::DeviceNotAvailable),
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(BuildStreamError::DeviceNotAvailable)
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
|
@ -809,24 +797,25 @@ impl Device {
|
|||
}
|
||||
|
||||
// finally initializing the audio client
|
||||
let hresult = (*audio_client).Initialize(share_mode,
|
||||
let hresult = (*audio_client).Initialize(
|
||||
share_mode,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
0,
|
||||
0,
|
||||
&format_attempt.Format,
|
||||
ptr::null());
|
||||
ptr::null(),
|
||||
);
|
||||
match check_result(hresult) {
|
||||
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) => {
|
||||
(*audio_client).Release();
|
||||
return Err(BuildStreamError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
(*audio_client).Release();
|
||||
let description = format!("{}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(()) => (),
|
||||
};
|
||||
|
||||
|
@ -849,7 +838,7 @@ impl Device {
|
|||
let description = format!("failed to call SetEventHandle: {}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(_) => (),
|
||||
};
|
||||
|
||||
|
@ -862,17 +851,16 @@ impl Device {
|
|||
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
|
||||
|
||||
match check_result(hresult) {
|
||||
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) => {
|
||||
(*audio_client).Release();
|
||||
return Err(BuildStreamError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
(*audio_client).Release();
|
||||
let description = format!("failed to obtain buffer size: {}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(()) => (),
|
||||
};
|
||||
|
||||
|
@ -882,23 +870,22 @@ impl Device {
|
|||
// Building a `IAudioRenderClient` that will be used to fill the samples buffer.
|
||||
let render_client = {
|
||||
let mut render_client: *mut audioclient::IAudioRenderClient = mem::uninitialized();
|
||||
let hresult = (*audio_client).GetService(&audioclient::IID_IAudioRenderClient,
|
||||
&mut render_client as
|
||||
*mut *mut audioclient::IAudioRenderClient as
|
||||
*mut _);
|
||||
let hresult = (*audio_client).GetService(
|
||||
&audioclient::IID_IAudioRenderClient,
|
||||
&mut render_client as *mut *mut audioclient::IAudioRenderClient as *mut _,
|
||||
);
|
||||
|
||||
match check_result(hresult) {
|
||||
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) => {
|
||||
(*audio_client).Release();
|
||||
return Err(BuildStreamError::DeviceNotAvailable);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
(*audio_client).Release();
|
||||
let description = format!("failed to build render client: {}", e);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
},
|
||||
}
|
||||
Ok(()) => (),
|
||||
};
|
||||
|
||||
|
@ -907,9 +894,7 @@ impl Device {
|
|||
|
||||
// Once we built the `StreamInner`, we add a command that will be picked up by the
|
||||
// `run()` method and added to the `RunContext`.
|
||||
let client_flow = AudioClientFlow::Render {
|
||||
render_client,
|
||||
};
|
||||
let client_flow = AudioClientFlow::Render { render_client };
|
||||
|
||||
Ok(StreamInner {
|
||||
audio_client,
|
||||
|
@ -946,27 +931,34 @@ impl PartialEq for Device {
|
|||
let rc1 = (*self.device).GetId(&mut id1);
|
||||
// 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.
|
||||
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 mut id2: LPWSTR = ptr::null_mut();
|
||||
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);
|
||||
// 16-bit null-terminated comparison.
|
||||
let mut offset = 0;
|
||||
loop {
|
||||
let w1: WCHAR = *id1.0.offset(offset);
|
||||
let w2: WCHAR = *id2.0.offset(offset);
|
||||
if w1 == 0 && w2 == 0 {return true}
|
||||
if w1 != w2 {return false}
|
||||
if w1 == 0 && w2 == 0 {
|
||||
return true;
|
||||
}
|
||||
if w1 != w2 {
|
||||
return false;
|
||||
}
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Device {
|
||||
}
|
||||
impl Eq for Device {}
|
||||
|
||||
impl Clone for Device {
|
||||
#[inline]
|
||||
|
@ -1025,9 +1017,7 @@ impl From<*const IMMDevice> for Endpoint {
|
|||
|
||||
impl Endpoint {
|
||||
fn data_flow(&self) -> EDataFlow {
|
||||
unsafe {
|
||||
data_flow_from_immendpoint(self.endpoint)
|
||||
}
|
||||
unsafe { data_flow_from_immendpoint(self.endpoint) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1058,10 +1048,8 @@ lazy_static! {
|
|||
/// RAII object around `IMMDeviceEnumerator`.
|
||||
struct Enumerator(*mut IMMDeviceEnumerator);
|
||||
|
||||
unsafe impl Send for Enumerator {
|
||||
}
|
||||
unsafe impl Sync for Enumerator {
|
||||
}
|
||||
unsafe impl Send for Enumerator {}
|
||||
unsafe impl Sync for Enumerator {}
|
||||
|
||||
impl Drop for Enumerator {
|
||||
#[inline]
|
||||
|
@ -1084,13 +1072,11 @@ impl Devices {
|
|||
unsafe {
|
||||
let mut collection: *mut IMMDeviceCollection = mem::uninitialized();
|
||||
// can fail because of wrong parameters (should never happen) or out of memory
|
||||
check_result_backend_specific(
|
||||
(*ENUMERATOR.0).EnumAudioEndpoints(
|
||||
check_result_backend_specific((*ENUMERATOR.0).EnumAudioEndpoints(
|
||||
eAll,
|
||||
DEVICE_STATE_ACTIVE,
|
||||
&mut collection,
|
||||
)
|
||||
)?;
|
||||
))?;
|
||||
|
||||
let mut count = mem::uninitialized();
|
||||
// can fail if the parameter is null, which should never happen
|
||||
|
@ -1105,10 +1091,8 @@ impl Devices {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Devices {
|
||||
}
|
||||
unsafe impl Sync for Devices {
|
||||
}
|
||||
unsafe impl Send for Devices {}
|
||||
unsafe impl Sync for Devices {}
|
||||
|
||||
impl Drop for Devices {
|
||||
#[inline]
|
||||
|
@ -1148,8 +1132,7 @@ impl Iterator for Devices {
|
|||
fn default_device(data_flow: EDataFlow) -> Option<Device> {
|
||||
unsafe {
|
||||
let mut device = mem::uninitialized();
|
||||
let hres = (*ENUMERATOR.0)
|
||||
.GetDefaultAudioEndpoint(data_flow, eConsole, &mut device);
|
||||
let hres = (*ENUMERATOR.0).GetDefaultAudioEndpoint(data_flow, eConsole, &mut device);
|
||||
if let Err(_err) = check_result(hres) {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
||||
//
|
||||
// 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 ex_size = mem::size_of::<mmreg::WAVEFORMATEX>();
|
||||
(extensible_size - ex_size) as WORD
|
||||
},
|
||||
}
|
||||
SampleFormat::U16 => return None,
|
||||
};
|
||||
let waveformatex = mmreg::WAVEFORMATEX {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
extern crate winapi;
|
||||
|
||||
use BackendSpecificError;
|
||||
use DevicesError;
|
||||
pub use self::device::{
|
||||
default_input_device, default_output_device, Device, Devices, SupportedInputFormats,
|
||||
SupportedOutputFormats,
|
||||
};
|
||||
pub use self::stream::Stream;
|
||||
use self::winapi::um::winnt::HRESULT;
|
||||
use std::io::Error as IoError;
|
||||
use traits::{HostTrait};
|
||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
||||
pub use self::stream::Stream;
|
||||
use traits::HostTrait;
|
||||
use BackendSpecificError;
|
||||
use DevicesError;
|
||||
|
||||
mod com;
|
||||
mod device;
|
||||
|
@ -60,10 +63,8 @@ fn check_result(result: HRESULT) -> Result<(), IoError> {
|
|||
fn check_result_backend_specific(result: HRESULT) -> Result<(), BackendSpecificError> {
|
||||
match check_result(result) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => {
|
||||
Err(BackendSpecificError {
|
||||
Err(err) => Err(BackendSpecificError {
|
||||
description: format!("{}", err),
|
||||
})
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,13 @@ use super::winapi::um::winnt;
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
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 std::{
|
||||
sync::Arc,
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
use BackendSpecificError;
|
||||
use PauseStreamError;
|
||||
|
@ -22,8 +24,8 @@ use PlayStreamError;
|
|||
use SampleFormat;
|
||||
use StreamData;
|
||||
use StreamError;
|
||||
use UnknownTypeOutputBuffer;
|
||||
use UnknownTypeInputBuffer;
|
||||
use UnknownTypeOutputBuffer;
|
||||
|
||||
pub struct Stream {
|
||||
/// The high-priority audio processing thread calling callbacks.
|
||||
|
@ -81,8 +83,15 @@ pub (crate) struct StreamInner {
|
|||
}
|
||||
|
||||
impl Stream {
|
||||
pub (crate) fn new<D, E>(stream_inner: Arc<StreamInner>, mut data_callback: D, mut error_callback: E) -> Stream
|
||||
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
|
||||
pub(crate) fn new<D, E>(
|
||||
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 =
|
||||
unsafe { synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()) };
|
||||
let (tx, rx) = channel();
|
||||
|
@ -93,9 +102,8 @@ impl Stream {
|
|||
commands: rx,
|
||||
};
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
run_inner(run_context, &mut data_callback, &mut error_callback)
|
||||
});
|
||||
let thread =
|
||||
thread::spawn(move || run_inner(run_context, &mut data_callback, &mut error_callback));
|
||||
|
||||
Stream {
|
||||
thread: Some(thread),
|
||||
|
@ -168,27 +176,23 @@ fn process_commands(run_context: &mut RunContext) -> Result<(), StreamError> {
|
|||
match command {
|
||||
Command::PlayStream => {
|
||||
if !run_context.stream.playing {
|
||||
let hresult = unsafe {
|
||||
(*run_context.stream.audio_client).Start()
|
||||
};
|
||||
let hresult = unsafe { (*run_context.stream.audio_client).Start() };
|
||||
|
||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||
return Err(err);
|
||||
}
|
||||
run_context.stream.playing = true;
|
||||
}
|
||||
},
|
||||
}
|
||||
Command::PauseStream => {
|
||||
if run_context.stream.playing {
|
||||
let hresult = unsafe {
|
||||
(*run_context.stream.audio_client).Stop()
|
||||
};
|
||||
let hresult = unsafe { (*run_context.stream.audio_client).Stop() };
|
||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||
return Err(err);
|
||||
}
|
||||
run_context.stream.playing = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,9 +218,7 @@ fn wait_for_handle_signal(handles: &[winnt::HANDLE]) -> Result<usize, BackendSpe
|
|||
)
|
||||
};
|
||||
if result == winbase::WAIT_FAILED {
|
||||
let err = unsafe {
|
||||
winapi::um::errhandlingapi::GetLastError()
|
||||
};
|
||||
let err = unsafe { winapi::um::errhandlingapi::GetLastError() };
|
||||
let description = format!("`WaitForMultipleObjectsEx failed: {}", err);
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err);
|
||||
|
@ -250,7 +252,11 @@ fn stream_error_from_hresult(hresult: winnt::HRESULT) -> Result<(), StreamError>
|
|||
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 {
|
||||
'stream_loop: loop {
|
||||
// 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.
|
||||
match stream.client_flow {
|
||||
|
||||
AudioClientFlow::Capture { capture_client } => {
|
||||
let mut frames_available = 0;
|
||||
// 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());
|
||||
|
||||
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.
|
||||
macro_rules! capture_callback {
|
||||
($T:ty, $Variant:ident) => {{
|
||||
let buffer_data = buffer as *mut _ as *const $T;
|
||||
let slice = slice::from_raw_parts(buffer_data, buffer_len);
|
||||
let unknown_buffer = UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
||||
let unknown_buffer =
|
||||
UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
||||
buffer: slice,
|
||||
});
|
||||
let data = StreamData::Input { buffer: unknown_buffer };
|
||||
let data = StreamData::Input {
|
||||
buffer: unknown_buffer,
|
||||
};
|
||||
data_callback(data);
|
||||
// Release the buffer.
|
||||
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),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
AudioClientFlow::Render { render_client } => {
|
||||
// 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 hresult = (*render_client).GetBuffer(
|
||||
frames_available,
|
||||
&mut buffer as *mut *mut _,
|
||||
);
|
||||
let hresult =
|
||||
(*render_client).GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
||||
|
||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||
error_callback(err);
|
||||
|
@ -368,26 +375,27 @@ fn run_inner(run_context: RunContext, data_callback: &mut dyn FnMut(StreamData),
|
|||
}
|
||||
|
||||
debug_assert!(!buffer.is_null());
|
||||
let buffer_len = frames_available as usize
|
||||
* stream.bytes_per_frame as usize / sample_size;
|
||||
let buffer_len =
|
||||
frames_available as usize * stream.bytes_per_frame as usize / sample_size;
|
||||
|
||||
// Simplify the render callback sample format branches.
|
||||
macro_rules! render_callback {
|
||||
($T:ty, $Variant:ident) => {{
|
||||
let buffer_data = buffer as *mut $T;
|
||||
let slice = slice::from_raw_parts_mut(buffer_data, buffer_len);
|
||||
let unknown_buffer = UnknownTypeOutputBuffer::$Variant(::OutputBuffer {
|
||||
buffer: slice
|
||||
});
|
||||
let data = StreamData::Output { buffer: unknown_buffer };
|
||||
let unknown_buffer =
|
||||
UnknownTypeOutputBuffer::$Variant(::OutputBuffer { buffer: slice });
|
||||
let data = StreamData::Output {
|
||||
buffer: unknown_buffer,
|
||||
};
|
||||
data_callback(data);
|
||||
let hresult = (*render_client)
|
||||
.ReleaseBuffer(frames_available as u32, 0);
|
||||
let hresult =
|
||||
(*render_client).ReleaseBuffer(frames_available as u32, 0);
|
||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||
error_callback(err);
|
||||
break 'stream_loop;
|
||||
}
|
||||
}}
|
||||
}};
|
||||
}
|
||||
|
||||
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::U16 => render_callback!(u16, U16),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue