mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 01:04:02 +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
|
struct
|
||||||
{
|
{
|
||||||
ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */
|
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 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 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
|
|||||||
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:
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
#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
|
|||||||
}
|
}
|
||||||
#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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user