mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 00:34:03 +02:00
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:
+88
-114
@@ -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,13 +22490,11 @@ 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);
|
||||
MA_ASSERT(pCmd != NULL);
|
||||
|
||||
result = ma_semaphore_wait(&pContextStateWASAPI->commandSem);
|
||||
if (result == MA_SUCCESS) {
|
||||
@@ -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,111 +23435,82 @@ 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)
|
||||
/* Desktop */
|
||||
HRESULT hr;
|
||||
ma_IMMDeviceEnumerator* pDeviceEnumerator;
|
||||
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
||||
{
|
||||
/* Desktop */
|
||||
HRESULT hr;
|
||||
ma_IMMDeviceEnumerator* pDeviceEnumerator;
|
||||
|
||||
hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
||||
if (FAILED(hr)) {
|
||||
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
|
||||
return ma_result_from_HRESULT(hr);
|
||||
}
|
||||
|
||||
ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
|
||||
ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
|
||||
|
||||
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
|
||||
#else
|
||||
/*
|
||||
UWP
|
||||
|
||||
The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
|
||||
over devices without using MMDevice, I'm restricting devices to defaults.
|
||||
|
||||
Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/
|
||||
*/
|
||||
if (callback) {
|
||||
ma_bool32 cbResult = MA_TRUE;
|
||||
|
||||
/* 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);
|
||||
hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
||||
if (FAILED(hr)) {
|
||||
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
|
||||
return ma_result_from_HRESULT(hr);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
|
||||
ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
|
||||
|
||||
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
|
||||
}
|
||||
#else
|
||||
{
|
||||
/*
|
||||
UWP
|
||||
|
||||
The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
|
||||
over devices without using MMDevice, I'm restricting devices to defaults.
|
||||
|
||||
Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/
|
||||
*/
|
||||
if (callback) {
|
||||
ma_bool32 cbResult = MA_TRUE;
|
||||
|
||||
/* Playback. */
|
||||
if (cbResult) {
|
||||
cbResult = ma_context_enumerate_device_from_type_UWP__wasapi(pContext, ma_device_type_playback, callback, pUserData);
|
||||
}
|
||||
|
||||
/* Capture. */
|
||||
if (cbResult) {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user