From 4e762692da9c71f5e129aa759db7ddf1564d4948 Mon Sep 17 00:00:00 2001 From: gigaherz Date: Mon, 5 Sep 2011 22:05:14 +0000 Subject: [PATCH] portaudio: Update codebase from svn 1723 to svn 1748. Notable changes: - fixed latency calculations for some modules - improved stability of the internal ringbuffer - added float32<->uint8 sample conversion git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4911 96395faa-99c1-11dd-bbfe-3dabce05a288 --- 3rdparty/portaudio/include/pa_win_ds.h | 12 +- 3rdparty/portaudio/include/portaudio.h | 26 +- 3rdparty/portaudio/src/common/pa_converters.c | 30 +- 3rdparty/portaudio/src/common/pa_front.c | 21 +- 3rdparty/portaudio/src/common/pa_hostapi.h | 68 ++- .../portaudio/src/common/pa_memorybarrier.h | 1 + 3rdparty/portaudio/src/common/pa_ringbuffer.c | 24 +- 3rdparty/portaudio/src/common/pa_ringbuffer.h | 6 +- .../src/hostapi/coreaudio/pa_mac_core.c | 12 +- .../portaudio/src/hostapi/dsound/pa_win_ds.c | 493 ++++++++++-------- .../src/hostapi/wdmks/pa_win_wdmks.c | 12 +- .../portaudio/src/hostapi/wmme/pa_win_wmme.c | 158 ++++-- .../portaudio/src/os/unix/pa_unix_hostapis.c | 22 +- .../portaudio/src/os/win/pa_win_hostapis.c | 4 +- 14 files changed, 575 insertions(+), 314 deletions(-) diff --git a/3rdparty/portaudio/include/pa_win_ds.h b/3rdparty/portaudio/include/pa_win_ds.h index 2e1fd5958f..0833a4c0d0 100644 --- a/3rdparty/portaudio/include/pa_win_ds.h +++ b/3rdparty/portaudio/include/pa_win_ds.h @@ -59,15 +59,14 @@ extern "C" typedef struct PaWinDirectSoundStreamInfo{ unsigned long size; /**< sizeof(PaWinDirectSoundStreamInfo) */ PaHostApiTypeId hostApiType; /**< paDirectSound */ - unsigned long version; /**< 1 */ + unsigned long version; /**< 2 */ unsigned long flags; /* low-level latency setting support - TODO ** NOT IMPLEMENTED ** - These settings control the number and size of host buffers in order - to set latency. They will be used instead of the generic parameters - to Pa_OpenStream() if flags contains the paWinDirectSoundUseLowLevelLatencyParameters + Control the size of host buffers in order to set latency. They will + be used instead of the generic parameters to Pa_OpenStream() if + flags contains the paWinDirectSoundUseLowLevelLatencyParameters flag. If PaWinDirectSoundStreamInfo structures with paWinDirectSoundUseLowLevelLatencyParameters @@ -76,9 +75,8 @@ typedef struct PaWinDirectSoundStreamInfo{ two must be a multiple of the smaller, otherwise a paIncompatibleHostApiSpecificStreamInfo error will be returned from Pa_OpenStream(). - - unsigned long framesPerBuffer; */ + unsigned long framesPerBuffer; /* NOT IMPLEMENTED see http://www.portaudio.com/trac/ticket/129 */ /* support for WAVEFORMATEXTENSIBLE channel masks. If flags contains diff --git a/3rdparty/portaudio/include/portaudio.h b/3rdparty/portaudio/include/portaudio.h index 5ea67d55e9..b80e1d1ef9 100644 --- a/3rdparty/portaudio/include/portaudio.h +++ b/3rdparty/portaudio/include/portaudio.h @@ -1,7 +1,7 @@ #ifndef PORTAUDIO_H #define PORTAUDIO_H /* - * $Id: portaudio.h 1702 2011-07-20 03:24:00Z rossb $ + * $Id: portaudio.h 1745 2011-08-25 17:44:01Z rossb $ * PortAudio Portable Real-Time Audio Library * PortAudio API Header File * Latest version available at: http://www.portaudio.com/ @@ -711,6 +711,28 @@ typedef enum PaStreamCallbackResult Functions of type PaStreamCallback are implemented by PortAudio clients. They consume, process or generate audio in response to requests from an active PortAudio stream. + + When a stream is running, PortAudio calls the stream callback periodically. + The callback function is responsible for processing buffers of audio samples + passed via the input and output parameters. + + The PortAudio stream callback runs at very high or real-time priority. + It is required to consistently meet its time deadlines. Do not allocate + memory, access the file system, call library functions or call other functions + from the stream callback that may block or take an unpredictable amount of + time to complete. + + In order for a stream to maintain glitch-free operation the callback + must consume and return audio data faster than it is recorded and/or + played. PortAudio anticipates that each callback invocation may execute for + a duration approaching the duration of frameCount audio frames at the stream + sample rate. It is reasonable to expect to be able to utilise 70% or more of + the available CPU time in the PortAudio callback. However, due to buffer size + adaption and other factors, not all host APIs are able to guarantee audio + stability under heavy CPU load with arbitrary fixed callback buffer sizes. + When high callback CPU utilisation is required the most robust behavior + can be achieved by using paFramesPerBufferUnspecified as the + Pa_OpenStream() framesPerBuffer parameter. @param input and @param output are either arrays of interleaved samples or; if non-interleaved samples were requested using the paNonInterleaved sample @@ -737,7 +759,7 @@ typedef enum PaStreamCallbackResult @return The stream callback should return one of the values in the - PaStreamCallbackResult enumeration. To ensure that the callback continues + ::PaStreamCallbackResult enumeration. To ensure that the callback continues to be called, it should return paContinue (0). Either paComplete or paAbort can be returned to finish stream processing, after either of these values is returned the callback will not be called again. If paAbort is returned the diff --git a/3rdparty/portaudio/src/common/pa_converters.c b/3rdparty/portaudio/src/common/pa_converters.c index 7f96a8f88e..ef65b68a5c 100644 --- a/3rdparty/portaudio/src/common/pa_converters.c +++ b/3rdparty/portaudio/src/common/pa_converters.c @@ -1,5 +1,5 @@ /* - * $Id: pa_converters.c 1576 2011-02-01 12:58:26Z rossb $ + * $Id: pa_converters.c 1748 2011-09-01 22:08:32Z philburk $ * Portable Audio I/O Library sample conversion mechanism * * Based on the Open Source API proposed by Ross Bencina @@ -49,8 +49,7 @@ see: "require clipping for dithering sample conversion functions?" http://www.portaudio.com/trac/ticket/112 - @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither, - Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither, + @todo implement the converters marked IMPLEMENT ME: Int32_To_Int24_Dither, Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither, Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither see: "some conversion functions are not implemented in pa_converters.c" @@ -729,8 +728,7 @@ static void Float32_To_Int8_Dither( { float *src = (float*)sourceBuffer; signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - + while( count-- ) { float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); @@ -821,12 +819,15 @@ static void Float32_To_UInt8_Dither( { float *src = (float*)sourceBuffer; unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - + while( count-- ) { - /* IMPLEMENT ME */ - + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = (*src * (126.0f)) + dither; + PaInt32 samp = (PaInt32) dithered; + *dest = (unsigned char) (128 + samp); + src += sourceStride; dest += destinationStride; } @@ -845,7 +846,9 @@ static void Float32_To_UInt8_Clip( while( count-- ) { - /* IMPLEMENT ME */ + PaInt32 samp = 128 + (PaInt32)(*src * (127.0f)); + PA_CLIP_( samp, 0x0000, 0x00FF ); + *dest = (unsigned char) samp; src += sourceStride; dest += destinationStride; @@ -865,7 +868,12 @@ static void Float32_To_UInt8_DitherClip( while( count-- ) { - /* IMPLEMENT ME */ + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = (*src * (126.0f)) + dither; + PaInt32 samp = 128 + (PaInt32) dithered; + PA_CLIP_( samp, 0x0000, 0x00FF ); + *dest = (unsigned char) samp; src += sourceStride; dest += destinationStride; diff --git a/3rdparty/portaudio/src/common/pa_front.c b/3rdparty/portaudio/src/common/pa_front.c index dc14f2bfcc..957d1eac47 100644 --- a/3rdparty/portaudio/src/common/pa_front.c +++ b/3rdparty/portaudio/src/common/pa_front.c @@ -1,5 +1,5 @@ /* - * $Id: pa_front.c 1584 2011-02-02 18:58:17Z rossb $ + * $Id: pa_front.c 1730 2011-08-18 03:43:51Z rossb $ * Portable Audio I/O Library Multi-Host API front end * Validate function parameters and manage multiple host APIs. * @@ -116,6 +116,7 @@ void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, static PaUtilHostApiRepresentation **hostApis_ = 0; static int hostApisCount_ = 0; +static int defaultHostApiIndex_ = 0; static int initializationCount_ = 0; static int deviceCount_ = 0; @@ -146,6 +147,7 @@ static void TerminateHostApis( void ) hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] ); } hostApisCount_ = 0; + defaultHostApiIndex_ = 0; deviceCount_ = 0; if( hostApis_ != 0 ) @@ -172,6 +174,7 @@ static PaError InitializeHostApis( void ) } hostApisCount_ = 0; + defaultHostApiIndex_ = -1; /* indicates that we haven't determined the default host API yet */ deviceCount_ = 0; baseDeviceIndex = 0; @@ -193,6 +196,16 @@ static PaError InitializeHostApis( void ) assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount ); assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount ); + /* the first successfully initialized host API with a default input *or* + output device is used as the default host API. + */ + if( (defaultHostApiIndex_ == -1) && + ( hostApi->info.defaultInputDevice != paNoDevice + || hostApi->info.defaultOutputDevice != paNoDevice ) ) + { + defaultHostApiIndex_ = hostApisCount_; + } + hostApi->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex; if( hostApi->info.defaultInputDevice != paNoDevice ) @@ -208,6 +221,10 @@ static PaError InitializeHostApis( void ) } } + /* if no host APIs have devices, the default host API is the first initialized host API */ + if( defaultHostApiIndex_ == -1 ) + defaultHostApiIndex_ = 0; + return result; error: @@ -525,7 +542,7 @@ PaHostApiIndex Pa_GetDefaultHostApi( void ) } else { - result = paDefaultHostApiIndex; + result = defaultHostApiIndex_; /* internal consistency check: make sure that the default host api index is within range */ diff --git a/3rdparty/portaudio/src/common/pa_hostapi.h b/3rdparty/portaudio/src/common/pa_hostapi.h index da8d90676e..1437f629dc 100644 --- a/3rdparty/portaudio/src/common/pa_hostapi.h +++ b/3rdparty/portaudio/src/common/pa_hostapi.h @@ -1,7 +1,7 @@ #ifndef PA_HOSTAPI_H #define PA_HOSTAPI_H /* - * $Id: pa_hostapi.h 1634 2011-03-04 04:12:08Z rossb $ + * $Id: pa_hostapi.h 1740 2011-08-25 07:17:48Z philburk $ * Portable Audio I/O Library * host api representation * @@ -106,6 +106,53 @@ are defaulted to 1. #define PA_USE_WDMKS 1 #endif +/* Set default values for Unix based APIs. */ +#if defined(PA_NO_OSS) || defined(PA_NO_ALSA) || defined(PA_NO_JACK) || defined(PA_NO_COREAUDIO) || defined(PA_NO_SGI) || defined(PA_NO_ASIHPI) +#error "Portaudio: PA_NO_ is no longer supported, please remove definition and use PA_USE_ instead" +#endif + +#ifndef PA_USE_OSS +#define PA_USE_OSS 0 +#elif (PA_USE_OSS != 0) && (PA_USE_OSS != 1) +#undef PA_USE_OSS +#define PA_USE_OSS 1 +#endif + +#ifndef PA_USE_ALSA +#define PA_USE_ALSA 0 +#elif (PA_USE_ALSA != 0) && (PA_USE_ALSA != 1) +#undef PA_USE_ALSA +#define PA_USE_ALSA 1 +#endif + +#ifndef PA_USE_JACK +#define PA_USE_JACK 0 +#elif (PA_USE_JACK != 0) && (PA_USE_JACK != 1) +#undef PA_USE_JACK +#define PA_USE_JACK 1 +#endif + +#ifndef PA_USE_SGI +#define PA_USE_SGI 0 +#elif (PA_USE_SGI != 0) && (PA_USE_SGI != 1) +#undef PA_USE_SGI +#define PA_USE_SGI 1 +#endif + +#ifndef PA_USE_COREAUDIO +#define PA_USE_COREAUDIO 0 +#elif (PA_USE_COREAUDIO != 0) && (PA_USE_COREAUDIO != 1) +#undef PA_USE_COREAUDIO +#define PA_USE_COREAUDIO 1 +#endif + +#ifndef PA_USE_ASIHPI +#define PA_USE_ASIHPI 0 +#elif (PA_USE_ASIHPI != 0) && (PA_USE_ASIHPI != 1) +#undef PA_USE_ASIHPI +#define PA_USE_ASIHPI 1 +#endif + #ifdef __cplusplus extern "C" { @@ -294,22 +341,21 @@ typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostA /** paHostApiInitializers is a NULL-terminated array of host API initialization functions. These functions are called by pa_front.c to initialize the host APIs - when the client calls Pa_Initialize(). + when the client calls Pa_Initialize(). + + The initialization functions are invoked in order. - There is a platform specific file which defines paHostApiInitializers for that + The first successfully initialized host API that has a default input *or* output + device is used as the default PortAudio host API. This is based on the logic that + there is only one default host API, and it must contain the default input and output + devices (if defined). + + There is a platform specific file that defines paHostApiInitializers for that platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example. */ extern PaUtilHostApiInitializer *paHostApiInitializers[]; -/** The index of the default host API in the paHostApiInitializers array. - - There is a platform specific file which defines paDefaultHostApiIndex for that - platform, see pa_win/pa_win_hostapis.c for example. -*/ -extern int paDefaultHostApiIndex; - - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/3rdparty/portaudio/src/common/pa_memorybarrier.h b/3rdparty/portaudio/src/common/pa_memorybarrier.h index f689622201..2879ce33ad 100644 --- a/3rdparty/portaudio/src/common/pa_memorybarrier.h +++ b/3rdparty/portaudio/src/common/pa_memorybarrier.h @@ -103,6 +103,7 @@ # pragma intrinsic(_ReadWriteBarrier) # pragma intrinsic(_ReadBarrier) # pragma intrinsic(_WriteBarrier) +/* note that MSVC intrinsics _ReadWriteBarrier(), _ReadBarrier(), _WriteBarrier() are just compiler barriers *not* memory barriers */ # define PaUtil_FullMemoryBarrier() _ReadWriteBarrier() # define PaUtil_ReadMemoryBarrier() _ReadBarrier() # define PaUtil_WriteMemoryBarrier() _WriteBarrier() diff --git a/3rdparty/portaudio/src/common/pa_ringbuffer.c b/3rdparty/portaudio/src/common/pa_ringbuffer.c index eb671bfa99..19c91497ce 100644 --- a/3rdparty/portaudio/src/common/pa_ringbuffer.c +++ b/3rdparty/portaudio/src/common/pa_ringbuffer.c @@ -1,5 +1,5 @@ /* - * $Id: pa_ringbuffer.c 1694 2011-06-17 08:01:02Z rossb $ + * $Id: pa_ringbuffer.c 1738 2011-08-18 11:47:28Z rossb $ * Portable Audio I/O Library * Ring Buffer utility. * @@ -79,14 +79,12 @@ ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buf ** Return number of elements available for reading. */ ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf ) { - PaUtil_ReadMemoryBarrier(); return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); } /*************************************************************************** ** Return number of elements available for writing. */ ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf ) { - /* Since we are calling PaUtil_GetRingBufferReadAvailable, we don't need an aditional MB */ return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf)); } @@ -128,6 +126,10 @@ ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, rin *dataPtr2 = NULL; *sizePtr2 = 0; } + + if( available ) + PaUtil_FullMemoryBarrier(); /* (write-after-read) => full barrier */ + return elementCount; } @@ -136,7 +138,9 @@ ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, rin */ ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) { - /* we need to ensure that previous writes are seen before we update the write index */ + /* ensure that previous writes are seen before we update the write index + (write after write) + */ PaUtil_WriteMemoryBarrier(); return rbuf->writeIndex = (rbuf->writeIndex + elementCount) & rbuf->bigMask; } @@ -152,7 +156,7 @@ ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring void **dataPtr2, ring_buffer_size_t *sizePtr2 ) { ring_buffer_size_t index; - ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); + ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); /* doesn't use memory barrier */ if( elementCount > available ) elementCount = available; /* Check to see if read is not contiguous. */ index = rbuf->readIndex & rbuf->smallMask; @@ -172,14 +176,20 @@ ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring *dataPtr2 = NULL; *sizePtr2 = 0; } + + if( available ) + PaUtil_ReadMemoryBarrier(); /* (read-after-read) => read barrier */ + return elementCount; } /*************************************************************************** */ ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) { - /* we need to ensure that previous writes are always seen before updating the index. */ - PaUtil_WriteMemoryBarrier(); + /* ensure that previous reads (copies out of the ring buffer) are always completed before updating (writing) the read index. + (write-after-read) => full barrier + */ + PaUtil_FullMemoryBarrier(); return rbuf->readIndex = (rbuf->readIndex + elementCount) & rbuf->bigMask; } diff --git a/3rdparty/portaudio/src/common/pa_ringbuffer.h b/3rdparty/portaudio/src/common/pa_ringbuffer.h index d7537bb2e5..51577f0707 100644 --- a/3rdparty/portaudio/src/common/pa_ringbuffer.h +++ b/3rdparty/portaudio/src/common/pa_ringbuffer.h @@ -1,7 +1,7 @@ #ifndef PA_RINGBUFFER_H #define PA_RINGBUFFER_H /* - * $Id: pa_ringbuffer.h 1694 2011-06-17 08:01:02Z rossb $ + * $Id: pa_ringbuffer.h 1734 2011-08-18 11:19:36Z rossb $ * Portable Audio I/O Library * Ring Buffer utility. * @@ -90,8 +90,8 @@ extern "C" typedef struct PaUtilRingBuffer { ring_buffer_size_t bufferSize; /**< Number of elements in FIFO. Power of 2. Set by PaUtil_InitRingBuffer. */ - ring_buffer_size_t writeIndex; /**< Index of next writable element. Set by PaUtil_AdvanceRingBufferWriteIndex. */ - ring_buffer_size_t readIndex; /**< Index of next readable element. Set by PaUtil_AdvanceRingBufferReadIndex. */ + volatile ring_buffer_size_t writeIndex; /**< Index of next writable element. Set by PaUtil_AdvanceRingBufferWriteIndex. */ + volatile ring_buffer_size_t readIndex; /**< Index of next readable element. Set by PaUtil_AdvanceRingBufferReadIndex. */ ring_buffer_size_t bigMask; /**< Used for wrapping indices with extra bit to distinguish full/empty. */ ring_buffer_size_t smallMask; /**< Used for fitting indices to buffer. */ ring_buffer_size_t elementSizeBytes; /**< Number of bytes per element. */ diff --git a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c index 9939be19ed..bab668ce60 100644 --- a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c +++ b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core.c @@ -1009,6 +1009,10 @@ static OSStatus UpdateSampleRateFromDeviceProperty( PaMacCoreStream *stream, Aud static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData ) { PaMacCoreStream *stream = (PaMacCoreStream*)inClientData; + + // Make sure the callback is operating on a stream that is still valid! + assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC ); + OSStatus osErr = UpdateSampleRateFromDeviceProperty( stream, inDevice, isInput ); if( osErr == noErr ) { @@ -1034,6 +1038,10 @@ static OSStatus AudioDevicePropertyGenericListenerProc( AudioDeviceID inDevice, { OSStatus osErr = noErr; PaMacCoreStream *stream = (PaMacCoreStream*)inClientData; + + // Make sure the callback is operating on a stream that is still valid! + assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC ); + PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties; UInt32 *valuePtr = NULL; switch( inPropertyID ) @@ -2107,7 +2115,7 @@ static OSStatus AudioIOProc( void *inRefCon, PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon; const bool isRender = inBusNumber == OUTPUT_ELEMENT; int callbackResult = paContinue ; - double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime); + double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime); VVDBUG(("AudioIOProc()\n")); @@ -2546,7 +2554,7 @@ static PaError CloseStream( PaStream* s ) if( stream->inputUnit ) { - Boolean isInput = FALSE; + Boolean isInput = TRUE; CleanupDevicePropertyListeners( stream, stream->inputDevice, isInput ); } diff --git a/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c b/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c index ffc98e3026..b5e12b02ad 100644 --- a/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c +++ b/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_ds.c 1685 2011-05-11 21:05:18Z rossb $ + * $Id: pa_win_ds.c 1744 2011-08-25 15:59:32Z rossb $ * Portable Audio I/O Library DirectSound implementation * * Authors: Phil Burk, Robert Marsanyi & Ross Bencina @@ -45,7 +45,7 @@ We're replacing this with a new implementation using a thread and a different timer mechanim. Defining PA_WIN_DS_USE_WMME_TIMER uses the old (pre-May 2011) behavior. */ -//#define PA_WIN_DS_USE_WMME_TIMER +//#define PA_WIN_DS_USE_WMME_TIMER #include #include @@ -56,6 +56,7 @@ #include #include + /* Use the earliest version of DX required, no need to polute the namespace */ @@ -142,17 +143,17 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg ); #define PA_USE_HIGH_LATENCY (0) #if PA_USE_HIGH_LATENCY -#define PA_WIN_9X_LATENCY (500) -#define PA_WIN_NT_LATENCY (600) +#define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.500) +#define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.600) #else -#define PA_WIN_9X_LATENCY (140) -#define PA_WIN_NT_LATENCY (280) +#define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.140) +#define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.280) #endif -#define PA_WIN_WDM_LATENCY (120) +#define PA_DS_WIN_WDM_DEFAULT_LATENCY_ (.120) #define SECONDS_PER_MSEC (0.001) -#define MSEC_PER_SECOND (1000) +#define MSECS_PER_SECOND (1000) /* prototypes for functions declared in this file */ @@ -254,7 +255,7 @@ typedef struct PaWinDsStream LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer; DWORD outputBufferWriteOffsetBytes; /* last write position */ INT outputBufferSizeBytes; - INT bytesPerOutputFrame; + INT outputFrameSizeBytes; /* Try to detect play buffer underflows. */ LARGE_INTEGER perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */ LARGE_INTEGER previousPlayTime; @@ -266,14 +267,15 @@ typedef struct PaWinDsStream /* Input */ LPDIRECTSOUNDCAPTURE pDirectSoundCapture; LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer; - INT bytesPerInputFrame; + INT inputFrameSizeBytes; UINT readOffset; /* last read position */ - UINT inputSize; + UINT inputBufferSizeBytes; - int framesPerDSBuffer; + int hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */ double framesWritten; double secondsPerHostByte; /* Used to optimize latency calculation for outTime */ + double pollingPeriodSeconds; PaStreamCallbackFlags callbackFlags; @@ -304,6 +306,77 @@ typedef struct PaWinDsStream } PaWinDsStream; +/* Set minimal latency based on the current OS version. + * NT has higher latency. + */ +static double PaWinDS_GetMinSystemLatencySeconds( void ) +{ + double minLatencySeconds; + /* Set minimal latency based on whether NT or other OS. + * NT has higher latency. + */ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof( osvi ); + GetVersionEx( &osvi ); + DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); + DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); + DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); + /* Check for NT */ + if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) + { + minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_; + } + else if(osvi.dwMajorVersion >= 5) + { + minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_; + } + else + { + minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_; + } + return minLatencySeconds; +} + + +/************************************************************************* +** Return minimum workable latency required for this host. This is returned +** As the default stream latency in PaDeviceInfo. +** Latency can be optionally set by user by setting an environment variable. +** For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the AUTOEXEC.BAT file and reboot. +** If the environment variable is not set, then the latency will be determined +** based on the OS. Windows NT has higher latency than Win95. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") +#define PA_ENV_BUF_SIZE (32) + +static double PaWinDs_GetMinLatencySeconds( double sampleRate ) +{ + char envbuf[PA_ENV_BUF_SIZE]; + DWORD hresult; + double minLatencySeconds = 0; + + /* Let user determine minimal latency by setting environment variable. */ + hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + minLatencySeconds = atoi( envbuf ) * SECONDS_PER_MSEC; + } + else + { + minLatencySeconds = PaWinDS_GetMinSystemLatencySeconds(); +#if PA_USE_HIGH_LATENCY + PRINT(("PA - Minimum Latency set to %f msec!\n", minLatencySeconds * MSECS_PER_SECOND )); +#endif + } + + return minLatencySeconds; +} + + /************************************************************************************ ** Duplicate the input string using the allocations allocator. ** A NULL string is converted to a zero length string. @@ -482,6 +555,7 @@ static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID, return TRUE; } + #ifdef PAWIN_USE_WDMKS_DEVICE_INFO static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source ) @@ -816,11 +890,6 @@ static PaError AddOutputDeviceInfoFromDirectSound( } #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ - deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */ - /* initialize defaultSampleRate */ if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) @@ -847,7 +916,7 @@ static PaError AddOutputDeviceInfoFromDirectSound( ** But it supports continuous sampling. ** So fake range of rates, and hope it really supports it. */ - deviceInfo->defaultSampleRate = 44100.0f; + deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */ DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name )); } @@ -862,14 +931,19 @@ static PaError AddOutputDeviceInfoFromDirectSound( ** But we know that they really support a range of rates! ** So when we see a ridiculous set of rates, assume it is a range. */ - deviceInfo->defaultSampleRate = 44100.0f; + deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */ DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name )); } else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; - //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate ); // dwFlags | DSCAPS_CONTINUOUSRATE + + deviceInfo->defaultLowInputLatency = 0.; + deviceInfo->defaultHighInputLatency = 0.; + + deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate ); + deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2; } } @@ -978,11 +1052,6 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture( } #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ - deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */ - /* constants from a WINE patch by Francois Gouget, see: http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html @@ -1024,7 +1093,7 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture( else if( caps.dwFormats & WAVE_FORMAT_96S16 ) deviceInfo->defaultSampleRate = 96000.0; else - deviceInfo->defaultSampleRate = 0.; + deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */ } else if( caps.dwChannels == 1 ) { @@ -1039,9 +1108,15 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture( else if( caps.dwFormats & WAVE_FORMAT_96M16 ) deviceInfo->defaultSampleRate = 96000.0; else - deviceInfo->defaultSampleRate = 0.; + deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */ } - else deviceInfo->defaultSampleRate = 0.; + else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */ + + deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate ); + deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2; + + deviceInfo->defaultLowOutputLatency = 0.; + deviceInfo->defaultHighOutputLatency = 0.; } } @@ -1250,38 +1325,6 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) PaWinDs_TerminateDSoundEntryPoints(); } - -/* Set minimal latency based on whether NT or Win95. - * NT has higher latency. - */ -static int PaWinDS_GetMinSystemLatency( void ) -{ - int minLatencyMsec; - /* Set minimal latency based on whether NT or other OS. - * NT has higher latency. - */ - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof( osvi ); - GetVersionEx( &osvi ); - DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); - DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); - DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); - /* Check for NT */ - if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) - { - minLatencyMsec = PA_WIN_NT_LATENCY; - } - else if(osvi.dwMajorVersion >= 5) - { - minLatencyMsec = PA_WIN_WDM_LATENCY; - } - else - { - minLatencyMsec = PA_WIN_9X_LATENCY; - } - return minLatencyMsec; -} - static PaError ValidateWinDirectSoundSpecificStreamInfo( const PaStreamParameters *streamParameters, const PaWinDirectSoundStreamInfo *streamInfo ) @@ -1289,7 +1332,7 @@ static PaError ValidateWinDirectSoundSpecificStreamInfo( if( streamInfo ) { if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo ) - || streamInfo->version != 1 ) + || streamInfo->version != 2 ) { return paIncompatibleHostApiSpecificStreamInfo; } @@ -1394,45 +1437,6 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, } -/************************************************************************* -** Determine minimum number of buffers required for this host based -** on minimum latency. Latency can be optionally set by user by setting -** an environment variable. For example, to set latency to 200 msec, put: -** -** set PA_MIN_LATENCY_MSEC=200 -** -** in the AUTOEXEC.BAT file and reboot. -** If the environment variable is not set, then the latency will be determined -** based on the OS. Windows NT has higher latency than Win95. -*/ -#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") -#define PA_ENV_BUF_SIZE (32) - -static int PaWinDs_GetMinLatencyFrames( double sampleRate ) -{ - char envbuf[PA_ENV_BUF_SIZE]; - DWORD hresult; - int minLatencyMsec = 0; - - /* Let user determine minimal latency by setting environment variable. */ - hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); - if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) - { - minLatencyMsec = atoi( envbuf ); - } - else - { - minLatencyMsec = PaWinDS_GetMinSystemLatency(); -#if PA_USE_HIGH_LATENCY - PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); -#endif - - } - - return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC); -} - - #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream, PaWinDsDeviceInfo *inputDevice, @@ -1668,6 +1672,119 @@ error: return result; } + +static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames, + unsigned long *pollingPeriodFrames, + int isFullDuplex, + unsigned long suggestedInputLatencyFrames, + unsigned long suggestedOutputLatencyFrames, + double sampleRate, unsigned long userFramesPerBuffer ) +{ + /* we allow the polling period to range between 1 and 100ms. + prior to August 2011 we limited the minimum polling period to 10ms. + */ + unsigned long minimumPollingPeriodFrames = sampleRate / 1000; /* 1ms */ + unsigned long maximumPollingPeriodFrames = sampleRate / 10; /* 100ms */ + unsigned long pollingJitterFrames = sampleRate / 1000; /* 1ms */ + + if( userFramesPerBuffer == paFramesPerBufferUnspecified ) + { + unsigned long suggestedLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames ); + + *pollingPeriodFrames = suggestedLatencyFrames / 4; + if( *pollingPeriodFrames < minimumPollingPeriodFrames ) + { + *pollingPeriodFrames = minimumPollingPeriodFrames; + } + else if( *pollingPeriodFrames > maximumPollingPeriodFrames ) + { + *pollingPeriodFrames = maximumPollingPeriodFrames; + } + + *hostBufferSizeFrames = *pollingPeriodFrames + + max( *pollingPeriodFrames + pollingJitterFrames, suggestedLatencyFrames); + } + else + { + unsigned long suggestedLatencyFrames = suggestedInputLatencyFrames; + if( isFullDuplex ) + { + /* in full duplex streams we know that the buffer adapter adds userFramesPerBuffer + extra fixed latency. so we subtract it here as a fixed latency before computing + the buffer size. being careful not to produce an unrepresentable negative result. + + Note: this only works as expected if output latency is greater than input latency. + Otherwise we use input latency anyway since we do max(in,out). + */ + + if( userFramesPerBuffer < suggestedOutputLatencyFrames ) + { + unsigned long adjustedSuggestedOutputLatencyFrames = + suggestedOutputLatencyFrames - userFramesPerBuffer; + + /* maximum of input and adjusted output suggested latency */ + if( adjustedSuggestedOutputLatencyFrames > suggestedInputLatencyFrames ) + suggestedLatencyFrames = adjustedSuggestedOutputLatencyFrames; + } + } + else + { + /* maximum of input and output suggested latency */ + if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames ) + suggestedLatencyFrames = suggestedOutputLatencyFrames; + } + + *hostBufferSizeFrames = userFramesPerBuffer + + max( userFramesPerBuffer + pollingJitterFrames, suggestedLatencyFrames); + + *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), suggestedLatencyFrames / 16 ); + + if( *pollingPeriodFrames > maximumPollingPeriodFrames ) + { + *pollingPeriodFrames = maximumPollingPeriodFrames; + } + } +} + + +static void SetStreamInfoLatencies( PaWinDsStream *stream, + unsigned long userFramesPerBuffer, + unsigned long pollingPeriodFrames, + double sampleRate ) +{ + /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames, + and the configuration of the buffer processor */ + + unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified) + ? pollingPeriodFrames + : userFramesPerBuffer; + + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + /* 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) + + effectiveFramesPerBuffer) / sampleRate; + } + else + { + stream->streamRepresentation.streamInfo.inputLatency = 0; + } + + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + stream->streamRepresentation.streamInfo.outputLatency = + (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) + + (stream->hostBufferSizeFrames - effectiveFramesPerBuffer)) / sampleRate; + } + else + { + stream->streamRepresentation.streamInfo.outputLatency = 0; + } +} + + /***********************************************************************************/ /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ @@ -1694,6 +1811,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames; PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo; PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask; + unsigned long pollingPeriodFrames = 0; if( inputParameters ) { @@ -1833,7 +1951,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, { /* IMPLEMENT ME - establish which host formats are available */ PaSampleFormat nativeInputFormats = paInt16; - //PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; + /* PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */ hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat ); @@ -1847,7 +1965,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, { /* IMPLEMENT ME - establish which host formats are available */ PaSampleFormat nativeOutputFormats = paInt16; - //PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; + /* PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat ); @@ -1861,7 +1979,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, inputChannelCount, inputSampleFormat, hostInputSampleFormat, outputChannelCount, outputSampleFormat, hostOutputSampleFormat, sampleRate, streamFlags, framesPerBuffer, - framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */ + 0, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */ /* This next mode is required because DS can split the host buffer when it wraps around. */ paUtilVariableHostBufferSizePartialUsageAllowed, streamCallback, userData ); @@ -1870,24 +1988,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, bufferProcessorIsInitialized = 1; - stream->streamRepresentation.streamInfo.inputLatency = - (PaTime)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */ - stream->streamRepresentation.streamInfo.outputLatency = - (PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */ - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - + /* DirectSound specific initialization */ { HRESULT hr; - int bytesPerDirectSoundInputBuffer, bytesPerDirectSoundOutputBuffer; - int userLatencyFrames; - int minLatencyFrames; unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5); - -#ifdef PA_WIN_DS_USE_WMME_TIMER - stream->timerID = 0; -#endif + stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL ); if( stream->processingCompleted == NULL ) { @@ -1895,6 +2001,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, goto error; } +#ifdef PA_WIN_DS_USE_WMME_TIMER + stream->timerID = 0; +#endif + #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL ); if( stream->waitableTimer == NULL ) @@ -1915,42 +2025,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } #endif - /* Get system minimum latency. */ - minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate ); - - /* Let user override latency by passing latency parameter. */ - userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames) - ? suggestedInputLatencyFrames - : suggestedOutputLatencyFrames; - if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames; - - /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) - { - /* App support variable framesPerBuffer */ - stream->framesPerDSBuffer = minLatencyFrames; - - stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate; - } - else - { - /* Round up to number of buffers needed to guarantee that latency. */ - int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer; - if( numUserBuffers < 1 ) numUserBuffers = 1; - numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */ - stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers; - - stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate; - } - - { - /** @todo REVIEW: this calculation seems incorrect to me - rossb. */ - int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate); - PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency )); - } - /* set up i/o parameters */ + CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames, + /* isFullDuplex = */ (inputParameters && outputParameters), + suggestedInputLatencyFrames, + suggestedOutputLatencyFrames, + sampleRate, framesPerBuffer ); + + stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate; + /* ------------------ OUTPUT */ if( outputParameters ) { @@ -1961,14 +2045,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device)); */ - int bytesPerSample = Pa_GetSampleSize(hostOutputSampleFormat); - bytesPerDirectSoundOutputBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample; - if( bytesPerDirectSoundOutputBuffer < DSBSIZE_MIN ) + int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat); + stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes; + + stream->outputBufferSizeBytes = stream->hostBufferSizeFrames * stream->outputFrameSizeBytes; + if( stream->outputBufferSizeBytes < DSBSIZE_MIN ) { result = paBufferTooSmall; goto error; } - else if( bytesPerDirectSoundOutputBuffer > DSBSIZE_MAX ) + else if( stream->outputBufferSizeBytes > DSBSIZE_MAX ) { result = paBufferTooBig; goto error; @@ -1979,15 +2065,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, (stream->bufferProcessor.bytesPerHostOutputSample * outputChannelCount * sampleRate); - stream->outputBufferSizeBytes = bytesPerDirectSoundOutputBuffer; stream->outputIsRunning = FALSE; stream->outputUnderflowCount = 0; - stream->bytesPerOutputFrame = outputParameters->channelCount * bytesPerSample; - + /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */ if( QueryPerformanceFrequency( &counterFrequency ) ) { - stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->framesPerDSBuffer) / integerSampleRate; + stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->hostBufferSizeFrames) / integerSampleRate; } else { @@ -2003,22 +2087,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device)); */ - int bytesPerSample = Pa_GetSampleSize(hostInputSampleFormat); - bytesPerDirectSoundInputBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample; - if( bytesPerDirectSoundInputBuffer < DSBSIZE_MIN ) + int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat); + stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes; + + stream->inputBufferSizeBytes = stream->hostBufferSizeFrames * stream->inputFrameSizeBytes; + if( stream->inputBufferSizeBytes < DSBSIZE_MIN ) { result = paBufferTooSmall; goto error; } - else if( bytesPerDirectSoundInputBuffer > DSBSIZE_MAX ) + else if( stream->inputBufferSizeBytes > DSBSIZE_MAX ) { result = paBufferTooBig; goto error; } - - stream->bytesPerInputFrame = inputParameters->channelCount * bytesPerSample; - - stream->inputSize = bytesPerDirectSoundInputBuffer; } /* open/create the DirectSound buffers */ @@ -2040,11 +2122,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, hr = InitFullDuplexInputOutputBuffers( stream, (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device], hostInputSampleFormat, - (WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer, + (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes, inputChannelMask, (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device], hostOutputSampleFormat, - (WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer, + (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes, outputChannelMask, integerSampleRate ); @@ -2066,7 +2148,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device], hostOutputSampleFormat, integerSampleRate, - (WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer, + (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes, outputChannelMask ); DBUG(("InitOutputBuffer() returns %x\n", hr)); if( hr != DS_OK ) @@ -2083,7 +2165,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device], hostInputSampleFormat, integerSampleRate, - (WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer, + (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes, inputChannelMask ); DBUG(("InitInputBuffer() returns %x\n", hr)); if( hr != DS_OK ) @@ -2096,6 +2178,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } } + SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate ); + + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + *s = (PaStream*)stream; return result; @@ -2256,7 +2342,7 @@ static int TimeSlice( PaWinDsStream *stream ) long bytesEmpty = 0; long bytesFilled = 0; long bytesToXfer = 0; - long framesToXfer = 0; + long framesToXfer = 0; /* the number of frames we'll process this tick */ long numInFramesReady = 0; long numOutFramesReady = 0; long bytesProcessed; @@ -2289,12 +2375,12 @@ static int TimeSlice( PaWinDsStream *stream ) if( hr == DS_OK ) { filled = readPos - stream->readOffset; - if( filled < 0 ) filled += stream->inputSize; // unwrap offset + if( filled < 0 ) filled += stream->inputBufferSizeBytes; // unwrap offset bytesFilled = filled; } // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails? - framesToXfer = numInFramesReady = bytesFilled / stream->bytesPerInputFrame; + framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes; outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; // FIXME: this doesn't look right. we're calculating output latency in input branch. also secondsPerHostByte is only initialized for the output stream /** @todo Check for overflow */ @@ -2305,14 +2391,15 @@ static int TimeSlice( PaWinDsStream *stream ) { UINT previousUnderflowCount = stream->outputUnderflowCount; QueryOutputSpace( stream, &bytesEmpty ); - framesToXfer = numOutFramesReady = bytesEmpty / stream->bytesPerOutputFrame; + framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes; /* Check for underflow */ if( stream->outputUnderflowCount != previousUnderflowCount ) stream->callbackFlags |= paOutputUnderflow; } - if( (numInFramesReady > 0) && (numOutFramesReady > 0) ) + /* if it's a full duplex stream, set framesToXfer to the minimum of input and output frames ready */ + if( stream->bufferProcessor.inputChannelCount > 0 && stream->bufferProcessor.outputChannelCount > 0 ) { framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady; } @@ -2333,7 +2420,7 @@ static int TimeSlice( PaWinDsStream *stream ) /* Input */ if( stream->bufferProcessor.inputChannelCount > 0 ) { - bytesToXfer = framesToXfer * stream->bytesPerInputFrame; + bytesToXfer = framesToXfer * stream->inputFrameSizeBytes; hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer, stream->readOffset, bytesToXfer, (void **) &lpInBuf1, &dwInSize1, @@ -2347,13 +2434,13 @@ static int TimeSlice( PaWinDsStream *stream ) goto error2; } - numFrames = dwInSize1 / stream->bytesPerInputFrame; + numFrames = dwInSize1 / stream->inputFrameSizeBytes; PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames ); PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 ); /* Is input split into two regions. */ if( dwInSize2 > 0 ) { - numFrames = dwInSize2 / stream->bytesPerInputFrame; + numFrames = dwInSize2 / stream->inputFrameSizeBytes; PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames ); PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 ); } @@ -2362,7 +2449,7 @@ static int TimeSlice( PaWinDsStream *stream ) /* Output */ if( stream->bufferProcessor.outputChannelCount > 0 ) { - bytesToXfer = framesToXfer * stream->bytesPerOutputFrame; + bytesToXfer = framesToXfer * stream->outputFrameSizeBytes; hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes, bytesToXfer, (void **) &lpOutBuf1, &dwOutSize1, @@ -2376,14 +2463,14 @@ static int TimeSlice( PaWinDsStream *stream ) goto error1; } - numFrames = dwOutSize1 / stream->bytesPerOutputFrame; + numFrames = dwOutSize1 / stream->outputFrameSizeBytes; PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames ); PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 ); /* Is output split into two regions. */ if( dwOutSize2 > 0 ) { - numFrames = dwOutSize2 / stream->bytesPerOutputFrame; + numFrames = dwOutSize2 / stream->outputFrameSizeBytes; PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames ); PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 ); } @@ -2397,7 +2484,7 @@ static int TimeSlice( PaWinDsStream *stream ) /* FIXME: an underflow could happen here */ /* Update our buffer offset and unlock sound buffer */ - bytesProcessed = numFrames * stream->bytesPerOutputFrame; + bytesProcessed = numFrames * stream->outputFrameSizeBytes; stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes; IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2); } @@ -2408,8 +2495,8 @@ error1: /* FIXME: an overflow could happen here */ /* Update our buffer offset and unlock sound buffer */ - bytesProcessed = numFrames * stream->bytesPerInputFrame; - stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputSize; + bytesProcessed = numFrames * stream->inputFrameSizeBytes; + stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputBufferSizeBytes; IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2); } error2: @@ -2548,28 +2635,27 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg ) MMRESULT mmResult; HANDLE hWaitableTimer; LARGE_INTEGER dueTime; - int framesPerWakeup, msecPerWakeup; + int timerPeriodMs; - framesPerWakeup = stream->framesPerDSBuffer / 4; /* always poll using quadruple buffering, probably not the best strategy */ - msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate; - if( msecPerWakeup < 1 ) msecPerWakeup = 1; - else if( msecPerWakeup > 100 ) msecPerWakeup = 100; + timerPeriodMs = stream->pollingPeriodSeconds * MSECS_PER_SECOND; + if( timerPeriodMs < 1 ) + timerPeriodMs = 1; #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT assert( stream->waitableTimer != NULL ); /* invoke first timeout immediately */ - dueTime.LowPart = msecPerWakeup * 1000 * 10; + dueTime.LowPart = timerPeriodMs * 1000 * 10; dueTime.HighPart = 0; /* tick using waitable timer */ - if( SetWaitableTimer( stream->waitableTimer, &dueTime, msecPerWakeup, WaitableTimerAPCProc, pArg, FALSE ) != 0 ) + if( SetWaitableTimer( stream->waitableTimer, &dueTime, timerPeriodMs, WaitableTimerAPCProc, pArg, FALSE ) != 0 ) { DWORD wfsoResult = 0; do { /* wait for processingCompleted to be signaled or our timer APC to be called */ - wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, msecPerWakeup * 10, /* alertable = */ TRUE ); + wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, timerPeriodMs * 10, /* alertable = */ TRUE ); }while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION ); } @@ -2579,7 +2665,7 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg ) #else /* tick using WaitForSingleObject timout */ - while ( WaitForSingleObject( stream->processingCompleted, msecPerWakeup ) == WAIT_TIMEOUT ) + while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT ) { TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 ); } @@ -2775,13 +2861,9 @@ static PaError StartStream( PaStream *s ) if( stream->streamRepresentation.streamCallback ) { TIMECAPS timecaps; - - int timerResolution; - int framesPerWakeup = stream->framesPerDSBuffer / 4; - int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate; - if( msecPerWakeup < 10 ) msecPerWakeup = 10; - else if( msecPerWakeup > 100 ) msecPerWakeup = 100; - timerResolution = msecPerWakeup/4; + int timerPeriodMs = stream->pollingPeriodSeconds * MSECS_PER_SECOND; + if( timerPeriodMs < 1 ) + timerPeriodMs = 1; /* set windows scheduler granularity only as fine as needed, no finer */ /* Although this is not fully documented by MS, it appears that @@ -2792,9 +2874,12 @@ static PaError StartStream( PaStream *s ) assert( stream->systemTimerResolutionPeriodMs == 0 ); if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 ) ) { - stream->systemTimerResolutionPeriodMs = timerResolution; + /* aim for resolution 4 times higher than polling rate */ + stream->systemTimerResolutionPeriodMs = (stream->pollingPeriodSeconds * MSECS_PER_SECOND) / 4; if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin ) stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin; + if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax ) + stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMax; if( timeBeginPeriod( stream->systemTimerResolutionPeriodMs ) != MMSYSERR_NOERROR ) stream->systemTimerResolutionPeriodMs = 0; /* timeBeginPeriod failed, so we don't need to call timeEndPeriod() later */ @@ -2807,7 +2892,7 @@ static PaError StartStream( PaStream *s ) are serialised onto a single thread. Which creates problems with multiple PA streams, or when also using timers for other time critical tasks */ - stream->timerID = timeSetEvent( msecPerWakeup, timerResolution, (LPTIMECALLBACK) TimerCallback, + stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback, (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS ); if( stream->timerID == 0 ) @@ -2876,7 +2961,7 @@ static PaError StopStream( PaStream *s ) stream->stopProcessing = 1; /* Set timeout at 4 times maximum time we might wait. */ - timeoutMsec = (int) (4 * MSEC_PER_SECOND * (stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate)); + timeoutMsec = (int) (4 * MSECS_PER_SECOND * (stream->hostBufferSizeFrames / stream->streamRepresentation.streamInfo.sampleRate)); WaitForSingleObject( stream->processingCompleted, timeoutMsec ); } diff --git a/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c b/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c index 869cc4dac8..60a61e474b 100644 --- a/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c +++ b/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c @@ -125,13 +125,13 @@ #endif #ifdef _MSC_VER - //#define NOMMIDS + #define NOMMIDS #define DYNAMIC_GUID(data) {data} - //#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */ - //#undef DEFINE_GUID - //#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data} - //#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data) - //#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n) + #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */ + #undef DEFINE_GUID + #define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data} + #define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data) + #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n) #endif #include diff --git a/3rdparty/portaudio/src/hostapi/wmme/pa_win_wmme.c b/3rdparty/portaudio/src/hostapi/wmme/pa_win_wmme.c index 6eda20937b..ec891a94ed 100644 --- a/3rdparty/portaudio/src/hostapi/wmme/pa_win_wmme.c +++ b/3rdparty/portaudio/src/hostapi/wmme/pa_win_wmme.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_wmme.c 1717 2011-07-28 12:03:09Z rossb $ + * $Id: pa_win_wmme.c 1739 2011-08-25 07:15:31Z rossb $ * pa_win_wmme.c * Implementation of PortAudio for Windows MultiMedia Extensions (WMME) * @@ -174,16 +174,27 @@ /* Use higher latency for NT because it is even worse at real-time operation than Win9x. */ -#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_WIN_NT_DEFAULT_LATENCY_ (0.4) + +/* Default low latency for WDM based systems. This is based on a rough + 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. + For now we choose a safe value across all Windows versions here. +*/ +#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (0.090) + /* 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_TARGET_HOST_BUFFER_COUNT_ 8 #define PA_MME_MIN_TIMEOUT_MSEC_ (1000) @@ -1341,13 +1352,9 @@ static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames( /* Calculate the number of buffers of length hostFramesPerBuffer that fit in suggestedLatencyFrames, rounding up to the next integer. - 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. + The value (hostBufferSizeFrames - 1) below is to ensure the buffer count is rounded up. */ - - unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 2)) / hostBufferSizeFrames); + unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 1)) / hostBufferSizeFrames); /* We always need one extra buffer for processing while the rest are queued/playing. i.e. latency is framesPerBuffer * (bufferCount - 1) @@ -1361,6 +1368,48 @@ static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames( } +static unsigned long ComputeHostBufferSizeGivenHardUpperLimit( + unsigned long userFramesPerBuffer, + unsigned long absoluteMaximumBufferSizeFrames ) +{ + 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; + int i; + + assert( absoluteMaximumBufferSizeFrames > 67 ); /* assume maximum is large and we're only factoring by small primes */ + + /* search for the largest integer factor of userFramesPerBuffer less + than or equal to absoluteMaximumBufferSizeFrames */ + + /* 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 ) + { + unsigned long p = primes_[i]; + unsigned long divided = result / p; + if( divided*p == result ) + { + result = divided; + break; /* continue with outer while loop */ + } + } + if( primes_[i] == 0 ) + { /* loop failed to find a prime factor, return an approximate result */ + unsigned long d = (userFramesPerBuffer + (absoluteMaximumBufferSizeFrames-1)) + / absoluteMaximumBufferSizeFrames; + return userFramesPerBuffer / d; + } + } + + return result; +} + + static PaError SelectHostBufferSizeFramesAndHostBufferCount( unsigned long suggestedLatencyFrames, unsigned long userFramesPerBuffer, @@ -1370,56 +1419,77 @@ static PaError SelectHostBufferSizeFramesAndHostBufferCount( unsigned long *hostBufferSizeFrames, unsigned long *hostBufferCount ) { + unsigned long effectiveUserFramesPerBuffer; + unsigned long numberOfUserBuffersPerHostBuffer; + + if( userFramesPerBuffer == paFramesPerBufferUnspecified ){ - *hostBufferSizeFrames = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_; + effectiveUserFramesPerBuffer = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_; }else{ - *hostBufferSizeFrames = userFramesPerBuffer; + if( userFramesPerBuffer > absoluteMaximumBufferSizeFrames ){ - 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; */ + /* user has requested a user buffer that's larger than absoluteMaximumBufferSizeFrames. + try to choose a buffer size that is equal or smaller than absoluteMaximumBufferSizeFrames + but is also an integer factor of userFramesPerBuffer, so as to distribute computation evenly. + the buffer processor will handle the block adaption between host and user buffer sizes. + see http://www.portaudio.com/trac/ticket/189 for discussion. + */ + + effectiveUserFramesPerBuffer = ComputeHostBufferSizeGivenHardUpperLimit( userFramesPerBuffer, absoluteMaximumBufferSizeFrames ); + assert( effectiveUserFramesPerBuffer <= absoluteMaximumBufferSizeFrames ); + + /* try to ensure that duration of host buffering is at least as + large as duration of user buffer. */ + if( suggestedLatencyFrames < userFramesPerBuffer ) + suggestedLatencyFrames = userFramesPerBuffer; + + }else{ + + effectiveUserFramesPerBuffer = userFramesPerBuffer; } } /* compute a host buffer count based on suggestedLatencyFrames and our granularity */ + *hostBufferSizeFrames = effectiveUserFramesPerBuffer; + *hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount ); - /* consider coalescing multiple user buffers into each host buffer */ + if( *hostBufferSizeFrames >= userFramesPerBuffer ) + { + /* + 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. - if( *hostBufferCount >= PA_MME_TARGET_HOST_BUFFER_COUNT_ * 2 ){ + 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. + Since our latency is computed based on (*hostBufferCount - 1) we compute a + coalescing factor based on (*hostBufferCount - 1) and (PA_MME_TARGET_HOST_BUFFER_COUNT_-1). + + 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 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. - */ + if( numberOfUserBuffersPerHostBuffer > 1 ) + { + unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* minimum of our limits */ + ? absoluteMaximumBufferSizeFrames + : preferredMaximumBufferSizeFrames; - unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* min of our limits */ - ? absoluteMaximumBufferSizeFrames - : preferredMaximumBufferSizeFrames; + unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / effectiveUserFramesPerBuffer; /* don't coalesce more than this */ - unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / *hostBufferSizeFrames; /* don't coalesce more than this */ + if( numberOfUserBuffersPerHostBuffer > maxUserBuffersPerHostBuffer ) + numberOfUserBuffersPerHostBuffer = maxUserBuffersPerHostBuffer; - /* 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; + *hostBufferSizeFrames = effectiveUserFramesPerBuffer * numberOfUserBuffersPerHostBuffer; /* recompute hostBufferCount to approximate suggestedLatencyFrames now that hostBufferSizeFrames is larger */ *hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( @@ -1515,7 +1585,7 @@ static PaError CalculateBufferSettings( : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_; result = SelectHostBufferSizeFramesAndHostBufferCount( - (unsigned long)(suggestedInputLatency * sampleRate), + (unsigned long)(suggestedInputLatency * sampleRate), /* (truncate) */ userFramesPerBuffer, minimumBufferCount, (unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */ @@ -1610,7 +1680,7 @@ static PaError CalculateBufferSettings( /* compute the output buffer size and count */ result = SelectHostBufferSizeFramesAndHostBufferCount( - (unsigned long)(suggestedOutputLatency * sampleRate), + (unsigned long)(suggestedOutputLatency * sampleRate), /* (truncate) */ userFramesPerBuffer, PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_, (unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */ diff --git a/3rdparty/portaudio/src/os/unix/pa_unix_hostapis.c b/3rdparty/portaudio/src/os/unix/pa_unix_hostapis.c index 339e1b148d..4399b875b1 100644 --- a/3rdparty/portaudio/src/os/unix/pa_unix_hostapis.c +++ b/3rdparty/portaudio/src/os/unix/pa_unix_hostapis.c @@ -1,5 +1,5 @@ /* - * $Id: pa_unix_hostapis.c 1413 2009-05-24 17:00:36Z aknudsen $ + * $Id: pa_unix_hostapis.c 1740 2011-08-25 07:17:48Z philburk $ * Portable Audio I/O Library UNIX initialization table * * Based on the Open Source API proposed by Ross Bencina @@ -59,47 +59,45 @@ PaUtilHostApiInitializer *paHostApiInitializers[] = { #ifdef __linux__ -#ifdef PA_USE_ALSA +#if PA_USE_ALSA PaAlsa_Initialize, #endif -#ifdef PA_USE_OSS +#if PA_USE_OSS PaOSS_Initialize, #endif #else /* __linux__ */ -#ifdef PA_USE_OSS +#if PA_USE_OSS PaOSS_Initialize, #endif -#ifdef PA_USE_ALSA +#if PA_USE_ALSA PaAlsa_Initialize, #endif #endif /* __linux__ */ -#ifdef PA_USE_JACK +#if PA_USE_JACK PaJack_Initialize, #endif /* Added for IRIX, Pieter, oct 2, 2003: */ -#ifdef PA_USE_SGI +#if PA_USE_SGI PaSGI_Initialize, #endif -#ifdef PA_USE_ASIHPI +#if PA_USE_ASIHPI PaAsiHpi_Initialize, #endif -#ifdef PA_USE_COREAUDIO +#if PA_USE_COREAUDIO PaMacCore_Initialize, #endif -#ifdef PA_USE_SKELETON +#if PA_USE_SKELETON PaSkeleton_Initialize, #endif 0 /* NULL terminated array */ }; - -int paDefaultHostApiIndex = 0; diff --git a/3rdparty/portaudio/src/os/win/pa_win_hostapis.c b/3rdparty/portaudio/src/os/win/pa_win_hostapis.c index b059e7f3a9..5d22438799 100644 --- a/3rdparty/portaudio/src/os/win/pa_win_hostapis.c +++ b/3rdparty/portaudio/src/os/win/pa_win_hostapis.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_hostapis.c 1687 2011-05-19 08:24:02Z rob_bielik $ + * $Id: pa_win_hostapis.c 1728 2011-08-18 03:31:51Z rossb $ * Portable Audio I/O Library Windows initialization table * * Based on the Open Source API proposed by Ross Bencina @@ -100,5 +100,3 @@ PaUtilHostApiInitializer *paHostApiInitializers[] = }; -int paDefaultHostApiIndex = 0; -