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
......@@ -6656,11 +6656,13 @@ 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. */
ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
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. */
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;
struct
{
......@@ -7402,6 +7404,8 @@ struct ma_device
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) 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 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
ma_bool8 noHardwareOffloading;
......@@ -20764,7 +20768,7 @@ done:
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;
HRESULT hr;
......@@ -20778,7 +20782,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex
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)) {
return ma_result_from_HRESULT(hr);
}
......@@ -20786,7 +20790,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex
return MA_SUCCESS;
}
#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_completion_handler_uwp completionHandler;
......@@ -20828,9 +20832,9 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m
}
#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
hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, pActivationParams, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
#endif
if (FAILED(hr)) {
ma_completion_handler_uwp_uninit(&completionHandler);
......@@ -20870,12 +20874,79 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m
}
#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)
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
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
}
......@@ -21048,6 +21119,8 @@ typedef struct
ma_bool32 noAutoConvertSRC;
ma_bool32 noDefaultQualitySRC;
ma_bool32 noHardwareOffloading;
ma_uint32 loopbackProcessID;
ma_bool32 loopbackProcessExclude;
/* Output. */
ma_IAudioClient* pAudioClient;
......@@ -21101,7 +21174,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device
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) {
goto done;
}
......@@ -21559,6 +21632,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev
data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
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);
if (result != MA_SUCCESS) {
return result;
......@@ -21646,6 +21721,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
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);
if (result != MA_SUCCESS) {
......@@ -21710,6 +21787,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
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);
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