From be7467c0e32c397d6223f2dc844a585d079b9875 Mon Sep 17 00:00:00 2001 From: Leland Lucius Date: Sun, 15 Dec 2019 02:38:06 -0600 Subject: [PATCH] Recreate and apply the WASAPI loopback patch --- lib-src/portaudio-v19/include/pa_win_wasapi.h | 9 + .../src/hostapi/wasapi/pa_win_wasapi.c | 87 +++++++++- lib-src/portaudio-v19/wasapi-loopback.patch | 162 ++++++++---------- 3 files changed, 168 insertions(+), 90 deletions(-) diff --git a/lib-src/portaudio-v19/include/pa_win_wasapi.h b/lib-src/portaudio-v19/include/pa_win_wasapi.h index a4416b121..1999e8dad 100644 --- a/lib-src/portaudio-v19/include/pa_win_wasapi.h +++ b/lib-src/portaudio-v19/include/pa_win_wasapi.h @@ -564,6 +564,15 @@ const wchar_t *PaWasapi_GetInputDeviceID( PaStream *s ); */ const wchar_t *PaWasapi_GetOutputDeviceID( PaStream *s ); +/** Returns device loopback indicator. + + @param nDevice device index. + + @return 0 = Not loopback, 1 = loopback, < 0 = PaErrorCode + if PortAudio is not initialized or an error is encountered. +*/ +int PaWasapi_IsLoopback( PaDeviceIndex nDevice ); + /* IMPORTANT: diff --git a/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c b/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c index f64049ad5..4b28f7f0b 100644 --- a/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c +++ b/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c @@ -449,6 +449,9 @@ typedef struct PaWasapiDeviceInfo // Formfactor EndpointFormFactor formFactor; + + // Loopback indicator + int loopBack; } PaWasapiDeviceInfo; @@ -1674,6 +1677,8 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA IMMDeviceEnumerator *pEnumerator = NULL; #endif IAudioClient *tmpClient; + UINT renderCount; + INT devIndex; // Make sure device list empty if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0)) @@ -1738,6 +1743,19 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA } } + hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender, DEVICE_STATE_ACTIVE, &pEndPoints); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); + + hr = IMMDeviceCollection_GetCount(pEndPoints, &renderCount); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); + + SAFE_RELEASE(pEndPoints); + pEndPoints = NULL; + hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints); // We need to set the result to a value otherwise we will return paNoError // [IF_FAILED_JUMP(hResult, error);] @@ -1748,6 +1766,7 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA // [IF_FAILED_JUMP(hResult, error);] IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); + paWasapi->deviceCount += renderCount; #else // Determine number of available devices by activating AudioClient for render and capture data flows if (SUCCEEDED(ActivateAudioInterface_WINRT(eRender, GetAudioClientIID(), &tmpClient))) @@ -1797,7 +1816,7 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA } memset(deviceInfoArray, 0, sizeof(PaDeviceInfo) * deviceCount); - for (i = 0; i < paWasapi->deviceCount; ++i) + for (devIndex = 0, i = 0; i < paWasapi->deviceCount; ++i, ++devIndex) { PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; deviceInfo->structVersion = 2; @@ -1807,7 +1826,7 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA PA_DEBUG(("WASAPI: ---------------\n")); #ifndef PA_WINRT - hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device); + hr = IMMDeviceCollection_Item(pEndPoints, devIndex, &paWasapi->devInfo[i].device); // We need to set the result to a value otherwise we will return paNoError // [IF_FAILED_JUMP(hResult, error);] IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); @@ -2039,6 +2058,42 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA hostApi->deviceInfos[i] = deviceInfo; ++hostApi->info.deviceCount; + + if (paWasapi->devInfo[i].flow == eRender) + { + char *deviceName; + + memcpy(&deviceInfoArray[i + 1], deviceInfo, sizeof(*deviceInfo)); + memcpy(&paWasapi->devInfo[i + 1], &paWasapi->devInfo[i], sizeof(*paWasapi->devInfo)); + + i++; + paWasapi->devInfo[i].loopBack = 1; + + deviceInfo = &deviceInfoArray[i]; + + deviceInfo->maxInputChannels = deviceInfo->maxOutputChannels; + deviceInfo->defaultHighInputLatency = deviceInfo->defaultHighOutputLatency; + deviceInfo->defaultLowInputLatency = deviceInfo->defaultLowOutputLatency; + deviceInfo->maxOutputChannels = 0; + deviceInfo->defaultHighOutputLatency = 0; + deviceInfo->defaultLowOutputLatency = 0; + PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate, + deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency)); + + IMMDevice_AddRef(paWasapi->devInfo[i].device); + + deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1); + if (deviceName == NULL) + { + result = paInsufficientMemory; + goto error; + } + _snprintf(deviceName, MAX_STR_LEN-1, "%s (loopback)", deviceInfo->name); + deviceInfo->name = deviceName; + + hostApi->deviceInfos[i] = deviceInfo; + ++hostApi->info.deviceCount; + } } #if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0) @@ -2392,6 +2447,29 @@ int PaWasapi_GetDeviceRole( PaDeviceIndex device ) return paWasapi->devInfo[ index ].formFactor; } +// ------------------------------------------------------------------------------------------ +int PaWasapi_IsLoopback( PaDeviceIndex nDevice ) +{ + PaError ret; + PaDeviceIndex index; + + // Get API + PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret); + if (paWasapi == NULL) + return paNotInitialized; + + // Get device index + ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep); + if (ret != paNoError) + return ret; + + // Validate index + if ((UINT32)index >= paWasapi->deviceCount) + return paInvalidDevice; + + return paWasapi->devInfo[ index ].loopBack; +} + // ------------------------------------------------------------------------------------------ PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *pInput, unsigned int *pOutput ) { @@ -3055,7 +3133,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2)) { // select mixer - pSub->monoMixer = GetMonoToStereoMixer(&pSub->wavex, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); + pSub->monoMixer = GetMonoToStereoMixer(&pSub->wavex, (output ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); if (pSub->monoMixer == NULL) { (*pa_error) = paInvalidChannelCount; @@ -3618,6 +3696,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiAutoConvert))) stream->in.streamFlags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY); + if (info->flow == eRender) + stream->in.streamFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK; + // Fill parameters for Audio Client creation stream->in.params.device_info = info; stream->in.params.stream_params = (*inputParameters); diff --git a/lib-src/portaudio-v19/wasapi-loopback.patch b/lib-src/portaudio-v19/wasapi-loopback.patch index ede8055d0..c4a7f5be2 100644 --- a/lib-src/portaudio-v19/wasapi-loopback.patch +++ b/lib-src/portaudio-v19/wasapi-loopback.patch @@ -1,10 +1,10 @@ diff --git a/lib-src/portaudio-v19/include/pa_win_wasapi.h b/lib-src/portaudio-v19/include/pa_win_wasapi.h -index 1087157..e52d0a7 100644 +index a4416b121..1999e8dad 100644 --- a/lib-src/portaudio-v19/include/pa_win_wasapi.h +++ b/lib-src/portaudio-v19/include/pa_win_wasapi.h -@@ -319,6 +319,16 @@ int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, Pa - int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ); - +@@ -564,6 +564,15 @@ const wchar_t *PaWasapi_GetInputDeviceID( PaStream *s ); + */ + const wchar_t *PaWasapi_GetOutputDeviceID( PaStream *s ); +/** Returns device loopback indicator. + @@ -15,15 +15,14 @@ index 1087157..e52d0a7 100644 +*/ +int PaWasapi_IsLoopback( PaDeviceIndex nDevice ); + -+ - /** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread - which makes calls to Pa_WriteStream/Pa_ReadStream. + /* + IMPORTANT: diff --git a/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c b/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c -index 0026033..2e9dca8 100644 +index f64049ad5..4b28f7f0b 100644 --- a/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c +++ b/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c -@@ -375,6 +375,9 @@ typedef struct PaWasapiDeviceInfo +@@ -449,6 +449,9 @@ typedef struct PaWasapiDeviceInfo // Formfactor EndpointFormFactor formFactor; @@ -33,110 +32,109 @@ index 0026033..2e9dca8 100644 } PaWasapiDeviceInfo; -@@ -1179,6 +1182,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd - HRESULT hr = S_OK; - IMMDeviceCollection* pEndPoints = NULL; - UINT i; -+ UINT renderCount; -+ UINT devIndex; +@@ -1674,6 +1677,8 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA + IMMDeviceEnumerator *pEnumerator = NULL; + #endif + IAudioClient *tmpClient; ++ UINT renderCount; ++ INT devIndex; - if (!SetupAVRT()) - { -@@ -1275,6 +1280,19 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd + // Make sure device list empty + if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0)) +@@ -1738,6 +1743,19 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA } } -+ hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eRender, DEVICE_STATE_ACTIVE, &pEndPoints); ++ hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender, DEVICE_STATE_ACTIVE, &pEndPoints); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); + -+ hr = IMMDeviceCollection_GetCount(pEndPoints, &renderCount); ++ hr = IMMDeviceCollection_GetCount(pEndPoints, &renderCount); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); + -+ SAFE_RELEASE(pEndPoints); -+ pEndPoints = NULL; ++ SAFE_RELEASE(pEndPoints); ++ pEndPoints = NULL; + - hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints); + hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints); // We need to set the result to a value otherwise we will return paNoError // [IF_FAILED_JUMP(hResult, error);] -@@ -1285,6 +1303,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd +@@ -1748,6 +1766,7 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA // [IF_FAILED_JUMP(hResult, error);] IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); -+ paWasapi->deviceCount += renderCount; -+ - paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_AllocateMemory(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount); - if (paWasapi->devInfo == NULL) - { -@@ -1313,7 +1333,7 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd - goto error; ++ paWasapi->deviceCount += renderCount; + #else + // Determine number of available devices by activating AudioClient for render and capture data flows + if (SUCCEEDED(ActivateAudioInterface_WINRT(eRender, GetAudioClientIID(), &tmpClient))) +@@ -1797,7 +1816,7 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA } + memset(deviceInfoArray, 0, sizeof(PaDeviceInfo) * deviceCount); - for (i = 0; i < paWasapi->deviceCount; ++i) + for (devIndex = 0, i = 0; i < paWasapi->deviceCount; ++i, ++devIndex) { - DWORD state = 0; PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; -@@ -1323,7 +1343,7 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd - PA_DEBUG(("WASAPI: device idx: %02d\n", i)); + deviceInfo->structVersion = 2; +@@ -1807,7 +1826,7 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA PA_DEBUG(("WASAPI: ---------------\n")); + #ifndef PA_WINRT - hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device); + hr = IMMDeviceCollection_Item(pEndPoints, devIndex, &paWasapi->devInfo[i].device); // We need to set the result to a value otherwise we will return paNoError // [IF_FAILED_JUMP(hResult, error);] IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); -@@ -1513,6 +1533,43 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd +@@ -2039,6 +2058,42 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA - (*hostApi)->deviceInfos[i] = deviceInfo; - ++(*hostApi)->info.deviceCount; + hostApi->deviceInfos[i] = deviceInfo; + ++hostApi->info.deviceCount; + -+ if (paWasapi->devInfo[i].flow == eRender) ++ if (paWasapi->devInfo[i].flow == eRender) + { -+ char *deviceName; -+ UINT deviceNameLen; ++ char *deviceName; + -+ memcpy(&deviceInfoArray[i + 1], deviceInfo, sizeof(*deviceInfo)); -+ memcpy(&paWasapi->devInfo[i + 1], &paWasapi->devInfo[i], sizeof(*paWasapi->devInfo)); ++ memcpy(&deviceInfoArray[i + 1], deviceInfo, sizeof(*deviceInfo)); ++ memcpy(&paWasapi->devInfo[i + 1], &paWasapi->devInfo[i], sizeof(*paWasapi->devInfo)); + -+ i++; -+ paWasapi->devInfo[i].loopBack = 1; ++ i++; ++ paWasapi->devInfo[i].loopBack = 1; + -+ deviceInfo = &deviceInfoArray[i]; ++ deviceInfo = &deviceInfoArray[i]; + -+ deviceInfo->maxInputChannels = deviceInfo->maxOutputChannels; -+ deviceInfo->defaultHighInputLatency = deviceInfo->defaultHighOutputLatency; -+ deviceInfo->defaultLowInputLatency = deviceInfo->defaultLowOutputLatency; -+ deviceInfo->maxOutputChannels = 0; -+ deviceInfo->defaultHighOutputLatency = 0; -+ deviceInfo->defaultLowOutputLatency = 0; ++ deviceInfo->maxInputChannels = deviceInfo->maxOutputChannels; ++ deviceInfo->defaultHighInputLatency = deviceInfo->defaultHighOutputLatency; ++ deviceInfo->defaultLowInputLatency = deviceInfo->defaultLowOutputLatency; ++ deviceInfo->maxOutputChannels = 0; ++ deviceInfo->defaultHighOutputLatency = 0; ++ deviceInfo->defaultLowOutputLatency = 0; + PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate, -+ deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency)); ++ deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency)); + -+ IMMDevice_AddRef(paWasapi->devInfo[i].device); ++ IMMDevice_AddRef(paWasapi->devInfo[i].device); + -+ deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1); -+ if (deviceName == NULL) ++ deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1); ++ if (deviceName == NULL) + { -+ result = paInsufficientMemory; -+ goto error; -+ } ++ result = paInsufficientMemory; ++ goto error; ++ } + _snprintf(deviceName, MAX_STR_LEN-1, "%s (loopback)", deviceInfo->name); -+ deviceInfo->name = deviceName; ++ deviceInfo->name = deviceName; + -+ (*hostApi)->deviceInfos[i] = deviceInfo; -+ ++(*hostApi)->info.deviceCount; -+ } ++ hostApi->deviceInfos[i] = deviceInfo; ++ ++hostApi->info.deviceCount; ++ } } - } -@@ -1691,6 +1748,29 @@ const wchar_t *PaWasapi_GetOutputDeviceID( PaStream* s ) + #if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0) +@@ -2392,6 +2447,29 @@ int PaWasapi_GetDeviceRole( PaDeviceIndex device ) + return paWasapi->devInfo[ index ].formFactor; } - // ------------------------------------------------------------------------------------------ ++// ------------------------------------------------------------------------------------------ +int PaWasapi_IsLoopback( PaDeviceIndex nDevice ) +{ + PaError ret; @@ -149,8 +147,8 @@ index 0026033..2e9dca8 100644 + + // Get device index + ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep); -+ if (ret != paNoError) -+ return ret; ++ if (ret != paNoError) ++ return ret; + + // Validate index + if ((UINT32)index >= paWasapi->deviceCount) @@ -159,31 +157,21 @@ index 0026033..2e9dca8 100644 + return paWasapi->devInfo[ index ].loopBack; +} + -+// ------------------------------------------------------------------------------------------ - PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput ) + // ------------------------------------------------------------------------------------------ + PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *pInput, unsigned int *pOutput ) { - PaWasapiStream *stream = (PaWasapiStream *)pStream; -@@ -2336,7 +2416,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu - }*/ - +@@ -3055,7 +3133,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu + if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2)) + { // select mixer -- pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); -+ pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (output ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); +- pSub->monoMixer = GetMonoToStereoMixer(&pSub->wavex, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); ++ pSub->monoMixer = GetMonoToStereoMixer(&pSub->wavex, (output ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); if (pSub->monoMixer == NULL) { (*pa_error) = paInvalidChannelCount; -@@ -2621,7 +2701,7 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu - }*/ - - // Select mixer -- pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); -+ pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (output ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); - if (pSub->monoMixer == NULL) - { - (*pa_error) = paInvalidChannelCount; -@@ -2941,6 +3021,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - if (fullDuplex) - stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also +@@ -3618,6 +3696,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiAutoConvert))) + stream->in.streamFlags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY); + if (info->flow == eRender) + stream->in.streamFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK;