Improvements to the WASAPI backend.

* Fix an error with the recent refactoring work.
  * Fix some build errors with the UWP build.
  * Update device enumeration to include format information.
This commit is contained in:
David Reid
2025-07-21 08:14:50 +10:00
parent 9507f61689
commit 8b3ac67c89
+59 -85
View File
@@ -21979,8 +21979,10 @@ typedef struct ma_context_state_wasapi
ma_handle hAvrt;
MA_PFN_AvSetMmThreadCharacteristicsA AvSetMmThreadCharacteristicsA;
MA_PFN_AvRevertMmThreadCharacteristics AvRevertMmThreadcharacteristics;
#if defined(MA_WIN32_UWP)
ma_handle hMMDevapi;
ma_proc ActivateAudioInterfaceAsync;
MA_PFN_ActivateAudioInterfaceAsync ActivateAudioInterfaceAsync;
#endif
} ma_context_state_wasapi;
typedef struct ma_device_state_wasapi
@@ -21989,7 +21991,9 @@ typedef struct ma_device_state_wasapi
ma_IAudioClient* pAudioClientCapture;
ma_IAudioRenderClient* pRenderClient;
ma_IAudioCaptureClient* pCaptureClient;
ma_IMMDeviceEnumerator* pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
#if defined(MA_WIN32_DESKTOP)
ma_IMMDeviceEnumerator* pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. Not used with the UWP build. */
#endif
ma_IMMNotificationClient notificationClient;
HANDLE hEventPlayback; /* Auto reset. Initialized to signaled. */
HANDLE hEventCapture; /* Auto reset. Initialized to unsignaled. */
@@ -22486,12 +22490,10 @@ static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_
return MA_SUCCESS;
}
static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
static ma_result ma_context_next_command__wasapi(ma_context_state_wasapi* pContextStateWASAPI, ma_context_command__wasapi* pCmd)
{
ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(pContext);
ma_result result = MA_SUCCESS;
MA_ASSERT(pContext != NULL);
MA_ASSERT(pCmd != NULL);
result = ma_semaphore_wait(&pContextStateWASAPI->commandSem);
@@ -22511,12 +22513,12 @@ static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_contex
static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
{
ma_result result;
ma_context* pContext = (ma_context*)pUserData;
MA_ASSERT(pContext != NULL);
ma_context_state_wasapi* pContextStateWASAPI = (ma_context_state_wasapi*)pUserData;
MA_ASSERT(pContextStateWASAPI != NULL);
for (;;) {
ma_context_command__wasapi cmd;
result = ma_context_next_command__wasapi(pContext, &cmd);
result = ma_context_next_command__wasapi(pContextStateWASAPI, &cmd);
if (result != MA_SUCCESS) {
break;
}
@@ -22628,7 +22630,7 @@ static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_
pInfo->nativeDataFormatCount += 1;
}
static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/ void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
{
HRESULT hr;
MA_WAVEFORMATEX* pWF = NULL;
@@ -22637,7 +22639,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context
MA_ASSERT(pInfo != NULL);
/* Shared Mode. We use GetMixFormat() here. */
hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF);
hr = ma_IAudioClient_GetMixFormat(pAudioClient, (MA_WAVEFORMATEX**)&pWF);
if (SUCCEEDED(hr)) {
ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);
} else {
@@ -22671,7 +22673,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context
In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
first. If this fails, fall back to a search.
*/
hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
hr = ma_IAudioClient_IsFormatSupported(pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
if (SUCCEEDED(hr)) {
/* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */
ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);
@@ -22717,7 +22719,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context
for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL);
hr = ma_IAudioClient_IsFormatSupported(pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL);
if (SUCCEEDED(hr)) {
ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);
found = MA_TRUE;
@@ -22920,7 +22922,7 @@ static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pCon
return MA_ERROR;
}
static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)
static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_device_info* pInfo)
{
ma_result result;
HRESULT hr;
@@ -22929,7 +22931,7 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC
MA_ASSERT(pMMDevice != NULL);
MA_ASSERT(pInfo != NULL);
/* ID. */
/* ID and Default. */
result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
if (result == MA_SUCCESS) {
if (pDefaultDeviceID != NULL) {
@@ -22958,7 +22960,7 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC
}
/* Format */
if (!onlySimpleInfo) {
{
ma_IAudioClient* pAudioClient;
hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
if (SUCCEEDED(hr)) {
@@ -23008,7 +23010,7 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte
hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
if (SUCCEEDED(hr)) {
result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, &deviceInfo);
ma_IMMDevice_Release(pMMDevice);
if (result == MA_SUCCESS) {
@@ -23059,6 +23061,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex
#else
static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface)
{
ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(pContext);
ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
ma_completion_handler_uwp completionHandler;
IID iid;
@@ -23098,7 +23101,7 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m
return result;
}
hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContextStateWASAPI->ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
hr = pContextStateWASAPI->ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, &pAsyncOp);
if (FAILED(hr)) {
ma_completion_handler_uwp_uninit(&completionHandler);
ma_CoTaskMemFree(pContext, iidStr);
@@ -23317,7 +23320,7 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const void* pCont
/* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */
pContextStateWASAPI->hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll");
if (pContextStateWASAPI->hMMDevapi) {
pContextStateWASAPI->ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContextStateWASAPI->hMMDevapi, "ActivateAudioInterfaceAsync");
pContextStateWASAPI->ActivateAudioInterfaceAsync = (MA_PFN_ActivateAudioInterfaceAsync)ma_dlsym(ma_context_get_log(pContext), pContextStateWASAPI->hMMDevapi, "ActivateAudioInterfaceAsync");
if (pContextStateWASAPI->ActivateAudioInterfaceAsync == NULL) {
/* ActivateAudioInterfaceAsync() could not be loaded. */
ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext));
@@ -23387,7 +23390,7 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const void* pCont
return result;
}
result = ma_thread_create(&pContextStateWASAPI->commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
result = ma_thread_create(&pContextStateWASAPI->commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContextStateWASAPI, &pContext->allocationCallbacks);
if (result != MA_SUCCESS) {
ma_semaphore_uninit(&pContextStateWASAPI->commandSem);
ma_mutex_uninit(&pContextStateWASAPI->commandLock);
@@ -23432,10 +23435,38 @@ static void ma_context_uninit__wasapi(ma_context* pContext)
ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext));
}
#if defined(MA_WIN32_UWP)
static ma_bool32 ma_context_enumerate_device_from_type_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
{
ma_device_info deviceInfo;
ma_IAudioClient* pAudioClient;
MA_ZERO_OBJECT(&deviceInfo);
/* Default. */
deviceInfo.isDefault = MA_TRUE;
/* ID. */
/* Nothing to do. Always default. */
/* Name. */
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
/* Data Format. */
if (ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, NULL, NULL, &pAudioClient, NULL) == MA_SUCCESS) {
ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, &deviceInfo);
ma_IAudioClient_Release(pAudioClient);
}
return callback(deviceType, &deviceInfo, pUserData);;
}
#endif
static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
/* Different enumeration for desktop and UWP. */
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
{
/* Desktop */
HRESULT hr;
ma_IMMDeviceEnumerator* pDeviceEnumerator;
@@ -23450,7 +23481,9 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e
ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
#else
}
#else
{
/*
UWP
@@ -23464,79 +23497,20 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e
/* Playback. */
if (cbResult) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
deviceInfo.isDefault = MA_TRUE;
cbResult = callback(ma_device_type_playback, &deviceInfo, pUserData);
cbResult = ma_context_enumerate_device_from_type_UWP__wasapi(pContext, ma_device_type_playback, callback, pUserData);
}
/* Capture. */
if (cbResult) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
deviceInfo.isDefault = MA_TRUE;
cbResult = callback(ma_device_type_capture, &deviceInfo, pUserData);
cbResult = ma_context_enumerate_device_from_type_UWP__wasapi(pContext, ma_device_type_capture, callback, pUserData);
}
}
#endif
}
#endif
return MA_SUCCESS;
}
static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
{
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
ma_result result;
ma_IMMDevice* pMMDevice = NULL;
WCHAR* pDefaultDeviceID = NULL;
result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
if (result != MA_SUCCESS) {
return result;
}
/* We need the default device ID so we can set the isDefault flag in the device info. */
pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
if (pDefaultDeviceID != NULL) {
ma_CoTaskMemFree(pContext, pDefaultDeviceID);
pDefaultDeviceID = NULL;
}
ma_IMMDevice_Release(pMMDevice);
return result;
#else
ma_IAudioClient* pAudioClient;
ma_result result;
/* UWP currently only uses default devices. */
if (deviceType == ma_device_type_playback) {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL);
if (result != MA_SUCCESS) {
return result;
}
result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */
ma_IAudioClient_Release(pAudioClient);
return result;
#endif
}
typedef struct
{
@@ -25061,7 +25035,7 @@ static ma_device_backend_vtable ma_gDeviceBackendVTable_WASAPI =
ma_context_init__wasapi,
ma_context_uninit__wasapi,
ma_context_enumerate_devices__wasapi,
ma_context_get_device_info__wasapi,
NULL,
ma_device_init__wasapi,
ma_device_uninit__wasapi,
ma_device_start__wasapi,