/*
* $Id$
* pa_win_wmme.c
- * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
- *
+ * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
+ *
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
*
*/
/*
- * The text above constitutes the entire PortAudio license; however,
+ * The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version. It is also
- * requested that these non-binding requests be included along with the
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
* license above.
*/
#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
- #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
- #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
- #define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_ (16)
- #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
- #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
+ #define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_ (16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
#else
- #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
- #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3) /* always use at least 3 input buffers for full duplex */
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
- #define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_ (16)
- #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
- #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3) /* always use at least 3 input buffers for full duplex */
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
+ #define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_ (16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
#endif
/* Use higher latency for NT because it is even worse at real-time
survey of workable latency settings using patest_wmme_find_best_latency_params.c.
See pdf attached to ticket 185 for a graph of the survey results:
http://www.portaudio.com/trac/ticket/185
-
+
Workable latencies varied between 40ms and ~80ms on different systems (different
combinations of hardware, 32 and 64 bit, WinXP, Vista and Win7. We didn't
get enough Vista results to know if Vista has systemically worse latency.
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (0.090)
-/* When client suggestedLatency could result in many host buffers, we aim to have around 8,
+/* When client suggestedLatency could result in many host buffers, we aim to have around 8,
based off Windows documentation that suggests that the kmixer uses 8 buffers. This choice
- is somewhat arbitrary here, since we haven't observed significant stability degredation
- with using either more, or less buffers.
+ is somewhat arbitrary here, since we haven't observed significant stability degredation
+ with using either more, or less buffers.
*/
#define PA_MME_TARGET_HOST_BUFFER_COUNT_ 8
{
/* The cbMultiByte parameter ["destLengthBytes" below] is:
"""
- Size, in bytes, of the buffer indicated by lpMultiByteStr ["destination" below].
- If this parameter is set to 0, the function returns the required buffer
+ Size, in bytes, of the buffer indicated by lpMultiByteStr ["destination" below].
+ If this parameter is set to 0, the function returns the required buffer
size for lpMultiByteStr and makes no use of the output parameter itself.
"""
Source: WideCharToMultiByte at MSDN:
{
intDestLengthBytes = INT_MAX;
}
-
+
if (WideCharToMultiByte(CP_UTF8, 0, source, -1, destination, /*cbMultiByte=*/intDestLengthBytes, NULL, NULL) == 0)
return NULL;
return destination;
}
-/* returns required length (in bytes) of destination buffer when
+/* returns required length (in bytes) of destination buffer when
converting WCHAR string to UTF8 bytes, not including the terminating null. */
static size_t WCharStringLen(const WCHAR *str)
{
PaError result = paNoError;
*handle = NULL;
-
+
*handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
if( *handle == NULL )
{
static PaError CloseHandleWithPaError( HANDLE handle )
{
PaError result = paNoError;
-
+
if( handle )
{
if( CloseHandle( handle ) == 0 )
PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
}
}
-
+
return result;
}
PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations;
-
+
int inputDeviceCount, outputDeviceCount;
/** winMmeDeviceIds is an array of WinMme device ids.
fields in the range [0, inputDeviceCount) are input device ids,
and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
device ids.
- */
+ */
UINT *winMmeDeviceIds;
}
PaWinMmeHostApiRepresentation;
static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
{
MMRESULT mmresult;
-
+
switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
{
case MMSYSERR_NOERROR:
return paNoError;
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
return paDeviceUnavailable;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
return paDeviceUnavailable;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
return paInsufficientMemory;
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
return paSampleFormatNotSupported;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
/* falls through */
default:
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
{
MMRESULT mmresult;
-
+
switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
{
case MMSYSERR_NOERROR:
return paNoError;
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
return paDeviceUnavailable;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
return paDeviceUnavailable;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
return paInsufficientMemory;
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
return paSampleFormatNotSupported;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
/* falls through */
default:
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
PaWinWaveFormat waveFormat;
PaSampleFormat sampleFormat;
int waveFormatTag;
-
+
/* @todo at the moment we only query with 16 bit sample format and directout speaker config*/
sampleFormat = paInt16;
waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
if( waveFormatTag == PaWin_SampleFormatToLinearWaveFormatTag( paInt16 ) ){
-
+
/* attempt bypass querying the device for linear formats */
if( sampleRate == 11025.0
}
- /* first, attempt to query the device using WAVEFORMATEXTENSIBLE,
+ /* first, attempt to query the device using WAVEFORMATEXTENSIBLE,
if this fails we fall back to WAVEFORMATEX */
PaWin_InitializeWaveFormatExtensible( &waveFormat, channels, sampleFormat, waveFormatTag,
{
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
int i;
-
+
deviceInfo->defaultSampleRate = 0.;
for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
{
- double sampleRate = defaultSampleRateSearchOrder_[ i ];
+ double sampleRate = defaultSampleRateSearchOrder_[ i ];
PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate, 0 );
if( paerror == paNoError )
{
WAVEINCAPSW wic;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
size_t len;
-
+
*success = 0;
mmresult = waveInGetDevCapsW( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPSW ) );
the entire initialisation process.
*/
return paNoError;
- }
+ }
/* NOTE: the WAVEOUTCAPS.szPname is a null-terminated array of 32 characters,
so we are limited to displaying only the first 31 characters of the device name. */
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- winMmeDeviceInfo->deviceInputChannelCountIsKnown =
+ winMmeDeviceInfo->deviceInputChannelCountIsKnown =
QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
*success = 1;
-
+
error:
return result;
}
/* Append I/O suffix to WAVE_MAPPER device. */
len = WCharStringLen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_);
deviceName = (char*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations,
+ winMmeHostApi->allocations,
(long)len );
if( !deviceName )
{
{
len = WCharStringLen( woc.szPname ) + 1;
deviceName = (char*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations,
+ winMmeHostApi->allocations,
(long)len );
if( !deviceName )
{
* device*ChannelCountIsKnown is false, OpenStream will try whatever
* channel count you supply.
* see also InitializeInputDeviceInfo() above.
- */
+ */
PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", woc.wChannels ));
deviceInfo->maxOutputChannels = 2;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- wdmksDeviceOutputChannelCountIsKnown = QueryWaveOutKSFilterMaxChannels(
+ wdmksDeviceOutputChannelCountIsKnown = QueryWaveOutKSFilterMaxChannels(
winMmeOutputDeviceId, &deviceInfo->maxOutputChannels );
if( wdmksDeviceOutputChannelCountIsKnown && !winMmeDeviceInfo->deviceOutputChannelCountIsKnown )
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
*success = 1;
-
+
error:
return result;
}
}
else if(osvi.dwMajorVersion >= 5)
{
- *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
+ *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
}
else
{
- *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
- }
+ *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
+ }
*defaultHighLatency = *defaultLowLatency * 2;
(*hostApi)->info.type = paMME;
(*hostApi)->info.name = "MME";
-
+
/* initialise device counts and default devices under the assumption that
there are no devices. These values are incremented below if and when
devices are successfully initialized.
inputDeviceCount = waveInGetNumDevs();
if( inputDeviceCount > 0 )
- maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
+ maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
outputDeviceCount = waveOutGetNumDevs();
if( outputDeviceCount > 0 )
- maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
+ maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
if( maximumPossibleDeviceCount > 0 ){
if( (*hostApi)->info.defaultInputDevice == paNoDevice ){
/* if there is currently no default device, use the first one available */
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
-
+
}else if( winMmeDeviceId == waveInPreferredDevice ){
/* set the default device to the system preferred device */
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
winMmeDeviceId, &deviceInfoInitializationSucceeded );
}
}
}
-
+
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
(*hostApi)->Terminate = Terminate;
PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
}
-
+
PaUtil_FreeMemory( winMmeHostApi );
}
&& deviceInfo->deviceInputChannelCountIsKnown
&& channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){
- result = paInvalidChannelCount;
+ result = paInvalidChannelCount;
}
return result;
&& deviceInfo->deviceOutputChannelCountIsKnown
&& channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){
- result = paInvalidChannelCount;
+ result = paInvalidChannelCount;
}
return result;
then the only thing that could fail is the sample rate. This isn't
strictly true, but I can't think of a better way to test that the
sample rate is valid.
- */
-
+ */
+
if( inputParameters )
{
inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat;
inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
-
+
/* all standard sample formats are supported by the buffer adapter,
this implementation doesn't support any custom sample formats */
if( inputSampleFormat & paCustomFormat )
for( i=0; i< inputStreamInfo->deviceCount; ++i )
{
inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
-
+
inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
/* check that input device can support inputChannelCount */
if( inputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
- paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo,
+ paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo,
inputStreamInfo->devices[i].channelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
- winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate,
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
+ winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate,
((inputStreamInfo) ? inputStreamInfo->flags : 0) );
if( paerror != paNoError )
return paInvalidSampleRate;
}
-
+
if( inputMultipleDeviceChannelCount != inputChannelCount )
- return paIncompatibleHostApiSpecificStreamInfo;
+ return paIncompatibleHostApiSpecificStreamInfo;
}
else
{
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
winMmeInputDeviceId, inputChannelCount, sampleRate,
((inputStreamInfo) ? inputStreamInfo->flags : 0) );
if( paerror != paNoError )
for( i=0; i< outputStreamInfo->deviceCount; ++i )
{
outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
-
+
outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
/* check that output device can support outputChannelCount */
if( outputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
- paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo,
+ paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo,
outputStreamInfo->devices[i].channelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate,
((outputStreamInfo) ? outputStreamInfo->flags : 0) );
if( paerror != paNoError )
return paInvalidSampleRate;
}
-
+
if( outputMultipleDeviceChannelCount != outputChannelCount )
- return paIncompatibleHostApiSpecificStreamInfo;
+ return paIncompatibleHostApiSpecificStreamInfo;
}
else
{
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
winMmeOutputDeviceId, outputChannelCount, sampleRate,
((outputStreamInfo) ? outputStreamInfo->flags : 0) );
if( paerror != paNoError )
return paInvalidSampleRate;
}
}
-
+
/*
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
- - check that the device supports sampleRate
+ - check that the device supports sampleRate
- for mme all we can do is test that the input and output devices
- support the requested sample rate and number of channels. we
- cannot test for full duplex compatibility.
- */
+ for mme all we can do is test that the input and output devices
+ support the requested sample rate and number of channels. we
+ cannot test for full duplex compatibility.
+ */
return paFormatIsSupported;
}
unsigned long hostBufferSizeFrames,
unsigned long minimumBufferCount )
{
- /* Calculate the number of buffers of length hostFramesPerBuffer
+ /* Calculate the number of buffers of length hostFramesPerBuffer
that fit in suggestedLatencyFrames, rounding up to the next integer.
The value (hostBufferSizeFrames - 1) below is to ensure the buffer count is rounded up.
}
-static unsigned long ComputeHostBufferSizeGivenHardUpperLimit(
+static unsigned long ComputeHostBufferSizeGivenHardUpperLimit(
unsigned long userFramesPerBuffer,
unsigned long absoluteMaximumBufferSizeFrames )
{
- static unsigned long primes_[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23,
+ static unsigned long primes_[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23,
29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 0 }; /* zero terminated */
unsigned long result = userFramesPerBuffer;
assert( absoluteMaximumBufferSizeFrames > 67 ); /* assume maximum is large and we're only factoring by small primes */
- /* search for the largest integer factor of userFramesPerBuffer less
+ /* search for the largest integer factor of userFramesPerBuffer less
than or equal to absoluteMaximumBufferSizeFrames */
- /* repeatedly divide by smallest prime factors until a buffer size
+ /* repeatedly divide by smallest prime factors until a buffer size
smaller than absoluteMaximumBufferSizeFrames is found */
while( result > absoluteMaximumBufferSizeFrames ){
/* search for the smallest prime factor of result */
- for( i=0; primes_[i] != 0; ++i )
+ for( i=0; primes_[i] != 0; ++i )
{
unsigned long p = primes_[i];
unsigned long divided = result / p;
effectiveUserFramesPerBuffer = ComputeHostBufferSizeGivenHardUpperLimit( userFramesPerBuffer, absoluteMaximumBufferSizeFrames );
assert( effectiveUserFramesPerBuffer <= absoluteMaximumBufferSizeFrames );
- /* try to ensure that duration of host buffering is at least as
+ /* try to ensure that duration of host buffering is at least as
large as duration of user buffer. */
if( suggestedLatencyFrames < userFramesPerBuffer )
- suggestedLatencyFrames = userFramesPerBuffer;
+ suggestedLatencyFrames = userFramesPerBuffer;
}else{
effectiveUserFramesPerBuffer = userFramesPerBuffer;
}
}
-
+
/* compute a host buffer count based on suggestedLatencyFrames and our granularity */
*hostBufferSizeFrames = effectiveUserFramesPerBuffer;
if( *hostBufferSizeFrames >= userFramesPerBuffer )
{
/*
- If there are too many host buffers we would like to coalesce
+ If there are too many host buffers we would like to coalesce
them by packing an integer number of user buffers into each host buffer.
- We try to coalesce such that hostBufferCount will lie between
+ We try to coalesce such that hostBufferCount will lie between
PA_MME_TARGET_HOST_BUFFER_COUNT_ and (PA_MME_TARGET_HOST_BUFFER_COUNT_*2)-1.
We limit coalescing to avoid exceeding either absoluteMaximumBufferSizeFrames and
- preferredMaximumBufferSizeFrames.
+ preferredMaximumBufferSizeFrames.
First, compute a coalescing factor: the number of user buffers per host buffer.
The goal is to achieve PA_MME_TARGET_HOST_BUFFER_COUNT_ total buffer count.
The + (PA_MME_TARGET_HOST_BUFFER_COUNT_-2) term below is intended to round up.
*/
numberOfUserBuffersPerHostBuffer = ((*hostBufferCount - 1) + (PA_MME_TARGET_HOST_BUFFER_COUNT_-2)) / (PA_MME_TARGET_HOST_BUFFER_COUNT_ - 1);
-
+
if( numberOfUserBuffersPerHostBuffer > 1 )
{
unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* minimum of our limits */
int *hostSampleFrameSizeBytes )
{
unsigned int i;
- /* PA WMME streams may aggregate multiple WMME devices. When the stream addresses
- more than one device in a single direction, maxDeviceChannelCount is the maximum
+ /* PA WMME streams may aggregate multiple WMME devices. When the stream addresses
+ more than one device in a single direction, maxDeviceChannelCount is the maximum
number of channels used by a single device.
*/
int maxDeviceChannelCount = channelCount;
double sampleRate, unsigned long userFramesPerBuffer )
{
PaError result = paNoError;
-
+
if( inputChannelCount > 0 ) /* stream has input */
{
int hostInputFrameSizeBytes;
- result = CalculateMaxHostSampleFrameSizeBytes(
+ result = CalculateMaxHostSampleFrameSizeBytes(
inputChannelCount, hostInputSampleFormat, inputStreamInfo, &hostInputFrameSizeBytes );
if( result != paNoError )
goto error;
}
else
{
- /* input - not using low level latency parameters, so compute
+ /* input - not using low level latency parameters, so compute
hostFramesPerInputBuffer and hostInputBufferCount
based on userFramesPerBuffer and suggestedInputLatency. */
userFramesPerBuffer,
minimumBufferCount,
(unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
- (PA_MME_MAX_HOST_BUFFER_BYTES_ / hostInputFrameSizeBytes), /* in frames. a hard limit. note truncation due to
+ (PA_MME_MAX_HOST_BUFFER_BYTES_ / hostInputFrameSizeBytes), /* in frames. a hard limit. note truncation due to
division is intentional here to limit max bytes */
hostFramesPerInputBuffer,
hostInputBufferCount );
{
if( inputStreamInfo
&& ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
- {
+ {
/* a custom StreamInfo was used for specifying both input
and output buffer sizes. We require that the larger buffer size
must be a multiple of the smaller buffer size */
result = paIncompatibleHostApiSpecificStreamInfo;
goto error;
}
- }
+ }
}
else
{
*hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
*hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
- (unsigned long)(suggestedInputLatency * sampleRate),
- *hostFramesPerInputBuffer,
+ (unsigned long)(suggestedInputLatency * sampleRate),
+ *hostFramesPerInputBuffer,
PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
}
}
based on userFramesPerBuffer and suggestedOutputLatency. */
int hostOutputFrameSizeBytes;
- result = CalculateMaxHostSampleFrameSizeBytes(
+ result = CalculateMaxHostSampleFrameSizeBytes(
outputChannelCount, hostOutputSampleFormat, outputStreamInfo, &hostOutputFrameSizeBytes );
if( result != paNoError )
goto error;
userFramesPerBuffer,
PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_,
(unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
- (PA_MME_MAX_HOST_BUFFER_BYTES_ / hostOutputFrameSizeBytes), /* in frames. a hard limit. note truncation due to
+ (PA_MME_MAX_HOST_BUFFER_BYTES_ / hostOutputFrameSizeBytes), /* in frames. a hard limit. note truncation due to
division is intentional here to limit max bytes */
hostFramesPerOutputBuffer,
hostOutputBufferCount );
*hostFramesPerOutputBuffer = *hostFramesPerInputBuffer;
*hostOutputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
- (unsigned long)(suggestedOutputLatency * sampleRate),
- *hostOutputBufferCount,
+ (unsigned long)(suggestedOutputLatency * sampleRate),
+ *hostOutputBufferCount,
PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ );
}
else
*hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
*hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
- (unsigned long)(suggestedInputLatency * sampleRate),
- *hostFramesPerInputBuffer,
+ (unsigned long)(suggestedInputLatency * sampleRate),
+ *hostFramesPerInputBuffer,
PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
}
- }
+ }
}
}
}
handlesAndBuffers->deviceCount = 0;
handlesAndBuffers->waveHeaders = 0;
handlesAndBuffers->bufferCount = 0;
-}
+}
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
int waveFormatTag;
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
- has already been called to zero some fields */
+ has already been called to zero some fields */
result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
if( result != paNoError ) goto error;
{
PaWinWaveFormat waveFormat;
UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
-
- /* @todo: consider providing a flag or #define to not try waveformat extensible
+
+ /* @todo: consider providing a flag or #define to not try waveformat extensible
this could just initialize j to 1 the first time round. */
for( j = 0; j < 2; ++j )
{
switch(j){
- case 0:
- /* first, attempt to open the device using WAVEFORMATEXTENSIBLE,
+ case 0:
+ /* first, attempt to open the device using WAVEFORMATEXTENSIBLE,
if this fails we fall back to WAVEFORMATEX */
- PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
+ PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
sampleFormat, waveFormatTag, sampleRate, channelMask );
break;
-
+
case 1:
/* retry with WAVEFORMATEX */
- PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount,
+ PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount,
sampleFormat, waveFormatTag, sampleRate );
break;
}
if( isInput )
{
- mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
+ mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
(WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
}
else
{
- mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
+ mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
(WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
}
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
result = paDeviceUnavailable;
break;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
result = paDeviceUnavailable;
break;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
result = paInsufficientMemory;
break;
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
/* falls through */
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
/* This can also occur if we try to open the device with an unsupported
* number of channels. This is attempted when device*ChannelCountIsKnown is
- * set to 0.
+ * set to 0.
*/
/* falls through */
default:
PaError result = paNoError;
MMRESULT mmresult;
signed int i;
-
+
if( handlesAndBuffers->waveHandles )
{
for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
handlesAndBuffers->bufferEvent = 0;
}
-
+
return result;
}
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
has already been called to zero some fields */
-
+
/* allocate an array of pointers to arrays of wave headers, one array of
wave headers per device */
result = paInsufficientMemory;
goto error;
}
-
+
for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
handlesAndBuffers->waveHeaders[i] = 0;
error:
TerminateWaveHeaders( handlesAndBuffers, isInput );
-
+
return result;
}
{
signed int i, j;
WAVEHDR *deviceWaveHeaders;
-
+
if( handlesAndBuffers->waveHeaders )
{
for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
HANDLE processingThread;
PA_THREAD_ID processingThreadId;
- char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
+ char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
int processingThreadPriority;
int highThreadPriority;
int throttledThreadPriority;
int isStopped;
volatile int isActive;
- volatile int stopProcessing; /* stop thread once existing buffers have been returned */
- volatile int abortProcessing; /* stop thread immediately */
+ volatile int stopProcessing; /* stop thread once existing buffers have been returned */
+ volatile int abortProcessing; /* stop thread immediately */
- DWORD allBuffersDurationMs; /* used to calculate timeouts */
+ DWORD allBuffersDurationMs; /* used to calculate timeouts */
};
/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
*throttleProcessingThreadOnOverload = 0;
-
+
if( streamInfo->flags & paWinMmeUseMultipleDevices )
{
if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
return paInvalidDevice;
-
+
*deviceCount = streamInfo->deviceCount;
- }
+ }
}
return paNoError;
unsigned int i;
int totalChannelCount;
PaDeviceIndex hostApiDevice;
-
+
if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
{
totalChannelCount = 0;
streamInfo->devices[i].device, hostApi );
if( result != paNoError )
return result;
-
+
devices[i].device = hostApiDevice;
devices[i].channelCount = streamInfo->devices[i].channelCount;
-
+
totalChannelCount += devices[i].channelCount;
}
-
+
if( totalChannelCount != streamParameters->channelCount )
{
/* channelCount must match total channels specified by multiple devices */
return paInvalidChannelCount; /* REVIEW use of this error code */
}
- }
+ }
else
- {
+ {
devices[0].device = streamParameters->device;
devices[0].channelCount = streamParameters->channelCount;
}
if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
- inputDeviceInfo =
+ inputDeviceInfo =
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount );
if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
- outputDeviceInfo =
+ outputDeviceInfo =
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount );
unsigned long framesPerHostOutputBuffer;
unsigned long hostOutputBufferCount;
unsigned long framesPerBufferProcessorCall;
- PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+ PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
unsigned long winMmeSpecificInputFlags = 0;
- unsigned long inputDeviceCount = 0;
+ unsigned long inputDeviceCount = 0;
PaWinMmeDeviceAndChannelCount *outputDevices = 0;
unsigned long winMmeSpecificOutputFlags = 0;
unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
char throttleProcessingThreadOnOverload = 1;
-
+
if( inputParameters )
{
inputChannelCount = inputParameters->channelCount;
if( inputDeviceCount != 1 ){
/* always use direct speakers when using multi-device multichannel mode */
- inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
+ inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
}
else
{
if( outputDeviceCount != 1 ){
/* always use direct speakers when using multi-device multichannel mode */
- outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
+ outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
}
else
{
else
{
assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
-
+
framesPerBufferProcessorCall = framesPerHostOutputBuffer;
}
}
framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
streamCallback, userData );
if( result != paNoError ) goto error;
-
+
bufferProcessorIsInitialized = 1;
/* stream info input latency is the minimum buffering latency (unlike suggested and default which are *maximums*) */
inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ );
if( result != paNoError ) goto error;
}
-
+
if( outputParameters )
{
result = InitializeWaveHandles( winMmeHostApi, &stream->output,
stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
}
-
+
if( streamCallback )
{
/* abort event is only needed for callback streams */
{
if( stream->abortEvent )
CloseHandle( stream->abortEvent );
-
+
TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
{
unsigned int i;
-
+
for( i=0; i < deviceCount; ++i )
{
if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
{
return 0;
- }
+ }
}
return 1;
#define PA_CIRCULAR_DECREMENT_( current, max )\
( ((current) == 0) ? ((max)-1) : (current-1) )
-
+
static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
{
signed long result = 0;
unsigned int i;
-
+
if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
{
/* we could calculate the following in O(1) if we kept track of the
PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
stream->output.framesUsedInCurrentBuffer = 0;
-
+
return result;
}
{
PaError result = paNoError;
unsigned int i;
-
+
for( i=0; i < stream->input.bufferCount - 1; ++i )
{
result = AdvanceToNextInputBuffer( stream );
int done = 0;
unsigned int channel, i;
unsigned long framesProcessed;
-
+
/* prepare event array for call to WaitForMultipleObjects() */
if( stream->input.bufferEvent )
events[eventCount++] = stream->input.bufferEvent;
events[eventCount++] = stream->abortEvent;
statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
-
+
/* loop until something causes us to stop */
do{
/* wait for MME to signal that a buffer is available, or for
{
hostInputBufferIndex = -1;
hostOutputBufferIndex = -1;
-
+
if( PA_IS_INPUT_STREAM_(stream) )
{
if( CurrentInputBuffersAreDone( stream ) )
if( CurrentOutputBuffersAreDone( stream ) )
{
/* ok, we have an output buffer */
-
+
if( NoBuffersAreQueued( &stream->output ) )
{
/*
}
}
-
+
if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
(PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
{
MMTIME mmtime;
double timeBeforeGetPosition, timeAfterGetPosition;
double time;
- long framesInBufferRing;
+ long framesInBufferRing;
long writePosition;
long playbackPosition;
HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
-
+
mmtime.wType = TIME_SAMPLES;
timeBeforeGetPosition = PaUtil_GetTime();
waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
/* approximate time at which wave out position was measured
as half way between timeBeforeGetPosition and timeAfterGetPosition */
time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
-
+
framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
playbackPosition = mmtime.u.sample % framesInBufferRing;
writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
+ stream->output.framesUsedInCurrentBuffer;
-
+
if( playbackPosition >= writePosition ){
timeInfo.outputBufferDacTime =
time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
-
+
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
stream->input.framesUsedInCurrentBuffer * channelCount *
stream->bufferProcessor.bytesPerHostInputSample,
channelCount );
-
+
channel += channelCount;
}
if( PA_IS_OUTPUT_STREAM_(stream) )
{
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
-
+
channel = 0;
for( i=0; i<stream->output.deviceCount; ++i )
{
{
stream->abortProcessing = 1;
done = 1;
- /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort
+ /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort
see: http://www.portaudio.com/trac/ticket/141
*/
result = paNoError;
done = 1;
}
-
+
if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
{
if( stream->stopProcessing &&
}
}
}
-
+
if( stream->throttleProcessingThreadOnOverload != 0 )
{
if( stream->stopProcessing || stream->abortProcessing )
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
-
+
return result;
}
result = CloseHandleWithPaError( stream->abortEvent );
if( result != paNoError ) goto error;
-
+
TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
-
+
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
PaUtil_FreeMemory( stream );
unsigned int channel;
unsigned long framesProcessed;
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
-
+
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
+
if( PA_IS_INPUT_STREAM_(stream) )
{
for( i=0; i<stream->input.bufferCount; ++i )
{
ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
}
- }
+ }
/* we queue all channels of a single buffer frame (across all
devices, because some multidevice multichannel drivers work
result = ResetEventWithPaError( stream->output.bufferEvent );
if( result != paNoError ) goto error;
-
-
+
+
if( stream->streamRepresentation.streamCallback )
{
/* callback stream */
DWORD waitResult;
MMRESULT mmresult;
signed int hostOutputBufferIndex;
- unsigned int channel, waitCount, i;
-
+ unsigned int channel, waitCount, i;
+
/** @todo
REVIEW: the error checking in this function needs review. the basic
idea is to return from this function in a known state - for example
PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
+
channel = 0;
for( i=0; i<stream->output.deviceCount; ++i )
{
*/
AdvanceToNextOutputBuffer( stream );
}
-
+
timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
DWORD waitResult;
MMRESULT mmresult;
unsigned int i;
-
+
/** @todo
REVIEW: the error checking in this function needs review. the basic
idea is to return from this function in a known state - for example
if( stream->processingThread )
{
/* callback stream */
-
+
/* Tell processing thread to abort immediately */
stream->abortProcessing = 1;
SetEvent( stream->abortEvent );
if( stream->processingThread )
{
/* callback stream */
-
+
PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
/* Calculate timeOut longer than longest time it could take to return all buffers. */
timeout = (int)(stream->allBuffersDurationMs * 1.5);
if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
-
+
waitResult = WaitForSingleObject( stream->processingThread, timeout );
if( waitResult == WAIT_TIMEOUT )
{
static PaTime GetStreamTime( PaStream *s )
{
(void) s; /* unused parameter */
-
+
return PaUtil_GetTime();
}
DWORD waitResult;
DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
unsigned int channel, i;
-
+
if( PA_IS_INPUT_STREAM_(stream) )
{
/* make a local copy of the user buffer pointer(s). this is necessary
for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
((void**)userBuffer)[i] = ((void**)buffer)[i];
}
-
+
do{
if( CurrentInputBuffersAreDone( stream ) )
{
{
/** @todo REVIEW: consider what to do if the input overflows.
do we requeue all of the buffers? should we be running
- a thread to make sure they are always queued?
+ a thread to make sure they are always queued?
see: http://www.portaudio.com/trac/ticket/117
*/
PaUtil_SetInputFrameCount( &stream->bufferProcessor,
stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
-
+
channel = 0;
for( i=0; i<stream->input.deviceCount; ++i )
{
channel += channelCount;
}
-
+
framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
stream->input.framesUsedInCurrentBuffer += framesProcessed;
break;
}
- framesRead += framesProcessed;
+ framesRead += framesProcessed;
}else{
/* wait for MME to signal that a buffer is available */
/* if a timeout is encountered, continue,
perhaps we should give up eventually
*/
- }
+ }
}
}while( framesRead < frames );
}
DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
unsigned int channel, i;
-
+
if( PA_IS_OUTPUT_STREAM_(stream) )
{
/* make a local copy of the user buffer pointer(s). this is necessary
/** @todo REVIEW: consider what to do if the output
underflows. do we requeue all the existing buffers with
zeros? should we run a separate thread to keep the buffers
- enqueued at all times?
+ enqueued at all times?
see: http://www.portaudio.com/trac/ticket/117
*/
PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
+
channel = 0;
for( i=0; i<stream->output.deviceCount; ++i )
{
channel += channelCount;
}
-
+
framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
stream->output.framesUsedInCurrentBuffer += framesProcessed;
/* if a timeout is encountered, continue,
perhaps we should give up eventually
*/
- }
- }
+ }
+ }
}while( framesWritten < frames );
}
else
{
result = paCanNotWriteToAnInputOnlyStream;
}
-
+
return result;
}
static signed long GetStreamReadAvailable( PaStream* s )
{
PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
+
if( PA_IS_INPUT_STREAM_(stream) )
return GetAvailableFrames( &stream->input );
else
static signed long GetStreamWriteAvailable( PaStream* s )
{
PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
+
if( PA_IS_OUTPUT_STREAM_(stream) )
return GetAvailableFrames( &stream->output );
else
PaError result;
PaUtilHostApiRepresentation *hostApi;
PaWinMmeHostApiRepresentation *winMmeHostApi;
-
+
result = PaUtil_ValidateStreamPointer( s );
if( result != paNoError )
return result;
return result;
winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
-
+
/* note, the following would be easier if there was a generic way of testing
that a stream belongs to a specific host API */
-
+
if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
|| PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
{