diff --git a/Cargo.toml b/Cargo.toml index 719ae9134..fd3b9274f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,11 +25,12 @@ clap = { version = "4.0", features = ["derive"] } ndk-glue = "0.7" [target.'cfg(target_os = "windows")'.dependencies] -windows = { version = "0.46.0", features = ["Win32_Media_Audio", "Win32_Foundation", "Win32_System_Com", "Win32_Devices_Properties", "Win32_Media_KernelStreaming", "Win32_System_Com_StructuredStorage", "Win32_System_Ole", "Win32_System_Threading", "Win32_Security", "Win32_System_SystemServices", "Win32_System_WindowsProgramming", "Win32_Media_Multimedia", "Win32_UI_Shell_PropertiesSystem"]} asio-sys = { version = "0.2", path = "asio-sys", optional = true } num-traits = { version = "0.2.6", optional = true } parking_lot = "0.12" once_cell = "1.12" +windows-core = "0.50" +windows-targets = "0.48" [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd"))'.dependencies] alsa = "0.7" diff --git a/src/host/wasapi/com.rs b/src/host/wasapi/com.rs index 0fe2dec2b..4afb2ea15 100644 --- a/src/host/wasapi/com.rs +++ b/src/host/wasapi/com.rs @@ -1,10 +1,13 @@ //! Handles COM initialization and cleanup. +#[rustfmt::skip] +pub(super) mod bindings; +pub(super) mod threading; + use super::IoError; use std::marker::PhantomData; -use windows::Win32::Foundation::RPC_E_CHANGED_MODE; -use windows::Win32::System::Com::{CoInitializeEx, CoUninitialize, COINIT_APARTMENTTHREADED}; +use bindings::{CoInitializeEx, CoUninitialize, COINIT_APARTMENTTHREADED, RPC_E_CHANGED_MODE}; thread_local!(static COM_INITIALIZED: ComInitialized = { unsafe { @@ -14,19 +17,15 @@ thread_local!(static COM_INITIALIZED: ComInitialized = { // This call can fail with RPC_E_CHANGED_MODE if another library initialized COM with MTA. // That's OK though since COM ensures thread-safety/compatibility through marshalling when // necessary. - let result = CoInitializeEx(None, COINIT_APARTMENTTHREADED); - match result.clone().map_err(|e| e.code()) { - Ok(_) | - Err(RPC_E_CHANGED_MODE) => { - ComInitialized { - result, - _ptr: PhantomData, - } - }, - Err(e) => { - // COM initialization failed in another way, something is really wrong. - panic!("Failed to initialize COM: {}", IoError::from_raw_os_error(e.0)); + let result = CoInitializeEx(std::ptr::null(), COINIT_APARTMENTTHREADED); + if result.is_ok() || result == RPC_E_CHANGED_MODE { + ComInitialized { + result, + _ptr: PhantomData, } + } else { + // COM initialization failed in another way, something is really wrong. + panic!("Failed to initialize COM: {}", IoError::from_raw_os_error(result.0)); } } }); @@ -36,7 +35,7 @@ thread_local!(static COM_INITIALIZED: ComInitialized = { // We store a raw pointer because it's the only way at the moment to remove `Send`/`Sync` from the // object. struct ComInitialized { - result: windows::core::Result<()>, + result: ::windows_core::HRESULT, _ptr: PhantomData<*mut ()>, } diff --git a/src/host/wasapi/com/audio.rs b/src/host/wasapi/com/audio.rs new file mode 100644 index 000000000..f94e2ec3c --- /dev/null +++ b/src/host/wasapi/com/audio.rs @@ -0,0 +1,680 @@ +#![allow(non_snake_case)] + +use super::*; +use windows_sys::Win32::Media::Audio::{EDataFlow, ERole, AUDCLNT_SHAREMODE, WAVEFORMATEX}; +use windows_sys::Win32::System::Com; + +type BOOL = i32; +type HANDLE = isize; + +pub trait AudioInterface { + const IID: GUID; +} + +#[repr(C)] +struct IAudioRenderClientV { + base: IUnknownV, + get_buffer: unsafe extern "system" fn( + this: *mut c_void, + numframesrequested: u32, + ppdata: *mut *mut u8, + ) -> HRESULT, + release_buffer: unsafe extern "system" fn( + this: *mut c_void, + numframeswritten: u32, + dwflags: u32, + ) -> HRESULT, +} + +#[repr(transparent)] +pub struct IAudioRenderClient(Object); + +impl AudioInterface for IAudioRenderClient { + const IID: GUID = GUID::from_u128(0xf294acfc_3146_4483_a7bf_addca7c260e2); +} + +impl IAudioRenderClient { + #[inline] + pub unsafe fn GetBuffer(&self, available_frames: u32) -> ComResult<*mut u8> { + let mut out = std::ptr::null_mut(); + let res = + unsafe { (self.0.vtbl().get_buffer)(self.0 .0.cast(), available_frames, &mut out) }; + + if res >= 0 { + Ok(out) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn ReleaseBuffer(&self, frames: u32, flags: u32) -> ComResult<()> { + let res = unsafe { (self.0.vtbl().release_buffer)(self.0 .0.cast(), frames, flags) }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } +} + +#[repr(C)] +struct IAudioCaptureClientV { + base: IUnknownV, + get_buffer: unsafe extern "system" fn( + this: *mut c_void, + ppdata: *mut *mut u8, + pnumframestoread: *mut u32, + pdwflags: *mut u32, + pu64deviceposition: Option<*mut u64>, + pu64qpcposition: Option<*mut u64>, + ) -> HRESULT, + release_buffer: unsafe extern "system" fn(this: *mut c_void, numframesread: u32) -> HRESULT, + get_next_packet_size: + unsafe extern "system" fn(this: *mut c_void, pnumframesinnextpacket: *mut u32) -> HRESULT, +} + +#[repr(transparent)] +pub struct IAudioCaptureClient(Object); + +impl AudioInterface for IAudioCaptureClient { + const IID: GUID = GUID::from_u128(0xc8adbd64_e71e_48a0_a4de_185c395cd317); +} + +impl IAudioCaptureClient { + #[inline] + pub unsafe fn GetNextPacketSize(&self) -> ComResult { + let mut num = 0; + let res = unsafe { (self.0.vtbl().get_next_packet_size)(self.0 .0.cast(), &mut num) }; + + if res >= 0 { + Ok(num) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn GetBuffer( + &self, + data: *mut *mut u8, + frames_stored: *mut u32, + flags: *mut u32, + device_position: Option<*mut u64>, + qpc_position: Option<*mut u64>, + ) -> ComResult<()> { + let res = unsafe { + (self.0.vtbl().get_buffer)( + self.0 .0.cast(), + data, + frames_stored, + flags, + device_position, + qpc_position, + ) + }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn ReleaseBuffer(&self, frames_read: u32) -> ComResult<()> { + let res = unsafe { (self.0.vtbl().release_buffer)(self.0 .0.cast(), frames_read) }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } +} + +#[repr(C)] +struct IAudioClientV { + base: IUnknownV, + initialize: unsafe extern "system" fn( + this: *mut c_void, + sharemode: AUDCLNT_SHAREMODE, + streamflags: u32, + hnsbufferduration: i64, + hnsperiodicity: i64, + pformat: *const WAVEFORMATEX, + audiosessionguid: Option<*const GUID>, + ) -> HRESULT, + get_buffer_size: + unsafe extern "system" fn(this: *mut c_void, pnumbufferframes: *mut u32) -> HRESULT, + __GetStreamLatency: usize, + get_current_padding: + unsafe extern "system" fn(this: *mut c_void, pnumpaddingframes: *mut u32) -> HRESULT, + is_format_supported: unsafe extern "system" fn( + this: *mut c_void, + sharemode: AUDCLNT_SHAREMODE, + pformat: *const WAVEFORMATEX, + ppclosestmatch: Option<*mut *mut WAVEFORMATEX>, + ) -> HRESULT, + get_mix_format: unsafe extern "system" fn( + this: *mut c_void, + ppdeviceformat: *mut *mut WAVEFORMATEX, + ) -> HRESULT, + __GetDevicePeriod: usize, + start: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, + stop: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, + __Reset: usize, + set_event_handle: unsafe extern "system" fn(this: *mut c_void, eventhandle: HANDLE) -> HRESULT, + get_service: unsafe extern "system" fn( + this: *mut c_void, + riid: *const GUID, + ppv: *mut *mut c_void, + ) -> HRESULT, +} + +#[derive(Clone)] +#[repr(transparent)] +pub struct IAudioClient(Object); + +impl AudioInterface for IAudioClient { + const IID: GUID = GUID::from_u128(0x1cb9ad4c_dbfa_4c32_b178_c2f568a703b2); +} + +impl IAudioClient { + #[inline] + pub unsafe fn Initialize( + &self, + share_mode: AUDCLNT_SHAREMODE, + stream_flags: u32, + buffer_duration: i64, + periodicity: i64, + format: *const WAVEFORMATEX, + session: Option<*const GUID>, + ) -> ComResult<()> { + let res = unsafe { + (self.0.vtbl().initialize)( + self.0 .0.cast(), + share_mode, + stream_flags, + buffer_duration, + periodicity, + format, + session, + ) + }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn GetService(&self) -> ComResult { + let mut out = std::mem::MaybeUninit::::uninit(); + let res = unsafe { + (self.0.vtbl().get_service)(self.0 .0.cast(), &A::IID, out.as_mut_ptr().cast()) + }; + + if res >= 0 { + Ok(unsafe { out.assume_init() }) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn GetBufferSize(&self) -> ComResult { + let mut out = 0; + let res = unsafe { (self.0.vtbl().get_buffer_size)(self.0 .0.cast(), &mut out) }; + + if res >= 0 { + Ok(out) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn GetCurrentPadding(&self) -> ComResult { + let mut out = 0; + let res = unsafe { (self.0.vtbl().get_current_padding)(self.0 .0.cast(), &mut out) }; + + if res >= 0 { + Ok(out) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn SetEventHandle(&self, handle: HANDLE) -> ComResult<()> { + let res = unsafe { (self.0.vtbl().set_event_handle)(self.0 .0.cast(), handle) }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn GetMixFormat(&self) -> ComResult<*mut WAVEFORMATEX> { + let mut out = std::mem::MaybeUninit::uninit(); + let res = unsafe { (self.0.vtbl().get_mix_format)(self.0 .0.cast(), out.as_mut_ptr()) }; + + if res >= 0 { + Ok(unsafe { out.assume_init() }) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn IsFormatSupported( + &self, + share_mode: AUDCLNT_SHAREMODE, + format: *const WAVEFORMATEX, + closest_match: Option<*mut *mut WAVEFORMATEX>, + ) -> HRESULT { + unsafe { + (self.0.vtbl().is_format_supported)(self.0 .0.cast(), share_mode, format, closest_match) + } + } + + #[inline] + pub unsafe fn Start(&self) -> ComResult<()> { + let res = unsafe { (self.0.vtbl().start)(self.0 .0.cast()) }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn Stop(&self) -> ComResult<()> { + let res = unsafe { (self.0.vtbl().stop)(self.0 .0.cast()) }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn cast(&self) -> ComResult { + super::query_interface(self.0.ptr().cast(), &T::IID) + } +} + +#[repr(C)] +struct IAudioClient2V { + base: IAudioClientV, + __IsOffloadCapable: usize, + __SetClientProperties: usize, + get_buffer_size_limits: unsafe extern "system" fn( + this: *mut c_void, + pformat: *const WAVEFORMATEX, + beventdriven: BOOL, + phnsminbufferduration: *mut i64, + phnsmaxbufferduration: *mut i64, + ) -> HRESULT, +} + +#[repr(transparent)] +pub struct IAudioClient2(Object); + +impl AudioInterface for IAudioClient2 { + const IID: GUID = GUID::from_u128(0x726778cd_f60a_4eda_82de_e47610cd78aa); +} + +impl IAudioClient2 { + #[inline] + pub unsafe fn GetBufferSizeLimits( + &self, + format: *const WAVEFORMATEX, + event_driven: bool, + min_buffer_dur: *mut i64, + max_buffer_dur: *mut i64, + ) -> ComResult<()> { + let res = unsafe { + (self.0.vtbl().get_buffer_size_limits)( + self.0 .0.cast(), + format, + event_driven as _, + min_buffer_dur, + max_buffer_dur, + ) + }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } +} + +#[repr(C)] +pub struct IAudioClockV { + base: IUnknownV, + __GetFrequency: usize, + get_position: unsafe extern "system" fn( + this: *mut c_void, + pu64position: *mut u64, + pu64qpcposition: Option<*mut u64>, + ) -> HRESULT, + __GetCharacteristics: usize, +} + +#[repr(transparent)] +pub struct IAudioClock(Object); + +impl AudioInterface for IAudioClock { + const IID: GUID = GUID::from_u128(0xcd63314f_3fba_4a1b_812c_ef96358728e7); +} + +impl IAudioClock { + #[inline] + pub unsafe fn GetPosition( + &self, + position: *mut u64, + qpc_position: Option<*mut u64>, + ) -> ComResult<()> { + let res = unsafe { (self.0.vtbl().get_position)(self.0 .0.cast(), position, qpc_position) }; + + if res >= 0 { + Ok(()) + } else { + Err(res) + } + } +} + +#[repr(C)] +struct IMMDeviceEnumeratorV { + base: IUnknownV, + enum_audio_endpoints: unsafe extern "system" fn( + this: *mut c_void, + dataflow: EDataFlow, + dwstatemask: u32, + ppdevices: *mut *mut c_void, + ) -> HRESULT, + get_default_audio_endpoint: unsafe extern "system" fn( + this: *mut c_void, + dataflow: EDataFlow, + role: ERole, + ppendpoint: *mut *mut c_void, + ) -> HRESULT, + __GetDevice: usize, + __RegisterEndpointNotificationCallback: usize, + __UnregisterEndpointNotificationCallback: usize, +} + +const IID_IMM_DEVICE_ENUMERATOR: GUID = GUID::from_u128(0xa95664d2_9614_4f35_a746_de8db63617e6); + +#[repr(transparent)] +pub struct IMMDeviceEnumerator(Object); + +impl IMMDeviceEnumerator { + #[inline] + pub unsafe fn new() -> ComResult { + let mut iptr = std::mem::MaybeUninit::::uninit(); + let res = Com::CoCreateInstance( + &windows_sys::Win32::Media::Audio::MMDeviceEnumerator, + std::ptr::null_mut(), + Com::CLSCTX_ALL, + &IID_IMM_DEVICE_ENUMERATOR, + iptr.as_mut_ptr().cast(), + ); + + if res >= 0 { + Ok(iptr.assume_init()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn EnumAudioEndpoints( + &self, + data_flow: EDataFlow, + mask: u32, + ) -> ComResult { + let mut out = std::mem::MaybeUninit::::uninit(); + let res = unsafe { + (self.0.vtbl().enum_audio_endpoints)( + self.0 .0.cast(), + data_flow, + mask, + out.as_mut_ptr().cast(), + ) + }; + + if res >= 0 { + Ok(unsafe { out.assume_init() }) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn GetDefaultAudioEndpoint( + &self, + data_flow: EDataFlow, + role: ERole, + ) -> ComResult { + let mut out = std::mem::MaybeUninit::::uninit(); + let res = unsafe { + (self.0.vtbl().get_default_audio_endpoint)( + self.0 .0.cast(), + data_flow, + role, + out.as_mut_ptr().cast(), + ) + }; + + if res >= 0 { + Ok(unsafe { out.assume_init() }) + } else { + Err(res) + } + } +} + +#[repr(C)] +pub struct PROPERTYKEY { + pub fmtid: GUID, + pub pid: u32, +} + +#[repr(C)] +struct IPropertyStoreV { + base: IUnknownV, + __GetCount: usize, + __GetAt: usize, + get_value: unsafe extern "system" fn( + this: *mut c_void, + key: *const PROPERTYKEY, + pv: *mut Com::StructuredStorage::PROPVARIANT, + ) -> HRESULT, + __SetValue: usize, + __Commit: usize, +} + +#[derive(Debug)] +#[repr(transparent)] +pub struct IPropertyStore(Object); + +impl IPropertyStore { + #[inline] + pub unsafe fn GetValue( + &self, + key: *const PROPERTYKEY, + ) -> ComResult { + let mut out = std::mem::MaybeUninit::uninit(); + let res = unsafe { (self.0.vtbl().get_value)(self.0 .0.cast(), key, out.as_mut_ptr()) }; + + if res >= 0 { + Ok(out.assume_init()) + } else { + Err(res) + } + } +} + +#[repr(C)] +struct IMMDeviceV { + base: IUnknownV, + activate: unsafe extern "system" fn( + this: *mut c_void, + iid: *const GUID, + dwclsctx: Com::CLSCTX, + pactivationparams: Option<*const Com::StructuredStorage::PROPVARIANT>, + ppinterface: *mut *mut c_void, + ) -> HRESULT, + open_property_store: unsafe extern "system" fn( + this: *mut c_void, + stgmaccess: Com::STGM, + ppproperties: *mut *mut c_void, + ) -> HRESULT, + get_id: unsafe extern "system" fn( + this: *mut c_void, + ppstrid: *mut windows_sys::core::PWSTR, + ) -> HRESULT, + __GetState: usize, +} + +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct IMMDevice(Object); + +impl IMMDevice { + #[inline] + pub unsafe fn Activate( + &self, + cls_ctx: Com::CLSCTX, + params: Option<*const Com::StructuredStorage::PROPVARIANT>, + ) -> ComResult { + let mut out = std::mem::MaybeUninit::::uninit(); + let res = unsafe { + (self.0.vtbl().activate)( + self.0 .0.cast(), + &A::IID, + cls_ctx, + params, + out.as_mut_ptr().cast(), + ) + }; + + if res >= 0 { + Ok(out.assume_init()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn GetId(&self) -> ComResult { + let mut out = std::mem::MaybeUninit::uninit(); + let res = unsafe { (self.0.vtbl().get_id)(self.0 .0.cast(), out.as_mut_ptr()) }; + + if res >= 0 { + Ok(out.assume_init()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn OpenPropertyStore(&self, access: Com::STGM) -> ComResult { + let mut out = std::mem::MaybeUninit::::uninit(); + let res = unsafe { + (self.0.vtbl().open_property_store)(self.0 .0.cast(), access, out.as_mut_ptr().cast()) + }; + + if res >= 0 { + Ok(out.assume_init()) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn cast(&self) -> ComResult { + super::query_interface(self.0.ptr().cast(), &T::IID) + } +} + +#[repr(C)] +struct IMMDeviceCollectionV { + base: IUnknownV, + get_count: unsafe extern "system" fn(this: *mut c_void, pcdevices: *mut u32) -> HRESULT, + item: unsafe extern "system" fn( + this: *mut c_void, + ndevice: u32, + ppdevice: *mut *mut c_void, + ) -> HRESULT, +} + +#[repr(transparent)] +pub struct IMMDeviceCollection(Object); + +impl IMMDeviceCollection { + #[inline] + pub unsafe fn GetCount(&self) -> ComResult { + let mut count = 0; + let res = unsafe { (self.0.vtbl().get_count)(self.0 .0.cast(), &mut count) }; + + if res >= 0 { + Ok(count) + } else { + Err(res) + } + } + + #[inline] + pub unsafe fn Item(&self, i: u32) -> ComResult { + let mut out = std::mem::MaybeUninit::::uninit(); + let res = unsafe { (self.0.vtbl().item)(self.0 .0.cast(), i, out.as_mut_ptr().cast()) }; + + if res >= 0 { + Ok(out.assume_init()) + } else { + Err(res) + } + } +} + +#[repr(C)] +struct IMMEndpointV { + base: IUnknownV, + get_data_flow: + unsafe extern "system" fn(this: *mut c_void, pdataflow: *mut EDataFlow) -> HRESULT, +} + +#[repr(transparent)] +pub struct IMMEndpoint(Object); + +impl AudioInterface for IMMEndpoint { + const IID: GUID = GUID::from_u128(0x1be09788_6894_4089_8586_9a2a6c265ac5); +} + +impl IMMEndpoint { + #[inline] + pub unsafe fn GetDataFlow(&self) -> ComResult { + let mut out = -1; + let res = unsafe { (self.0.vtbl().get_data_flow)(self.0 .0.cast(), &mut out) }; + + if res >= 0 { + Ok(out) + } else { + Err(res) + } + } +} diff --git a/src/host/wasapi/com/bindings.rs b/src/host/wasapi/com/bindings.rs new file mode 100644 index 000000000..0536ed263 --- /dev/null +++ b/src/host/wasapi/com/bindings.rs @@ -0,0 +1,1121 @@ +//! Bindings generated by `minwin` 0.1.0 +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + clippy::upper_case_acronyms +)] +::windows_targets::link!( + "kernel32.dll" "system" fn CloseHandle(hObject : HANDLE) -> BOOL +); +::windows_targets::link!( + "kernel32.dll" "system" fn CreateEventA(lpEventAttributes : * const + SECURITY_ATTRIBUTES, bManualReset : BOOL, bInitialState : BOOL, lpName : + ::windows_core::PCSTR) -> HANDLE +); +::windows_targets::link!("kernel32.dll" "system" fn SetEvent(hEvent : HANDLE) -> BOOL); +::windows_targets::link!( + "kernel32.dll" "system" fn WaitForMultipleObjectsEx(nCount : u32, lpHandles : * const + HANDLE, bWaitAll : BOOL, dwMilliseconds : u32, bAlertable : BOOL) -> WIN32_ERROR +); +::windows_targets::link!( + "ole32.dll" "system" fn CoCreateInstance(rclsid : * const ::windows_core::GUID, + pUnkOuter : * mut ::core::ffi::c_void, dwClsContext : CLSCTX, riid : * const + ::windows_core::GUID, ppv : * mut * mut ::core::ffi::c_void) -> + ::windows_core::HRESULT +); +::windows_targets::link!( + "ole32.dll" "system" fn CoInitializeEx(pvReserved : * const ::core::ffi::c_void, + dwCoInit : COINIT) -> ::windows_core::HRESULT +); +::windows_targets::link!( + "ole32.dll" "system" fn CoTaskMemFree(pv : * const ::core::ffi::c_void) -> () +); +::windows_targets::link!("ole32.dll" "system" fn CoUninitialize() -> ()); +::windows_targets::link!( + "ole32.dll" "system" fn PropVariantClear(pvar : * mut PROPVARIANT) -> + ::windows_core::HRESULT +); +pub const AUDCLNT_STREAMFLAGS_EVENTCALLBACK: u32 = 262144; +pub const AUDCLNT_STREAMFLAGS_LOOPBACK: u32 = 131072; +pub const DEVICE_STATE_ACTIVE: u32 = 1; +pub const DEVPKEY_Device_FriendlyName: DEVPROPKEY = DEVPROPKEY { + fmtid: ::windows_core::GUID::from_u128(0xa45c254e_df1c_4efd_8020_67d146a850e0), + pid: 14, +}; +pub const INFINITE: u32 = 4294967295; +pub const KSAUDIO_SPEAKER_DIRECTOUT: u32 = 0; +pub const MAXIMUM_WAIT_OBJECTS: u32 = 64; +pub const WAVE_FORMAT_EXTENSIBLE: u32 = 65534; +pub const WAVE_FORMAT_IEEE_FLOAT: u32 = 3; +pub const WAVE_FORMAT_PCM: u32 = 1; +pub const RPC_E_CHANGED_MODE: ::windows_core::HRESULT = ::windows_core::HRESULT( + -2147417850, +); +pub const AUDCLNT_E_DEVICE_INVALIDATED: ::windows_core::HRESULT = ::windows_core::HRESULT( + -2004287484, +); +pub const S_FALSE: ::windows_core::HRESULT = ::windows_core::HRESULT(1); +pub const AUDCLNT_S_BUFFER_EMPTY: ::windows_core::HRESULT = ::windows_core::HRESULT( + 143196161, +); +pub type ADVANCED_FEATURE_FLAGS = u16; +pub type AUDCLNT_SHAREMODE = i32; +pub const AUDCLNT_SHAREMODE_SHARED: AUDCLNT_SHAREMODE = 0; +#[repr(C)] +pub struct BLOB { + pub cbSize: u32, + pub pBlobData: *mut u8, +} +pub type BOOL = i32; +#[repr(C)] +pub struct BSTRBLOB { + pub cbSize: u32, + pub pData: *mut u8, +} +#[repr(C, packed(8))] +pub struct CABOOL { + pub cElems: u32, + pub pElems: *mut VARIANT_BOOL, +} +#[repr(C, packed(8))] +pub struct CABSTR { + pub cElems: u32, + pub pElems: *mut ::windows_core::BSTR, +} +#[repr(C, packed(8))] +pub struct CABSTRBLOB { + pub cElems: u32, + pub pElems: *mut BSTRBLOB, +} +#[repr(C, packed(8))] +pub struct CAC { + pub cElems: u32, + pub pElems: ::windows_core::PSTR, +} +#[repr(C, packed(8))] +pub struct CACLIPDATA { + pub cElems: u32, + pub pElems: *mut CLIPDATA, +} +#[repr(C, packed(8))] +pub struct CACLSID { + pub cElems: u32, + pub pElems: *mut ::windows_core::GUID, +} +#[repr(C, packed(8))] +pub struct CACY { + pub cElems: u32, + pub pElems: *mut CY, +} +#[repr(C, packed(8))] +pub struct CADATE { + pub cElems: u32, + pub pElems: *mut f64, +} +#[repr(C, packed(8))] +pub struct CADBL { + pub cElems: u32, + pub pElems: *mut f64, +} +#[repr(C, packed(8))] +pub struct CAFILETIME { + pub cElems: u32, + pub pElems: *mut FILETIME, +} +#[repr(C, packed(8))] +pub struct CAFLT { + pub cElems: u32, + pub pElems: *mut f32, +} +#[repr(C, packed(8))] +pub struct CAH { + pub cElems: u32, + pub pElems: *mut i64, +} +#[repr(C, packed(8))] +pub struct CAI { + pub cElems: u32, + pub pElems: *mut i16, +} +#[repr(C, packed(8))] +pub struct CAL { + pub cElems: u32, + pub pElems: *mut i32, +} +#[repr(C, packed(8))] +pub struct CALPSTR { + pub cElems: u32, + pub pElems: *mut ::windows_core::PSTR, +} +#[repr(C, packed(8))] +pub struct CALPWSTR { + pub cElems: u32, + pub pElems: *mut ::windows_core::PWSTR, +} +#[repr(C, packed(8))] +pub struct CAPROPVARIANT { + pub cElems: u32, + pub pElems: *mut PROPVARIANT, +} +#[repr(C, packed(8))] +pub struct CASCODE { + pub cElems: u32, + pub pElems: *mut i32, +} +#[repr(C, packed(8))] +pub struct CAUB { + pub cElems: u32, + pub pElems: *mut u8, +} +#[repr(C, packed(8))] +pub struct CAUH { + pub cElems: u32, + pub pElems: *mut u64, +} +#[repr(C, packed(8))] +pub struct CAUI { + pub cElems: u32, + pub pElems: *mut u16, +} +#[repr(C, packed(8))] +pub struct CAUL { + pub cElems: u32, + pub pElems: *mut u32, +} +#[repr(C)] +pub struct CLIPDATA { + pub cbSize: u32, + pub ulClipFmt: i32, + pub pClipData: *mut u8, +} +pub type CLSCTX = u32; +pub const CLSCTX_ALL: CLSCTX = 23; +pub type COINIT = i32; +pub const COINIT_APARTMENTTHREADED: COINIT = 2; +#[repr(C)] +pub union CY { + pub Anonymous: ::std::mem::ManuallyDrop, + pub int64: i64, +} +#[repr(C)] +pub struct CY_0 { + pub Lo: u32, + pub Hi: i32, +} +#[repr(C)] +pub struct DECIMAL { + pub wReserved: u16, + pub Anonymous1: DECIMAL_0, + pub Hi32: u32, + pub Anonymous2: DECIMAL_1, +} +#[repr(C)] +pub union DECIMAL_0 { + pub Anonymous: ::std::mem::ManuallyDrop, + pub signscale: u16, +} +#[repr(C)] +pub struct DECIMAL_0_0 { + pub scale: u8, + pub sign: u8, +} +#[repr(C)] +pub union DECIMAL_1 { + pub Anonymous: ::std::mem::ManuallyDrop, + pub Lo64: u64, +} +#[repr(C)] +pub struct DECIMAL_1_0 { + pub Lo32: u32, + pub Mid32: u32, +} +#[repr(C)] +pub struct DEVPROPKEY { + pub fmtid: ::windows_core::GUID, + pub pid: u32, +} +pub type EDataFlow = i32; +pub const eRender: EDataFlow = 0; +pub const eCapture: EDataFlow = 1; +pub const eAll: EDataFlow = 2; +pub type ERole = i32; +pub const eConsole: ERole = 0; +#[repr(C)] +pub struct FILETIME { + pub dwLowDateTime: u32, + pub dwHighDateTime: u32, +} +pub type HANDLE = isize; +#[repr(C)] +pub struct IAudioCaptureClient_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + pub GetBuffer: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + ppData: *mut *mut u8, + pNumFramesToRead: *mut u32, + pdwFlags: *mut u32, + pu64DevicePosition: *mut u64, + pu64QPCPosition: *mut u64, + ) -> ::windows_core::HRESULT, + pub ReleaseBuffer: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + NumFramesRead: u32, + ) -> ::windows_core::HRESULT, + pub GetNextPacketSize: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + pNumFramesInNextPacket: *mut u32, + ) -> ::windows_core::HRESULT, +} +#[repr(transparent)] +pub struct IAudioCaptureClient(::windows_core::IUnknown); +impl IAudioCaptureClient { + pub unsafe fn GetBuffer( + &self, + ppData: *mut *mut u8, + pNumFramesToRead: *mut u32, + pdwFlags: *mut u32, + pu64DevicePosition: ::core::option::Option<::core::ptr::NonNull>, + pu64QPCPosition: ::core::option::Option<::core::ptr::NonNull>, + ) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .GetBuffer)( + ::windows_core::Interface::as_raw(self), + ppData, + pNumFramesToRead, + pdwFlags, + pu64DevicePosition + .map_or(::std::ptr::null_mut(), std::ptr::NonNull::as_ptr), + pu64QPCPosition.map_or(::std::ptr::null_mut(), std::ptr::NonNull::as_ptr), + ) + .ok() + } + pub unsafe fn ReleaseBuffer( + &self, + NumFramesRead: u32, + ) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .ReleaseBuffer)(::windows_core::Interface::as_raw(self), NumFramesRead) + .ok() + } + pub unsafe fn GetNextPacketSize(&self) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetNextPacketSize)( + ::windows_core::Interface::as_raw(self), + result__.as_mut_ptr().cast(), + ), + result__, + ) + } +} +impl ::core::clone::Clone for IAudioCaptureClient { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IAudioCaptureClient { + type Vtable = IAudioCaptureClient_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IAudioCaptureClient { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0xc8adbd64_e71e_48a0_a4de_185c395cd317, + ); +} +#[repr(C)] +pub struct IAudioClient2_Vtbl { + pub base__: IAudioClient_Vtbl, + IsOffloadCapable: usize, + SetClientProperties: usize, + pub GetBufferSizeLimits: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + pFormat: *const WAVEFORMATEX, + bEventDriven: BOOL, + phnsMinBufferDuration: *mut i64, + phnsMaxBufferDuration: *mut i64, + ) -> ::windows_core::HRESULT, +} +#[repr(transparent)] +pub struct IAudioClient2(::windows_core::IUnknown); +impl IAudioClient2 { + pub unsafe fn GetBufferSizeLimits( + &self, + pFormat: *const WAVEFORMATEX, + bEventDriven: BOOL, + phnsMinBufferDuration: *mut i64, + phnsMaxBufferDuration: *mut i64, + ) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .GetBufferSizeLimits)( + ::windows_core::Interface::as_raw(self), + pFormat, + bEventDriven, + phnsMinBufferDuration, + phnsMaxBufferDuration, + ) + .ok() + } +} +impl ::core::clone::Clone for IAudioClient2 { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IAudioClient2 { + type Vtable = IAudioClient2_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IAudioClient2 { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0x726778cd_f60a_4eda_82de_e47610cd78aa, + ); +} +#[repr(C)] +pub struct IAudioClient_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + pub Initialize: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + ShareMode: AUDCLNT_SHAREMODE, + StreamFlags: u32, + hnsBufferDuration: i64, + hnsPeriodicity: i64, + pFormat: *const WAVEFORMATEX, + AudioSessionGuid: *const ::windows_core::GUID, + ) -> ::windows_core::HRESULT, + pub GetBufferSize: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + pNumBufferFrames: *mut u32, + ) -> ::windows_core::HRESULT, + GetStreamLatency: usize, + pub GetCurrentPadding: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + pNumPaddingFrames: *mut u32, + ) -> ::windows_core::HRESULT, + pub IsFormatSupported: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + ShareMode: AUDCLNT_SHAREMODE, + pFormat: *const WAVEFORMATEX, + ppClosestMatch: *mut *mut WAVEFORMATEX, + ) -> ::windows_core::HRESULT, + pub GetMixFormat: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + ppDeviceFormat: *mut *mut WAVEFORMATEX, + ) -> ::windows_core::HRESULT, + GetDevicePeriod: usize, + pub Start: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, + pub Stop: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, + Reset: usize, + pub SetEventHandle: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + eventHandle: HANDLE, + ) -> ::windows_core::HRESULT, + pub GetService: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + riid: *const ::windows_core::GUID, + ppv: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, +} +#[repr(transparent)] +pub struct IAudioClient(::windows_core::IUnknown); +impl IAudioClient { + pub unsafe fn Initialize( + &self, + ShareMode: AUDCLNT_SHAREMODE, + StreamFlags: u32, + hnsBufferDuration: i64, + hnsPeriodicity: i64, + pFormat: *const WAVEFORMATEX, + AudioSessionGuid: ::core::option::Option< + ::core::ptr::NonNull<::windows_core::GUID>, + >, + ) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .Initialize)( + ::windows_core::Interface::as_raw(self), + ShareMode, + StreamFlags, + hnsBufferDuration, + hnsPeriodicity, + pFormat, + AudioSessionGuid.map_or(::std::ptr::null(), |p| p.as_ptr() as *const _), + ) + .ok() + } + pub unsafe fn GetBufferSize(&self) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetBufferSize)( + ::windows_core::Interface::as_raw(self), + result__.as_mut_ptr().cast(), + ), + result__, + ) + } + pub unsafe fn GetCurrentPadding(&self) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetCurrentPadding)( + ::windows_core::Interface::as_raw(self), + result__.as_mut_ptr().cast(), + ), + result__, + ) + } + pub unsafe fn IsFormatSupported( + &self, + ShareMode: AUDCLNT_SHAREMODE, + pFormat: *const WAVEFORMATEX, + ppClosestMatch: ::core::option::Option<::core::ptr::NonNull<*mut WAVEFORMATEX>>, + ) -> ::windows_core::HRESULT { + (::windows_core::Interface::vtable(self) + .IsFormatSupported)( + ::windows_core::Interface::as_raw(self), + ShareMode, + pFormat, + ppClosestMatch.map_or(::std::ptr::null_mut(), std::ptr::NonNull::as_ptr), + ) + } + pub unsafe fn GetMixFormat(&self) -> ::windows_core::Result<*mut WAVEFORMATEX> { + let mut result__ = ::std::mem::MaybeUninit::<*mut WAVEFORMATEX>::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetMixFormat)( + ::windows_core::Interface::as_raw(self), + result__.as_mut_ptr().cast(), + ), + result__, + ) + } + pub unsafe fn Start(&self) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .Start)(::windows_core::Interface::as_raw(self)) + .ok() + } + pub unsafe fn Stop(&self) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .Stop)(::windows_core::Interface::as_raw(self)) + .ok() + } + pub unsafe fn SetEventHandle( + &self, + eventHandle: HANDLE, + ) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .SetEventHandle)(::windows_core::Interface::as_raw(self), eventHandle) + .ok() + } + pub unsafe fn GetService(&self) -> ::windows_core::Result + where + T: ::windows_core::ComInterface, + { + let mut result__ = ::std::mem::MaybeUninit::uninit(); + wrap_interface_result( + (::windows_core::Interface::vtable(self) + .GetService)( + ::windows_core::Interface::as_raw(self), + &::IID, + result__.as_mut_ptr(), + ), + result__, + ) + } +} +impl ::core::clone::Clone for IAudioClient { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IAudioClient { + type Vtable = IAudioClient_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IAudioClient { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0x1cb9ad4c_dbfa_4c32_b178_c2f568a703b2, + ); +} +#[repr(C)] +pub struct IAudioClock_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + GetFrequency: usize, + pub GetPosition: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + pu64Position: *mut u64, + pu64QPCPosition: *mut u64, + ) -> ::windows_core::HRESULT, + GetCharacteristics: usize, +} +#[repr(transparent)] +pub struct IAudioClock(::windows_core::IUnknown); +impl IAudioClock { + pub unsafe fn GetPosition( + &self, + pu64Position: *mut u64, + pu64QPCPosition: ::core::option::Option<::core::ptr::NonNull>, + ) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .GetPosition)( + ::windows_core::Interface::as_raw(self), + pu64Position, + pu64QPCPosition.map_or(::std::ptr::null_mut(), std::ptr::NonNull::as_ptr), + ) + .ok() + } +} +impl ::core::clone::Clone for IAudioClock { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IAudioClock { + type Vtable = IAudioClock_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IAudioClock { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0xcd63314f_3fba_4a1b_812c_ef96358728e7, + ); +} +#[repr(C)] +pub struct IAudioRenderClient_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + pub GetBuffer: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + NumFramesRequested: u32, + ppData: *mut *mut u8, + ) -> ::windows_core::HRESULT, + pub ReleaseBuffer: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + NumFramesWritten: u32, + dwFlags: u32, + ) -> ::windows_core::HRESULT, +} +#[repr(transparent)] +pub struct IAudioRenderClient(::windows_core::IUnknown); +impl IAudioRenderClient { + pub unsafe fn GetBuffer( + &self, + NumFramesRequested: u32, + ) -> ::windows_core::Result<*mut u8> { + let mut result__ = ::std::mem::MaybeUninit::<*mut u8>::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetBuffer)( + ::windows_core::Interface::as_raw(self), + NumFramesRequested, + result__.as_mut_ptr().cast(), + ), + result__, + ) + } + pub unsafe fn ReleaseBuffer( + &self, + NumFramesWritten: u32, + dwFlags: u32, + ) -> ::windows_core::Result<()> { + (::windows_core::Interface::vtable(self) + .ReleaseBuffer)( + ::windows_core::Interface::as_raw(self), + NumFramesWritten, + dwFlags, + ) + .ok() + } +} +impl ::core::clone::Clone for IAudioRenderClient { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IAudioRenderClient { + type Vtable = IAudioRenderClient_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IAudioRenderClient { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0xf294acfc_3146_4483_a7bf_addca7c260e2, + ); +} +#[repr(transparent)] +pub struct IDispatch(::windows_core::IUnknown); +#[repr(C)] +pub struct IMMDeviceCollection_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + pub GetCount: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + pcDevices: *mut u32, + ) -> ::windows_core::HRESULT, + pub Item: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + nDevice: u32, + ppDevice: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, +} +#[repr(transparent)] +pub struct IMMDeviceCollection(::windows_core::IUnknown); +impl IMMDeviceCollection { + pub unsafe fn GetCount(&self) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetCount)( + ::windows_core::Interface::as_raw(self), + result__.as_mut_ptr().cast(), + ), + result__, + ) + } + pub unsafe fn Item(&self, nDevice: u32) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .Item)( + ::windows_core::Interface::as_raw(self), + nDevice, + result__.as_mut_ptr().cast(), + ), + result__, + ) + } +} +impl ::core::clone::Clone for IMMDeviceCollection { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IMMDeviceCollection { + type Vtable = IMMDeviceCollection_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IMMDeviceCollection { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0x0bd7a1be_7a1a_44db_8397_cc5392387b5e, + ); +} +#[repr(C)] +pub struct IMMDeviceEnumerator_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + pub EnumAudioEndpoints: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + dataFlow: EDataFlow, + dwStateMask: u32, + ppDevices: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, + pub GetDefaultAudioEndpoint: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + dataFlow: EDataFlow, + role: ERole, + ppEndpoint: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, + GetDevice: usize, + RegisterEndpointNotificationCallback: usize, + UnregisterEndpointNotificationCallback: usize, +} +#[repr(transparent)] +pub struct IMMDeviceEnumerator(::windows_core::IUnknown); +impl IMMDeviceEnumerator { + pub unsafe fn EnumAudioEndpoints( + &self, + dataFlow: EDataFlow, + dwStateMask: u32, + ) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .EnumAudioEndpoints)( + ::windows_core::Interface::as_raw(self), + dataFlow, + dwStateMask, + result__.as_mut_ptr().cast(), + ), + result__, + ) + } + pub unsafe fn GetDefaultAudioEndpoint( + &self, + dataFlow: EDataFlow, + role: ERole, + ) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetDefaultAudioEndpoint)( + ::windows_core::Interface::as_raw(self), + dataFlow, + role, + result__.as_mut_ptr().cast(), + ), + result__, + ) + } +} +impl ::core::clone::Clone for IMMDeviceEnumerator { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IMMDeviceEnumerator { + type Vtable = IMMDeviceEnumerator_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IMMDeviceEnumerator { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0xa95664d2_9614_4f35_a746_de8db63617e6, + ); +} +#[repr(C)] +pub struct IMMDevice_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + pub Activate: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + iid: *const ::windows_core::GUID, + dwClsCtx: CLSCTX, + pActivationParams: *const PROPVARIANT, + ppInterface: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, + pub OpenPropertyStore: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + stgmAccess: STGM, + ppProperties: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT, + pub GetId: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + ppstrId: *mut ::windows_core::PWSTR, + ) -> ::windows_core::HRESULT, + GetState: usize, +} +#[repr(transparent)] +pub struct IMMDevice(::windows_core::IUnknown); +impl IMMDevice { + pub unsafe fn Activate( + &self, + dwClsCtx: CLSCTX, + pActivationParams: ::core::option::Option<::core::ptr::NonNull>, + ) -> ::windows_core::Result + where + T: ::windows_core::ComInterface, + { + let mut result__ = ::std::mem::MaybeUninit::uninit(); + wrap_interface_result( + (::windows_core::Interface::vtable(self) + .Activate)( + ::windows_core::Interface::as_raw(self), + &::IID, + dwClsCtx, + pActivationParams.map_or(::std::ptr::null(), |p| p.as_ptr() as *const _), + result__.as_mut_ptr(), + ), + result__, + ) + } + pub unsafe fn OpenPropertyStore( + &self, + stgmAccess: STGM, + ) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .OpenPropertyStore)( + ::windows_core::Interface::as_raw(self), + stgmAccess, + result__.as_mut_ptr().cast(), + ), + result__, + ) + } + pub unsafe fn GetId(&self) -> ::windows_core::Result<::windows_core::PWSTR> { + let mut result__ = ::std::mem::MaybeUninit::<::windows_core::PWSTR>::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetId)( + ::windows_core::Interface::as_raw(self), + result__.as_mut_ptr().cast(), + ), + result__, + ) + } +} +impl ::core::clone::Clone for IMMDevice { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IMMDevice { + type Vtable = IMMDevice_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IMMDevice { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0xd666063f_1587_4e43_81f1_b948e807363f, + ); +} +#[repr(C)] +pub struct IMMEndpoint_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + pub GetDataFlow: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + pDataFlow: *mut EDataFlow, + ) -> ::windows_core::HRESULT, +} +#[repr(transparent)] +pub struct IMMEndpoint(::windows_core::IUnknown); +impl IMMEndpoint { + pub unsafe fn GetDataFlow(&self) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetDataFlow)( + ::windows_core::Interface::as_raw(self), + result__.as_mut_ptr().cast(), + ), + result__, + ) + } +} +impl ::core::clone::Clone for IMMEndpoint { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IMMEndpoint { + type Vtable = IMMEndpoint_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IMMEndpoint { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0x1be09788_6894_4089_8586_9a2a6c265ac5, + ); +} +#[repr(C)] +pub struct IPropertyStore_Vtbl { + pub base__: ::windows_core::IUnknown_Vtbl, + GetCount: usize, + GetAt: usize, + pub GetValue: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + key: *const PROPERTYKEY, + pv: *mut PROPVARIANT, + ) -> ::windows_core::HRESULT, + SetValue: usize, + Commit: usize, +} +#[repr(transparent)] +pub struct IPropertyStore(::windows_core::IUnknown); +impl IPropertyStore { + pub unsafe fn GetValue( + &self, + key: *const PROPERTYKEY, + ) -> ::windows_core::Result { + let mut result__ = ::std::mem::MaybeUninit::::uninit(); + wrap_value_result( + (::windows_core::Interface::vtable(self) + .GetValue)( + ::windows_core::Interface::as_raw(self), + key, + result__.as_mut_ptr().cast(), + ), + result__, + ) + } +} +impl ::core::clone::Clone for IPropertyStore { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +unsafe impl ::windows_core::Interface for IPropertyStore { + type Vtable = IPropertyStore_Vtbl; +} +unsafe impl ::windows_core::ComInterface for IPropertyStore { + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0x886d8eeb_8cf2_4446_8d02_cdba1dbdcf99, + ); +} +#[repr(transparent)] +pub struct IStorage(::windows_core::IUnknown); +#[repr(transparent)] +pub struct IStream(::windows_core::IUnknown); +pub const KSDATAFORMAT_SUBTYPE_IEEE_FLOAT: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0x00000003_0000_0010_8000_00aa00389b71, +); +pub const KSDATAFORMAT_SUBTYPE_PCM: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0x00000001_0000_0010_8000_00aa00389b71, +); +pub const MMDeviceEnumerator: ::windows_core::GUID = ::windows_core::GUID::from_u128( + 0xbcde0395_e52f_467c_8e3d_c4579291692e, +); +#[repr(C)] +pub struct PROPERTYKEY { + pub fmtid: ::windows_core::GUID, + pub pid: u32, +} +#[repr(C)] +pub struct PROPVARIANT { + pub Anonymous: PROPVARIANT_0, +} +#[repr(C)] +pub union PROPVARIANT_0 { + pub Anonymous: ::std::mem::ManuallyDrop, + pub decVal: ::std::mem::ManuallyDrop, +} +#[repr(C)] +pub struct PROPVARIANT_0_0 { + pub vt: VARENUM, + pub wReserved1: u16, + pub wReserved2: u16, + pub wReserved3: u16, + pub Anonymous: PROPVARIANT_0_0_0, +} +#[repr(C)] +pub union PROPVARIANT_0_0_0 { + pub cVal: u8, + pub bVal: u8, + pub iVal: i16, + pub uiVal: u16, + pub lVal: i32, + pub ulVal: u32, + pub intVal: i32, + pub uintVal: u32, + pub hVal: i64, + pub uhVal: u64, + pub fltVal: f32, + pub dblVal: f64, + pub boolVal: VARIANT_BOOL, + pub __OBSOLETE__VARIANT_BOOL: VARIANT_BOOL, + pub scode: i32, + pub cyVal: ::std::mem::ManuallyDrop, + pub date: f64, + pub filetime: ::std::mem::ManuallyDrop, + pub puuid: *mut ::windows_core::GUID, + pub pclipdata: *mut CLIPDATA, + pub bstrVal: ::std::mem::ManuallyDrop<::windows_core::BSTR>, + pub bstrblobVal: ::std::mem::ManuallyDrop, + pub blob: ::std::mem::ManuallyDrop, + pub pszVal: ::windows_core::PSTR, + pub pwszVal: ::windows_core::PWSTR, + pub punkVal: ::std::mem::ManuallyDrop<::windows_core::IUnknown>, + pub pdispVal: ::std::mem::ManuallyDrop, + pub pStream: ::std::mem::ManuallyDrop, + pub pStorage: ::std::mem::ManuallyDrop, + pub pVersionedStream: *mut VERSIONEDSTREAM, + pub parray: *mut SAFEARRAY, + pub cac: ::std::mem::ManuallyDrop, + pub caub: ::std::mem::ManuallyDrop, + pub cai: ::std::mem::ManuallyDrop, + pub caui: ::std::mem::ManuallyDrop, + pub cal: ::std::mem::ManuallyDrop, + pub caul: ::std::mem::ManuallyDrop, + pub cah: ::std::mem::ManuallyDrop, + pub cauh: ::std::mem::ManuallyDrop, + pub caflt: ::std::mem::ManuallyDrop, + pub cadbl: ::std::mem::ManuallyDrop, + pub cabool: ::std::mem::ManuallyDrop, + pub cascode: ::std::mem::ManuallyDrop, + pub cacy: ::std::mem::ManuallyDrop, + pub cadate: ::std::mem::ManuallyDrop, + pub cafiletime: ::std::mem::ManuallyDrop, + pub cauuid: ::std::mem::ManuallyDrop, + pub caclipdata: ::std::mem::ManuallyDrop, + pub cabstr: ::std::mem::ManuallyDrop, + pub cabstrblob: ::std::mem::ManuallyDrop, + pub calpstr: ::std::mem::ManuallyDrop, + pub calpwstr: ::std::mem::ManuallyDrop, + pub capropvar: ::std::mem::ManuallyDrop, + pub pcVal: ::windows_core::PSTR, + pub pbVal: *mut u8, + pub piVal: *mut i16, + pub puiVal: *mut u16, + pub plVal: *mut i32, + pub pulVal: *mut u32, + pub pintVal: *mut i32, + pub puintVal: *mut u32, + pub pfltVal: *mut f32, + pub pdblVal: *mut f64, + pub pboolVal: *mut VARIANT_BOOL, + pub pdecVal: *mut DECIMAL, + pub pscode: *mut i32, + pub pcyVal: *mut CY, + pub pdate: *mut f64, + pub pbstrVal: *mut ::windows_core::BSTR, + pub ppunkVal: *mut ::windows_core::IUnknown, + pub ppdispVal: *mut IDispatch, + pub pparray: *mut *mut SAFEARRAY, + pub pvarVal: *mut PROPVARIANT, +} +#[repr(C, packed(8))] +pub struct SAFEARRAY { + pub cDims: u16, + pub fFeatures: ADVANCED_FEATURE_FLAGS, + pub cbElements: u32, + pub cLocks: u32, + pub pvData: *mut ::core::ffi::c_void, + pub rgsabound: [SAFEARRAYBOUND; 1], +} +#[repr(C, packed(8))] +pub struct SAFEARRAYBOUND { + pub cElements: u32, + pub lLbound: i32, +} +#[repr(C)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut ::core::ffi::c_void, + pub bInheritHandle: BOOL, +} +pub type STGM = u32; +pub const STGM_READ: STGM = 0; +pub type VARENUM = u16; +pub const VT_LPWSTR: VARENUM = 31; +pub type VARIANT_BOOL = i16; +#[repr(C, packed(8))] +pub struct VERSIONEDSTREAM { + pub guidVersion: ::windows_core::GUID, + pub pStream: IStream, +} +#[repr(C, packed(1))] +pub struct WAVEFORMATEX { + pub wFormatTag: u16, + pub nChannels: u16, + pub nSamplesPerSec: u32, + pub nAvgBytesPerSec: u32, + pub nBlockAlign: u16, + pub wBitsPerSample: u16, + pub cbSize: u16, +} +#[repr(C, packed(1))] +pub struct WAVEFORMATEXTENSIBLE { + pub Format: WAVEFORMATEX, + pub Samples: WAVEFORMATEXTENSIBLE_0, + pub dwChannelMask: u32, + pub SubFormat: ::windows_core::GUID, +} +#[repr(C, packed(1))] +pub union WAVEFORMATEXTENSIBLE_0 { + pub wValidBitsPerSample: u16, + pub wSamplesPerBlock: u16, + pub wReserved: u16, +} +pub type WIN32_ERROR = u32; +pub const WAIT_OBJECT_0: WIN32_ERROR = 0; +pub const WAIT_FAILED: WIN32_ERROR = 4294967295; +#[inline] +unsafe fn wrap_interface_result( + hresult: ::windows_core::HRESULT, + res: ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>, +) -> ::windows_core::Result { + if hresult.is_ok() { + Ok(std::mem::transmute_copy(&res.assume_init())) + } else { + Err(::windows_core::Error::from(hresult)) + } +} +#[inline] +unsafe fn wrap_value_result( + hresult: ::windows_core::HRESULT, + res: ::std::mem::MaybeUninit, +) -> ::windows_core::Result { + if hresult.is_ok() { + Ok(res.assume_init()) + } else { + Err(::windows_core::Error::from(hresult)) + } +} diff --git a/src/host/wasapi/com/bindings.toml b/src/host/wasapi/com/bindings.toml new file mode 100644 index 000000000..3516af2f5 --- /dev/null +++ b/src/host/wasapi/com/bindings.toml @@ -0,0 +1,73 @@ +output = "bindings.rs" +binds = [ + # Events + "CloseHandle", + "CreateEventA", + "SetEvent", + "WaitForMultipleObjectsEx", + "INFINITE", + "MAXIMUM_WAIT_OBJECTS", + "WAIT_FAILED", + "WAIT_OBJECT_0", + + # COM + "CoCreateInstance", + "CoInitializeEx", + "CoTaskMemFree", + "CoUninitialize", + "CLSCTX_ALL", + "COINIT_APARTMENTTHREADED", + "RPC_E_CHANGED_MODE", + "S_FALSE", + + # Properties + "PropVariantClear", + "DEVPKEY_Device_FriendlyName", + "STGM_READ", + "VT_LPWSTR", + + # Wave format inspection + "KSAUDIO_SPEAKER_DIRECTOUT", + "KSDATAFORMAT_SUBTYPE_IEEE_FLOAT", + "KSDATAFORMAT_SUBTYPE_PCM", + "WAVE_FORMAT_EXTENSIBLE", + "WAVE_FORMAT_IEEE_FLOAT", + "WAVE_FORMAT_PCM", + "WAVEFORMATEXTENSIBLE", + + # General audio stuff + "AUDCLNT_E_DEVICE_INVALIDATED", + "AUDCLNT_S_BUFFER_EMPTY", + "AUDCLNT_SHAREMODE_SHARED", + "AUDCLNT_STREAMFLAGS_EVENTCALLBACK", + "AUDCLNT_STREAMFLAGS_LOOPBACK", + "DEVICE_STATE_ACTIVE", + # there is another enum variant with same name, hence the full qualification + "Windows.Win32.Media.Audio.eAll", + "eCapture", + "eConsole", + "eRender", + "MMDeviceEnumerator", +] + +[interfaces] +IAudioCaptureClient = ["GetNextPacketSize", "GetBuffer", "ReleaseBuffer"] +IAudioClient = [ + "Initialize", + "GetBufferSize", + "GetCurrentPadding", + "IsFormatSupported", + "GetMixFormat", + "Start", + "Stop", + "SetEventHandle", + "GetService", +] +IAudioClient2 = ["GetBufferSizeLimits"] +IAudioClock = ["GetPosition"] +IAudioRenderClient = ["GetBuffer", "ReleaseBuffer"] +IMMDevice = ["Activate", "OpenPropertyStore", "GetId"] +IMMDeviceCollection = ["GetCount", "Item"] +IMMDeviceEnumerator = ["EnumAudioEndpoints", "GetDefaultAudioEndpoint"] +IMMEndpoint = ["GetDataFlow"] +IPropertyStore = ["GetValue"] diff --git a/src/host/wasapi/com/threading.rs b/src/host/wasapi/com/threading.rs new file mode 100644 index 000000000..0791825fd --- /dev/null +++ b/src/host/wasapi/com/threading.rs @@ -0,0 +1,12 @@ +/// Simple wrapper around `CreateEventA` as every usage in this crate is the same +#[inline] +pub unsafe fn create_event() -> Result { + let handle = + super::bindings::CreateEventA(std::ptr::null(), 0, 0, ::windows_core::PCSTR::null()); + + if handle != 0 { + Ok(handle) + } else { + Err(std::io::Error::last_os_error()) + } +} diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index 2bf922441..8e9f971a8 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -11,22 +11,14 @@ use std::fmt; use std::mem; use std::ops::{Deref, DerefMut}; use std::os::windows::ffi::OsStringExt; -use std::ptr; -use std::slice; use std::sync::{Arc, Mutex, MutexGuard}; use std::time::Duration; use super::com; +use super::com::bindings as wb; +use super::com::threading::create_event; use super::{windows_err_to_cpal_err, windows_err_to_cpal_err_message}; -use windows::core::ComInterface; -use windows::core::GUID; -use windows::Win32::Devices::Properties; -use windows::Win32::Foundation; -use windows::Win32::Media::Audio::IAudioRenderClient; -use windows::Win32::Media::{Audio, KernelStreaming, Multimedia}; -use windows::Win32::System::Com; -use windows::Win32::System::Com::{StructuredStorage, STGM_READ, VT_LPWSTR}; -use windows::Win32::System::Threading; +use windows_core::{ComInterface, GUID}; use super::stream::{AudioClientFlow, Stream, StreamInner}; use crate::{traits::DeviceTrait, BuildStreamError, StreamError}; @@ -36,14 +28,14 @@ pub type SupportedOutputConfigs = std::vec::IntoIter /// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers. #[derive(Clone)] -struct IAudioClientWrapper(Audio::IAudioClient); +struct IAudioClientWrapper(wb::IAudioClient); unsafe impl Send for IAudioClientWrapper {} unsafe impl Sync for IAudioClientWrapper {} /// An opaque type that identifies an end point. #[derive(Clone)] pub struct Device { - device: Audio::IMMDevice, + device: wb::IMMDevice, /// We cache an uninitialized `IAudioClient` so that we can call functions from it without /// having to create/destroy audio clients all the time. future_audio_client: Arc>>, // TODO: add NonZero around the ptr @@ -120,36 +112,46 @@ impl DeviceTrait for Device { } struct Endpoint { - endpoint: Audio::IMMEndpoint, + endpoint: wb::IMMEndpoint, } enum WaveFormat { - Ex(Audio::WAVEFORMATEX), - Extensible(Audio::WAVEFORMATEXTENSIBLE), + Ex(wb::WAVEFORMATEX), + Extensible(wb::WAVEFORMATEXTENSIBLE), } // Use RAII to make sure CoTaskMemFree is called when we are responsible for freeing. -struct WaveFormatExPtr(*mut Audio::WAVEFORMATEX); +struct WaveFormatExPtr(*mut wb::WAVEFORMATEX); impl Drop for WaveFormatExPtr { fn drop(&mut self) { unsafe { - Com::CoTaskMemFree(Some(self.0 as *mut _)); + wb::CoTaskMemFree(self.0.cast()); } } } +// By default windows/windows-sys makes _all_ structs/unions Copy, the +// embedded bindings could have Copy implemented for specific structs, but it's +// really unnecessary for the limited number of cases in this file +#[inline] +unsafe fn memcpy(src: *const T) -> T { + let mut dst = std::mem::zeroed(); + std::ptr::copy_nonoverlapping(src, &mut dst, 1); + dst +} + impl WaveFormat { // Given a pointer to some format, returns a valid copy of the format. - pub fn copy_from_waveformatex_ptr(ptr: *const Audio::WAVEFORMATEX) -> Option { + pub fn copy_from_waveformatex_ptr(ptr: *const wb::WAVEFORMATEX) -> Option { unsafe { match (*ptr).wFormatTag as u32 { - Audio::WAVE_FORMAT_PCM | Multimedia::WAVE_FORMAT_IEEE_FLOAT => { - Some(WaveFormat::Ex(*ptr)) + wb::WAVE_FORMAT_PCM | wb::WAVE_FORMAT_IEEE_FLOAT => { + Some(WaveFormat::Ex(memcpy(ptr))) } - KernelStreaming::WAVE_FORMAT_EXTENSIBLE => { - let extensible_ptr = ptr as *const Audio::WAVEFORMATEXTENSIBLE; - Some(WaveFormat::Extensible(*extensible_ptr)) + wb::WAVE_FORMAT_EXTENSIBLE => { + let extensible_ptr = ptr as *const wb::WAVEFORMATEXTENSIBLE; + Some(WaveFormat::Extensible(memcpy(extensible_ptr))) } _ => None, } @@ -157,13 +159,13 @@ impl WaveFormat { } // Get the pointer to the WAVEFORMATEX struct. - pub fn as_ptr(&self) -> *const Audio::WAVEFORMATEX { + pub fn as_ptr(&self) -> *const wb::WAVEFORMATEX { self.deref() as *const _ } } impl Deref for WaveFormat { - type Target = Audio::WAVEFORMATEX; + type Target = wb::WAVEFORMATEX; fn deref(&self) -> &Self::Target { match *self { WaveFormat::Ex(ref f) => f, @@ -181,13 +183,15 @@ impl DerefMut for WaveFormat { } } -unsafe fn immendpoint_from_immdevice(device: Audio::IMMDevice) -> Audio::IMMEndpoint { +#[inline] +unsafe fn immendpoint_from_immdevice(device: wb::IMMDevice) -> wb::IMMEndpoint { device - .cast::() + .cast::() .expect("could not query IMMDevice interface for IMMEndpoint") } -unsafe fn data_flow_from_immendpoint(endpoint: &Audio::IMMEndpoint) -> Audio::EDataFlow { +#[inline] +unsafe fn data_flow_from_immendpoint(endpoint: &wb::IMMEndpoint) -> wb::EDataFlow { endpoint .GetDataFlow() .expect("could not get endpoint data_flow") @@ -195,24 +199,23 @@ unsafe fn data_flow_from_immendpoint(endpoint: &Audio::IMMEndpoint) -> Audio::ED // Given the audio client and format, returns whether or not the format is supported. pub unsafe fn is_format_supported( - client: &Audio::IAudioClient, - waveformatex_ptr: *const Audio::WAVEFORMATEX, + client: &wb::IAudioClient, + waveformatex_ptr: *const wb::WAVEFORMATEX, ) -> Result { // Check if the given format is supported. let is_supported = |waveformatex_ptr, closest_waveformatex_ptr| { let result = client.IsFormatSupported( - Audio::AUDCLNT_SHAREMODE_SHARED, + wb::AUDCLNT_SHAREMODE_SHARED, waveformatex_ptr, - Some(closest_waveformatex_ptr), + std::ptr::NonNull::new(closest_waveformatex_ptr), ); // `IsFormatSupported` can return `S_FALSE` (which means that a compatible format // has been found, but not an exact match) so we also treat this as unsupported. match result { - Audio::AUDCLNT_E_DEVICE_INVALIDATED => { + wb::AUDCLNT_E_DEVICE_INVALIDATED => { Err(SupportedStreamConfigsError::DeviceNotAvailable) } - r if r.is_err() => Ok(false), - Foundation::S_FALSE => Ok(false), + r if r == wb::S_FALSE || r.is_err() => Ok(false), _ => Ok(true), } }; @@ -222,17 +225,17 @@ pub unsafe fn is_format_supported( // the pointer itself may actually point to a `WAVEFORMATEXTENSIBLE` structure. // We check the wFormatTag to determine this and get a pointer to the correct type. match (*waveformatex_ptr).wFormatTag as u32 { - Audio::WAVE_FORMAT_PCM | Multimedia::WAVE_FORMAT_IEEE_FLOAT => { - let mut closest_waveformatex = *waveformatex_ptr; + wb::WAVE_FORMAT_PCM | wb::WAVE_FORMAT_IEEE_FLOAT => { + let mut closest_waveformatex = memcpy(waveformatex_ptr); let mut closest_waveformatex_ptr = &mut closest_waveformatex as *mut _; is_supported(waveformatex_ptr, &mut closest_waveformatex_ptr as *mut _) } - KernelStreaming::WAVE_FORMAT_EXTENSIBLE => { - let waveformatextensible_ptr = waveformatex_ptr as *const Audio::WAVEFORMATEXTENSIBLE; - let mut closest_waveformatextensible = *waveformatextensible_ptr; + wb::WAVE_FORMAT_EXTENSIBLE => { + let waveformatextensible_ptr = waveformatex_ptr as *const wb::WAVEFORMATEXTENSIBLE; + let mut closest_waveformatextensible = memcpy(waveformatextensible_ptr); let closest_waveformatextensible_ptr = &mut closest_waveformatextensible as *mut _; let mut closest_waveformatex_ptr = - closest_waveformatextensible_ptr as *mut Audio::WAVEFORMATEX; + closest_waveformatextensible_ptr as *mut wb::WAVEFORMATEX; is_supported(waveformatex_ptr, &mut closest_waveformatex_ptr as *mut _) } _ => Ok(false), @@ -241,8 +244,8 @@ pub unsafe fn is_format_supported( // Get a cpal Format from a WAVEFORMATEX. unsafe fn format_from_waveformatex_ptr( - waveformatex_ptr: *const Audio::WAVEFORMATEX, - audio_client: &Audio::IAudioClient, + waveformatex_ptr: *const wb::WAVEFORMATEX, + audio_client: &wb::IAudioClient, ) -> Option { fn cmp_guid(a: &GUID, b: &GUID) -> bool { (a.data1, a.data2, a.data3, a.data4) == (b.data1, b.data2, b.data3, b.data4) @@ -251,14 +254,14 @@ unsafe fn format_from_waveformatex_ptr( (*waveformatex_ptr).wBitsPerSample, (*waveformatex_ptr).wFormatTag as u32, ) { - (16, Audio::WAVE_FORMAT_PCM) => SampleFormat::I16, - (32, Multimedia::WAVE_FORMAT_IEEE_FLOAT) => SampleFormat::F32, - (n_bits, KernelStreaming::WAVE_FORMAT_EXTENSIBLE) => { - let waveformatextensible_ptr = waveformatex_ptr as *const Audio::WAVEFORMATEXTENSIBLE; + (16, wb::WAVE_FORMAT_PCM) => SampleFormat::I16, + (32, wb::WAVE_FORMAT_IEEE_FLOAT) => SampleFormat::F32, + (n_bits, wb::WAVE_FORMAT_EXTENSIBLE) => { + let waveformatextensible_ptr = waveformatex_ptr as *const wb::WAVEFORMATEXTENSIBLE; let sub = (*waveformatextensible_ptr).SubFormat; - if n_bits == 16 && cmp_guid(&sub, &KernelStreaming::KSDATAFORMAT_SUBTYPE_PCM) { + if n_bits == 16 && cmp_guid(&sub, &wb::KSDATAFORMAT_SUBTYPE_PCM) { SampleFormat::I16 - } else if n_bits == 32 && cmp_guid(&sub, &Multimedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { + } else if n_bits == 32 && cmp_guid(&sub, &wb::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { SampleFormat::F32 } else { return None; @@ -283,11 +286,11 @@ unsafe fn format_from_waveformatex_ptr( // https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/hardware-offloaded-audio-processing let (mut min_buffer_duration, mut max_buffer_duration) = (0, 0); let buffer_size_is_limited = audio_client - .cast::() + .cast::() .and_then(|audio_client| { audio_client.GetBufferSizeLimits( waveformatex_ptr, - true, + 1, &mut min_buffer_duration, &mut max_buffer_duration, ) @@ -323,12 +326,12 @@ impl Device { // Open the device's property store. let property_store = self .device - .OpenPropertyStore(STGM_READ) + .OpenPropertyStore(wb::STGM_READ) .expect("could not open property store"); // Get the endpoint's friendly-name property. let mut property_value = property_store - .GetValue(&Properties::DEVPKEY_Device_FriendlyName as *const _ as *const _) + .GetValue(((&wb::DEVPKEY_Device_FriendlyName) as *const wb::DEVPROPKEY).cast()) .map_err(|err| { let description = format!("failed to retrieve name from property store: {}", err); @@ -339,7 +342,7 @@ impl Device { let prop_variant = &property_value.Anonymous.Anonymous; // Read the friendly-name from the union data field, expecting a *const u16. - if prop_variant.vt != VT_LPWSTR { + if prop_variant.vt != wb::VT_LPWSTR { let description = format!( "property store produced invalid data: {:?}", prop_variant.vt @@ -347,16 +350,8 @@ impl Device { let err = BackendSpecificError { description }; return Err(err.into()); } - let ptr_utf16 = *(&prop_variant.Anonymous as *const _ as *const *const u16); - - // Find the length of the friendly name. - let mut len = 0; - while *ptr_utf16.offset(len) != 0 { - len += 1; - } - // Create the utf16 slice and convert it into a string. - let name_slice = slice::from_raw_parts(ptr_utf16, len as usize); + let name_slice = prop_variant.Anonymous.pwszVal.as_wide(); let name_os_string: OsString = OsStringExt::from_wide(name_slice); let name_string = match name_os_string.into_string() { Ok(string) => string, @@ -364,14 +359,14 @@ impl Device { }; // Clean up the property. - StructuredStorage::PropVariantClear(&mut property_value).ok(); + let _ = wb::PropVariantClear(&mut property_value); Ok(name_string) } } #[inline] - fn from_immdevice(device: Audio::IMMDevice) -> Self { + fn from_immdevice(device: wb::IMMDevice) -> Self { Device { device, future_audio_client: Arc::new(Mutex::new(None)), @@ -381,16 +376,16 @@ impl Device { /// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it. fn ensure_future_audio_client( &self, - ) -> Result>, windows::core::Error> { + ) -> ::windows_core::Result>> { let mut lock = self.future_audio_client.lock().unwrap(); if lock.is_some() { return Ok(lock); } - let audio_client: Audio::IAudioClient = unsafe { + let audio_client: wb::IAudioClient = unsafe { // can fail if the device has been disconnected since we enumerated it, or if // the device doesn't support playback for some reason - self.device.Activate(Com::CLSCTX_ALL, None)? + self.device.Activate(wb::CLSCTX_ALL, None)? }; *lock = Some(IAudioClientWrapper(audio_client)); @@ -399,7 +394,7 @@ impl Device { /// Returns an uninitialized `IAudioClient`. #[inline] - pub(crate) fn build_audioclient(&self) -> Result { + pub(crate) fn build_audioclient(&self) -> ::windows_core::Result { let mut lock = self.ensure_future_audio_client()?; Ok(lock.take().unwrap().0) } @@ -422,13 +417,8 @@ impl Device { // Retrieve the `IAudioClient`. let lock = match self.ensure_future_audio_client() { Ok(lock) => lock, - Err(ref e) if e.code() == Audio::AUDCLNT_E_DEVICE_INVALIDATED => { - return Err(SupportedStreamConfigsError::DeviceNotAvailable) - } Err(e) => { - let description = format!("{}", e); - let err = BackendSpecificError { description }; - return Err(err.into()); + return Err(windows_err_to_cpal_err(e)); } }; let client = &lock.as_ref().unwrap().0; @@ -508,7 +498,7 @@ impl Device { pub fn supported_input_configs( &self, ) -> Result { - if self.data_flow() == Audio::eCapture { + if self.data_flow() == wb::eCapture { self.supported_formats() // If it's an output device, assume no input formats. } else { @@ -519,7 +509,7 @@ impl Device { pub fn supported_output_configs( &self, ) -> Result { - if self.data_flow() == Audio::eRender { + if self.data_flow() == wb::eRender { self.supported_formats() // If it's an input device, assume no output formats. } else { @@ -537,13 +527,8 @@ impl Device { let lock = match self.ensure_future_audio_client() { Ok(lock) => lock, - Err(ref e) if e.code() == Audio::AUDCLNT_E_DEVICE_INVALIDATED => { - return Err(DefaultStreamConfigError::DeviceNotAvailable) - } Err(e) => { - let description = format!("{}", e); - let err = BackendSpecificError { description }; - return Err(err.into()); + return Err(windows_err_to_cpal_err(e)); } }; let client = &lock.as_ref().unwrap().0; @@ -559,13 +544,13 @@ impl Device { } } - pub(crate) fn data_flow(&self) -> Audio::EDataFlow { + pub(crate) fn data_flow(&self) -> wb::EDataFlow { let endpoint = Endpoint::from(self.device.clone()); endpoint.data_flow() } pub fn default_input_config(&self) -> Result { - if self.data_flow() == Audio::eCapture { + if self.data_flow() == wb::eCapture { self.default_format() } else { Err(DefaultStreamConfigError::StreamTypeNotSupported) @@ -574,7 +559,7 @@ impl Device { pub fn default_output_config(&self) -> Result { let data_flow = self.data_flow(); - if data_flow == Audio::eRender { + if data_flow == wb::eRender { self.default_format() } else { Err(DefaultStreamConfigError::StreamTypeNotSupported) @@ -594,30 +579,25 @@ impl Device { // Obtaining a `IAudioClient`. let audio_client = match self.build_audioclient() { Ok(client) => client, - Err(ref e) if e.code() == Audio::AUDCLNT_E_DEVICE_INVALIDATED => { - return Err(BuildStreamError::DeviceNotAvailable) - } Err(e) => { - let description = format!("{}", e); - let err = BackendSpecificError { description }; - return Err(err.into()); + return Err(windows_err_to_cpal_err(e)); } }; let buffer_duration = buffer_size_to_duration(&config.buffer_size, config.sample_rate.0); - let mut stream_flags = Audio::AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + let mut stream_flags = wb::AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if self.data_flow() == Audio::eRender { - stream_flags |= Audio::AUDCLNT_STREAMFLAGS_LOOPBACK; + if self.data_flow() == wb::eRender { + stream_flags |= wb::AUDCLNT_STREAMFLAGS_LOOPBACK; } // Computing the format and initializing the device. let waveformatex = { let format_attempt = config_to_waveformatextensible(config, sample_format) .ok_or(BuildStreamError::StreamConfigNotSupported)?; - let share_mode = Audio::AUDCLNT_SHAREMODE_SHARED; + let share_mode = wb::AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(&audio_client, &format_attempt.Format) { @@ -627,25 +607,16 @@ impl Device { } // Finally, initializing the audio client - let hresult = audio_client.Initialize( + if let Err(err) = audio_client.Initialize( share_mode, stream_flags, buffer_duration, 0, &format_attempt.Format, None, - ); - match hresult { - Err(ref e) if e.code() == Audio::AUDCLNT_E_DEVICE_INVALIDATED => { - return Err(BuildStreamError::DeviceNotAvailable); - } - Err(e) => { - let description = format!("{}", e); - let err = BackendSpecificError { description }; - return Err(err.into()); - } - Ok(()) => (), - }; + ) { + return Err(windows_err_to_cpal_err(err)); + } format_attempt.Format }; @@ -657,13 +628,11 @@ impl Device { // Creating the event that will be signalled whenever we need to submit some samples. let event = { - let event = - Threading::CreateEventA(None, false, false, windows::core::PCSTR(ptr::null())) - .map_err(|e| { - let description = format!("failed to create event: {}", e); - let err = BackendSpecificError { description }; - BuildStreamError::from(err) - })?; + let event = create_event().map_err(|e| { + let description = format!("failed to create event: {}", e); + let err = BackendSpecificError { description }; + BuildStreamError::from(err) + })?; if let Err(e) = audio_client.SetEventHandle(event) { let description = format!("failed to call SetEventHandle: {}", e); @@ -676,7 +645,7 @@ impl Device { // Building a `IAudioCaptureClient` that will be used to read captured samples. let capture_client = audio_client - .GetService::() + .GetService::() .map_err(|e| { windows_err_to_cpal_err_message::( e, @@ -726,7 +695,7 @@ impl Device { let waveformatex = { let format_attempt = config_to_waveformatextensible(config, sample_format) .ok_or(BuildStreamError::StreamConfigNotSupported)?; - let share_mode = Audio::AUDCLNT_SHAREMODE_SHARED; + let share_mode = wb::AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(&audio_client, &format_attempt.Format) { @@ -739,7 +708,7 @@ impl Device { audio_client .Initialize( share_mode, - Audio::AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + wb::AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffer_duration, 0, &format_attempt.Format, @@ -752,13 +721,11 @@ impl Device { // Creating the event that will be signalled whenever we need to submit some samples. let event = { - let event = - Threading::CreateEventA(None, false, false, windows::core::PCSTR(ptr::null())) - .map_err(|e| { - let description = format!("failed to create event: {}", e); - let err = BackendSpecificError { description }; - BuildStreamError::from(err) - })?; + let event = create_event().map_err(|e| { + let description = format!("failed to create event: {}", e); + let err = BackendSpecificError { description }; + BuildStreamError::from(err) + })?; if let Err(e) = audio_client.SetEventHandle(event) { let description = format!("failed to call SetEventHandle: {}", e); @@ -779,7 +746,7 @@ impl Device { // Building a `IAudioRenderClient` that will be used to fill the samples buffer. let render_client = audio_client - .GetService::() + .GetService::() .map_err(|e| { windows_err_to_cpal_err_message::( e, @@ -819,32 +786,19 @@ impl PartialEq for Device { // 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 unsafe { - struct IdRAII(windows::core::PWSTR); + struct IdRAII(::windows_core::PWSTR); /// RAII for device IDs. impl Drop for IdRAII { fn drop(&mut self) { - unsafe { Com::CoTaskMemFree(Some(self.0 .0 as *mut _)) } + unsafe { wb::CoTaskMemFree(self.0 .0.cast()) } } } // 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. - let id1 = self.device.GetId().expect("cpal: GetId failure"); - let id1 = IdRAII(id1); - let id2 = other.device.GetId().expect("cpal: GetId failure"); - let id2 = IdRAII(id2); - // 16-bit null-terminated comparison. - let mut offset = 0; - loop { - let w1: u16 = *(id1.0).0.offset(offset); - let w2: u16 = *(id2.0).0.offset(offset); - if w1 == 0 && w2 == 0 { - return true; - } - if w1 != w2 { - return false; - } - offset += 1; - } + let id1 = IdRAII(self.device.GetId().expect("cpal: GetId failure")); + let id2 = IdRAII(other.device.GetId().expect("cpal: GetId failure")); + + id1.0.as_wide() == id2.0.as_wide() } } } @@ -854,14 +808,13 @@ impl Eq for Device {} impl fmt::Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Device") - .field("device", &self.device) .field("name", &self.name()) .finish() } } -impl From for Endpoint { - fn from(device: Audio::IMMDevice) -> Self { +impl From for Endpoint { + fn from(device: wb::IMMDevice) -> Self { unsafe { let endpoint = immendpoint_from_immdevice(device); Endpoint { endpoint } @@ -870,7 +823,7 @@ impl From for Endpoint { } impl Endpoint { - fn data_flow(&self) -> Audio::EDataFlow { + fn data_flow(&self) -> wb::EDataFlow { unsafe { data_flow_from_immendpoint(&self.endpoint) } } } @@ -880,28 +833,38 @@ static ENUMERATOR: Lazy = Lazy::new(|| { // thread we create the objects in com::com_initialized(); - // building the devices enumerator object + // build the devices enumerator object + // https://learn.microsoft.com/en-us/windows/win32/coreaudio/mmdevice-api unsafe { - let enumerator = Com::CoCreateInstance::<_, Audio::IMMDeviceEnumerator>( - &Audio::MMDeviceEnumerator, - None, - Com::CLSCTX_ALL, - ) - .unwrap(); - - Enumerator(enumerator) + let mut iptr = std::mem::MaybeUninit::::uninit(); + let res = wb::CoCreateInstance( + &wb::MMDeviceEnumerator, + std::ptr::null_mut(), + wb::CLSCTX_ALL, + &wb::IMMDeviceEnumerator::IID, + iptr.as_mut_ptr().cast(), + ); + + if res.is_ok() { + Enumerator(iptr.assume_init()) + } else { + panic!( + "failed to create device enumerator: {}", + ::windows_core::Error::from(res) + ); + } } }); /// Send/Sync wrapper around `IMMDeviceEnumerator`. -struct Enumerator(Audio::IMMDeviceEnumerator); +struct Enumerator(wb::IMMDeviceEnumerator); unsafe impl Send for Enumerator {} unsafe impl Sync for Enumerator {} /// WASAPI implementation for `Devices`. pub struct Devices { - collection: Audio::IMMDeviceCollection, + collection: wb::IMMDeviceCollection, total_count: u32, next_item: u32, } @@ -912,7 +875,7 @@ impl Devices { // can fail because of wrong parameters (should never happen) or out of memory let collection = ENUMERATOR .0 - .EnumAudioEndpoints(Audio::eAll, Audio::DEVICE_STATE_ACTIVE) + .EnumAudioEndpoints(wb::eAll, wb::DEVICE_STATE_ACTIVE) .map_err(BackendSpecificError::from)?; let count = collection.GetCount().map_err(BackendSpecificError::from)?; @@ -952,11 +915,11 @@ impl Iterator for Devices { } } -fn default_device(data_flow: Audio::EDataFlow) -> Option { +fn default_device(data_flow: wb::EDataFlow) -> Option { unsafe { let device = ENUMERATOR .0 - .GetDefaultAudioEndpoint(data_flow, Audio::eConsole) + .GetDefaultAudioEndpoint(data_flow, wb::eConsole) .ok()?; // TODO: check specifically for `E_NOTFOUND`, and panic otherwise Some(Device::from_immdevice(device)) @@ -964,22 +927,20 @@ fn default_device(data_flow: Audio::EDataFlow) -> Option { } pub fn default_input_device() -> Option { - default_device(Audio::eCapture) + default_device(wb::eCapture) } pub fn default_output_device() -> Option { - default_device(Audio::eRender) + default_device(wb::eRender) } /// Get the audio clock used to produce `StreamInstant`s. unsafe fn get_audio_clock( - audio_client: &Audio::IAudioClient, -) -> Result { - audio_client - .GetService::() - .map_err(|e| { - windows_err_to_cpal_err_message::(e, "failed to build audio clock: ") - }) + audio_client: &wb::IAudioClient, +) -> Result { + audio_client.GetService::().map_err(|e| { + windows_err_to_cpal_err_message::(e, "failed to build audio clock: ") + }) } // Turns a `Format` into a `WAVEFORMATEXTENSIBLE`. @@ -988,10 +949,10 @@ unsafe fn get_audio_clock( fn config_to_waveformatextensible( config: &StreamConfig, sample_format: SampleFormat, -) -> Option { +) -> Option { let format_tag = match sample_format { - SampleFormat::I16 => Audio::WAVE_FORMAT_PCM, - SampleFormat::F32 => KernelStreaming::WAVE_FORMAT_EXTENSIBLE, + SampleFormat::I16 => wb::WAVE_FORMAT_PCM, + SampleFormat::F32 => wb::WAVE_FORMAT_EXTENSIBLE, _ => return None, } as u16; let channels = config.channels; @@ -1003,13 +964,13 @@ fn config_to_waveformatextensible( let cb_size = match sample_format { SampleFormat::I16 => 0, SampleFormat::F32 => { - let extensible_size = mem::size_of::(); - let ex_size = mem::size_of::(); + let extensible_size = mem::size_of::(); + let ex_size = mem::size_of::(); (extensible_size - ex_size) as u16 } _ => return None, }; - let waveformatex = Audio::WAVEFORMATEX { + let waveformatex = wb::WAVEFORMATEX { wFormatTag: format_tag, nChannels: channels, nSamplesPerSec: sample_rate, @@ -1020,16 +981,16 @@ fn config_to_waveformatextensible( }; // CPAL does not care about speaker positions, so pass audio ight through. - let channel_mask = KernelStreaming::KSAUDIO_SPEAKER_DIRECTOUT; + let channel_mask = wb::KSAUDIO_SPEAKER_DIRECTOUT; let sub_format = match sample_format { - SampleFormat::I16 => KernelStreaming::KSDATAFORMAT_SUBTYPE_PCM, - SampleFormat::F32 => Multimedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, + SampleFormat::I16 => wb::KSDATAFORMAT_SUBTYPE_PCM, + SampleFormat::F32 => wb::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, _ => return None, }; - let waveformatextensible = Audio::WAVEFORMATEXTENSIBLE { + let waveformatextensible = wb::WAVEFORMATEXTENSIBLE { Format: waveformatex, - Samples: Audio::WAVEFORMATEXTENSIBLE_0 { + Samples: wb::WAVEFORMATEXTENSIBLE_0 { wSamplesPerBlock: bits_per_sample, }, dwChannelMask: channel_mask, diff --git a/src/host/wasapi/mod.rs b/src/host/wasapi/mod.rs index e80760267..3e6b4fe40 100644 --- a/src/host/wasapi/mod.rs +++ b/src/host/wasapi/mod.rs @@ -7,7 +7,6 @@ use crate::traits::HostTrait; use crate::BackendSpecificError; use crate::DevicesError; use std::io::Error as IoError; -use windows::Win32::Media::Audio; mod com; mod device; @@ -49,8 +48,8 @@ impl HostTrait for Host { } } -impl From for BackendSpecificError { - fn from(error: windows::core::Error) -> Self { +impl From<::windows_core::Error> for BackendSpecificError { + fn from(error: ::windows_core::Error) -> Self { BackendSpecificError { description: format!("{}", IoError::from(error)), } @@ -85,20 +84,21 @@ impl ErrDeviceNotAvailable for crate::StreamError { } } -fn windows_err_to_cpal_err(e: windows::core::Error) -> E { - windows_err_to_cpal_err_message::(e, "") +#[inline] +fn windows_err_to_cpal_err(e: ::windows_core::Error) -> E { + windows_err_to_cpal_err_message(e, "") } +#[inline] fn windows_err_to_cpal_err_message( - e: windows::core::Error, + e: ::windows_core::Error, message: &str, ) -> E { - match e.code() { - Audio::AUDCLNT_E_DEVICE_INVALIDATED => E::device_not_available(), - _ => { - let description = format!("{}{}", message, e); - let err = BackendSpecificError { description }; - err.into() - } + if let com::bindings::AUDCLNT_E_DEVICE_INVALIDATED = e.code() { + E::device_not_available() + } else { + let description = format!("{}{}", message, e); + let err = BackendSpecificError { description }; + err.into() } } diff --git a/src/host/wasapi/stream.rs b/src/host/wasapi/stream.rs index 5a04695b0..bf7cf87ff 100644 --- a/src/host/wasapi/stream.rs +++ b/src/host/wasapi/stream.rs @@ -1,3 +1,5 @@ +use super::com::bindings as wb; +use super::com::threading; use super::windows_err_to_cpal_err; use crate::traits::StreamTrait; use crate::{ @@ -8,12 +10,6 @@ use std::mem; use std::ptr; use std::sync::mpsc::{channel, Receiver, SendError, Sender}; use std::thread::{self, JoinHandle}; -use windows::Win32::Foundation; -use windows::Win32::Foundation::WAIT_OBJECT_0; -use windows::Win32::Media::Audio; -use windows::Win32::System::SystemServices; -use windows::Win32::System::Threading; -use windows::Win32::System::WindowsProgramming; pub struct Stream { /// The high-priority audio processing thread calling callbacks. @@ -29,7 +25,7 @@ pub struct Stream { // This event is signalled after a new entry is added to `commands`, so that the `run()` // method can be notified. - pending_scheduled_event: Foundation::HANDLE, + pending_scheduled_event: wb::HANDLE, } struct RunContext { @@ -38,7 +34,7 @@ struct RunContext { // Handles corresponding to the `event` field of each element of `voices`. Must always be in // sync with `voices`, except that the first element is always `pending_scheduled_event`. - handles: Vec, + handles: Vec, commands: Receiver, } @@ -54,19 +50,19 @@ pub enum Command { pub enum AudioClientFlow { Render { - render_client: Audio::IAudioRenderClient, + render_client: wb::IAudioRenderClient, }, Capture { - capture_client: Audio::IAudioCaptureClient, + capture_client: wb::IAudioCaptureClient, }, } pub struct StreamInner { - pub audio_client: Audio::IAudioClient, - pub audio_clock: Audio::IAudioClock, + pub audio_client: wb::IAudioClient, + pub audio_clock: wb::IAudioClock, pub client_flow: AudioClientFlow, // Event that is signalled by WASAPI whenever audio data must be written. - pub event: Foundation::HANDLE, + pub event: wb::HANDLE, // True if the stream is currently playing. False if paused. pub playing: bool, // Number of frames of audio data in the underlying buffer allocated by WASAPI. @@ -89,10 +85,8 @@ impl Stream { D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let pending_scheduled_event = unsafe { - Threading::CreateEventA(None, false, false, windows::core::PCSTR(ptr::null())) - } - .expect("cpal: could not create input stream event"); + let pending_scheduled_event = unsafe { threading::create_event() } + .expect("cpal: could not create input stream event"); let (tx, rx) = channel(); let run_context = RunContext { @@ -122,10 +116,8 @@ impl Stream { D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let pending_scheduled_event = unsafe { - Threading::CreateEventA(None, false, false, windows::core::PCSTR(ptr::null())) - } - .expect("cpal: could not create output stream event"); + let pending_scheduled_event = unsafe { threading::create_event() } + .expect("cpal: could not create output stream event"); let (tx, rx) = channel(); let run_context = RunContext { @@ -150,8 +142,8 @@ impl Stream { fn push_command(&self, command: Command) -> Result<(), SendError> { self.commands.send(command)?; unsafe { - let result = Threading::SetEvent(self.pending_scheduled_event); - assert_ne!(result, false); + let result = wb::SetEvent(self.pending_scheduled_event); + assert_ne!(result, 0); } Ok(()) } @@ -163,7 +155,7 @@ impl Drop for Stream { if let Ok(_) = self.push_command(Command::Terminate) { self.thread.take().unwrap().join().unwrap(); unsafe { - Foundation::CloseHandle(self.pending_scheduled_event); + wb::CloseHandle(self.pending_scheduled_event); } } } @@ -186,7 +178,7 @@ impl Drop for StreamInner { #[inline] fn drop(&mut self) { unsafe { - Foundation::CloseHandle(self.event); + wb::CloseHandle(self.event); } } } @@ -233,24 +225,25 @@ fn process_commands(run_context: &mut RunContext) -> Result { // This is called when the `run` thread is ready to wait for the next event. The // next event might be some command submitted by the user (the first handle) or // might indicate that one of the streams is ready to deliver or receive audio. -fn wait_for_handle_signal(handles: &[Foundation::HANDLE]) -> Result { - debug_assert!(handles.len() <= SystemServices::MAXIMUM_WAIT_OBJECTS as usize); +fn wait_for_handle_signal(handles: &[wb::HANDLE]) -> Result { + debug_assert!(handles.len() <= wb::MAXIMUM_WAIT_OBJECTS as usize); let result = unsafe { - Threading::WaitForMultipleObjectsEx( - handles, - false, // Don't wait for all, just wait for the first - WindowsProgramming::INFINITE, // TODO: allow setting a timeout - false, // irrelevant parameter here + wb::WaitForMultipleObjectsEx( + handles.len() as _, + handles.as_ptr(), + 0, // Don't wait for all, just wait for the first + wb::INFINITE, // TODO: allow setting a timeout + 0, // irrelevant parameter here ) }; - if result == Foundation::WAIT_FAILED { - let err = unsafe { Foundation::GetLastError() }; - let description = format!("`WaitForMultipleObjectsEx failed: {}", err.0); + if result == wb::WAIT_FAILED { + let err = ::windows_core::Error::from_win32(); + let description = format!("`WaitForMultipleObjectsEx failed: {err}",); let err = BackendSpecificError { description }; return Err(err); } // Notifying the corresponding task handler. - let handle_idx = (result.0 - WAIT_OBJECT_0.0) as usize; + let handle_idx = (result - wb::WAIT_OBJECT_0) as usize; Ok(handle_idx) } @@ -276,9 +269,8 @@ fn run_input( Some(ControlFlow::Continue) => continue, None => (), } - let capture_client = match run_ctxt.stream.client_flow { - AudioClientFlow::Capture { ref capture_client } => capture_client.clone(), - _ => unreachable!(), + let AudioClientFlow::Capture { capture_client } = &run_ctxt.stream.client_flow else { + unreachable!() }; match process_input( &run_ctxt.stream, @@ -303,9 +295,8 @@ fn run_output( Some(ControlFlow::Continue) => continue, None => (), } - let render_client = match run_ctxt.stream.client_flow { - AudioClientFlow::Render { ref render_client } => render_client.clone(), - _ => unreachable!(), + let AudioClientFlow::Render { render_client } = &run_ctxt.stream.client_flow else { + unreachable!() }; match process_output( &run_ctxt.stream, @@ -359,7 +350,7 @@ fn process_commands_and_await_signal( // The loop for processing pending input data. fn process_input( stream: &StreamInner, - capture_client: Audio::IAudioCaptureClient, + capture_client: &wb::IAudioCaptureClient, data_callback: &mut dyn FnMut(&Data, &InputCallbackInfo), error_callback: &mut dyn FnMut(StreamError), ) -> ControlFlow { @@ -382,12 +373,12 @@ fn process_input( &mut frames_available, flags.as_mut_ptr(), None, - Some(&mut qpc_position), + ptr::NonNull::new(&mut qpc_position), ); match result { // TODO: Can this happen? - Err(e) if e.code() == Audio::AUDCLNT_S_BUFFER_EMPTY => continue, + Err(e) if e.code() == wb::AUDCLNT_S_BUFFER_EMPTY => continue, Err(e) => { error_callback(windows_err_to_cpal_err(e)); return ControlFlow::Break; @@ -428,7 +419,7 @@ fn process_input( // The loop for writing output data. fn process_output( stream: &StreamInner, - render_client: Audio::IAudioRenderClient, + render_client: &wb::IAudioRenderClient, data_callback: &mut dyn FnMut(&mut Data, &OutputCallbackInfo), error_callback: &mut dyn FnMut(StreamError), ) -> ControlFlow { @@ -494,7 +485,7 @@ fn stream_instant(stream: &StreamInner) -> Result)?; }; // The `qpc_position` is in 100 nanosecond units. Convert it to nanoseconds.