Commit 05066e71 authored by David Reid's avatar David Reid

WASAPI: Add support for process-specific loopback capture.

Public issue https://github.com/mackron/miniaudio/issues/484
parent 79179703
...@@ -6661,6 +6661,8 @@ struct ma_device_config ...@@ -6661,6 +6661,8 @@ struct ma_device_config
ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */
ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. */
ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */
} wasapi; } wasapi;
struct struct
{ {
...@@ -7402,6 +7404,8 @@ struct ma_device ...@@ -7402,6 +7404,8 @@ struct ma_device
ma_uint32 mappedBufferPlaybackLen; ma_uint32 mappedBufferPlaybackLen;
MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
ma_uint32 loopbackProcessID;
ma_bool8 loopbackProcessExclude;
ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ 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 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
ma_bool8 noHardwareOffloading; ma_bool8 noHardwareOffloading;
...@@ -20764,7 +20768,7 @@ done: ...@@ -20764,7 +20768,7 @@ done:
return result; return result;
} }
static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice)
{ {
ma_result result; ma_result result;
HRESULT hr; HRESULT hr;
...@@ -20778,7 +20782,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex ...@@ -20778,7 +20782,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex
return result; return result;
} }
hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient); hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient);
if (FAILED(hr)) { if (FAILED(hr)) {
return ma_result_from_HRESULT(hr); return ma_result_from_HRESULT(hr);
} }
...@@ -20786,7 +20790,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex ...@@ -20786,7 +20790,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex
return MA_SUCCESS; return MA_SUCCESS;
} }
#else #else
static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface)
{ {
ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
ma_completion_handler_uwp completionHandler; ma_completion_handler_uwp completionHandler;
...@@ -20828,9 +20832,9 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m ...@@ -20828,9 +20832,9 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m
} }
#if defined(__cplusplus) #if defined(__cplusplus)
hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, pActivationParams, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
#else #else
hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, pActivationParams, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
#endif #endif
if (FAILED(hr)) { if (FAILED(hr)) {
ma_completion_handler_uwp_uninit(&completionHandler); ma_completion_handler_uwp_uninit(&completionHandler);
...@@ -20870,12 +20874,79 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m ...@@ -20870,12 +20874,79 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m
} }
#endif #endif
static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface)
/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */
typedef enum
{
MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT,
MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK
} MA_AUDIOCLIENT_ACTIVATION_TYPE;
/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */
typedef enum
{
MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE,
MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE
} MA_PROCESS_LOOPBACK_MODE;
/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */
typedef struct {
DWORD TargetProcessId;
MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode;
} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS;
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
#endif
#endif
/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */
typedef struct
{
MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType;
union
{
MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams;
};
} MA_AUDIOCLIENT_ACTIVATION_PARAMS;
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
#pragma GCC diagnostic pop
#endif
static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface)
{ {
MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams;
PROPVARIANT activationParams;
PROPVARIANT* pActivationParams = NULL;
/* Activation parameters specific to loopback mode. */
if (deviceType == ma_device_type_loopback && loopbackProcessID != 0) {
MA_ZERO_OBJECT(&audioclientActivationParams);
audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK;
audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE;
audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID;
ma_PropVariantInit(&activationParams);
activationParams.vt = VT_BLOB;
activationParams.blob.cbSize = sizeof(audioclientActivationParams);
activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams;
pActivationParams = &activationParams;
} else {
pActivationParams = NULL; /* No activation parameters required. */
}
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
#else #else
return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
#endif #endif
} }
...@@ -21048,6 +21119,8 @@ typedef struct ...@@ -21048,6 +21119,8 @@ typedef struct
ma_bool32 noAutoConvertSRC; ma_bool32 noAutoConvertSRC;
ma_bool32 noDefaultQualitySRC; ma_bool32 noDefaultQualitySRC;
ma_bool32 noHardwareOffloading; ma_bool32 noHardwareOffloading;
ma_uint32 loopbackProcessID;
ma_bool32 loopbackProcessExclude;
/* Output. */ /* Output. */
ma_IAudioClient* pAudioClient; ma_IAudioClient* pAudioClient;
...@@ -21101,7 +21174,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ...@@ -21101,7 +21174,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device
streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
} }
result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface); result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
goto done; goto done;
} }
...@@ -21559,6 +21632,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev ...@@ -21559,6 +21632,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev
data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
data.loopbackProcessID = pDevice->wasapi.loopbackProcessID;
data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude;
result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
...@@ -21646,6 +21721,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ...@@ -21646,6 +21721,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -21710,6 +21787,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ...@@ -21710,6 +21787,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment