From 89406b53db922a42c6d659747290591b4e2237a0 Mon Sep 17 00:00:00 2001 From: dmitrykos Date: Fri, 19 Feb 2010 11:16:01 +0000 Subject: [PATCH] windows WASAPI fixes/improvements: - fixed dependency of framesPerBuffer parameter in Pa_OpenStream and latency (now only latency setting affects device latency which is correct behavior); - paFramesPerBufferUnspecified is now supported for Pa_OpenStream; - sound distortion fixed for Shared mode if framesPerBuffer is lower than device limit and when latency was set to 0; - fixed timeout for Exclusive mode due to buffer misalignment; - improved precision of DAC/ADC time for running stream; - avoided memory leaks on failure of Pa_OpenStream; - NULL checks on stream pointer for all external methods; - correct host buffer mode for paWinWasapiPolling mode to deliver fixed number of frames to user space; - implemented workaround for Vista x64 WOW64 bug if Event-mode is used for Shared mode (due to incorrect Event signaling audio dropouts were happening): will fall back to safe Polling method automatically. --- src/hostapi/wasapi/pa_win_wasapi.cpp | 382 +++++++++++++++++++++------ 1 file changed, 298 insertions(+), 84 deletions(-) diff --git a/src/hostapi/wasapi/pa_win_wasapi.cpp b/src/hostapi/wasapi/pa_win_wasapi.cpp index 86b0267..9540398 100644 --- a/src/hostapi/wasapi/pa_win_wasapi.cpp +++ b/src/hostapi/wasapi/pa_win_wasapi.cpp @@ -102,7 +102,7 @@ enum { S_INPUT = 0, S_OUTPUT, S_COUNT }; #define PRINT(x) PA_DEBUG(x); #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) + PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText ) #ifndef IF_FAILED_JUMP #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label; @@ -243,7 +243,10 @@ typedef struct WCHAR defaultRenderer [MAX_STR_LEN]; WCHAR defaultCapturer [MAX_STR_LEN]; - PaWasapiDeviceInfo *devInfo; + PaWasapiDeviceInfo *devInfo; + + // Is true when WOW64 Vista Workaround is needed + bool useVistaWOW64Workaround; } PaWasapiHostApiRepresentation; @@ -254,7 +257,7 @@ typedef struct PaWasapiSubStream IAudioClient *client; WAVEFORMATEXTENSIBLE wavex; UINT32 bufferSize; - REFERENCE_TIME latency; + REFERENCE_TIME device_latency; REFERENCE_TIME period; double latency_seconds; UINT32 framesPerHostCallback; @@ -478,7 +481,7 @@ static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 { #define HDA_PACKET_SIZE 128 - long packets_total = nSamplesPerSec * nBlockAlign / HDA_PACKET_SIZE; + long packets_total = 10000 * (nSamplesPerSec * nBlockAlign / HDA_PACKET_SIZE); long frame_bytes = nFrames * nBlockAlign; // align to packet size @@ -538,6 +541,89 @@ static void CloseAVRT() hDInputDLL = NULL; } +// ------------------------------------------------------------------------------------------ +static BOOL IsWow64() +{ + // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx + + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + LPFN_ISWOW64PROCESS fnIsWow64Process; + + BOOL bIsWow64 = FALSE; + + // IsWow64Process is not available on all supported versions of Windows. + // Use GetModuleHandle to get a handle to the DLL that contains the function + // and GetProcAddress to get a pointer to the function if available. + + fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( + GetModuleHandle(TEXT("kernel32")), TEXT("IsWow64Process")); + + if (fnIsWow64Process == NULL) + return FALSE; + + if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) + return FALSE; + + return bIsWow64; +} + +// ------------------------------------------------------------------------------------------ +typedef enum EWindowsVersion +{ + WINDOWS_UNKNOWN, + WINDOWS_VISTA_SERVER2008, + WINDOWS_7_SERVER2008R2 +} +EWindowsVersion; +// The function is limited to Vista/7 mostly as we need just to find out Vista/WOW64 combination +// in order to use WASAPI WOW64 workarounds. +static EWindowsVersion GetWindowsVersion() +{ + DWORD dwVersion = 0; + DWORD dwMajorVersion = 0; + DWORD dwMinorVersion = 0; + DWORD dwBuild = 0; + + typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID); + LPFN_GETVERSION fnGetVersion; + + fnGetVersion = (LPFN_GETVERSION) GetProcAddress( + GetModuleHandle(TEXT("kernel32")), TEXT("GetVersion")); + + if (fnGetVersion == NULL) + return WINDOWS_UNKNOWN; + + dwVersion = fnGetVersion(); + + // Get the Windows version + dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); + dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); + + // Get the build number + if (dwVersion < 0x80000000) + dwBuild = (DWORD)(HIWORD(dwVersion)); + + switch (dwMajorVersion) + { + case 6: + switch (dwMinorVersion) + { + case 0: + return WINDOWS_VISTA_SERVER2008; + case 1: + return WINDOWS_7_SERVER2008R2; + } + } + + return WINDOWS_UNKNOWN; +} + +// ------------------------------------------------------------------------------------------ +static bool UseWOW64VistaWorkaround() +{ + return (::IsWow64() && (::GetWindowsVersion() == WINDOWS_VISTA_SERVER2008)); +} + // ------------------------------------------------------------------------------------------ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) { @@ -834,6 +920,10 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd GetStreamTime, PaUtil_DummyGetCpuLoad, ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + // findout if platform workaround is required + paWasapi->useVistaWOW64Workaround = ::UseWOW64VistaWorkaround(); + SAFE_RELEASE(pEndPoints); return result; @@ -1274,7 +1364,10 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *outputParameters, double sampleRate) { - if (inputParameters != NULL) + if (hostApi == NULL) + return paHostApiNotFound; + + if (inputParameters != NULL) { /* all standard sample formats are supported by the buffer adapter, this implementation doesn't support any custom sample formats */ @@ -1407,12 +1500,12 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, // ------------------------------------------------------------------------------------------ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info, - const PaStreamParameters *params, UINT32 framesPerBuffer, double sampleRate, UINT32 streamFlags, + const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate, UINT32 streamFlags, PaError *pa_error) { if (!pSubStream || !info || !params) return E_POINTER; - if ((sampleRate == 0) || (framesPerBuffer == 0)) + if ((UINT32)sampleRate == 0) return E_INVALIDARG; PaError error; @@ -1436,19 +1529,32 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn return AUDCLNT_E_UNSUPPORTED_FORMAT; } - // Align frames - framesPerBuffer = AlignFramesPerBuffer(framesPerBuffer, - pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign); + // Add latency frames + framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec); + + // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only. + // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will + // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary + // for Exclusive mode only! when audio data is feeded directly to hardware. + if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) + { + framesPerLatency = AlignFramesPerBuffer(framesPerLatency, + pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign); + } // Calculate period - pSubStream->period = MakeHnsPeriod(framesPerBuffer, pSubStream->wavex.Format.nSamplesPerSec); - pSubStream->period += ::SecondsTonano100(params->suggestedLatency); + pSubStream->period = MakeHnsPeriod(framesPerLatency, pSubStream->wavex.Format.nSamplesPerSec); - // Let's do not enforce min, it is even a bug as MinimumDevicePeriod will make buffer not aligned - // on Vista - //pSubStream->period = (pSubStream->period < info->MinimumDevicePeriod ? info->MinimumDevicePeriod : pSubStream->period); + // Enforce min/max period for device in Shared mode to avoid distorted sound. + // Avoid doing so for Exclusive mode as alignment will suffer. Exclusive mode processes + // big buffers without problem. Push Exclusive beyond limits if possible. + if (pSubStream->shareMode == AUDCLNT_SHAREMODE_SHARED) + { + if (pSubStream->period < info->DefaultDevicePeriod) + pSubStream->period = info->DefaultDevicePeriod; + } - // Open the stream and associate it with an audio session + // Open the stream and associate it with an audio session hr = pAudioClient->Initialize( pSubStream->shareMode, streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, @@ -1509,14 +1615,6 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn goto done; } - // Get the next aligned frame - hr = pAudioClient->GetBufferSize(&nFrames); - if (hr != S_OK) - { - LogHostError(hr); - goto done; - } - // Set result pSubStream->client = pAudioClient; pSubStream->client->AddRef(); @@ -1546,6 +1644,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaSampleFormat inputSampleFormat, outputSampleFormat; PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL; + PaWasapiDeviceInfo *info = NULL; // validate PaStreamParameters if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError) @@ -1568,13 +1667,31 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, // Default thread priority is Audio: for exclusive mode we will use Pro Audio. stream->nThreadPriority = eThreadPriorityAudio; + // Set default number of frames: paFramesPerBufferUnspecified + if (framesPerBuffer == paFramesPerBufferUnspecified) + { + UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0; + if (inputParameters != NULL) + { + info = &paWasapi->devInfo[inputParameters->device]; + framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate); + } + if (outputParameters != NULL) + { + info = &paWasapi->devInfo[outputParameters->device]; + framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate); + } + // choosing maximum default size + framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut); + } + // Try create device: Input if (inputParameters != NULL) { inputChannelCount = inputParameters->channelCount; inputSampleFormat = inputParameters->sampleFormat; inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo; - PaWasapiDeviceInfo *info = &paWasapi->devInfo[inputParameters->device]; + info = &paWasapi->devInfo[inputParameters->device]; stream->in.flags = (inputStreamInfo ? inputStreamInfo->flags : 0); // Select Exclusive/Shared mode @@ -1597,13 +1714,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, // Choose processing mode stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (paWasapi->useVistaWOW64Workaround) + stream->in.streamFlags = 0; // polling interface if (streamCallback == NULL) stream->in.streamFlags = 0; // polling interface if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling)) stream->in.streamFlags = 0; // polling interface // Create Audio client - HRESULT hr = CreateAudioClient(&stream->in, info, inputParameters, framesPerBuffer, + HRESULT hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/, sampleRate, stream->in.streamFlags, &result); if (hr != S_OK) { @@ -1616,6 +1735,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } LogWAVEFORMATEXTENSIBLE(&stream->in.wavex); + // Get closest format + hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat ); + // Create volume mgr stream->inVol = NULL; /*hResult = info->device->Activate( @@ -1638,31 +1760,34 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if (hr != S_OK) { LogHostError(hr); - return paInvalidDevice; + LogPaError(result = paInvalidDevice); + goto error; } // Correct buffer to max size if it maxed out result of GetBufferSize - stream->in.bufferSize = (framesPerBuffer > maxBufferSize ? maxBufferSize : framesPerBuffer); + stream->in.bufferSize = maxBufferSize; - // Get stream latency - hr = stream->in.client->GetStreamLatency(&stream->in.latency); + // Get interface latency (actually uneeded as we calculate latency from the size + // of maxBufferSize). + hr = stream->in.client->GetStreamLatency(&stream->in.device_latency); if (hr != S_OK) { LogHostError(hr); - return paInvalidDevice; + LogPaError(result = paInvalidDevice); + goto error; } - stream->in.latency_seconds = nano100ToSeconds(stream->in.latency); + //stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency); - // Number of samples that are required at each period - stream->in.framesPerHostCallback = - (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? maxBufferSize : stream->in.bufferSize); + // Number of frames that are required at each period + stream->in.framesPerHostCallback = maxBufferSize; - // Get closest format - hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat ); + // Calculate buffer latency + PaTime buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec; + + // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) + stream->in.latency_seconds += buffer_latency; - UINT32 latency = (1000 * maxBufferSize) / stream->in.wavex.Format.nSamplesPerSec + (UINT32)nano100ToMillis(stream->in.latency); - PRINT(("PaWASAPIOpenStream: (input) framesPerHostCallback: %d\n", (UINT32)stream->in.framesPerHostCallback)); - PRINT(("PaWASAPIOpenStream: (input) latency: %d\n", stream->in.latency)); + PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f))); } else { @@ -1676,7 +1801,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, outputChannelCount = outputParameters->channelCount; outputSampleFormat = outputParameters->sampleFormat; outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo; - PaWasapiDeviceInfo *info = &paWasapi->devInfo[outputParameters->device]; + info = &paWasapi->devInfo[outputParameters->device]; stream->out.flags = (outputStreamInfo ? outputStreamInfo->flags : 0); // Select Exclusive/Shared mode @@ -1699,13 +1824,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, // Choose processing mode stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (paWasapi->useVistaWOW64Workaround) + stream->out.streamFlags = 0; // polling interface if (streamCallback == NULL) stream->out.streamFlags = 0; // polling interface if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling)) stream->out.streamFlags = 0; // polling interface // Create Audio client - HRESULT hr = CreateAudioClient(&stream->out, info, outputParameters, framesPerBuffer, + HRESULT hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/, sampleRate, stream->out.streamFlags, &result); if (hr != S_OK) { @@ -1718,6 +1845,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } LogWAVEFORMATEXTENSIBLE(&stream->out.wavex); + // Get closest format + hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat ); + // Activate volume stream->outVol = NULL; /*hResult = info->device->Activate( @@ -1745,29 +1875,30 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } // Correct buffer to max size if it maxed out result of GetBufferSize - stream->out.bufferSize = (framesPerBuffer > maxBufferSize ? maxBufferSize : framesPerBuffer); + stream->out.bufferSize = maxBufferSize; - // Get stream latency - hr = stream->out.client->GetStreamLatency(&stream->out.latency); + // Get interface latency (actually uneeded as we calculate latency from the size + // of maxBufferSize). + hr = stream->out.client->GetStreamLatency(&stream->out.device_latency); if (hr != S_OK) { LogHostError(hr); LogPaError(result = paInvalidDevice); goto error; } - stream->out.latency_seconds = nano100ToSeconds(stream->out.latency); + //stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency); - // Number of samples that are required at each period - stream->out.framesPerHostCallback = - (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? maxBufferSize : stream->out.bufferSize); + // Number of frames that are required at each period + stream->out.framesPerHostCallback = maxBufferSize; - // Get closest format - hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat ); + // Calculate buffer latency + PaTime buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec; - UINT32 latency = (1000 * maxBufferSize) / stream->out.wavex.Format.nSamplesPerSec + (UINT32)nano100ToMillis(stream->out.latency); - PRINT(("PaWASAPIOpenStream: (output) framesPerHostCallback: %d\n", (UINT32)stream->out.framesPerHostCallback)); - PRINT(("PaWASAPIOpenStream: (output) latency: %d\n", latency)); - } + // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) + stream->out.latency_seconds += buffer_latency; + + PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f))); + } else { outputChannelCount = 0; @@ -1791,19 +1922,23 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } } + // Initialize stream representation if (streamCallback) { stream->bBlocking = false; PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, - &paWasapi->callbackStreamInterface, streamCallback, userData); + &paWasapi->callbackStreamInterface, + streamCallback, userData); } else { stream->bBlocking = true; PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, - &paWasapi->blockingStreamInterface, streamCallback, userData); + &paWasapi->blockingStreamInterface, + streamCallback, userData); } + // Initialize CPU measurer PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate); if (outputParameters && inputParameters) @@ -1826,17 +1961,34 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, ULONG framesPerHostCallback = (outputParameters) ? stream->out.framesPerHostCallback: stream->in.framesPerHostCallback; - /* we assume a fixed host buffer size in this example, but the buffer processor - can also support bounded and unknown host buffer sizes by passing - paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of - paUtilFixedHostBufferSize below. - */ - result = PaUtil_InitializeBufferProcessor(&stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerHostCallback, paUtilFixedHostBufferSize, - streamCallback, userData); + // Choose correct mode of buffer processing: + // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed + // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary + PaUtilHostBufferSizeMode bufferMode = paUtilFixedHostBufferSize; + if (inputParameters && + (!stream->in.streamFlags || ((stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0))) + bufferMode = paUtilBoundedHostBufferSize; + else + if (outputParameters && + (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0))) + bufferMode = paUtilBoundedHostBufferSize; + + // Initialize buffer processor + result = PaUtil_InitializeBufferProcessor( + &stream->bufferProcessor, + inputChannelCount, + inputSampleFormat, + hostInputSampleFormat, + outputChannelCount, + outputSampleFormat, + hostOutputSampleFormat, + sampleRate, + streamFlags, + framesPerBuffer, + framesPerHostCallback, + bufferMode, + streamCallback, + userData); if (result != paNoError) { LogPaError(result); @@ -1853,16 +2005,19 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) + ((outputParameters)?stream->out.latency_seconds :0); + // Set SR stream->streamRepresentation.streamInfo.sampleRate = sampleRate; (*s) = (PaStream *)stream; - return result; error: if (stream) + { + CloseStream(stream); PaUtil_FreeMemory(stream); + } return result; } @@ -1873,6 +2028,10 @@ static PaError CloseStream( PaStream* s ) PaError result = paNoError; PaWasapiStream *stream = (PaWasapiStream*)s; + // validate + if (s == NULL) + return paBadStreamPtr; + // abort active stream if (IsStreamActive(s)) { @@ -1907,6 +2066,10 @@ static PaError StartStream( PaStream *s ) HRESULT hr; PaWasapiStream *stream = (PaWasapiStream*)s; + // validate + if (s == NULL) + return paBadStreamPtr; + // check if stream is active already if (IsStreamActive(s)) return paStreamIsNotStopped; @@ -2023,6 +2186,10 @@ static PaError StopStream( PaStream *s ) { PaError result = paNoError; + // validate + if (s == NULL) + return paBadStreamPtr; + // Finish stream _FinishStream((PaWasapiStream *)s); @@ -2034,6 +2201,10 @@ static PaError AbortStream( PaStream *s ) { PaError result = paNoError; + // validate + if (s == NULL) + return paBadStreamPtr; + // Finish stream _FinishStream((PaWasapiStream *)s); @@ -2058,7 +2229,9 @@ static PaError IsStreamActive( PaStream *s ) static PaTime GetStreamTime( PaStream *s ) { PaWasapiStream *stream = (PaWasapiStream*)s; - if (stream == NULL) + + // validate + if (s == NULL) return paBadStreamPtr; /* suppress unused variable warnings */ @@ -2084,8 +2257,14 @@ static double GetStreamCpuLoad( PaStream* s ) static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames ) { PaWasapiStream *stream = (PaWasapiStream*)s; - if (stream == NULL) + + // validate + if (s == NULL) return paBadStreamPtr; + if (_buffer == NULL) + return paBadBufferPtr; + if (_frames == 0) + return paBufferTooSmall; HRESULT hr = S_OK; UINT32 frames; @@ -2148,8 +2327,13 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra { PaWasapiStream *stream = (PaWasapiStream*)s; - if (stream == NULL) + // validate + if (s == NULL) return paBadStreamPtr; + if (_buffer == NULL) + return paBadBufferPtr; + if (_frames == 0) + return paBufferTooSmall; if (!stream->running) return paStreamIsStopped; @@ -2274,7 +2458,8 @@ static signed long GetStreamReadAvailable( PaStream* s ) { PaWasapiStream *stream = (PaWasapiStream*)s; - if (stream == NULL) + // validate + if (s == NULL) return paBadStreamPtr; if (!stream->running) @@ -2301,7 +2486,8 @@ static signed long GetStreamWriteAvailable( PaStream* s ) { PaWasapiStream *stream = (PaWasapiStream*)s; - if (stream == NULL) + // validate + if (s == NULL) return paBadStreamPtr; if (!stream->running) @@ -2353,9 +2539,9 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames, { PaTime pending_time; if ((hResult = stream->in.client->GetCurrentPadding(&pending)) == S_OK) - pending_time = ((PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec) + stream->in.latency_seconds; + pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec; else - pending_time = (PaTime)stream->in.framesPerHostCallback / (PaTime)stream->in.wavex.Format.nSamplesPerSec; + pending_time = (PaTime)stream->in.latency_seconds; timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time; } @@ -2364,9 +2550,9 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames, { PaTime pending_time; if ((hResult = stream->out.client->GetCurrentPadding(&pending)) == S_OK) - pending_time = ((PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec) + stream->out.latency_seconds; + pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec; else - pending_time = (PaTime)stream->out.framesPerHostCallback / (PaTime)stream->out.wavex.Format.nSamplesPerSec; + pending_time = (PaTime)stream->out.latency_seconds; timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time; } @@ -2521,7 +2707,35 @@ static __forceinline HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHo // Get buffer if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK) - return LogHostError(hr); + { + if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED) + { + // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in + // shared mode results in no sound in Event-driven mode (MSDN does not + // document this, or is it WASAPI bug?), thus we better + // try to acquire buffer next time when GetBuffer allows to do so. +#if 0 + // Get Read position + UINT32 padding = 0; + hr = stream->out.client->GetCurrentPadding(&padding); + if (hr != S_OK) + return LogHostError(hr); + + // Get frames to write + frames -= padding; + if (frames == 0) + return S_OK; + + if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK) + return LogHostError(hr); +#else + if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) + return S_OK; // be silent in shared mode, try again next time +#endif + } + else + return LogHostError(hr); + } // Process data processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData); @@ -2660,7 +2874,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) for (;;) { // 2 sec timeout - dwResult = WaitForMultipleObjects(S_COUNT, event, FALSE, 2000); + dwResult = WaitForMultipleObjects(S_COUNT, event, FALSE, 10000); // Check for close event (after wait for buffers to avoid any calls to user // callback when hCloseRequest was set) @@ -2673,7 +2887,6 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) case WAIT_TIMEOUT: { PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n")); goto processing_stop; - //SetEvent(stream->hCloseRequest); break; } // Input stream @@ -2781,14 +2994,15 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) break; UINT32 frames = stream->out.framesPerHostCallback; - BYTE *data = NULL; - DWORD flags = 0; // Get Read position UINT32 padding = 0; hr = stream->out.client->GetCurrentPadding(&padding); - if (hr != S_OK || padding == 0) + if (hr != S_OK) + { + LogHostError(hr); break; + } // Fill buffer frames -= padding; -- 2.43.0