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.
This commit is contained in:
parent
75e59cb874
commit
89406b53db
1 changed files with 298 additions and 84 deletions
|
|
@ -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;
|
||||
|
|
@ -244,6 +244,9 @@ typedef struct
|
|||
WCHAR defaultCapturer [MAX_STR_LEN];
|
||||
|
||||
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,6 +1364,9 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
|
|||
const PaStreamParameters *outputParameters,
|
||||
double sampleRate)
|
||||
{
|
||||
if (hostApi == NULL)
|
||||
return paHostApiNotFound;
|
||||
|
||||
if (inputParameters != NULL)
|
||||
{
|
||||
/* all standard sample formats are supported by the buffer adapter,
|
||||
|
|
@ -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,17 +1529,30 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
|
|||
return AUDCLNT_E_UNSUPPORTED_FORMAT;
|
||||
}
|
||||
|
||||
// Align frames
|
||||
framesPerBuffer = AlignFramesPerBuffer(framesPerBuffer,
|
||||
// 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
|
||||
hr = pAudioClient->Initialize(
|
||||
|
|
@ -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;
|
||||
|
||||
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));
|
||||
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
|
||||
stream->in.latency_seconds += buffer_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,28 +1875,29 @@ 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
|
||||
{
|
||||
|
|
@ -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,8 +2707,36 @@ static __forceinline HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHo
|
|||
|
||||
// Get buffer
|
||||
if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK)
|
||||
{
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue