#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
-
+#define PA_MME_MAX_TIMEOUT_MSEC_ (2000)
#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
static const char constInputMapperSuffix_[] = " - Input";
for( i = 0 ; i < maximumPossibleDeviceCount; ++i )
{
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
- deviceInfo->structVersion = 3;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = 0;
+ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[i];
+ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+
+ deviceInfo->structVersion = 3;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = 0;
+ /*
+ * The fields maxInputChannels and maxOutputChannels used to be
+ * initialized in each of the loops bellow to the effect of the
+ * second loop overwriting/throwing away the results of the first
+ * loop. In order to resolve the described issue, the initialization
+ * of the two fields in question has been moved here.
+ */
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ /*
+ * The fields defaultLowInputLatency, defaultLowOutputLatency,
+ * defaultHighInputLatency and defaultHighOutputLatency used to be
+ * initialized in each of the loops bellow. For the sake of clarity,
+ * their initialization has been moved here.
+ */
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+ /*
+ * The fields deviceInputChannelCountIsKnown and
+ * deviceOutputChannelCountIsKnown used to be initialized in each of
+ * the loops bellow to the effect of the second loop
+ * overwriting/throwing away the results of the first loop. In order
+ * to resolve the described issue, the initialization of the two
+ * fields in question has been moved here.
+ */
+ wmmeDeviceInfo->deviceInputChannelCountIsKnown = 0;
+ wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 0;
outArgument->deviceInfos[ i ] = deviceInfo;
}
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
PaWinMmeDeviceInfo *wmmeDeviceInfo = (PaWinMmeDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 3;
- deviceInfo->hostApi = hostApiIndex;
-
- deviceInfo->maxInputChannels = 0;
- wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
- deviceInfo->maxOutputChannels = 0;
- wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
-
- deviceInfo->defaultLowInputLatency = defaultLowLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowLatency;
- deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
winMmeDeviceId, &deviceInfoInitializationSucceeded );
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
PaWinMmeDeviceInfo *wmmeDeviceInfo = (PaWinMmeDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 3;
- deviceInfo->hostApi = hostApiIndex;
-
- deviceInfo->maxInputChannels = 0;
- wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
- deviceInfo->maxOutputChannels = 0;
- wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
-
- deviceInfo->defaultLowInputLatency = defaultLowLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowLatency;
- deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
winMmeDeviceId, &deviceInfoInitializationSucceeded );
{
PaError result = paNoError;
PaWinMmeStream *stream = (PaWinMmeStream*)s;
- int timeout;
+ DWORD timeout;
DWORD waitResult;
MMRESULT mmresult;
signed int hostOutputBufferIndex;
stream->stopProcessing = 1;
/* Calculate timeOut longer than longest time it could take to return all buffers. */
- timeout = (int)(stream->allBuffersDurationMs * 1.5);
+ timeout = (DWORD)(stream->allBuffersDurationMs * 1.5);
if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
if( PA_IS_OUTPUT_STREAM_(stream) )
{
+ /* If WaitForSingleObject starts repetitively and consecutively
+ returning WAIT_TIMEOUT, do eventually give up. Otherwise, the
+ method may never return.
+ */
+ DWORD totalTimeout;
+
if( stream->output.framesUsedInCurrentBuffer > 0 )
{
/* there are still unqueued frames in the current buffer, so flush them */
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
waitCount = 0;
+ totalTimeout = 0;
while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
{
/* wait for MME to signal that a buffer is available */
}
else if( waitResult == WAIT_TIMEOUT )
{
- /* keep waiting */
+ /* Keep waiting. However, testing has shown that it is
+ possible to unplug a device and to wait here forever. In
+ order to prevent such a scenario, do eventually given up.
+ */
+ totalTimeout += timeout;
+ if( PA_MME_MAX_TIMEOUT_MSEC_ <= totalTimeout)
+ {
+ result = paTimedOut;
+ break;
+ }
}
+ else
+ totalTimeout = 0;
++waitCount;
}
if( PA_IS_INPUT_STREAM_(stream) )
{
+ /* If WaitForSingleObject starts repetitively and consecutively
+ returning WAIT_TIMEOUT, do eventually give up in order to allow the
+ caller to handle such cases.
+ */
+ DWORD totalTimeout = 0;
+
/* make a local copy of the user buffer pointer(s). this is necessary
because PaUtil_CopyInput() advances these pointers every time
it is called.
do{
if( CurrentInputBuffersAreDone( stream ) )
{
+ totalTimeout = 0;
+
if( NoBuffersAreQueued( &stream->input ) )
{
/** @todo REVIEW: consider what to do if the input overflows.
}
else if( waitResult == WAIT_TIMEOUT )
{
- /* if a timeout is encountered, continue,
- perhaps we should give up eventually
+ /* If a timeout is encountered, continue. However, testing
+ has shown that it is possible to unplug a device and to
+ wait here forever. In order to allow the caller to handle
+ such cases of repeated timeouts, do eventually given up.
*/
+ totalTimeout += timeout;
+ if( PA_MME_MAX_TIMEOUT_MSEC_ <= totalTimeout)
+ {
+ result = paTimedOut;
+ break;
+ }
}
}
}while( framesRead < frames );
if( PA_IS_OUTPUT_STREAM_(stream) )
{
+ /* If WaitForSingleObject starts repetitively and consecutively
+ returning WAIT_TIMEOUT, do eventually give up in order to allow the
+ caller to handle such cases.
+ */
+ DWORD totalTimeout = 0;
+
/* make a local copy of the user buffer pointer(s). this is necessary
because PaUtil_CopyOutput() advances these pointers every time
it is called.
do{
if( CurrentOutputBuffersAreDone( stream ) )
{
+ totalTimeout = 0;
+
if( NoBuffersAreQueued( &stream->output ) )
{
/** @todo REVIEW: consider what to do if the output
}
else if( waitResult == WAIT_TIMEOUT )
{
- /* if a timeout is encountered, continue,
- perhaps we should give up eventually
+ /* If a timeout is encountered, continue. However, testing
+ has shown that it is possible to unplug a device and to
+ wait here forever. In order to allow the caller to handle
+ such cases of repeated timeouts, do eventually given up.
*/
+ totalTimeout += timeout;
+ if( PA_MME_MAX_TIMEOUT_MSEC_ <= totalTimeout)
+ {
+ result = paTimedOut;
+ break;
+ }
}
}
}while( framesWritten < frames );