mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
WASAPI: Add support for process-specific loopback capture.
Public issue https://github.com/mackron/miniaudio/issues/484
This commit is contained in:
+93
-14
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user