From: rossb Date: Thu, 28 Jul 2011 12:03:09 +0000 (+0000) Subject: pa_win_wmme.c fixed bug in stream info inputLatency see ticket #178. improved interpr... X-Git-Tag: pa_stable_v19_20111121_r1788~54 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=e07e139e0d954f1c54a16c2dbec696190f043819;p=portaudio pa_win_wmme.c fixed bug in stream info inputLatency see ticket #178. improved interpretation of suggestedLatency based on recent discussions. see tickets #98 #99 #181 --- diff --git a/src/hostapi/wmme/pa_win_wmme.c b/src/hostapi/wmme/pa_win_wmme.c index 813af50..f22fbcb 100644 --- a/src/hostapi/wmme/pa_win_wmme.c +++ b/src/hostapi/wmme/pa_win_wmme.c @@ -154,21 +154,21 @@ #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_MIN_HOST_BUFFER_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) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2) - #define PA_MME_MIN_HOST_BUFFER_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 @@ -177,6 +177,13 @@ #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_) +/* 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 havn't observed significant stability degredation + with using either more, or less buffers. +*/ + +#define PA_MME_TARGET_HOST_BUFFER_COUNT_ 8 #define PA_MME_MIN_TIMEOUT_MSEC_ (1000) @@ -791,7 +798,9 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme MMRESULT mmresult; WAVEOUTCAPS woc; PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; +#ifdef PAWIN_USE_WDMKS_DEVICE_INFO int wdmksDeviceOutputChannelCountIsKnown; +#endif *success = 0; @@ -1324,126 +1333,135 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, } - -static void SelectBufferSizeAndCount( unsigned long baseBufferSize, - unsigned long requestedLatency, - unsigned long baseBufferCount, unsigned long minimumBufferCount, - unsigned long maximumBufferSize, unsigned long *hostBufferSize, - unsigned long *hostBufferCount ) +static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames( + unsigned long suggestedLatencyFrames, + unsigned long hostBufferSizeFrames, + unsigned long minimumBufferCount ) { - unsigned long sizeMultiplier, bufferCount, latency; - unsigned long nextLatency, nextBufferSize; - int baseBufferSizeIsPowerOfTwo; - - sizeMultiplier = 1; - bufferCount = baseBufferCount; + /* Calculate the number of buffers of length hostFramesPerBuffer + that fit in suggestedLatencyFrames, rounding up to the next integer. - /* count-1 below because latency is always determined by one less - than the total number of buffers. + The exact formula for round-up is: + ((suggestedLatencyFrames + (hostFramesPerBuffer - 1)) / hostFramesPerBuffer) + However we subtract 2 instead of 1 below to account for rounding errors. + This ensures we don't erroneously overallocate an extra buffer due to a one-sample rounding error. */ - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - if( latency > requestedLatency ) - { + unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 2)) / hostBufferSizeFrames); - /* reduce number of buffers without falling below suggested latency */ - - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) - { - --bufferCount; - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - } + /* We always need one extra buffer for processing while the rest are queued/playing. + i.e. latency is framesPerBuffer * (bufferCount - 1) + */ + resultBufferCount += 1; - }else if( latency < requestedLatency ){ + if( resultBufferCount < minimumBufferCount ) /* clamp to minimum buffer count */ + resultBufferCount = minimumBufferCount; - baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1))); - if( baseBufferSizeIsPowerOfTwo ){ + return resultBufferCount; +} - /* double size of buffers without exceeding requestedLatency */ - nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); - nextLatency = nextBufferSize * (bufferCount-1); - while( nextBufferSize <= maximumBufferSize - && nextLatency < requestedLatency ) - { - sizeMultiplier *= 2; - nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); - nextLatency = nextBufferSize * (bufferCount-1); - } +static PaError SelectHostBufferSizeFramesAndHostBufferCount( + unsigned long suggestedLatencyFrames, + unsigned long userFramesPerBuffer, + unsigned long minimumBufferCount, + unsigned long preferredMaximumBufferSizeFrames, /* try not to exceed this. for example, don't exceed when coalescing buffers */ + unsigned long absoluteMaximumBufferSizeFrames, /* never exceed this, a hard limit */ + unsigned long *hostBufferSizeFrames, + unsigned long *hostBufferCount ) +{ + if( userFramesPerBuffer == paFramesPerBufferUnspecified ){ - }else{ + *hostBufferSizeFrames = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_; - /* increase size of buffers upto first excess of requestedLatency */ + }else{ - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - while( nextBufferSize <= maximumBufferSize - && nextLatency < requestedLatency ) - { - ++sizeMultiplier; - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - } + *hostBufferSizeFrames = userFramesPerBuffer; - if( nextLatency < requestedLatency ) - ++sizeMultiplier; + if( *hostBufferSizeFrames > absoluteMaximumBufferSizeFrames ){ + /* user has requested a user buffer that's larger than what we can handle */ + + /* @todo FIXME right now we allow the user to request an oversize host buffer, + even though elsewhere in the code there are suggestions that oversize buffers + can cause crashes with some drivers. */ + /* return paBufferTooBig; */ } + } + + /* compute a host buffer count based on suggestedLatencyFrames and our granularity */ - /* increase number of buffers until requestedLatency is reached */ + *hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount ); - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - while( latency < requestedLatency ) - { - ++bufferCount; - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); + /* consider coalescing multiple user buffers into each host buffer */ + + if( *hostBufferCount >= PA_MME_TARGET_HOST_BUFFER_COUNT_ * 2 ){ + + /* 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 + 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. + */ + + unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* min of our limits */ + ? absoluteMaximumBufferSizeFrames + : preferredMaximumBufferSizeFrames; + + unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / *hostBufferSizeFrames; /* don't coalesce more than this */ + + /* we truncate here to compute a conservative value of numUserBuffersPerHostBuffer + then we recompute hostBufferCount after coalescing. + */ + unsigned long numUserBuffersPerHostBuffer = *hostBufferCount / PA_MME_TARGET_HOST_BUFFER_COUNT_; + if( numUserBuffersPerHostBuffer > maxUserBuffersPerHostBuffer ) + numUserBuffersPerHostBuffer = maxUserBuffersPerHostBuffer; + + if( numUserBuffersPerHostBuffer > 1 ){ + *hostBufferSizeFrames *= numUserBuffersPerHostBuffer; + + /* recompute hostBufferCount to approximate suggestedLatencyFrames now that hostBufferSizeFrames is larger */ + *hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount ); } } - *hostBufferSize = baseBufferSize * sizeMultiplier; - *hostBufferCount = bufferCount; + return paNoError; } -static void ReselectBufferCount( unsigned long bufferSize, - unsigned long requestedLatency, - unsigned long baseBufferCount, unsigned long minimumBufferCount, - unsigned long *hostBufferCount ) +static PaError CalculateMaxHostSampleFrameSizeBytes( + int channelCount, + PaSampleFormat hostSampleFormat, + const PaWinMmeStreamInfo *streamInfo, + int *hostSampleFrameSizeBytes ) { - unsigned long bufferCount, latency; - unsigned long nextLatency; - - bufferCount = baseBufferCount; - - /* count-1 below because latency is always determined by one less - than the total number of buffers. + 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 + number of channels used by a single device. */ - latency = bufferSize * (bufferCount-1); - - if( latency > requestedLatency ) + int maxDeviceChannelCount = channelCount; + int hostSampleSizeBytes = Pa_GetSampleSize( hostSampleFormat ); + if( hostSampleSizeBytes < 0 ) { - /* reduce number of buffers without falling below suggested latency */ + return hostSampleSizeBytes; /* the value of hostSampleSize here is an error code, not a sample size */ + } - nextLatency = bufferSize * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) + if( streamInfo && ( streamInfo->flags & paWinMmeUseMultipleDevices ) ) + { + maxDeviceChannelCount = streamInfo->devices[0].channelCount; + for( i=1; i< streamInfo->deviceCount; ++i ) { - --bufferCount; - nextLatency = bufferSize * (bufferCount-2); + if( streamInfo->devices[i].channelCount > maxDeviceChannelCount ) + maxDeviceChannelCount = streamInfo->devices[i].channelCount; } - - }else if( latency < requestedLatency ){ - - /* increase number of buffers until requestedLatency is reached */ - - latency = bufferSize * (bufferCount-1); - while( latency < requestedLatency ) - { - ++bufferCount; - latency = bufferSize * (bufferCount-1); - } } - *hostBufferCount = bufferCount; + *hostSampleFrameSizeBytes = hostSampleSizeBytes * maxDeviceChannelCount; + + return paNoError; } @@ -1456,48 +1474,26 @@ static PaError CalculateBufferSettings( unsigned long *hostFramesPerInputBuffer, unsigned long *hostInputBufferCount, unsigned long *hostFramesPerOutputBuffer, unsigned long *hostOutputBufferCount, int inputChannelCount, PaSampleFormat hostInputSampleFormat, - PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo, + PaTime suggestedInputLatency, const PaWinMmeStreamInfo *inputStreamInfo, int outputChannelCount, PaSampleFormat hostOutputSampleFormat, - PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo, + PaTime suggestedOutputLatency, const PaWinMmeStreamInfo *outputStreamInfo, double sampleRate, unsigned long userFramesPerBuffer ) { PaError result = paNoError; - int effectiveInputChannelCount, effectiveOutputChannelCount; - int hostInputFrameSize = 0; - unsigned int i; - if( inputChannelCount > 0 ) + if( inputChannelCount > 0 ) /* stream has input */ { - int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat ); - if( hostInputSampleSize < 0 ) - { - result = hostInputSampleSize; /* this is an error code */ + int hostInputFrameSizeBytes; + result = CalculateMaxHostSampleFrameSizeBytes( + inputChannelCount, hostInputSampleFormat, inputStreamInfo, &hostInputFrameSizeBytes ); + if( result != paNoError ) goto error; - } - - if( inputStreamInfo - && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) - { - /* set effectiveInputChannelCount to the largest number of - channels on any one device. - */ - effectiveInputChannelCount = 0; - for( i=0; i< inputStreamInfo->deviceCount; ++i ) - { - if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount ) - effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount; - } - } - else - { - effectiveInputChannelCount = inputChannelCount; - } - - hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount; if( inputStreamInfo && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) { + /* input - using low level latency parameters if provided */ + if( inputStreamInfo->bufferCount <= 0 || inputStreamInfo->framesPerBuffer <= 0 ) { @@ -1510,28 +1506,25 @@ static PaError CalculateBufferSettings( } else { - unsigned long hostBufferSizeBytes, hostBufferCount; + /* input - not using low level latency parameters, so compute + hostFramesPerInputBuffer and hostInputBufferCount + based on userFramesPerBuffer and suggestedInputLatency. */ + unsigned long minimumBufferCount = (outputChannelCount > 0) ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_; - unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize); - if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) - maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; - - /* compute the following in bytes, then convert back to frames */ - - SelectBufferSizeAndCount( - ((userFramesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : userFramesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *hostFramesPerInputBuffer = hostBufferSizeBytes / hostInputFrameSize; - *hostInputBufferCount = hostBufferCount; + result = SelectHostBufferSizeFramesAndHostBufferCount( + (unsigned long)(suggestedInputLatency * sampleRate), + 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 + division is intentional here to limit max bytes */ + hostFramesPerInputBuffer, + hostInputBufferCount ); + if( result != paNoError ) + goto error; } } else @@ -1540,11 +1533,13 @@ static PaError CalculateBufferSettings( *hostInputBufferCount = 0; } - if( outputChannelCount > 0 ) + if( outputChannelCount > 0 ) /* stream has output */ { if( outputStreamInfo && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) { + /* output - using low level latency parameters */ + if( outputStreamInfo->bufferCount <= 0 || outputStreamInfo->framesPerBuffer <= 0 ) { @@ -1555,16 +1550,17 @@ static PaError CalculateBufferSettings( *hostFramesPerOutputBuffer = outputStreamInfo->framesPerBuffer; *hostOutputBufferCount = outputStreamInfo->bufferCount; - if( inputChannelCount > 0 ) /* full duplex */ { + /* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */ + if( *hostFramesPerInputBuffer != *hostFramesPerOutputBuffer ) { if( inputStreamInfo && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) { /* a custom StreamInfo was used for specifying both input - and output buffer sizes, the larger buffer size + and output buffer sizes. We require that the larger buffer size must be a multiple of the smaller buffer size */ if( *hostFramesPerInputBuffer < *hostFramesPerOutputBuffer ) @@ -1588,111 +1584,72 @@ static PaError CalculateBufferSettings( else { /* a custom StreamInfo was not used for specifying the input buffer size, - so use the output buffer size, and approximately the same latency. */ + so use the output buffer size, and approximately the suggested input latency. */ *hostFramesPerInputBuffer = *hostFramesPerOutputBuffer; - *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *hostFramesPerInputBuffer) + 1; - if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ) - *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; + *hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + (unsigned long)(suggestedInputLatency * sampleRate), + *hostFramesPerInputBuffer, + PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ); } } } } else { - unsigned long hostBufferSizeBytes, hostBufferCount; - unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; - unsigned long maximumBufferSize; - int hostOutputFrameSize; - int hostOutputSampleSize; - - hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat ); - if( hostOutputSampleSize < 0 ) - { - result = hostOutputSampleSize; - goto error; - } - - if( outputStreamInfo - && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) - { - /* set effectiveOutputChannelCount to the largest number of - channels on any one device. - */ - effectiveOutputChannelCount = 0; - for( i=0; i< outputStreamInfo->deviceCount; ++i ) - { - if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount ) - effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount; - } - } - else - { - effectiveOutputChannelCount = outputChannelCount; - } - - hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount; - - maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize); - if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) - maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; + /* output - no low level latency parameters, so compute hostFramesPerOutputBuffer and hostOutputBufferCount + based on userFramesPerBuffer and suggestedOutputLatency. */ + int hostOutputFrameSizeBytes; + result = CalculateMaxHostSampleFrameSizeBytes( + outputChannelCount, hostOutputSampleFormat, outputStreamInfo, &hostOutputFrameSizeBytes ); + if( result != paNoError ) + goto error; - /* compute the following in bytes, then convert back to frames */ - - SelectBufferSizeAndCount( - ((userFramesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : userFramesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *hostFramesPerOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize; - *hostOutputBufferCount = hostBufferCount; - + /* compute the output buffer size and count */ + + result = SelectHostBufferSizeFramesAndHostBufferCount( + (unsigned long)(suggestedOutputLatency * sampleRate), + 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 + division is intentional here to limit max bytes */ + hostFramesPerOutputBuffer, + hostOutputBufferCount ); + if( result != paNoError ) + goto error; - if( inputChannelCount > 0 ) + if( inputChannelCount > 0 ) /* full duplex */ { + /* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */ + /* ensure that both input and output buffer sizes are the same. if they don't match at this stage, choose the smallest one - and use that for input and output + and use that for input and output and recompute the corresponding + buffer count accordingly. */ if( *hostFramesPerOutputBuffer != *hostFramesPerInputBuffer ) { if( hostFramesPerInputBuffer < hostFramesPerOutputBuffer ) { - unsigned long framesPerHostBuffer = *hostFramesPerInputBuffer; - - minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; - ReselectBufferCount( - framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - &hostBufferCount ); - - *hostFramesPerOutputBuffer = framesPerHostBuffer; - *hostOutputBufferCount = hostBufferCount; + *hostFramesPerOutputBuffer = *hostFramesPerInputBuffer; + + *hostOutputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + (unsigned long)(suggestedOutputLatency * sampleRate), + *hostOutputBufferCount, + PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ ); } else { - unsigned long framesPerHostBuffer = *hostFramesPerOutputBuffer; - - minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; - ReselectBufferCount( - framesPerHostBuffer * hostInputFrameSize, /* bufferSize */ - ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - &hostBufferCount ); - - *hostFramesPerInputBuffer = framesPerHostBuffer; - *hostInputBufferCount = hostBufferCount; + *hostFramesPerInputBuffer = *hostFramesPerOutputBuffer; + + *hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + (unsigned long)(suggestedInputLatency * sampleRate), + *hostFramesPerInputBuffer, + PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ); } } } @@ -2476,12 +2433,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, bufferProcessorIsInitialized = 1; + /* stream info input latency is the minimum buffering latency (unlike suggested and default which are *maximums*) */ stream->streamRepresentation.streamInfo.inputLatency = (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) - +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate; + + framesPerHostInputBuffer) / sampleRate; stream->streamRepresentation.streamInfo.outputLatency = (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) - +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; + + (framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; stream->streamRepresentation.streamInfo.sampleRate = sampleRate; stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;