From 1022e1c08f1df8ce076da17f11f88825c287b677 Mon Sep 17 00:00:00 2001 From: Steven Noonan Date: Wed, 10 Aug 2022 14:59:54 -0700 Subject: [PATCH] wasapi: implement support for avrt.dll thread characteristics API Signed-off-by: Steven Noonan --- miniaudio.h | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/miniaudio.h b/miniaudio.h index bf139b55..ff7efb07 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -6509,6 +6509,14 @@ typedef enum ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ } ma_opensl_recording_preset; +/* WASAPI audio thread priority characteristics. */ +typedef enum +{ + ma_wasapi_usage_default = 0, + ma_wasapi_usage_games, + ma_wasapi_usage_pro_audio, +} ma_wasapi_usage; + /* AAudio usage types. */ typedef enum { @@ -6652,6 +6660,7 @@ struct ma_device_config struct { + ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ @@ -6898,6 +6907,9 @@ struct ma_context ma_uint32 commandIndex; ma_uint32 commandCount; ma_context_command__wasapi commands[4]; + ma_handle hAvrt; + ma_proc AvSetMmThreadCharacteristicsW; + ma_proc AvRevertMmThreadcharacteristics; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -7401,6 +7413,8 @@ struct ma_device ma_bool8 allowPlaybackAutoStreamRouting; ma_bool8 isDetachedPlayback; ma_bool8 isDetachedCapture; + ma_wasapi_usage usage; + void *hAvrtHandle; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -19785,6 +19799,10 @@ static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } +/* Avrt Functions */ +typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsW)(LPCWSTR TaskName, LPDWORD TaskIndex); +typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); + #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) #include typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; @@ -20137,6 +20155,18 @@ static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { }; #endif /* MA_WIN32_DESKTOP */ +static LPCWSTR ma_to_usage_string__wasapi(ma_wasapi_usage usage) +{ + switch (usage) { + case ma_wasapi_usage_default: return NULL; + case ma_wasapi_usage_games: return L"Games"; + case ma_wasapi_usage_pro_audio: return L"Pro Audio"; + default: break; + } + + return NULL; +} + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) typedef ma_IMMDevice ma_WASAPIDeviceInterface; #else @@ -21584,6 +21614,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->wasapi); + pDevice->wasapi.usage = pConfig->wasapi.usage; pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; @@ -21877,6 +21908,14 @@ static ma_result ma_device_start__wasapi(ma_device* pDevice) MA_ASSERT(pDevice != NULL); + if (pDevice->pContext->wasapi.hAvrt) { + LPCWSTR pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + if (pTaskName) { + DWORD idx = 0; + pDevice->wasapi.hAvrtHandle = ((MA_PFN_AvSetMmThreadCharacteristicsW)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsW)(pTaskName, &idx); + } + } + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { @@ -21907,6 +21946,11 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) MA_ASSERT(pDevice != NULL); + if (pDevice->wasapi.hAvrtHandle) { + ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); + pDevice->wasapi.hAvrtHandle = NULL; + } + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { @@ -22261,6 +22305,11 @@ static ma_result ma_context_uninit__wasapi(ma_context* pContext) ma_context_post_command__wasapi(pContext, &cmd); ma_thread_wait(&pContext->wasapi.commandThread); + if (pContext->wasapi.hAvrt) { + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); @@ -22366,6 +22415,21 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_mutex_uninit(&pContext->wasapi.commandLock); return result; } + + /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ + pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); + if (pContext->wasapi.hAvrt) { + pContext->wasapi.AvSetMmThreadCharacteristicsW = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsW"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + + /* If either function could not be found, disable use of avrt entirely. */ + if (!pContext->wasapi.AvSetMmThreadCharacteristicsW || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsW = NULL; + pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + } }