diff --git a/bazaar/plugin/portaudio/common/pa_converters.c b/bazaar/plugin/portaudio/common/pa_converters.c index 009149c68..ef65b68a5 100644 --- a/bazaar/plugin/portaudio/common/pa_converters.c +++ b/bazaar/plugin/portaudio/common/pa_converters.c @@ -1,5 +1,5 @@ /* - * $Id: pa_converters.c 1495 2010-04-17 07:43:00Z dmitrykos $ + * $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 @@ -45,12 +45,15 @@ @todo Consider whether functions which dither but don't clip should exist, V18 automatically enabled clipping whenever dithering was selected. Perhaps - we should do the same. + we should do the same. + 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, + Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither + see: "some conversion functions are not implemented in pa_converters.c" + http://www.portaudio.com/trac/ticket/35 @todo review the converters marked REVIEW: Float32_To_Int32, Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip, @@ -459,7 +462,7 @@ static void Float32_To_Int24( while( count-- ) { /* convert to 32 bit and drop the low 8 bits */ - double scaled = *src * 0x7FFFFFFF; + double scaled = (double)(*src) * 2147483647.0; temp = (PaInt32) scaled; #if defined(PA_LITTLE_ENDIAN) @@ -725,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 ); @@ -817,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; } @@ -841,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; @@ -861,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; @@ -1163,39 +1175,33 @@ static void Int24_To_Int16_Dither( void *sourceBuffer, signed int sourceStride, unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) { - #define _PA_CNV_RESCALE(__max_from,__max_to,g) ((1.0f/(float)(__max_from/2+1))*((float)((__max_to/2+1)+g))) -#ifndef PA_BIG_ENDIAN - #define _PA_INT24_TO_INT32(v) ((int)(((int)v[2] << 24)|((int)v[1] << 16)|((int)v[0] << 8)) >> 8) -#else - #define _PA_INT24_TO_INT32(v) ((int)(((int)v[0] << 24)|((int)v[1] << 16)|((int)v[2] << 8)) >> 8) -#endif - #define _PA_INT24_TO_FLOAT(v,g) ((float)(_PA_INT24_TO_INT32(v)) * _PA_CNV_RESCALE(0xffffff,0xffff,g)) + unsigned char *src = (unsigned char*)sourceBuffer; + PaInt16 *dest = (PaInt16*)destinationBuffer; - unsigned char *src = (unsigned char *)sourceBuffer; - PaInt16 *dest = (PaInt16 *)destinationBuffer; - float dither, dithered; + PaInt32 temp, dither; while( count-- ) { - dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* downscale 24-bit int to 16-bit int placed into 32-bit float container, - 16-bit scaler is decreased by 2 to leave space for dither in order not to overflow - */ - dithered = _PA_INT24_TO_FLOAT(src, -2.0f) + dither; - -#ifdef PA_USE_C99_LRINTF - *dest = lrintf(dithered-0.5f); -#else - *dest = (PaInt16) dithered; +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[0]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[2]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[0]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[2]) << 8); #endif - src += sourceStride * 3; + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (PaInt16) (((temp >> 1) + dither) >> 15); + + src += sourceStride * 3; dest += destinationStride; } } - /* -------------------------------------------------------------------------- */ static void Int24_To_Int8( @@ -1233,13 +1239,31 @@ static void Int24_To_Int8_Dither( void *sourceBuffer, signed int sourceStride, unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) { - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ + unsigned char *src = (unsigned char*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + + PaInt32 temp, dither; + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[0]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[2]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[0]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[2]) << 8); +#endif + + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (signed char) (((temp >> 1) + dither) >> 23); + + src += sourceStride * 3; + dest += destinationStride; + } } /* -------------------------------------------------------------------------- */ diff --git a/bazaar/plugin/portaudio/common/pa_cpuload.c b/bazaar/plugin/portaudio/common/pa_cpuload.c index 445503c29..4465a50b6 100644 --- a/bazaar/plugin/portaudio/common/pa_cpuload.c +++ b/bazaar/plugin/portaudio/common/pa_cpuload.c @@ -1,5 +1,5 @@ /* - * $Id: pa_cpuload.c 1097 2006-08-26 08:27:53Z rossb $ + * $Id: pa_cpuload.c 1577 2011-02-01 13:03:45Z rossb $ * Portable Audio I/O Library CPU Load measurement functions * Portable CPU load measurement facility. * @@ -46,7 +46,7 @@ @todo Dynamically calculate the coefficients used to smooth the CPU Load Measurements over time to provide a uniform characterisation of CPU Load independent of rate at which PaUtil_BeginCpuLoadMeasurement / - PaUtil_EndCpuLoadMeasurement are called. + PaUtil_EndCpuLoadMeasurement are called. see http://www.portaudio.com/trac/ticket/113 */ @@ -89,7 +89,7 @@ void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned lon measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent; /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */ - /** FIXME @todo these coefficients shouldn't be hardwired */ + /** FIXME @todo these coefficients shouldn't be hardwired see: http://www.portaudio.com/trac/ticket/113 */ #define LOWPASS_COEFFICIENT_0 (0.9) #define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) diff --git a/bazaar/plugin/portaudio/common/pa_debugprint.c b/bazaar/plugin/portaudio/common/pa_debugprint.c index 7ee8f7f23..67e414adb 100644 --- a/bazaar/plugin/portaudio/common/pa_debugprint.c +++ b/bazaar/plugin/portaudio/common/pa_debugprint.c @@ -48,9 +48,6 @@ "byte code/abi portable". So the technique used here is to allocate a local a static array, write in it, then callback the user with a pointer to its start. - - @todo Consider allocating strdump using dynamic allocation. - @todo Consider reentrancy and possibly corrupted strdump buffer. */ #include diff --git a/bazaar/plugin/portaudio/common/pa_front.c b/bazaar/plugin/portaudio/common/pa_front.c index 3e7abac4e..957d1eac4 100644 --- a/bazaar/plugin/portaudio/common/pa_front.c +++ b/bazaar/plugin/portaudio/common/pa_front.c @@ -1,5 +1,5 @@ /* - * $Id: pa_front.c 1396 2008-11-03 19:31:30Z philburk $ + * $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. * @@ -59,18 +59,6 @@ All PortAudio API functions can be conditionally compiled with logging code. To compile with logging, define the PA_LOG_API_CALLS precompiler symbol. - - @todo Consider adding host API specific error text in Pa_GetErrorText() for - paUnanticipatedHostError - - @todo Consider adding a new error code for when (inputParameters == NULL) - && (outputParameters == NULL) - - @todo review whether Pa_CloseStream() should call the interface's - CloseStream function if aborting the stream returns an error code. - - @todo Create new error codes if a NULL buffer pointer, or a - zero frame count is passed to Pa_ReadStream or Pa_WriteStream. */ @@ -128,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; @@ -158,6 +147,7 @@ static void TerminateHostApis( void ) hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] ); } hostApisCount_ = 0; + defaultHostApiIndex_ = 0; deviceCount_ = 0; if( hostApis_ != 0 ) @@ -184,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; @@ -205,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 ) @@ -220,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: @@ -381,7 +386,7 @@ const char *Pa_GetErrorText( PaError errorCode ) { case paNoError: result = "Success"; break; case paNotInitialized: result = "PortAudio not initialized"; break; - /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */ + /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError. see: http://www.portaudio.com/trac/ticket/114 */ case paUnanticipatedHostError: result = "Unanticipated host error"; break; case paInvalidChannelCount: result = "Invalid number of channels"; break; case paInvalidSampleRate: result = "Invalid sample rate"; break; @@ -537,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 */ @@ -1343,7 +1348,7 @@ PaError Pa_CloseStream( PaStream* stream ) else if( result == 0 ) result = interface->Abort( stream ); - if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? */ + if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? see: http://www.portaudio.com/trac/ticket/115 */ result = interface->Close( stream ); } @@ -1484,7 +1489,6 @@ PaError Pa_IsStreamActive( PaStream *stream ) PA_LOGAPI(("\tPaStream* stream: 0x%p\n", stream )); if( result == paNoError ) -// result = ((PaUtilStreamRepresentation*) ((stream)) )->streamInterface->IsActive( stream ); result = PA_STREAM_INTERFACE(stream)->IsActive( stream ); @@ -1602,7 +1606,7 @@ PaError Pa_ReadStream( PaStream* stream, { if( frames == 0 ) { - /* XXX: Should we not allow the implementation to signal any overflow condition? */ + /* @todo Should we not allow the implementation to signal any overflow condition? see: http://www.portaudio.com/trac/ticket/116*/ result = paNoError; } else if( buffer == 0 ) @@ -1642,7 +1646,7 @@ PaError Pa_WriteStream( PaStream* stream, { if( frames == 0 ) { - /* XXX: Should we not allow the implementation to signal any underflow condition? */ + /* @todo Should we not allow the implementation to signal any underflow condition? see: http://www.portaudio.com/trac/ticket/116*/ result = paNoError; } else if( buffer == 0 ) diff --git a/bazaar/plugin/portaudio/common/pa_hostapi.h b/bazaar/plugin/portaudio/common/pa_hostapi.h index 5462d4439..1437f629d 100644 --- a/bazaar/plugin/portaudio/common/pa_hostapi.h +++ b/bazaar/plugin/portaudio/common/pa_hostapi.h @@ -1,7 +1,7 @@ #ifndef PA_HOSTAPI_H #define PA_HOSTAPI_H /* - * $Id: pa_hostapi.h 1339 2008-02-15 07:50:33Z rossb $ + * $Id: pa_hostapi.h 1740 2011-08-25 07:17:48Z philburk $ * Portable Audio I/O Library * host api representation * @@ -46,9 +46,113 @@ to manage and communicate with host API implementations. */ - #include "portaudio.h" +/** +The PA_NO_* host API macros are now deprecated in favor of PA_USE_* macros. +PA_USE_* indicates whether a particular host API will be initialized by PortAudio. +An undefined or 0 value indicates that the host API will not be used. A value of 1 +indicates that the host API will be used. PA_USE_* macros should be left undefined +or defined to either 0 or 1. + +The code below ensures that PA_USE_* macros are always defined and have value +0 or 1. Undefined symbols are defaulted to 0. Symbols that are neither 0 nor 1 +are defaulted to 1. +*/ + +#ifndef PA_USE_SKELETON +#define PA_USE_SKELETON 0 +#elif (PA_USE_SKELETON != 0) && (PA_USE_SKELETON != 1) +#undef PA_USE_SKELETON +#define PA_USE_SKELETON 1 +#endif + +#if defined(PA_NO_ASIO) || defined(PA_NO_DS) || defined(PA_NO_WMME) || defined(PA_NO_WASAPI) || defined(PA_NO_WDMKS) +#error "Portaudio: PA_NO_ is no longer supported, please remove definition and use PA_USE_ instead" +#endif + +#ifndef PA_USE_ASIO +#define PA_USE_ASIO 0 +#elif (PA_USE_ASIO != 0) && (PA_USE_ASIO != 1) +#undef PA_USE_ASIO +#define PA_USE_ASIO 1 +#endif + +#ifndef PA_USE_DS +#define PA_USE_DS 0 +#elif (PA_USE_DS != 0) && (PA_USE_DS != 1) +#undef PA_USE_DS +#define PA_USE_DS 1 +#endif + +#ifndef PA_USE_WMME +#define PA_USE_WMME 0 +#elif (PA_USE_WMME != 0) && (PA_USE_WMME != 1) +#undef PA_USE_WMME +#define PA_USE_WMME 1 +#endif + +#ifndef PA_USE_WASAPI +#define PA_USE_WASAPI 0 +#elif (PA_USE_WASAPI != 0) && (PA_USE_WASAPI != 1) +#undef PA_USE_WASAPI +#define PA_USE_WASAPI 1 +#endif + +#ifndef PA_USE_WDMKS +#define PA_USE_WDMKS 0 +#elif (PA_USE_WDMKS != 0) && (PA_USE_WDMKS != 1) +#undef PA_USE_WDMKS +#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" { @@ -237,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/bazaar/plugin/portaudio/common/pa_memorybarrier.h b/bazaar/plugin/portaudio/common/pa_memorybarrier.h index f68962220..2879ce33a 100644 --- a/bazaar/plugin/portaudio/common/pa_memorybarrier.h +++ b/bazaar/plugin/portaudio/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/bazaar/plugin/portaudio/common/pa_process.c b/bazaar/plugin/portaudio/common/pa_process.c index 69ab5fdeb..383f9cafa 100644 --- a/bazaar/plugin/portaudio/common/pa_process.c +++ b/bazaar/plugin/portaudio/common/pa_process.c @@ -1,5 +1,5 @@ /* - * $Id: pa_process.c 1555 2010-11-12 17:16:21Z richard_ash $ + * $Id: pa_process.c 1706 2011-07-21 18:44:58Z philburk $ * Portable Audio I/O Library * streamCallback <-> host buffer processing adapter * @@ -41,41 +41,6 @@ @ingroup common_src @brief Buffer Processor implementation. - - The code in this file is not optimised yet - although it's not clear that - it needs to be. there may appear to be redundancies - that could be factored into common functions, but the redundanceis are left - intentionally as each appearance may have different optimisation possibilities. - - The optimisations which are planned involve only converting data in-place - where possible, rather than copying to the temp buffer(s). - - Note that in the extreme case of being able to convert in-place, and there - being no conversion necessary there should be some code which short-circuits - the operation. - - @todo Consider cache tilings for intereave<->deinterleave. - - @todo specify and implement some kind of logical policy for handling the - underflow and overflow stream flags when the underflow/overflow overlaps - multiple user buffers/callbacks. - - @todo provide support for priming the buffers with data from the callback. - The client interface is now implemented through PaUtil_SetNoInput() - which sets bp->hostInputChannels[0][0].data to zero. However this is - currently only implemented in NonAdaptingProcess(). It shouldn't be - needed for AdaptingInputOnlyProcess() (no priming should ever be - requested for AdaptingInputOnlyProcess()). - Not sure if additional work should be required to make it work with - AdaptingOutputOnlyProcess, but it definitely is required for - AdaptingProcess. - - @todo implement PaUtil_SetNoOutput for AdaptingProcess - - @todo don't allocate temp buffers for blocking streams unless they are - needed. At the moment they are needed, but perhaps for host APIs - where the implementation passes a buffer to the host they could be - used. */ @@ -137,6 +102,7 @@ PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp, PaError result = paNoError; PaError bytesPerSample; unsigned long tempInputBufferSize, tempOutputBufferSize; + PaStreamFlags tempInputStreamFlags; if( streamFlags & paNeverDropInput ) { @@ -257,8 +223,20 @@ PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp, goto error; } + /* Under the assumption that no ADC in existence delivers better than 24bits resolution, + we disable dithering when host input format is paInt32 and user format is paInt24, + since the host samples will just be padded with zeros anyway. */ + + tempInputStreamFlags = streamFlags; + if( !(tempInputStreamFlags & paDitherOff) /* dither is on */ + && (hostInputSampleFormat & paInt32) /* host input format is int32 */ + && (userInputSampleFormat & paInt24) /* user requested format is int24 */ ){ + + tempInputStreamFlags = tempInputStreamFlags | paDitherOff; + } + bp->inputConverter = - PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags ); + PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, tempInputStreamFlags ); bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat ); @@ -450,13 +428,13 @@ void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp ) } -unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp ) +unsigned long PaUtil_GetBufferProcessorInputLatencyFrames( PaUtilBufferProcessor* bp ) { return bp->initialFramesInTempInputBuffer; } -unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp ) +unsigned long PaUtil_GetBufferProcessorOutputLatencyFrames( PaUtilBufferProcessor* bp ) { return bp->initialFramesInTempOutputBuffer; } @@ -590,6 +568,8 @@ void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp ) assert( bp->outputChannelCount > 0 ); bp->hostOutputChannels[0][0].data = 0; + + /* note that only NonAdaptingProcess is able to deal with no output at this stage. not implemented for AdaptingProcess */ } diff --git a/bazaar/plugin/portaudio/common/pa_process.h b/bazaar/plugin/portaudio/common/pa_process.h index 12274eecf..4d5f56ad6 100644 --- a/bazaar/plugin/portaudio/common/pa_process.h +++ b/bazaar/plugin/portaudio/common/pa_process.h @@ -1,7 +1,7 @@ #ifndef PA_PROCESS_H #define PA_PROCESS_H /* - * $Id: pa_process.h 1523 2010-07-10 17:41:25Z dmitrykos $ + * $Id: pa_process.h 1668 2011-05-02 17:07:11Z rossb $ * Portable Audio I/O Library callback buffer processing adapters * * Based on the Open Source API proposed by Ross Bencina @@ -406,25 +406,25 @@ void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); -/** Retrieve the input latency of a buffer processor. +/** Retrieve the input latency of a buffer processor, in frames. @param bufferProcessor The buffer processor examine. @return The input latency introduced by the buffer processor, in frames. - @see PaUtil_GetBufferProcessorOutputLatency + @see PaUtil_GetBufferProcessorOutputLatencyFrames */ -unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor ); +unsigned long PaUtil_GetBufferProcessorInputLatencyFrames( PaUtilBufferProcessor* bufferProcessor ); -/** Retrieve the output latency of a buffer processor. +/** Retrieve the output latency of a buffer processor, in frames. @param bufferProcessor The buffer processor examine. @return The output latency introduced by the buffer processor, in frames. - @see PaUtil_GetBufferProcessorInputLatency + @see PaUtil_GetBufferProcessorInputLatencyFrames */ -unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor ); +unsigned long PaUtil_GetBufferProcessorOutputLatencyFrames( PaUtilBufferProcessor* bufferProcessor ); /*@}*/ diff --git a/bazaar/plugin/portaudio/common/pa_ringbuffer.c b/bazaar/plugin/portaudio/common/pa_ringbuffer.c index 7ae9a2bc9..19c91497c 100644 --- a/bazaar/plugin/portaudio/common/pa_ringbuffer.c +++ b/bazaar/plugin/portaudio/common/pa_ringbuffer.c @@ -1,5 +1,5 @@ /* - * $Id: pa_ringbuffer.c 1549 2010-10-24 10:21:35Z rossb $ + * $Id: pa_ringbuffer.c 1738 2011-08-18 11:47:28Z rossb $ * Portable Audio I/O Library * Ring Buffer utility. * @@ -77,16 +77,14 @@ ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buf /*************************************************************************** ** Return number of elements available for reading. */ -ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf ) +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( PaUtilRingBuffer *rbuf ) +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; } @@ -145,14 +149,14 @@ ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, r ** Get address of region(s) from which we can read data. ** If the region is contiguous, size2 will be zero. ** If non-contiguous, size2 will be the size of second region. -** Returns room available to be written or elementCount, whichever is smaller. +** Returns room available to be read or elementCount, whichever is smaller. */ ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, void **dataPtr1, ring_buffer_size_t *sizePtr1, 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/bazaar/plugin/portaudio/common/pa_ringbuffer.h b/bazaar/plugin/portaudio/common/pa_ringbuffer.h index 389cd351c..51577f070 100644 --- a/bazaar/plugin/portaudio/common/pa_ringbuffer.h +++ b/bazaar/plugin/portaudio/common/pa_ringbuffer.h @@ -1,7 +1,7 @@ #ifndef PA_RINGBUFFER_H #define PA_RINGBUFFER_H /* - * $Id: pa_ringbuffer.h 1549 2010-10-24 10:21:35Z 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. */ @@ -125,7 +125,7 @@ void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf ); @return The number of elements available for writing. */ -ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf ); +ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf ); /** Retrieve the number of elements available in the ring buffer for reading. @@ -133,7 +133,7 @@ ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf ); @return The number of elements available for reading. */ -ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf ); +ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf ); /** Write data to the ring buffer. diff --git a/bazaar/plugin/portaudio/common/pa_util.h b/bazaar/plugin/portaudio/common/pa_util.h index 95ea6789a..c454ea771 100644 --- a/bazaar/plugin/portaudio/common/pa_util.h +++ b/bazaar/plugin/portaudio/common/pa_util.h @@ -1,7 +1,7 @@ #ifndef PA_UTIL_H #define PA_UTIL_H /* - * $Id: pa_util.h 1339 2008-02-15 07:50:33Z rossb $ + * $Id: pa_util.h 1584 2011-02-02 18:58:17Z rossb $ * Portable Audio I/O Library implementation utilities header * common implementation utilities and interfaces * @@ -46,9 +46,6 @@ Some functions declared here are defined in pa_front.c while others are implemented separately for each platform. - - @todo Document and adhere to the alignment guarantees provided by - PaUtil_AllocateMemory(). */ diff --git a/bazaar/plugin/portaudio/hostapi/alsa/pa_linux_alsa.c b/bazaar/plugin/portaudio/hostapi/alsa/pa_linux_alsa.c index fc99c193b..97c2ebcfc 100644 --- a/bazaar/plugin/portaudio/hostapi/alsa/pa_linux_alsa.c +++ b/bazaar/plugin/portaudio/hostapi/alsa/pa_linux_alsa.c @@ -1,5 +1,5 @@ /* - * $Id: pa_linux_alsa.c 1543 2010-10-15 20:39:23Z dmitrykos $ + * $Id: pa_linux_alsa.c 1691 2011-05-26 20:19:19Z aknudsen $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * ALSA implementation by Joshua Haberman and Arve Knudsen @@ -62,6 +62,9 @@ #include #include #include /* For sig_atomic_t */ +#ifdef PA_ALSA_DYNAMIC + #include /* For dlXXX functions */ +#endif #include "portaudio.h" #include "pa_util.h" @@ -76,29 +79,502 @@ #include "pa_linux_alsa.h" +#ifndef SND_PCM_TSTAMP_ENABLE +#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP +#endif + +/* Defines Alsa function types and pointers to these functions. */ +#define _PA_DEFINE_FUNC(x) typedef typeof(x) x##_ft; static x##_ft *alsa_##x = 0 + +/* Alloca helper. */ +#define __alsa_snd_alloca(ptr,type) do { size_t __alsa_alloca_size = alsa_##type##_sizeof(); (*ptr) = (type##_t *) alloca(__alsa_alloca_size); memset(*ptr, 0, __alsa_alloca_size); } while (0) + +_PA_DEFINE_FUNC(snd_pcm_open); +_PA_DEFINE_FUNC(snd_pcm_close); +_PA_DEFINE_FUNC(snd_pcm_nonblock); +_PA_DEFINE_FUNC(snd_pcm_frames_to_bytes); +_PA_DEFINE_FUNC(snd_pcm_prepare); +_PA_DEFINE_FUNC(snd_pcm_start); +_PA_DEFINE_FUNC(snd_pcm_resume); +_PA_DEFINE_FUNC(snd_pcm_wait); +_PA_DEFINE_FUNC(snd_pcm_state); +_PA_DEFINE_FUNC(snd_pcm_avail_update); +_PA_DEFINE_FUNC(snd_pcm_areas_silence); +_PA_DEFINE_FUNC(snd_pcm_mmap_begin); +_PA_DEFINE_FUNC(snd_pcm_mmap_commit); +_PA_DEFINE_FUNC(snd_pcm_readi); +_PA_DEFINE_FUNC(snd_pcm_readn); +_PA_DEFINE_FUNC(snd_pcm_writei); +_PA_DEFINE_FUNC(snd_pcm_writen); +_PA_DEFINE_FUNC(snd_pcm_drain); +_PA_DEFINE_FUNC(snd_pcm_recover); +_PA_DEFINE_FUNC(snd_pcm_drop); +_PA_DEFINE_FUNC(snd_pcm_area_copy); +_PA_DEFINE_FUNC(snd_pcm_poll_descriptors); +_PA_DEFINE_FUNC(snd_pcm_poll_descriptors_count); +_PA_DEFINE_FUNC(snd_pcm_poll_descriptors_revents); +_PA_DEFINE_FUNC(snd_pcm_format_size); +_PA_DEFINE_FUNC(snd_pcm_link); +_PA_DEFINE_FUNC(snd_pcm_delay); + +_PA_DEFINE_FUNC(snd_pcm_hw_params_sizeof); +_PA_DEFINE_FUNC(snd_pcm_hw_params_malloc); +_PA_DEFINE_FUNC(snd_pcm_hw_params_free); +_PA_DEFINE_FUNC(snd_pcm_hw_params_any); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_access); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_format); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_channels); +//_PA_DEFINE_FUNC(snd_pcm_hw_params_set_periods_near); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_rate_near); //!!! +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_rate); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_rate_resample); +//_PA_DEFINE_FUNC(snd_pcm_hw_params_set_buffer_time_near); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_buffer_size); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_buffer_size_near); //!!! +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_buffer_size_min); +//_PA_DEFINE_FUNC(snd_pcm_hw_params_set_period_time_near); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_period_size_near); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_periods_integer); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_periods_min); + +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_buffer_size); +//_PA_DEFINE_FUNC(snd_pcm_hw_params_get_period_size); +//_PA_DEFINE_FUNC(snd_pcm_hw_params_get_access); +//_PA_DEFINE_FUNC(snd_pcm_hw_params_get_periods); +//_PA_DEFINE_FUNC(snd_pcm_hw_params_get_rate); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_channels_min); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_channels_max); + +_PA_DEFINE_FUNC(snd_pcm_hw_params_test_period_size); +_PA_DEFINE_FUNC(snd_pcm_hw_params_test_format); +_PA_DEFINE_FUNC(snd_pcm_hw_params_test_access); +_PA_DEFINE_FUNC(snd_pcm_hw_params_dump); +_PA_DEFINE_FUNC(snd_pcm_hw_params); + +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_periods_min); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_periods_max); +_PA_DEFINE_FUNC(snd_pcm_hw_params_set_period_size); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_period_size_min); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_period_size_max); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_buffer_size_max); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_rate_min); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_rate_max); +_PA_DEFINE_FUNC(snd_pcm_hw_params_get_rate_numden); +#define alsa_snd_pcm_hw_params_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_hw_params) + +_PA_DEFINE_FUNC(snd_pcm_sw_params_sizeof); +_PA_DEFINE_FUNC(snd_pcm_sw_params_malloc); +_PA_DEFINE_FUNC(snd_pcm_sw_params_current); +_PA_DEFINE_FUNC(snd_pcm_sw_params_set_avail_min); +_PA_DEFINE_FUNC(snd_pcm_sw_params); +_PA_DEFINE_FUNC(snd_pcm_sw_params_free); +_PA_DEFINE_FUNC(snd_pcm_sw_params_set_start_threshold); +_PA_DEFINE_FUNC(snd_pcm_sw_params_set_stop_threshold); +_PA_DEFINE_FUNC(snd_pcm_sw_params_get_boundary); +_PA_DEFINE_FUNC(snd_pcm_sw_params_set_silence_threshold); +_PA_DEFINE_FUNC(snd_pcm_sw_params_set_silence_size); +_PA_DEFINE_FUNC(snd_pcm_sw_params_set_xfer_align); +_PA_DEFINE_FUNC(snd_pcm_sw_params_set_tstamp_mode); +#define alsa_snd_pcm_sw_params_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_sw_params) + +_PA_DEFINE_FUNC(snd_pcm_info); +_PA_DEFINE_FUNC(snd_pcm_info_sizeof); +_PA_DEFINE_FUNC(snd_pcm_info_malloc); +_PA_DEFINE_FUNC(snd_pcm_info_free); +_PA_DEFINE_FUNC(snd_pcm_info_set_device); +_PA_DEFINE_FUNC(snd_pcm_info_set_subdevice); +_PA_DEFINE_FUNC(snd_pcm_info_set_stream); +_PA_DEFINE_FUNC(snd_pcm_info_get_name); +_PA_DEFINE_FUNC(snd_pcm_info_get_card); +#define alsa_snd_pcm_info_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_info) + +_PA_DEFINE_FUNC(snd_ctl_pcm_next_device); +_PA_DEFINE_FUNC(snd_ctl_pcm_info); +_PA_DEFINE_FUNC(snd_ctl_open); +_PA_DEFINE_FUNC(snd_ctl_close); +_PA_DEFINE_FUNC(snd_ctl_card_info_malloc); +_PA_DEFINE_FUNC(snd_ctl_card_info_free); +_PA_DEFINE_FUNC(snd_ctl_card_info); +_PA_DEFINE_FUNC(snd_ctl_card_info_sizeof); +_PA_DEFINE_FUNC(snd_ctl_card_info_get_name); +#define alsa_snd_ctl_card_info_alloca(ptr) __alsa_snd_alloca(ptr, snd_ctl_card_info) + +_PA_DEFINE_FUNC(snd_config); +_PA_DEFINE_FUNC(snd_config_update); +_PA_DEFINE_FUNC(snd_config_search); +_PA_DEFINE_FUNC(snd_config_iterator_entry); +_PA_DEFINE_FUNC(snd_config_iterator_first); +_PA_DEFINE_FUNC(snd_config_iterator_end); +_PA_DEFINE_FUNC(snd_config_iterator_next); +_PA_DEFINE_FUNC(snd_config_get_string); +_PA_DEFINE_FUNC(snd_config_get_id); +_PA_DEFINE_FUNC(snd_config_update_free_global); + +_PA_DEFINE_FUNC(snd_pcm_status); +_PA_DEFINE_FUNC(snd_pcm_status_sizeof); +_PA_DEFINE_FUNC(snd_pcm_status_get_tstamp); +_PA_DEFINE_FUNC(snd_pcm_status_get_state); +_PA_DEFINE_FUNC(snd_pcm_status_get_trigger_tstamp); +_PA_DEFINE_FUNC(snd_pcm_status_get_delay); +#define alsa_snd_pcm_status_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_status) + +_PA_DEFINE_FUNC(snd_card_next); +_PA_DEFINE_FUNC(snd_strerror); +_PA_DEFINE_FUNC(snd_output_stdio_attach); + +#define alsa_snd_config_for_each(pos, next, node)\ + for (pos = alsa_snd_config_iterator_first(node),\ + next = alsa_snd_config_iterator_next(pos);\ + pos != alsa_snd_config_iterator_end(node); pos = next, next = alsa_snd_config_iterator_next(pos)) + +#undef _PA_DEFINE_FUNC + +/* Redefine 'PA_ALSA_PATHNAME' to a different Alsa library name if desired. */ +#ifndef PA_ALSA_PATHNAME + #define PA_ALSA_PATHNAME "libasound.so" +#endif +static const char *g_AlsaLibName = PA_ALSA_PATHNAME; + +/* Handle to dynamically loaded library. */ +static void *g_AlsaLib = NULL; + +#ifdef PA_ALSA_DYNAMIC + +#define _PA_LOCAL_IMPL(x) __pa_local_##x + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_rate_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + int ret; + + if ((ret = alsa_snd_pcm_hw_params_set_rate(pcm, params, (*val), (*dir))) < 0) + return ret; + + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_buffer_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) +{ + int ret; + + if ((ret = alsa_snd_pcm_hw_params_set_buffer_size(pcm, params, (*val))) < 0) + return ret; + + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_period_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) +{ + int ret; + + if ((ret = alsa_snd_pcm_hw_params_set_period_size(pcm, params, (*val), (*dir))) < 0) + return ret; + + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_channels_min) (const snd_pcm_hw_params_t *params, unsigned int *val) +{ + (*val) = 1; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_channels_max) (const snd_pcm_hw_params_t *params, unsigned int *val) +{ + (*val) = 2; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_min) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + (*val) = 2; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + (*val) = 8; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_min) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir) +{ + (*frames) = 64; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir) +{ + (*frames) = 512; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_buffer_size_max) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) +{ + int ret; + int dir = 0; + snd_pcm_uframes_t pmax = 0; + unsigned int pcnt = 0; + + if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max)(params, &pmax, &dir)) < 0) + return ret; + if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max)(params, &pcnt, &dir)) < 0) + return ret; + + (*val) = pmax * pcnt; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_rate_min) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + (*val) = 44100; + return 0; +} + +int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_rate_max) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + (*val) = 44100; + return 0; +} + +#endif // PA_ALSA_DYNAMIC + +/* Trying to load Alsa library dynamically if 'PA_ALSA_DYNAMIC' is defined, othervise + will link during compilation. +*/ +static int PaAlsa_LoadLibrary() +{ +#ifdef PA_ALSA_DYNAMIC + + PA_DEBUG(( "%s: loading ALSA library file - %s\n", __FUNCTION__, g_AlsaLibName )); + + dlerror(); + g_AlsaLib = dlopen(g_AlsaLibName, (RTLD_NOW|RTLD_GLOBAL)); + if (g_AlsaLib == NULL) + { + PA_DEBUG(( "%s: failed dlopen() ALSA library file - %s, error: %s\n", __FUNCTION__, g_AlsaLibName, dlerror() )); + return 0; + } + + PA_DEBUG(( "%s: loading ALSA API\n", __FUNCTION__ )); + + #define _PA_LOAD_FUNC(x) do { \ + alsa_##x = dlsym(g_AlsaLib, #x); \ + if (alsa_##x == NULL) { \ + PA_DEBUG(( "%s: symbol [%s] not found in - %s, error: %s\n", __FUNCTION__, #x, g_AlsaLibName, dlerror() )); }\ + } while(0) + +#else + + #define _PA_LOAD_FUNC(x) alsa_##x = &x + +#endif + +_PA_LOAD_FUNC(snd_pcm_open); +_PA_LOAD_FUNC(snd_pcm_close); +_PA_LOAD_FUNC(snd_pcm_nonblock); +_PA_LOAD_FUNC(snd_pcm_frames_to_bytes); +_PA_LOAD_FUNC(snd_pcm_prepare); +_PA_LOAD_FUNC(snd_pcm_start); +_PA_LOAD_FUNC(snd_pcm_resume); +_PA_LOAD_FUNC(snd_pcm_wait); +_PA_LOAD_FUNC(snd_pcm_state); +_PA_LOAD_FUNC(snd_pcm_avail_update); +_PA_LOAD_FUNC(snd_pcm_areas_silence); +_PA_LOAD_FUNC(snd_pcm_mmap_begin); +_PA_LOAD_FUNC(snd_pcm_mmap_commit); +_PA_LOAD_FUNC(snd_pcm_readi); +_PA_LOAD_FUNC(snd_pcm_readn); +_PA_LOAD_FUNC(snd_pcm_writei); +_PA_LOAD_FUNC(snd_pcm_writen); +_PA_LOAD_FUNC(snd_pcm_drain); +_PA_LOAD_FUNC(snd_pcm_recover); +_PA_LOAD_FUNC(snd_pcm_drop); +_PA_LOAD_FUNC(snd_pcm_area_copy); +_PA_LOAD_FUNC(snd_pcm_poll_descriptors); +_PA_LOAD_FUNC(snd_pcm_poll_descriptors_count); +_PA_LOAD_FUNC(snd_pcm_poll_descriptors_revents); +_PA_LOAD_FUNC(snd_pcm_format_size); +_PA_LOAD_FUNC(snd_pcm_link); +_PA_LOAD_FUNC(snd_pcm_delay); + +_PA_LOAD_FUNC(snd_pcm_hw_params_sizeof); +_PA_LOAD_FUNC(snd_pcm_hw_params_malloc); +_PA_LOAD_FUNC(snd_pcm_hw_params_free); +_PA_LOAD_FUNC(snd_pcm_hw_params_any); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_access); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_format); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_channels); +//_PA_LOAD_FUNC(snd_pcm_hw_params_set_periods_near); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_rate_near); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_rate); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_rate_resample); +//_PA_LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_buffer_size); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min); +//_PA_LOAD_FUNC(snd_pcm_hw_params_set_period_time_near); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_period_size_near); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_periods_integer); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_periods_min); + +_PA_LOAD_FUNC(snd_pcm_hw_params_get_buffer_size); +//_PA_LOAD_FUNC(snd_pcm_hw_params_get_period_size); +//_PA_LOAD_FUNC(snd_pcm_hw_params_get_access); +//_PA_LOAD_FUNC(snd_pcm_hw_params_get_periods); +//_PA_LOAD_FUNC(snd_pcm_hw_params_get_rate); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_channels_min); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_channels_max); + +_PA_LOAD_FUNC(snd_pcm_hw_params_test_period_size); +_PA_LOAD_FUNC(snd_pcm_hw_params_test_format); +_PA_LOAD_FUNC(snd_pcm_hw_params_test_access); +_PA_LOAD_FUNC(snd_pcm_hw_params_dump); +_PA_LOAD_FUNC(snd_pcm_hw_params); + +_PA_LOAD_FUNC(snd_pcm_hw_params_get_periods_min); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_periods_max); +_PA_LOAD_FUNC(snd_pcm_hw_params_set_period_size); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_period_size_min); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_period_size_max); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_buffer_size_max); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_rate_min); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_rate_max); +_PA_LOAD_FUNC(snd_pcm_hw_params_get_rate_numden); + +_PA_LOAD_FUNC(snd_pcm_sw_params_sizeof); +_PA_LOAD_FUNC(snd_pcm_sw_params_malloc); +_PA_LOAD_FUNC(snd_pcm_sw_params_current); +_PA_LOAD_FUNC(snd_pcm_sw_params_set_avail_min); +_PA_LOAD_FUNC(snd_pcm_sw_params); +_PA_LOAD_FUNC(snd_pcm_sw_params_free); +_PA_LOAD_FUNC(snd_pcm_sw_params_set_start_threshold); +_PA_LOAD_FUNC(snd_pcm_sw_params_set_stop_threshold); +_PA_LOAD_FUNC(snd_pcm_sw_params_get_boundary); +_PA_LOAD_FUNC(snd_pcm_sw_params_set_silence_threshold); +_PA_LOAD_FUNC(snd_pcm_sw_params_set_silence_size); +_PA_LOAD_FUNC(snd_pcm_sw_params_set_xfer_align); +_PA_LOAD_FUNC(snd_pcm_sw_params_set_tstamp_mode); + +_PA_LOAD_FUNC(snd_pcm_info); +_PA_LOAD_FUNC(snd_pcm_info_sizeof); +_PA_LOAD_FUNC(snd_pcm_info_malloc); +_PA_LOAD_FUNC(snd_pcm_info_free); +_PA_LOAD_FUNC(snd_pcm_info_set_device); +_PA_LOAD_FUNC(snd_pcm_info_set_subdevice); +_PA_LOAD_FUNC(snd_pcm_info_set_stream); +_PA_LOAD_FUNC(snd_pcm_info_get_name); +_PA_LOAD_FUNC(snd_pcm_info_get_card); + +_PA_LOAD_FUNC(snd_ctl_pcm_next_device); +_PA_LOAD_FUNC(snd_ctl_pcm_info); +_PA_LOAD_FUNC(snd_ctl_open); +_PA_LOAD_FUNC(snd_ctl_close); +_PA_LOAD_FUNC(snd_ctl_card_info_malloc); +_PA_LOAD_FUNC(snd_ctl_card_info_free); +_PA_LOAD_FUNC(snd_ctl_card_info); +_PA_LOAD_FUNC(snd_ctl_card_info_sizeof); +_PA_LOAD_FUNC(snd_ctl_card_info_get_name); + +_PA_LOAD_FUNC(snd_config); +_PA_LOAD_FUNC(snd_config_update); +_PA_LOAD_FUNC(snd_config_search); +_PA_LOAD_FUNC(snd_config_iterator_entry); +_PA_LOAD_FUNC(snd_config_iterator_first); +_PA_LOAD_FUNC(snd_config_iterator_end); +_PA_LOAD_FUNC(snd_config_iterator_next); +_PA_LOAD_FUNC(snd_config_get_string); +_PA_LOAD_FUNC(snd_config_get_id); +_PA_LOAD_FUNC(snd_config_update_free_global); + +_PA_LOAD_FUNC(snd_pcm_status); +_PA_LOAD_FUNC(snd_pcm_status_sizeof); +_PA_LOAD_FUNC(snd_pcm_status_get_tstamp); +_PA_LOAD_FUNC(snd_pcm_status_get_state); +_PA_LOAD_FUNC(snd_pcm_status_get_trigger_tstamp); +_PA_LOAD_FUNC(snd_pcm_status_get_delay); + +_PA_LOAD_FUNC(snd_card_next); +_PA_LOAD_FUNC(snd_strerror); +_PA_LOAD_FUNC(snd_output_stdio_attach); +#undef _PA_LOAD_FUNC + +#ifdef PA_ALSA_DYNAMIC + PA_DEBUG(( "%s: loaded ALSA API - ok\n", __FUNCTION__ )); + +#define _PA_VALIDATE_LOAD_REPLACEMENT(x)\ + do {\ + if (alsa_##x == NULL)\ + {\ + alsa_##x = &_PA_LOCAL_IMPL(x);\ + PA_DEBUG(( "%s: replacing [%s] with local implementation\n", __FUNCTION__, #x ));\ + }\ + } while (0) + + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_rate_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_buffer_size_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_period_size_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_buffer_size_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_max); + +#undef _PA_LOCAL_IMPL +#undef _PA_VALIDATE_LOAD_REPLACEMENT + +#endif // PA_ALSA_DYNAMIC + + return 1; +} + +void PaAlsa_SetLibraryPathName( const char *pathName ) +{ +#ifdef PA_ALSA_DYNAMIC + g_AlsaLibName = pathName; +#else + (void)pathName; +#endif +} + +/* Close handle to Alsa library. */ +static void PaAlsa_CloseLibrary() +{ +#ifdef PA_ALSA_DYNAMIC + dlclose(g_AlsaLib); + g_AlsaLib = NULL; +#endif +} + /* Check return value of ALSA function, and map it to PaError */ #define ENSURE_(expr, code) \ do { \ - if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \ + int __pa_unsure_error_id;\ + if( UNLIKELY( (__pa_unsure_error_id = (expr)) < 0 ) ) \ { \ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ if( (code) == paUnanticipatedHostError && pthread_equal( pthread_self(), paUnixMainThread) ) \ { \ - PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \ + PaUtil_SetLastHostErrorInfo( paALSA, __pa_unsure_error_id, alsa_snd_strerror( __pa_unsure_error_id ) ); \ } \ PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \ if( (code) == paUnanticipatedHostError ) \ - PA_DEBUG(( "Host error description: %s\n", snd_strerror( aErr_ ) )); \ + PA_DEBUG(( "Host error description: %s\n", alsa_snd_strerror( __pa_unsure_error_id ) )); \ result = (code); \ goto error; \ } \ - } while( 0 ); + } while (0) #define ASSERT_CALL_(expr, success) \ - aErr_ = (expr); \ - assert( success == aErr_ ); + do {\ + int __pa_assert_error_id;\ + __pa_assert_error_id = (expr);\ + assert( success == __pa_assert_error_id );\ + } while (0) -static int aErr_; /* Used with ENSURE_ */ static int numPeriods_ = 4; static int busyRetries_ = 100; @@ -239,7 +715,7 @@ static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device]; } -/** Uncommented because AlsaErrorHandler is unused for anything good yet. If AlsaErrorHandler is +/** Uncommented because AlsaErrorHandler is unused for anything good yet. If AlsaErrorHandler is to be used, do not forget to register this callback in PaAlsa_Initialize, and unregister in Terminate. */ /*static void AlsaErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...) @@ -251,6 +727,10 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex PaError result = paNoError; PaAlsaHostApiRepresentation *alsaHostApi = NULL; + /* Try loading Alsa library. */ + if (!PaAlsa_LoadLibrary()) + return paHostApiNotFound; + PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory( sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory ); PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); @@ -265,7 +745,7 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; - /** If AlsaErrorHandler is to be used, do not forget to unregister callback pointer in + /** If AlsaErrorHandler is to be used, do not forget to unregister callback pointer in Terminate function. */ /*ENSURE_( snd_lib_error_set_handler(AlsaErrorHandler), paUnanticipatedHostError );*/ @@ -314,7 +794,7 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; assert( hostApi ); - + /** See AlsaErrorHandler and PaAlsa_Initialize for details. */ /*snd_lib_error_set_handler(NULL);*/ @@ -326,7 +806,10 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) } PaUtil_FreeMemory( alsaHostApi ); - snd_config_update_free_global(); + alsa_snd_config_update_free_global(); + + /* Close Alsa library. */ + PaAlsa_CloseLibrary(); } /** Determine max channels and default latencies. @@ -349,6 +832,8 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in assert( pcm ); + PA_DEBUG(( "%s: collecting info ..\n", __FUNCTION__ )); + if( StreamDirection_In == mode ) { minChannels = &devInfo->minInputChannels; @@ -364,10 +849,10 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in defaultHighLatency = &devInfo->baseDeviceInfo.defaultHighOutputLatency; } - ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError ); - snd_pcm_hw_params_alloca( &hwParams ); - snd_pcm_hw_params_any( pcm, hwParams ); + alsa_snd_pcm_hw_params_alloca( &hwParams ); + alsa_snd_pcm_hw_params_any( pcm, hwParams ); if( defaultSr >= 0 ) { @@ -383,7 +868,7 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in if( defaultSr < 0. ) /* Default sample rate not set */ { unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */ - if( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ) < 0) + if( alsa_snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ) < 0) { result = paUnanticipatedHostError; goto error; @@ -391,8 +876,8 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError ); } - ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError ); assert( maxChans <= INT_MAX ); assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called, resulting in zeroed values */ @@ -423,11 +908,11 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in * select the nearest setting that will work at stream * config time. */ - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); /* Have to reset hwParams, to set new buffer size */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError ); *minChannels = (int)minChans; *maxChannels = (int)maxChans; @@ -436,7 +921,7 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in *defaultHighLatency = (double) highLatency / *defaultSampleRate; end: - snd_pcm_close( pcm ); + alsa_snd_pcm_close( pcm ); return result; error: @@ -487,6 +972,23 @@ HwDevInfo predefinedNames[] = { { "surround50", NULL, 0, 1, 0 }, { "surround51", NULL, 0, 1, 0 }, { "surround71", NULL, 0, 1, 0 }, + + { "AndroidPlayback_Earpiece_normal", NULL, 0, 1, 0 }, + { "AndroidPlayback_Speaker_normal", NULL, 0, 1, 0 }, + { "AndroidPlayback_Bluetooth_normal", NULL, 0, 1, 0 }, + { "AndroidPlayback_Headset_normal", NULL, 0, 1, 0 }, + { "AndroidPlayback_Speaker_Headset_normal", NULL, 0, 1, 0 }, + { "AndroidPlayback_Bluetooth-A2DP_normal", NULL, 0, 1, 0 }, + { "AndroidPlayback_ExtraDockSpeaker_normal", NULL, 0, 1, 0 }, + { "AndroidPlayback_TvOut_normal", NULL, 0, 1, 0 }, + + { "AndroidRecord_Microphone", NULL, 0, 0, 1 }, + { "AndroidRecord_Earpiece_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Speaker_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Headset_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Bluetooth_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Speaker_Headset_normal", NULL, 0, 0, 1 }, + { NULL, NULL, 0, 1, 0 } }; @@ -543,7 +1045,7 @@ static int IgnorePlugin( const char *pluginId ) /** Open PCM device. * - * Wrapper around snd_pcm_open which may repeatedly retry opening a device if it is busy, for + * Wrapper around alsa_snd_pcm_open which may repeatedly retry opening a device if it is busy, for * a certain time. This is because dmix may temporarily hold on to a device after it (dmix) * has been opened and closed. * @param mode: Open mode (e.g., SND_PCM_BLOCKING). @@ -552,11 +1054,11 @@ static int IgnorePlugin( const char *pluginId ) static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode, int waitOnBusy ) { int tries = 0, maxTries = waitOnBusy ? busyRetries_ : 0; - int ret = snd_pcm_open( pcmp, name, stream, mode ); + int ret = alsa_snd_pcm_open( pcmp, name, stream, mode ); for( tries = 0; tries < maxTries && -EBUSY == ret; ++tries ) { Pa_Sleep( 10 ); - ret = snd_pcm_open( pcmp, name, stream, mode ); + ret = alsa_snd_pcm_open( pcmp, name, stream, mode ); if( -EBUSY != ret ) { PA_DEBUG(( "%s: Successfully opened initially busy device after %d tries\n", @@ -565,8 +1067,12 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, } if( -EBUSY == ret ) { - PA_DEBUG(( "%s: Failed to open busy device '%s'\n", - __FUNCTION__, name )); + PA_DEBUG(( "%s: Failed to open busy device '%s'\n", __FUNCTION__, name )); + } + else + { + if (ret < 0) + PA_DEBUG(( "%s: Opened device '%s' ptr[%p] - result: [%d:%s]\n", __FUNCTION__, name, *pcmp, ret, alsa_snd_strerror(ret) )); } return ret; @@ -577,9 +1083,11 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d { PaError result = 0; PaDeviceInfo *baseDeviceInfo = &devInfo->baseDeviceInfo; - snd_pcm_t *pcm; + snd_pcm_t *pcm = NULL; PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep; + PA_DEBUG(( "%s: filling device info for: %s\n", __FUNCTION__, deviceName->name )); + /* Zero fields */ InitializeDeviceInfo( baseDeviceInfo ); @@ -640,6 +1148,10 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d baseApi->deviceInfos[*devIdx] = (PaDeviceInfo *) devInfo; (*devIdx) += 1; } + else + { + PA_DEBUG(( "%s: skipped device: %s, all channels - 0\n", __FUNCTION__, deviceName->name )); + } end: return result; @@ -673,16 +1185,16 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) /* Gather info about hw devices - * snd_card_next() modifies the integer passed to it to be: + * alsa_snd_card_next() modifies the integer passed to it to be: * the index of the first card if the parameter is -1 * the index of the next card if the parameter is the index of a card * -1 if there are no more cards * * The function itself returns 0 if it succeeded. */ cardIdx = -1; - snd_ctl_card_info_alloca( &cardInfo ); - snd_pcm_info_alloca( &pcmInfo ); - while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) + alsa_snd_ctl_card_info_alloca( &cardInfo ); + alsa_snd_pcm_info_alloca( &pcmInfo ); + while( alsa_snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) { char *cardName; int devIdx = -1; @@ -692,17 +1204,17 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); /* Acquire name of card */ - if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) + if( alsa_snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) { /* Unable to open card :( */ PA_DEBUG(( "%s: Unable to open device %s\n", __FUNCTION__, alsaCardName )); continue; } - snd_ctl_card_info( ctl, cardInfo ); + alsa_snd_ctl_card_info( ctl, cardInfo ); - PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) ); + PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, alsa_snd_ctl_card_info_get_name( cardInfo )) ); - while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) + while( alsa_snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) { char *alsaDeviceName, *deviceName; size_t len; @@ -710,16 +1222,16 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) snprintf( buf, sizeof (buf), "hw:%d,%d", cardIdx, devIdx ); /* Obtain info about this particular device */ - snd_pcm_info_set_device( pcmInfo, devIdx ); - snd_pcm_info_set_subdevice( pcmInfo, 0 ); - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + alsa_snd_pcm_info_set_device( pcmInfo, devIdx ); + alsa_snd_pcm_info_set_subdevice( pcmInfo, 0 ); + alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); + if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) { hasCapture = 1; } - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); + if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) { hasPlayback = 1; } @@ -731,11 +1243,11 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) } /* The length of the string written by snprintf plus terminating 0 */ - len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1; + len = snprintf( NULL, 0, "%s: %s (%s)", cardName, alsa_snd_pcm_info_get_name( pcmInfo ), buf ) + 1; PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), paInsufficientMemory ); snprintf( deviceName, len, "%s: %s (%s)", cardName, - snd_pcm_info_get_name( pcmInfo ), buf ); + alsa_snd_pcm_info_get_name( pcmInfo ), buf ); ++numDeviceNames; if( !hwDevInfos || numDeviceNames > maxDeviceNames ) @@ -753,32 +1265,32 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) hwDevInfos[ numDeviceNames - 1 ].hasPlayback = hasPlayback; hwDevInfos[ numDeviceNames - 1 ].hasCapture = hasCapture; } - snd_ctl_close( ctl ); + alsa_snd_ctl_close( ctl ); } /* Iterate over plugin devices */ - if( NULL == snd_config ) + if( NULL == (*alsa_snd_config) ) { - /* snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */ - ENSURE_( snd_config_update(), paUnanticipatedHostError ); + /* alsa_snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */ + ENSURE_( alsa_snd_config_update(), paUnanticipatedHostError ); PA_DEBUG(( "Updating snd_config\n" )); } - assert( snd_config ); - if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 ) + assert( *alsa_snd_config ); + if( (res = alsa_snd_config_search( *alsa_snd_config, "pcm", &topNode )) >= 0 ) { snd_config_iterator_t i, next; - snd_config_for_each( i, next, topNode ) + alsa_snd_config_for_each( i, next, topNode ) { const char *tpStr = "unknown", *idStr = NULL; int err = 0; char *alsaDeviceName, *deviceName; const HwDevInfo *predefined = NULL; - snd_config_t *n = snd_config_iterator_entry( i ), * tp = NULL;; + snd_config_t *n = alsa_snd_config_iterator_entry( i ), * tp = NULL;; - if( (err = snd_config_search( n, "type", &tp )) < 0 ) + if( (err = alsa_snd_config_search( n, "type", &tp )) < 0 ) { if( -ENOENT != err ) { @@ -787,15 +1299,15 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) } else { - ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError ); } - ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_config_get_id( n, &idStr ), paUnanticipatedHostError ); if( IgnorePlugin( idStr ) ) { - PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr )); + PA_DEBUG(( "%s: Ignoring ALSA plugin device [%s] of type [%s]\n", __FUNCTION__, idStr, tpStr )); continue; } - PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr )); + PA_DEBUG(( "%s: Found plugin [%s] of type [%s]\n", __FUNCTION__, idStr, tpStr )); PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, strlen(idStr) + 6 ), paInsufficientMemory ); @@ -831,7 +1343,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) } } else - PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) )); + PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, alsa_snd_strerror( res ) )); /* allocate deviceInfo memory based on the number of devices */ PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( @@ -849,7 +1361,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) * (dmix) is closed. The 'default' plugin may also point to the dmix plugin, so the same goes * for this. */ - + PA_DEBUG(( "%s: filling device info for %d devices\n", __FUNCTION__, numDeviceNames )); for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) { PaAlsaDeviceInfo* devInfo = &deviceInfoArray[i]; @@ -933,36 +1445,158 @@ static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm ) { PaSampleFormat available = 0; snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); + alsa_snd_pcm_hw_params_alloca( &hwParams ); - snd_pcm_hw_params_any( pcm, hwParams ); + alsa_snd_pcm_hw_params_any( pcm, hwParams ); - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) available |= paFloat32; - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) available |= paInt32; #ifdef PA_LITTLE_ENDIAN - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0) available |= paInt24; #elif defined PA_BIG_ENDIAN - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0) available |= paInt24; #endif - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) available |= paInt16; - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) available |= paUInt8; - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) available |= paInt8; return available; } +/* Output to console all formats supported by device */ +static void LogAllAvailableFormats( snd_pcm_t *pcm ) +{ + PaSampleFormat available = 0; + snd_pcm_hw_params_t *hwParams; + alsa_snd_pcm_hw_params_alloca( &hwParams ); + + alsa_snd_pcm_hw_params_any( pcm, hwParams ); + + PA_DEBUG(( " --- Supported Formats ---\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S8\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U8\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16_LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S16_LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16_BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S16_BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U16_LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U16_LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U16_BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U16_BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S24_LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S24_BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U24_LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U24_LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U24_BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U24_BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT_LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_FLOAT_LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT_BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_FLOAT_BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT64_LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_FLOAT64_LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT64_BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_FLOAT64_BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_IEC958_SUBFRAME_LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_IEC958_SUBFRAME_LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_IEC958_SUBFRAME_BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_IEC958_SUBFRAME_BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_MU_LAW ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_MU_LAW\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_A_LAW ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_A_LAW\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_IMA_ADPCM ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_IMA_ADPCM\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_MPEG ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_MPEG\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_GSM ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_GSM\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_SPECIAL ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_SPECIAL\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S24_3LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S24_3BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U24_3LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U24_3LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U24_3BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U24_3BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S20_3LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S20_3LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S20_3BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S20_3BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U20_3LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U20_3LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U20_3BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U20_3BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S18_3LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S18_3LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S18_3BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S18_3BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U18_3LE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U18_3LE\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U18_3BE ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U18_3BE\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S16\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U16 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U16\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S24\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U24 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U24\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_S32\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U32 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_U32\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_FLOAT\n" )); + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT64 ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_FLOAT64\n" )); + + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_IEC958_SUBFRAME ) >= 0) + PA_DEBUG(( "SND_PCM_FORMAT_IEC958_SUBFRAME\n" )); + + PA_DEBUG(( " -------------------------\n" )); +} + static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) { switch( paFormat ) @@ -1034,7 +1668,7 @@ static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStr *pcm = NULL; ENSURE_( ret, -EBUSY == ret ? paDeviceUnavailable : paBadIODeviceCombination ); } - ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError ); end: return result; @@ -1053,7 +1687,7 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const unsigned int numHostChannels; PaSampleFormat hostFormat; snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); + alsa_snd_pcm_hw_params_alloca( &hwParams ); if( !parameters->hostApiSpecificStreamInfo ) { @@ -1066,7 +1700,7 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) ); - snd_pcm_hw_params_any( pcm, hwParams ); + alsa_snd_pcm_hw_params_any( pcm, hwParams ); if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 ) { @@ -1074,7 +1708,7 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const goto error; } - if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 ) + if( alsa_snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 ) { result = paInvalidChannelCount; goto error; @@ -1083,12 +1717,14 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const /* See if we can find a best possible match */ availableFormats = GetAvailableFormats( pcm ); PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) ); - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); + + /* Some specific hardware (reported: Audio8 DJ) can fail with assertion during this step. */ + ENSURE_( alsa_snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); { /* It happens that this call fails because the device is busy */ int ret = 0; - if( (ret = snd_pcm_hw_params( pcm, hwParams )) < 0) + if( (ret = alsa_snd_pcm_hw_params( pcm, hwParams )) < 0) { if( -EINVAL == ret ) { @@ -1113,7 +1749,7 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const end: if( pcm ) { - snd_pcm_close( pcm ); + alsa_snd_pcm_close( pcm ); } return result; @@ -1169,7 +1805,7 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa const PaStreamParameters *params, StreamDirection streamDir, int callbackMode ) { PaError result = paNoError; - PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat; + PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat = paNoError; assert( params->channelCount > 0 ); /* Make sure things have an initial value */ @@ -1190,8 +1826,9 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa self->device = params->device; PA_ENSURE( AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm ) ); - self->nfds = snd_pcm_poll_descriptors_count( self->pcm ); - hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ); + self->nfds = alsa_snd_pcm_poll_descriptors_count( self->pcm ); + + PA_ENSURE( hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ) ); self->hostSampleFormat = hostSampleFormat; self->nativeFormat = Pa2AlsaFormat( hostSampleFormat ); @@ -1210,12 +1847,20 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa } error: + + /* Log all available formats. */ + if ( hostSampleFormat == paSampleFormatNotSupported ) + { + LogAllAvailableFormats( self->pcm ); + PA_DEBUG(( "%s: Please provide the log output to PortAudio developers, your hardware does not have any sample format implemented yet.\n", __FUNCTION__ )); + } + return result; } static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self ) { - snd_pcm_close( self->pcm ); + alsa_snd_pcm_close( self->pcm ); if( self->userBuffers ) PaUtil_FreeMemory( self->userBuffers ); } @@ -1252,12 +1897,12 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se /* ... fill up the configuration space with all possibile * combinations of parameters this device will accept */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */ dir = 0; - ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError ); if( self->userInterleaved ) { @@ -1265,11 +1910,11 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; /* test if MMAP supported */ - self->canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 || - snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0; + self->canMmap = alsa_snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 || + alsa_snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0; - PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO"))); - PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO"))); + PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (alsa_snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO"))); + PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (alsa_snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO"))); if (!self->canMmap) { @@ -1283,11 +1928,11 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; /* test if MMAP supported */ - self->canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 || - snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0; + self->canMmap = alsa_snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 || + alsa_snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0; - PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO"))); - PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO"))); + PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (alsa_snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO"))); + PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (alsa_snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO"))); if (!self->canMmap) { @@ -1299,20 +1944,21 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se PA_DEBUG(("%s: device can MMAP: %s\n", __FUNCTION__, (self->canMmap ? "YES" : "NO"))); /* If requested access mode fails, try alternate mode */ - if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 ) + if( alsa_snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 ) { int err = 0; - if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0) + if( (err = alsa_snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0) { result = paUnanticipatedHostError; - PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) ); + PaUtil_SetLastHostErrorInfo( paALSA, err, alsa_snd_strerror( err ) ); goto error; } /* Flip mode */ self->hostInterleaved = !self->userInterleaved; } - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError ); + /* Some specific hardware (reported: Audio8 DJ) can fail with assertion during this step. */ + ENSURE_( alsa_snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError ); ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate ); ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError ); @@ -1323,7 +1969,7 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se PA_ENSURE( paInvalidSampleRate ); } - ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount ); + ENSURE_( alsa_snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount ); *sampleRate = sr; @@ -1348,49 +1994,57 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel snd_pcm_uframes_t bufSz = 0; *latency = -1.; - snd_pcm_sw_params_alloca( &swParams ); + alsa_snd_pcm_sw_params_alloca( &swParams ); bufSz = params->suggestedLatency * sampleRate; - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError ); /* Set the parameters! */ { - int r = snd_pcm_hw_params( self->pcm, hwParams ); + int r = alsa_snd_pcm_hw_params( self->pcm, hwParams ); #ifdef PA_ENABLE_DEBUG_OUTPUT if( r < 0 ) { snd_output_t *output = NULL; - snd_output_stdio_attach( &output, stderr, 0 ); - snd_pcm_hw_params_dump( hwParams, output ); + alsa_snd_output_stdio_attach( &output, stderr, 0 ); + alsa_snd_pcm_hw_params_dump( hwParams, output ); } #endif ENSURE_(r, paUnanticipatedHostError ); } - ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); + if (alsa_snd_pcm_hw_params_get_buffer_size != NULL) + { + ENSURE_( alsa_snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); + } + else + { + self->bufferSize = bufSz; + } + /* Latency in seconds */ *latency = self->bufferSize / sampleRate; /* Now software parameters... */ - ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_start_threshold( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_stop_threshold( self->pcm, swParams, self->bufferSize ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_start_threshold( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_stop_threshold( self->pcm, swParams, self->bufferSize ), paUnanticipatedHostError ); /* Silence buffer in the case of underrun */ if( !primeBuffers ) /* XXX: Make sense? */ { snd_pcm_uframes_t boundary; - ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_threshold( self->pcm, swParams, 0 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_size( self->pcm, swParams, boundary ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_silence_threshold( self->pcm, swParams, 0 ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_silence_size( self->pcm, swParams, boundary ), paUnanticipatedHostError ); } - ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError ); /* Set the parameters! */ - ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError ); error: return result; @@ -1590,8 +2244,8 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo /* It may be that the device only supports 2 periods for instance */ dir = 0; - ENSURE_( snd_pcm_hw_params_get_periods_min( hwParams, &minPeriods, &dir ), paUnanticipatedHostError ) - ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_periods_min( hwParams, &minPeriods, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError ); assert( maxPeriods > 1 ); /* Clamp to min/max */ @@ -1614,24 +2268,24 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo if( framesPerHostBuffer < framesPerUserBuffer ) { assert( framesPerUserBuffer % framesPerHostBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 ) framesPerHostBuffer *= 2; - else - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 ) + else + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 ) framesPerHostBuffer /= 2; } } else { assert( framesPerHostBuffer % framesPerUserBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 ) framesPerHostBuffer += framesPerUserBuffer; - else - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 ) + else + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 ) framesPerHostBuffer -= framesPerUserBuffer; } } @@ -1672,22 +2326,22 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo if( framesPerHostBuffer < framesPerUserBuffer ) { assert( framesPerUserBuffer % framesPerHostBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 ) framesPerHostBuffer *= 2; - else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 ) + else if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 ) framesPerHostBuffer /= 2; } } else { assert( framesPerHostBuffer % framesPerUserBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 ) framesPerHostBuffer += framesPerUserBuffer; - else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 ) + else if( alsa_snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 ) framesPerHostBuffer -= framesPerUserBuffer; } } @@ -1707,8 +2361,8 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo { /* Get min/max period sizes and adjust our chosen */ snd_pcm_uframes_t min = 0, max = 0, minmax_diff; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParams, &min, NULL ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParams, &max, NULL ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_period_size_min( hwParams, &min, NULL ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_period_size_max( hwParams, &max, NULL ), paUnanticipatedHostError ); minmax_diff = max - min; if( framesPerHostBuffer < min ) @@ -1716,7 +2370,7 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo PA_DEBUG(( "%s: The determined period size (%lu) is less than minimum (%lu)\n", __FUNCTION__, framesPerHostBuffer, min )); framesPerHostBuffer = ((minmax_diff == 2) ? min + 1 : min); } - else + else if( framesPerHostBuffer > max ) { PA_DEBUG(( "%s: The determined period size (%lu) is greater than maximum (%lu)\n", __FUNCTION__, framesPerHostBuffer, max )); @@ -1730,7 +2384,7 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo /* Try setting period size */ dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ), paUnanticipatedHostError ); if( dir != 0 ) { PA_DEBUG(( "%s: The configured period size is non-integer.\n", __FUNCTION__, dir )); @@ -1798,13 +2452,13 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double minCapture, minPlayback, maxCapture, maxPlayback; dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); minPeriodSize = PA_MAX( minPlayback, minCapture ); maxPeriodSize = PA_MIN( maxPlayback, maxCapture ); PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination ); @@ -1815,8 +2469,8 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double { snd_pcm_uframes_t maxBufferSize; snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback; - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError ); maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback ); desiredBufSz = PA_MIN( desiredBufSz, maxBufferSize ); @@ -1830,8 +2484,8 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double while( periodSize <= maxPeriodSize ) { - if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 && - snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 ) + if( alsa_snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 && + alsa_snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 ) { /* OK! */ break; @@ -1851,8 +2505,8 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double while( optimalPeriodSize >= periodSize ) { - if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) - >= 0 && snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, + if( alsa_snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) + >= 0 && alsa_snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 ) { break; @@ -1866,9 +2520,9 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double if( periodSize <= maxPeriodSize ) { /* Looks good, the periodSize _should_ be acceptable by both devices */ - ENSURE_( snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ), + ENSURE_( alsa_snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ), + ENSURE_( alsa_snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ), paUnanticipatedHostError ); self->capture.framesPerBuffer = self->playback.framesPerBuffer = periodSize; framesPerHostBuffer = periodSize; @@ -1881,11 +2535,11 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double self->capture.framesPerBuffer = optimalPeriodSize; dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ), + ENSURE_( alsa_snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ), paUnanticipatedHostError ); self->playback.framesPerBuffer = optimalPeriodSize; dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->playback.pcm, hwParamsPlayback, &self->playback.framesPerBuffer, &dir ), + ENSURE_( alsa_snd_pcm_hw_params_set_period_size_near( self->playback.pcm, hwParamsPlayback, &self->playback.framesPerBuffer, &dir ), paUnanticipatedHostError ); framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer ); *hostBufferSizeMode = paUtilBoundedHostBufferSize; @@ -1903,7 +2557,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double snd_pcm_hw_params_t* firstHwParams = hwParamsCapture, * secondHwParams = hwParamsPlayback; dir = 0; - ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError ); if( maxPeriods < numPeriods ) { /* The playback component is trickier to get right, try that first */ @@ -1919,7 +2573,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double second->framesPerBuffer = first->framesPerBuffer; dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( second->pcm, secondHwParams, &second->framesPerBuffer, &dir ), + ENSURE_( alsa_snd_pcm_hw_params_set_period_size_near( second->pcm, secondHwParams, &second->framesPerBuffer, &dir ), paUnanticipatedHostError ); if( self->capture.framesPerBuffer == self->playback.framesPerBuffer ) { @@ -1976,8 +2630,8 @@ static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParamet double realSr = sampleRate; snd_pcm_hw_params_t* hwParamsCapture, * hwParamsPlayback; - snd_pcm_hw_params_alloca( &hwParamsCapture ); - snd_pcm_hw_params_alloca( &hwParamsPlayback ); + alsa_snd_pcm_hw_params_alloca( &hwParamsCapture ); + alsa_snd_pcm_hw_params_alloca( &hwParamsPlayback ); if( self->capture.pcm ) PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture, @@ -2013,11 +2667,11 @@ static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParamet */ if( self->callbackMode && self->capture.pcm && self->playback.pcm ) { - int err = snd_pcm_link( self->capture.pcm, self->playback.pcm ); + int err = alsa_snd_pcm_link( self->capture.pcm, self->playback.pcm ); if( err == 0 ) self->pcmsSynced = 1; else - PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) )); + PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, alsa_snd_strerror( err ) )); } { @@ -2118,10 +2772,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, /* Ok, buffer processor is initialized, now we can deduce it's latency */ if( numInputChannels > 0 ) stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)( - PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate); + PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) / sampleRate); if( numOutputChannels > 0 ) stream->streamRepresentation.streamInfo.outputLatency = outputLatency + (PaTime)( - PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate); + PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) / sampleRate); PA_DEBUG(( "%s: Stream: framesPerBuffer = %lu, maxFramesPerHostBuffer = %lu, latency = i(%f)/o(%f), \n", __FUNCTION__, framesPerBuffer, stream->maxFramesPerHostBuffer, stream->streamRepresentation.streamInfo.inputLatency, stream->streamRepresentation.streamInfo.outputLatency)); @@ -2155,11 +2809,11 @@ static PaError CloseStream( PaStream* s ) static void SilenceBuffer( PaAlsaStream *stream ) { const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset; + snd_pcm_uframes_t frames = (snd_pcm_uframes_t)alsa_snd_pcm_avail_update( stream->playback.pcm ), offset; - snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames ); - snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat ); - snd_pcm_mmap_commit( stream->playback.pcm, offset, frames ); + alsa_snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames ); + alsa_snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat ); + alsa_snd_pcm_mmap_commit( stream->playback.pcm, offset, frames ); } /** Start/prepare pcm(s) for streaming. @@ -2182,21 +2836,21 @@ static PaError AlsaStart( PaAlsaStream *stream, int priming ) if( !priming ) { /* Buffer isn't primed, so prepare and silence */ - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); if( stream->playback.canMmap ) SilenceBuffer( stream ); } if( stream->playback.canMmap ) - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); } else - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); } if( stream->capture.pcm && !stream->pcmsSynced ) { - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */ - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); } end: @@ -2216,7 +2870,7 @@ static int IsRunning( PaAlsaStream *stream ) PA_ENSURE( PaUnixMutex_Lock( &stream->stateMtx ) ); if( stream->capture.pcm ) { - snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm ); + snd_pcm_state_t capture_state = alsa_snd_pcm_state( stream->capture.pcm ); if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN || capture_state == SND_PCM_STATE_DRAINING ) @@ -2228,7 +2882,7 @@ static int IsRunning( PaAlsaStream *stream ) if( stream->playback.pcm ) { - snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm ); + snd_pcm_state_t playback_state = alsa_snd_pcm_state( stream->playback.pcm ); if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN || playback_state == SND_PCM_STATE_DRAINING ) @@ -2285,7 +2939,7 @@ error: static PaError AlsaStop( PaAlsaStream *stream, int abort ) { PaError result = paNoError; - /* XXX: snd_pcm_drain tends to lock up, avoid it until we find out more */ + /* XXX: alsa_snd_pcm_drain tends to lock up, avoid it until we find out more */ abort = 1; /* if( stream->capture.pcm && !strcmp( Pa_GetDeviceInfo( stream->capture.device )->name, @@ -2304,11 +2958,11 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort ) { if( stream->playback.pcm ) { - ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError ); } if( stream->capture.pcm && !stream->pcmsSynced ) { - ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError ); } PA_DEBUG(( "%s: Dropped frames\n", __FUNCTION__ )); @@ -2317,8 +2971,8 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort ) { if( stream->playback.pcm ) { - ENSURE_( snd_pcm_nonblock( stream->playback.pcm, 0 ), paUnanticipatedHostError ); - if( snd_pcm_drain( stream->playback.pcm ) < 0 ) + ENSURE_( alsa_snd_pcm_nonblock( stream->playback.pcm, 0 ), paUnanticipatedHostError ); + if( alsa_snd_pcm_drain( stream->playback.pcm ) < 0 ) { PA_DEBUG(( "%s: Draining playback handle failed!\n", __FUNCTION__ )); } @@ -2326,7 +2980,7 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort ) if( stream->capture.pcm && !stream->pcmsSynced ) { /* We don't need to retrieve any remaining frames */ - if( snd_pcm_drain( stream->capture.pcm ) < 0 ) + if( alsa_snd_pcm_drain( stream->capture.pcm ) < 0 ) { PA_DEBUG(( "%s: Draining capture handle failed!\n", __FUNCTION__ )); } @@ -2424,7 +3078,7 @@ static PaTime GetStreamTime( PaStream *s ) snd_timestamp_t timestamp; snd_pcm_status_t* status; - snd_pcm_status_alloca( &status ); + alsa_snd_pcm_status_alloca( &status ); /* TODO: what if we have both? does it really matter? */ @@ -2434,14 +3088,14 @@ static PaTime GetStreamTime( PaStream *s ) if( stream->capture.pcm ) { - snd_pcm_status( stream->capture.pcm, status ); + alsa_snd_pcm_status( stream->capture.pcm, status ); } else if( stream->playback.pcm ) { - snd_pcm_status( stream->playback.pcm, status ); + alsa_snd_pcm_status( stream->playback.pcm, status ); } - snd_pcm_status_get_tstamp( status, ×tamp ); + alsa_snd_pcm_status_get_tstamp( status, ×tamp ); return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1e6; } @@ -2472,9 +3126,9 @@ static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwPara dir = 1; } - if( snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ) < 0) + if( alsa_snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ) < 0) result = paInvalidSampleRate; - + end: return result; @@ -2484,8 +3138,8 @@ error: /* Log */ { unsigned int _min = 0, _max = 0; int _dir = 0; - ENSURE_( snd_pcm_hw_params_get_rate_min( hwParams, &_min, &_dir ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_rate_max( hwParams, &_max, &_dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_rate_min( hwParams, &_min, &_dir ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_hw_params_get_rate_max( hwParams, &_max, &_dir ), paUnanticipatedHostError ); PA_DEBUG(( "%s: SR min = %d, max = %d, req = %lu\n", __FUNCTION__, _min, _max, approx )); } @@ -2500,7 +3154,7 @@ static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate assert( hwParams ); - err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den ); + err = alsa_snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den ); *sampleRate = (double) num / den; return err; @@ -2536,19 +3190,19 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self ) snd_timestamp_t t; int restartAlsa = 0; /* do not restart Alsa by default */ - snd_pcm_status_alloca( &st ); + alsa_snd_pcm_status_alloca( &st ); if( self->playback.pcm ) { - snd_pcm_status( self->playback.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) + alsa_snd_pcm_status( self->playback.pcm, st ); + if( alsa_snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) { - snd_pcm_status_get_trigger_tstamp( st, &t ); + alsa_snd_pcm_status_get_trigger_tstamp( st, &t ); self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); if (!self->playback.canMmap) { - if (snd_pcm_recover( self->playback.pcm, -EPIPE, 0 ) < 0) + if (alsa_snd_pcm_recover( self->playback.pcm, -EPIPE, 0 ) < 0) { PA_DEBUG(( "%s: [playback] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ )); ++ restartAlsa; /* did not manage to recover */ @@ -2560,15 +3214,15 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self ) } if( self->capture.pcm ) { - snd_pcm_status( self->capture.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) + alsa_snd_pcm_status( self->capture.pcm, st ); + if( alsa_snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) { - snd_pcm_status_get_trigger_tstamp( st, &t ); + alsa_snd_pcm_status_get_trigger_tstamp( st, &t ); self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); if (!self->capture.canMmap) { - if (snd_pcm_recover( self->capture.pcm, -EPIPE, 0 ) < 0) + if (alsa_snd_pcm_recover( self->capture.pcm, -EPIPE, 0 ) < 0) { PA_DEBUG(( "%s: [capture] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ )); ++ restartAlsa; /* did not manage to recover */ @@ -2614,8 +3268,8 @@ static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamD otherComponent = &stream->capture; } - /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */ - if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 ) + /* ALSA docs say that negative delay should indicate xrun, but in my experience alsa_snd_pcm_delay returns -EPIPE */ + if( (err = alsa_snd_pcm_delay( otherComponent->pcm, &delay )) < 0 ) { if( err == -EPIPE ) { @@ -2680,21 +3334,21 @@ static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *t snd_timestamp_t capture_timestamp, playback_timestamp; PaTime capture_time = 0., playback_time = 0.; - snd_pcm_status_alloca( &capture_status ); - snd_pcm_status_alloca( &playback_status ); + alsa_snd_pcm_status_alloca( &capture_status ); + alsa_snd_pcm_status_alloca( &playback_status ); if( stream->capture.pcm ) { snd_pcm_sframes_t capture_delay; - snd_pcm_status( stream->capture.pcm, capture_status ); - snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); + alsa_snd_pcm_status( stream->capture.pcm, capture_status ); + alsa_snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); capture_time = capture_timestamp.tv_sec + ((PaTime)capture_timestamp.tv_usec / 1000000.0); timeInfo->currentTime = capture_time; - capture_delay = snd_pcm_status_get_delay( capture_status ); + capture_delay = alsa_snd_pcm_status_get_delay( capture_status ); timeInfo->inputBufferAdcTime = timeInfo->currentTime - (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; } @@ -2702,8 +3356,8 @@ static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *t { snd_pcm_sframes_t playback_delay; - snd_pcm_status( stream->playback.pcm, playback_status ); - snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); + alsa_snd_pcm_status( stream->playback.pcm, playback_status ); + alsa_snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); playback_time = playback_timestamp.tv_sec + ((PaTime)playback_timestamp.tv_usec / 1000000.0); @@ -2718,7 +3372,7 @@ static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *t else timeInfo->currentTime = playback_time; - playback_delay = snd_pcm_status_get_delay( playback_status ); + playback_delay = alsa_snd_pcm_status_get_delay( playback_status ); timeInfo->outputBufferDacTime = timeInfo->currentTime + (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; } @@ -2746,11 +3400,11 @@ static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, { /* Play sound */ if( self->hostInterleaved ) - res = snd_pcm_writei( self->pcm, self->nonMmapBuffer, numFrames ); + res = alsa_snd_pcm_writei( self->pcm, self->nonMmapBuffer, numFrames ); else { void *bufs[self->numHostChannels]; - int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 ); + int bufsize = alsa_snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 ); unsigned char *buffer = self->nonMmapBuffer; int i; for( i = 0; i < self->numHostChannels; ++i ) @@ -2758,12 +3412,12 @@ static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, bufs[i] = buffer; buffer += bufsize; } - res = snd_pcm_writen( self->pcm, bufs, numFrames ); + res = alsa_snd_pcm_writen( self->pcm, bufs, numFrames ); } } if( self->canMmap ) - res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames ); + res = alsa_snd_pcm_mmap_commit( self->pcm, self->offset, numFrames ); else { /* using realloc for optimisation @@ -2810,7 +3464,7 @@ static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *s if( self->hostInterleaved ) { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); + int swidth = alsa_snd_pcm_format_size( self->nativeFormat, 1 ); unsigned char *buffer = self->canMmap ? ExtractAddress( self->channelAreas, self->offset ) : self->nonMmapBuffer; /* Start after the last user channel */ @@ -2847,13 +3501,13 @@ static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *s /* We extract the last user channel */ if( convertMono ) { - ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas + + ENSURE_( alsa_snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas + (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError ); --unusedChans; } if( unusedChans > 0 ) { - snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames, + alsa_snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames, self->nativeFormat ); } } @@ -2891,7 +3545,7 @@ error: static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred ) { PaError result = paNoError; - snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm ); + snd_pcm_sframes_t framesAvail = alsa_snd_pcm_avail_update( self->pcm ); *xrunOccurred = 0; if( -EPIPE == framesAvail ) @@ -2915,7 +3569,7 @@ error: static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent* self, struct pollfd* pfds ) { PaError result = paNoError; - int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds ); + int ret = alsa_snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds ); (void)ret; /* Prevent unused variable warning if asserts are turned off */ assert( ret == self->nfds ); @@ -2935,7 +3589,7 @@ static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, st PaError result = paNoError; unsigned short revents; - ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError ); if( revents != 0 ) { if( revents & POLLERR ) @@ -3063,8 +3717,9 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr int totalFds = 0; struct pollfd *capturePfds = NULL, *playbackPfds = NULL; +#ifdef PTHREAD_CANCELED pthread_testcancel(); - +#endif if( pollCapture ) { capturePfds = self->pfds; @@ -3098,13 +3753,13 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr { /* Suspended, paused or failed device can provide 0 poll results. To avoid deadloop in such situation - * we simply run counter 'timeouts' which detects 0 poll result and accumulates. As soon as 64 timouts + * we simply run counter 'timeouts' which detects 0 poll result and accumulates. As soon as 2048 timouts (around 2 seconds) * are achieved we simply fail function with paTimedOut to notify waiting methods that device is not capable * of providing audio data anymore and needs some corresponding recovery action. * Note that 'timeouts' is reset to 0 if poll() managed to return non 0 results. */ - /*PA_DEBUG(( "%s: poll == 0 results, timed out, %d times left\n", __FUNCTION__, 64 - timeouts ));*/ + /*PA_DEBUG(( "%s: poll == 0 results, timed out, %d times left\n", __FUNCTION__, 2048 - timeouts ));*/ ++ timeouts; if (timeouts > 1) /* sometimes device times out, but normally once, so we do not sleep any time */ @@ -3112,12 +3767,13 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr Pa_Sleep( 1 ); /* avoid hot loop */ } /* not else ! */ - if (timeouts >= 64) /* audio device not working, shall return error to notify waiters */ + if (timeouts >= 2048) /* audio device not working, shall return error to notify waiters */ { *framesAvail = 0; /* no frames available for processing */ + xrun = 1; /* try recovering device */ - PA_DEBUG(( "%s: poll timed out, returning error\n", __FUNCTION__, timeouts )); - PA_ENSURE( paTimedOut ); + PA_DEBUG(( "%s: poll timed out\n", __FUNCTION__, timeouts )); + goto end;/*PA_ENSURE( paTimedOut );*/ } } else @@ -3236,13 +3892,13 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se if( self->canMmap ) { - ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError ); /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */ self->channelAreas = (snd_pcm_channel_area_t *)areas; } else { - unsigned int bufferSize = self->numHostChannels * snd_pcm_format_size( self->nativeFormat, *numFrames ); + unsigned int bufferSize = self->numHostChannels * alsa_snd_pcm_format_size( self->nativeFormat, *numFrames ); if (bufferSize > self->nonMmapBufferSize) { self->nonMmapBuffer = realloc(self->nonMmapBuffer, (self->nonMmapBufferSize = bufferSize)); @@ -3256,7 +3912,7 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se if( self->hostInterleaved ) { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); + int swidth = alsa_snd_pcm_format_size( self->nativeFormat, 1 ); p = buffer = self->canMmap ? ExtractAddress( areas, self->offset ) : self->nonMmapBuffer; for( i = 0; i < self->numUserChannels; ++i ) @@ -3294,7 +3950,7 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se /* Read sound */ int res; if( self->hostInterleaved ) - res = snd_pcm_readi( self->pcm, self->nonMmapBuffer, *numFrames ); + res = alsa_snd_pcm_readi( self->pcm, self->nonMmapBuffer, *numFrames ); else { void *bufs[self->numHostChannels]; @@ -3306,7 +3962,7 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se bufs[i] = buffer; buffer += buf_per_ch_size; } - res = snd_pcm_readn( self->pcm, bufs, *numFrames ); + res = alsa_snd_pcm_readn( self->pcm, bufs, *numFrames ); } if( res == -EPIPE || res == -ESTRPIPE ) { @@ -3466,13 +4122,13 @@ static void *CallbackThreadFunc( void *userData ) snd_pcm_sframes_t avail; if( stream->playback.pcm ) - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); if( stream->capture.pcm && !stream->pcmsSynced ) - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); /* We can't be certain that the whole ring buffer is available for priming, but there should be * at least one period */ - avail = snd_pcm_avail_update( stream->playback.pcm ); + avail = alsa_snd_pcm_avail_update( stream->playback.pcm ); startThreshold = avail - (avail % stream->playback.framesPerBuffer); assert( startThreshold >= stream->playback.framesPerBuffer ); } @@ -3491,7 +4147,9 @@ static void *CallbackThreadFunc( void *userData ) unsigned long framesAvail, framesGot; int xrun = 0; - pthread_testcancel(); +#ifdef PTHREAD_CANCELED + pthread_testcancel(); +#endif /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output). @@ -3539,7 +4197,9 @@ static void *CallbackThreadFunc( void *userData ) { xrun = 0; - pthread_testcancel(); +#ifdef PTHREAD_CANCELED + pthread_testcancel(); +#endif /** @concern Xruns Under/overflows are to be reported to the callback */ if( stream->underrun > 0.0 ) @@ -3614,13 +4274,16 @@ static void *CallbackThreadFunc( void *userData ) } } +end: + ; /* Hack to fix "label at end of compound statement" error caused by pthread_cleanup_pop(1) macro. */ /* Match pthread_cleanup_push */ pthread_cleanup_pop( 1 ); -end: PA_DEBUG(( "%s: Thread %d exiting\n ", __FUNCTION__, pthread_self() )); PaUnixThreading_EXIT( result ); + error: + PA_DEBUG(( "%s: Thread %d is canceled due to error %d\n ", __FUNCTION__, pthread_self(), result )); goto end; } @@ -3659,9 +4322,9 @@ static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames ) } /* Start stream if in prepared state */ - if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) + if( alsa_snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) { - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); } while( frames > 0 ) @@ -3739,10 +4402,10 @@ static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frame framesAvail = err; hwAvail = stream->playback.bufferSize - framesAvail; - if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED && + if( alsa_snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED && hwAvail >= stream->playback.framesPerBuffer ) { - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); } } @@ -3789,7 +4452,7 @@ static signed long GetStreamWriteAvailable( PaStream* s ) snd_pcm_sframes_t savail; PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - savail = snd_pcm_avail_update( stream->playback.pcm ); + savail = alsa_snd_pcm_avail_update( stream->playback.pcm ); /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */ ENSURE_( savail, paUnanticipatedHostError ); @@ -3856,9 +4519,9 @@ PaError PaAlsa_GetStreamInputCard(PaStream* s, int* card) { /* XXX: More descriptive error? */ PA_UNLESS( stream->capture.pcm, paDeviceUnavailable ); - snd_pcm_info_alloca( &pcmInfo ); - PA_ENSURE( snd_pcm_info( stream->capture.pcm, pcmInfo ) ); - *card = snd_pcm_info_get_card( pcmInfo ); + alsa_snd_pcm_info_alloca( &pcmInfo ); + PA_ENSURE( alsa_snd_pcm_info( stream->capture.pcm, pcmInfo ) ); + *card = alsa_snd_pcm_info_get_card( pcmInfo ); error: return result; @@ -3874,9 +4537,9 @@ PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) { /* XXX: More descriptive error? */ PA_UNLESS( stream->playback.pcm, paDeviceUnavailable ); - snd_pcm_info_alloca( &pcmInfo ); - PA_ENSURE( snd_pcm_info( stream->playback.pcm, pcmInfo ) ); - *card = snd_pcm_info_get_card( pcmInfo ); + alsa_snd_pcm_info_alloca( &pcmInfo ); + PA_ENSURE( alsa_snd_pcm_info( stream->playback.pcm, pcmInfo ) ); + *card = alsa_snd_pcm_info_get_card( pcmInfo ); error: return result; diff --git a/bazaar/plugin/portaudio/hostapi/asihpi/pa_linux_asihpi.c b/bazaar/plugin/portaudio/hostapi/asihpi/pa_linux_asihpi.c index a5b2df43c..e9627ec30 100644 --- a/bazaar/plugin/portaudio/hostapi/asihpi/pa_linux_asihpi.c +++ b/bazaar/plugin/portaudio/hostapi/asihpi/pa_linux_asihpi.c @@ -1826,8 +1826,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaTime bufferDuration = ( stream->input->hostBufferSize + stream->input->hardwareBufferSize ) / sampleRate / stream->input->bytesPerFrame; stream->baseStreamRep.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) + - bufferDuration - stream->maxFramesPerHostBuffer / sampleRate; + bufferDuration + + ((PaTime)PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) - + stream->maxFramesPerHostBuffer) / sampleRate; assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 ); } /* Determine output latency from buffer processor and buffer sizes */ @@ -1842,8 +1843,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame ); } stream->baseStreamRep.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) + - bufferDuration - stream->maxFramesPerHostBuffer / sampleRate; + bufferDuration + + ((PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) - + stream->maxFramesPerHostBuffer) / sampleRate; assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 ); } diff --git a/bazaar/plugin/portaudio/hostapi/asio/pa_asio.cpp b/bazaar/plugin/portaudio/hostapi/asio/pa_asio.cpp index f4786ee3e..b14228a3f 100644 --- a/bazaar/plugin/portaudio/hostapi/asio/pa_asio.cpp +++ b/bazaar/plugin/portaudio/hostapi/asio/pa_asio.cpp @@ -1,5 +1,5 @@ /* - * $Id: pa_asio.cpp 1525 2010-07-14 06:45:25Z rossb $ + * $Id: pa_asio.cpp 1681 2011-05-10 15:58:15Z rossb $ * Portable Audio I/O Library for ASIO Drivers * * Author: Stephane Letz @@ -78,46 +78,9 @@ Note that specific support for paInputUnderflow, paOutputOverflow and paNeverDropInput is not necessary or possible with this driver due to the synchronous full duplex double-buffered architecture of ASIO. - - @todo implement host api specific extension to set i/o buffer sizes in frames - - @todo review ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable - - @todo review Blocking i/o latency computations in OpenStream(), changing ring - buffer to a non-power-of-two structure could reduce blocking i/o latency. - - @todo implement IsFormatSupported - - @todo work out how to implement stream stoppage from callback and - implement IsStreamActive properly. Stream stoppage could be implemented - using a high-priority thread blocked on an Event which is signalled - by the callback. Or, we could just call ASIO stop from the callback - and see what happens. - - @todo rigorously check asio return codes and convert to pa error codes - - @todo Different channels of a multichannel stream can have different sample - formats, but we assume that all are the same as the first channel for now. - Fixing this will require the block processor to maintain per-channel - conversion functions - could get nasty. - - @todo investigate whether the asio processNow flag needs to be honoured - - @todo handle asioMessages() callbacks in a useful way, or at least document - what cases we don't handle. - - @todo miscellaneous other FIXMEs - - @todo provide an asio-specific method for setting the systems specific - value (aka main window handle) - check that this matches the value - passed to PaAsio_ShowControlPanel, or remove it entirely from - PaAsio_ShowControlPanel. - this would allow PaAsio_ShowControlPanel - to be called for the currently open stream (at present all streams - must be closed). */ - #include #include #include @@ -138,6 +101,8 @@ #include "pa_debugprint.h" #include "pa_ringbuffer.h" +#include "pa_win_coinitialize.h" + /* This version of pa_asio.cpp is currently only targetted at Win32, It would require a few tweaks to work with pre-OS X Macintosh. To make configuration easier, we define WIN32 here to make sure @@ -326,6 +291,8 @@ typedef struct PaUtilAllocationGroup *allocations; + PaWinUtilComInitializationResult comInitializationResult; + AsioDrivers *asioDrivers; void *systemSpecific; @@ -943,8 +910,8 @@ typedef struct PaAsioDeviceInfo PaAsioDeviceInfo; -PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, - long *minLatency, long *maxLatency, long *preferredLatency, long *granularity ) +PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device, + long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity ) { PaError result; PaUtilHostApiRepresentation *hostApi; @@ -961,9 +928,9 @@ PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - *minLatency = asioDeviceInfo->minBufferSize; - *maxLatency = asioDeviceInfo->maxBufferSize; - *preferredLatency = asioDeviceInfo->preferredBufferSize; + *minBufferSizeFrames = asioDeviceInfo->minBufferSize; + *maxBufferSizeFrames = asioDeviceInfo->maxBufferSize; + *preferredBufferSizeFrames = asioDeviceInfo->preferredBufferSize; *granularity = asioDeviceInfo->bufferGranularity; } } @@ -972,12 +939,10 @@ PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, } /* Unload whatever we loaded in LoadAsioDriver(). - Also balance the call to CoInitialize(0). */ static void UnloadAsioDriver( void ) { ASIOExit(); - CoUninitialize(); } /* @@ -993,23 +958,8 @@ static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const c ASIOError asioError; int asioIsInitialized = 0; - /* - ASIO uses CoCreateInstance() to load a driver. That requires that - CoInitialize(0) be called for every thread that loads a driver. - It is OK to call CoInitialize(0) multiple times form one thread as long - as it is balanced by a call to CoUninitialize(). See UnloadAsioDriver(). - - The V18 version called CoInitialize() starting on 2/19/02. - That was removed from PA V19 for unknown reasons. - Phil Burk added it back on 6/27/08 so that JSyn would work. - */ - CoInitialize( 0 ); - if( !asioHostApi->asioDrivers->loadDriver( const_cast(driverName) ) ) { - /* If this returns an error then it might be because CoInitialize(0) was removed. - It should be called right before this. - */ result = paUnanticipatedHostError; PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" ); goto error; @@ -1058,7 +1008,7 @@ error: { ASIOExit(); } - CoUninitialize(); + return result; } @@ -1090,6 +1040,24 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex goto error; } + /* + We initialize COM ourselves here and uninitialize it in Terminate(). + This should be the only COM initialization needed in this module. + + The ASIO SDK may also initialize COM but since we want to reduce dependency + on the ASIO SDK we manage COM initialization ourselves. + + There used to be code that initialized COM in other situations + such as when creating a Stream. This made PA work when calling Pa_CreateStream + from a non-main thread. However we currently consider initialization + of COM in non-main threads to be the caller's responsibility. + */ + result = PaWinUtil_CoInitialize( paASIO, &asioHostApi->comInitializationResult ); + if( result != paNoError ) + { + goto error; + } + asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */ asioHostApi->allocations = PaUtil_CreateAllocationGroup(); @@ -1102,7 +1070,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex /* Allocate the AsioDrivers() driver list (class from ASIO SDK) */ try { - asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */ + asioHostApi->asioDrivers = new AsioDrivers(); /* invokes CoInitialize(0) in AsioDriverList::AsioDriverList */ } catch (std::bad_alloc) { @@ -1170,7 +1138,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex goto error; } - IsDebuggerPresent_ = (IsDebuggerPresentPtr)GetProcAddress( LoadLibrary( "Kernel32.dll" ), "IsDebuggerPresent" ); + IsDebuggerPresent_ = (IsDebuggerPresentPtr)GetProcAddress( LoadLibraryA( "Kernel32.dll" ), "IsDebuggerPresent" ); for( i=0; i < driverCount; ++i ) { @@ -1384,8 +1352,11 @@ error: delete asioHostApi->asioDrivers; asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */ + PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult ); + PaUtil_FreeMemory( asioHostApi ); } + return result; } @@ -1405,9 +1376,11 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); } - delete asioHostApi->asioDrivers; /* calls CoUninitialize() */ + delete asioHostApi->asioDrivers; asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */ + PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult ); + PaUtil_FreeMemory( asioHostApi ); } @@ -1606,7 +1579,7 @@ typedef struct PaAsioStream ASIOBufferInfo *asioBufferInfos; ASIOChannelInfo *asioChannelInfos; - long inputLatency, outputLatency; // actual latencies returned by asio + long asioInputLatencyFrames, asioOutputLatencyFrames; // actual latencies returned by asio long inputChannelCount, outputChannelCount; bool postOutput; @@ -1656,7 +1629,7 @@ static void ZeroOutputBuffers( PaAsioStream *stream, long index ) } -static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames, +static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames, unsigned long userFramesPerBuffer, PaAsioDriverInfo *driverInfo ) { unsigned long result; @@ -2122,15 +2095,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if( usingBlockingIo ) { /** @todo REVIEW selection of host buffer size for blocking i/o */ - /* Use default host latency for blocking i/o. */ - framesPerHostBuffer = SelectHostBufferSize( 0, driverInfo ); + + framesPerHostBuffer = SelectHostBufferSize( 0, framesPerBuffer, driverInfo ); } else /* Using callback interface... */ { framesPerHostBuffer = SelectHostBufferSize( (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames ) - ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames), + ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames), framesPerBuffer, driverInfo ); } @@ -2241,7 +2214,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if( inputChannelCount > 0 ) { - /* FIXME: assume all channels use the same type for now */ + /* FIXME: assume all channels use the same type for now + + see: "ASIO devices with multiple sample formats are unsupported" + http://www.portaudio.com/trac/ticket/106 + */ ASIOSampleType inputType = stream->asioChannelInfos[0].type; PA_DEBUG(("ASIO Input type:%d",inputType)); @@ -2258,7 +2235,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if( outputChannelCount > 0 ) { - /* FIXME: assume all channels use the same type for now */ + /* FIXME: assume all channels use the same type for now + + see: "ASIO devices with multiple sample formats are unsupported" + http://www.portaudio.com/trac/ticket/106 + */ ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type; PA_DEBUG(("ASIO Output type:%d",outputType)); @@ -2274,7 +2255,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } - ASIOGetLatencies( &stream->inputLatency, &stream->outputLatency ); + ASIOGetLatencies( &stream->asioInputLatencyFrames, &stream->asioOutputLatencyFrames ); /* Using blocking i/o interface... */ @@ -2386,7 +2367,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, to samples frames. 5) Get the next larger (or equal) power-of-two buffer size. */ - lBlockingBufferSize = suggestedInputLatencyFrames - stream->inputLatency; + lBlockingBufferSize = suggestedInputLatencyFrames - stream->asioInputLatencyFrames; lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1; lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer; lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer; @@ -2398,10 +2379,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, /* Compute total intput latency in seconds */ stream->streamRepresentation.streamInfo.inputLatency = - (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor ) - + PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor ) + + PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer - + stream->inputLatency ) + + stream->asioInputLatencyFrames ) / sampleRate; /* The code below prints the ASIO latency which doesn't include @@ -2409,12 +2390,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, reports the added latency separately. */ PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n", - stream->inputLatency, - (long)( stream->inputLatency * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor), - (long)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, - (long)( (PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) + stream->asioInputLatencyFrames, + (long)( stream->asioInputLatencyFrames * (1000.0 / sampleRate) ), + PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor), + (long)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ), + PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, + (long)( (PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) )); /* Determine the size of ring buffer in bytes. */ @@ -2472,7 +2453,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, to samples frames. 5) Get the next larger (or equal) power-of-two buffer size. */ - lBlockingBufferSize = suggestedOutputLatencyFrames - stream->outputLatency; + lBlockingBufferSize = suggestedOutputLatencyFrames - stream->asioOutputLatencyFrames; lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1; lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer; lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer; @@ -2489,10 +2470,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, /* Compute total output latency in seconds */ stream->streamRepresentation.streamInfo.outputLatency = - (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor ) - + PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) + + PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer - + stream->outputLatency ) + + stream->asioOutputLatencyFrames ) / sampleRate; /* The code below prints the ASIO latency which doesn't include @@ -2500,12 +2481,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, reports the added latency separately. */ PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n", - stream->outputLatency, - (long)( stream->inputLatency * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor), - (long)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, - (long)( (PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) + stream->asioOutputLatencyFrames, + (long)( stream->asioOutputLatencyFrames * (1000.0 / sampleRate) ), + PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor), + (long)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ), + PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, + (long)( (PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) )); /* Determine the size of ring buffer in bytes. */ @@ -2546,27 +2527,27 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, callbackBufferProcessorInited = TRUE; stream->streamRepresentation.streamInfo.inputLatency = - (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) - + stream->inputLatency) / sampleRate; // seconds + (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) + + stream->asioInputLatencyFrames) / sampleRate; // seconds stream->streamRepresentation.streamInfo.outputLatency = - (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) - + stream->outputLatency) / sampleRate; // seconds + (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) + + stream->asioOutputLatencyFrames) / sampleRate; // seconds stream->streamRepresentation.streamInfo.sampleRate = sampleRate; // the code below prints the ASIO latency which doesn't include the // buffer processor latency. it reports the added latency separately PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", - stream->inputLatency, - (long)((stream->inputLatency*1000)/ sampleRate), - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor), - (long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate) + stream->asioInputLatencyFrames, + (long)((stream->asioInputLatencyFrames*1000)/ sampleRate), + PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor), + (long)((PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate) )); PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", - stream->outputLatency, - (long)((stream->outputLatency*1000)/ sampleRate), - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor), - (long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate) + stream->asioOutputLatencyFrames, + (long)((stream->asioOutputLatencyFrames*1000)/ sampleRate), + PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor), + (long)((PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate) )); } @@ -2791,10 +2772,6 @@ static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool if( !theAsioStream ) return 0L; - // Keep sample position - // FIXME: asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo; - - // protect against reentrancy if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) ) { @@ -2849,6 +2826,11 @@ static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool { #if 0 +/* + see: "ASIO callback underflow/overflow buffer slip detection doesn't work" + http://www.portaudio.com/trac/ticket/110 +*/ + // test code to try to detect slip conditions... these may work on some systems // but neither of them work on the RME Digi96 @@ -2899,10 +2881,10 @@ previousIndex = index; /* patch from Paul Boege */ paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - - ((double)theAsioStream->inputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate); + ((double)theAsioStream->asioInputLatencyFrames/theAsioStream->streamRepresentation.streamInfo.sampleRate); paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + - ((double)theAsioStream->outputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate); + ((double)theAsioStream->asioOutputLatencyFrames/theAsioStream->streamRepresentation.streamInfo.sampleRate); /* old version is buggy because the buffer processor also adds in its latency to the time parameters paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency; @@ -3061,7 +3043,11 @@ static long asioMessages(long selector, long value, void* message, double* opt) // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction // Afterwards you initialize the driver again. - /*FIXME: commented the next line out */ + /*FIXME: commented the next line out + + see: "PA/ASIO ignores some driver notifications it probably shouldn't" + http://www.portaudio.com/trac/ticket/108 + */ //asioDriverInfo.stopped; // In this sample the processing will just stop ret = 1L; break; @@ -3860,7 +3846,12 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific ) int asioIsInitialized = 0; PaAsioHostApiRepresentation *asioHostApi; PaAsioDeviceInfo *asioDeviceInfo; + PaWinUtilComInitializationResult comInitializationResult; + /* initialize COM again here, we might be in another thread */ + result = PaWinUtil_CoInitialize( paASIO, &comInitializationResult ); + if( result != paNoError ) + return result; result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); if( result != paNoError ) @@ -3887,9 +3878,6 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific ) asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - /* See notes about CoInitialize(0) in LoadAsioDriver(). */ - CoInitialize(0); - if( !asioHostApi->asioDrivers->loadDriver( const_cast(asioDeviceInfo->commonDeviceInfo.name) ) ) { result = paUnanticipatedHostError; @@ -3938,7 +3926,6 @@ PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErr goto error; } - CoUninitialize(); PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) )); return result; @@ -3948,7 +3935,8 @@ error: { ASIOExit(); } - CoUninitialize(); + + PaWinUtil_CoUninitialize( paASIO, &comInitializationResult ); return result; } diff --git a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core.c b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core.c index 7862d0e65..bab668ce6 100644 --- a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core.c +++ b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core.c @@ -76,9 +76,11 @@ extern "C" { #endif /* __cplusplus */ - + +/* This is a reasonable size for a small buffer based on experience. */ +#define PA_MAC_SMALL_BUFFER_SIZE (64) + /* prototypes for functions declared in this file */ - PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); /* @@ -397,6 +399,153 @@ static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) return paNoError; } +/* =================================================================================================== */ +/** + * @internal + * @brief Clip the desired size against the allowed IO buffer size range for the device. + */ +static PaError ClipToDeviceBufferSize( AudioDeviceID macCoreDeviceId, + int isInput, UInt32 desiredSize, UInt32 *allowedSize ) +{ + UInt32 resultSize = desiredSize; + AudioValueRange audioRange; + UInt32 propSize = sizeof( audioRange ); + PaError err = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) ); + resultSize = MAX( resultSize, audioRange.mMinimum ); + resultSize = MIN( resultSize, audioRange.mMaximum ); + *allowedSize = resultSize; + return err; +} + +/* =================================================================================================== */ +#if 0 +static void DumpDeviceProperties( AudioDeviceID macCoreDeviceId, + int isInput ) +{ + PaError err; + int i; + UInt32 propSize; + UInt32 deviceLatency; + UInt32 streamLatency; + UInt32 bufferFrames; + UInt32 safetyOffset; + AudioStreamID streamIDs[128]; + + printf("\n======= latency query : macCoreDeviceId = %d, isInput %d =======\n", (int)macCoreDeviceId, isInput ); + + propSize = sizeof(UInt32); + err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames)); + printf("kAudioDevicePropertyBufferFrameSize: err = %d, propSize = %d, value = %d\n", err, propSize, bufferFrames ); + + propSize = sizeof(UInt32); + err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset)); + printf("kAudioDevicePropertySafetyOffset: err = %d, propSize = %d, value = %d\n", err, propSize, safetyOffset ); + + propSize = sizeof(UInt32); + err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency)); + printf("kAudioDevicePropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, deviceLatency ); + + AudioValueRange audioRange; + propSize = sizeof( audioRange ); + err = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) ); + printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, minimum = %g\n", err, propSize, audioRange.mMinimum); + printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, maximum = %g\n", err, propSize, audioRange.mMaximum ); + + /* Get the streams from the device and query their latency. */ + propSize = sizeof(streamIDs); + err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0])); + int numStreams = propSize / sizeof(AudioStreamID); + for( i=0; imaxOutputChannels = numChannels; - if (numChannels > 0) /* do not try to retrieve the latency if there is no channels. */ + if (numChannels > 0) /* do not try to retrieve the latency if there are no channels. */ { - /* Get the latency. Don't fail if we can't get this. */ - /* default to something reasonable */ - deviceInfo->defaultLowInputLatency = .01; - deviceInfo->defaultHighInputLatency = .10; - deviceInfo->defaultLowOutputLatency = .01; - deviceInfo->defaultHighOutputLatency = .10; - propSize = sizeof(UInt32); - err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); - if (!err) - { - /** FEEDBACK: - * This code was arrived at by trial and error, and some extentive, but not exhaustive - * testing. Sebastien Beaulieu has suggested using - * kAudioDevicePropertyLatency + kAudioDevicePropertySafetyOffset + buffer size instead. - * At the time this code was written, many users were reporting dropouts with audio - * programs that probably used this formula. This was probably - * around 10.4.4, and the problem is probably fixed now. So perhaps - * his formula should be reviewed and used. - * */ - double secondLatency = frameLatency / deviceInfo->defaultSampleRate; - if (isInput) - { - deviceInfo->defaultLowInputLatency = 3 * secondLatency; - deviceInfo->defaultHighInputLatency = 3 * 10 * secondLatency; - } - else - { - deviceInfo->defaultLowOutputLatency = 3 * secondLatency; - deviceInfo->defaultHighOutputLatency = 3 * 10 * secondLatency; - } - } + /* Get the latency. Don't fail if we can't get this. */ + /* default to something reasonable */ + deviceInfo->defaultLowInputLatency = .01; + deviceInfo->defaultHighInputLatency = .10; + deviceInfo->defaultLowOutputLatency = .01; + deviceInfo->defaultHighOutputLatency = .10; + UInt32 lowLatencyFrames = 0; + UInt32 highLatencyFrames = 0; + err = CalculateDefaultDeviceLatencies( macCoreDeviceId, isInput, &lowLatencyFrames, &highLatencyFrames ); + if( err == 0 ) + { + + double lowLatencySeconds = lowLatencyFrames / deviceInfo->defaultSampleRate; + double highLatencySeconds = highLatencyFrames / deviceInfo->defaultSampleRate; + if (isInput) + { + deviceInfo->defaultLowInputLatency = lowLatencySeconds; + deviceInfo->defaultHighInputLatency = highLatencySeconds; + } + else + { + deviceInfo->defaultLowOutputLatency = lowLatencySeconds; + deviceInfo->defaultHighOutputLatency = highLatencySeconds; + } + } } PaUtil_FreeMemory( buflist ); return paNoError; @@ -474,6 +616,7 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi, return err; } +/* =================================================================================================== */ static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi, PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, @@ -797,75 +940,180 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, return paFormatIsSupported; } - -static void UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( PaMacCoreStream *stream ) +/* ================================================================================= */ +static void InitializeDeviceProperties( PaMacCoreDeviceProperties *deviceProperties ) { + memset( deviceProperties, 0, sizeof(PaMacCoreDeviceProperties) ); + deviceProperties->sampleRate = 1.0; // Better than random. Overwritten by actual values later on. + deviceProperties->samplePeriod = 1.0 / deviceProperties->sampleRate; +} + +static Float64 CalculateSoftwareLatencyFromProperties( PaMacCoreStream *stream, PaMacCoreDeviceProperties *deviceProperties ) +{ + UInt32 latencyFrames = deviceProperties->bufferFrameSize + deviceProperties->deviceLatency + deviceProperties->safetyOffset; + return latencyFrames * deviceProperties->samplePeriod; // same as dividing by sampleRate but faster +} + +static Float64 CalculateHardwareLatencyFromProperties( PaMacCoreStream *stream, PaMacCoreDeviceProperties *deviceProperties ) +{ + return deviceProperties->deviceLatency * deviceProperties->samplePeriod; // same as dividing by sampleRate but faster +} + +/* Calculate values used to convert Apple timestamps into PA timestamps + * from the device properties. The final results of this calculation + * will be used in the audio callback function. + */ +static void UpdateTimeStampOffsets( PaMacCoreStream *stream ) +{ + Float64 inputSoftwareLatency = 0.0; + Float64 inputHardwareLatency = 0.0; + Float64 outputSoftwareLatency = 0.0; + Float64 outputHardwareLatency = 0.0; + + if( stream->inputUnit != NULL ) + { + inputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->inputProperties ); + inputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->inputProperties ); + } + if( stream->outputUnit != NULL ) + { + outputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->outputProperties ); + outputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->outputProperties ); + } + + /* We only need a mutex around setting these variables as a group. */ + pthread_mutex_lock( &stream->timingInformationMutex ); + stream->timestampOffsetCombined = inputSoftwareLatency + outputSoftwareLatency; + stream->timestampOffsetInputDevice = inputHardwareLatency; + stream->timestampOffsetOutputDevice = outputHardwareLatency; + pthread_mutex_unlock( &stream->timingInformationMutex ); +} + +/* ================================================================================= */ +/* Query sample rate property. */ +static OSStatus UpdateSampleRateFromDeviceProperty( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput ) +{ + PaMacCoreDeviceProperties * deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties; /* FIXME: not sure if this should be the sample rate of the output device or the output unit */ - Float64 actualOutputSampleRate = stream->outDeviceSampleRate; + Float64 actualSampleRate = deviceProperties->sampleRate; UInt32 propSize = sizeof(Float64); - OSStatus osErr = AudioDeviceGetProperty( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate, &propSize, &actualOutputSampleRate); - if( osErr != noErr || actualOutputSampleRate < .01 ) // avoid divide by zero if there's an error - actualOutputSampleRate = stream->outDeviceSampleRate; - - stream->recipricalOfActualOutputSampleRate = 1. / actualOutputSampleRate; + OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate, &propSize, &actualSampleRate); + if( (osErr == noErr) && (actualSampleRate > 1000.0) ) // avoid divide by zero if there's an error + { + deviceProperties->sampleRate = actualSampleRate; + deviceProperties->samplePeriod = 1.0 / actualSampleRate; + } + return osErr; } static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData ) { PaMacCoreStream *stream = (PaMacCoreStream*)inClientData; - - pthread_mutex_lock( &stream->timingInformationMutex ); - UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( stream ); - pthread_mutex_unlock( &stream->timingInformationMutex ); + + // Make sure the callback is operating on a stream that is still valid! + assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC ); - return noErr; + OSStatus osErr = UpdateSampleRateFromDeviceProperty( stream, inDevice, isInput ); + if( osErr == noErr ) + { + UpdateTimeStampOffsets( stream ); + } + return osErr; } -static void UpdateOutputLatencySamplesFromDeviceProperty( PaMacCoreStream *stream ) +/* ================================================================================= */ +static OSStatus QueryUInt32DeviceProperty( AudioDeviceID deviceID, Boolean isInput, AudioDevicePropertyID propertyID, UInt32 *outValue ) { - UInt32 deviceOutputLatencySamples = 0; - UInt32 propSize = sizeof(UInt32); - OSStatus osErr = AudioDeviceGetProperty( stream->outputDevice, 0, /* isInput= */ FALSE, kAudioDevicePropertyLatency, &propSize, &deviceOutputLatencySamples); - if( osErr != noErr ) - deviceOutputLatencySamples = 0; - - stream->deviceOutputLatencySamples = deviceOutputLatencySamples; + UInt32 propertyValue = 0; + UInt32 propertySize = sizeof(UInt32); + OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, propertyID, &propertySize, &propertyValue); + if( osErr == noErr ) + { + *outValue = propertyValue; + } + return osErr; } -static OSStatus AudioDevicePropertyOutputLatencySamplesListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData ) +static OSStatus AudioDevicePropertyGenericListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData ) { + OSStatus osErr = noErr; PaMacCoreStream *stream = (PaMacCoreStream*)inClientData; - - pthread_mutex_lock( &stream->timingInformationMutex ); - UpdateOutputLatencySamplesFromDeviceProperty( stream ); - pthread_mutex_unlock( &stream->timingInformationMutex ); - - return noErr; + + // 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 ) + { + case kAudioDevicePropertySafetyOffset: + valuePtr = &deviceProperties->safetyOffset; + break; + + case kAudioDevicePropertyLatency: + valuePtr = &deviceProperties->deviceLatency; + break; + + case kAudioDevicePropertyBufferFrameSize: + valuePtr = &deviceProperties->bufferFrameSize; + break; + } + if( valuePtr != NULL ) + { + osErr = QueryUInt32DeviceProperty( inDevice, isInput, inPropertyID, valuePtr ); + if( osErr == noErr ) + { + UpdateTimeStampOffsets( stream ); + } + } + return osErr; } -static void UpdateInputLatencySamplesFromDeviceProperty( PaMacCoreStream *stream ) +/* ================================================================================= */ +/* + * Setup listeners in case device properties change during the run. */ +static OSStatus SetupDevicePropertyListeners( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput ) { - UInt32 deviceInputLatencySamples = 0; - UInt32 propSize = sizeof(UInt32); - OSStatus osErr = AudioDeviceGetProperty( stream->inputDevice, 0, /* isInput= */ TRUE, kAudioDevicePropertyLatency, &propSize, &deviceInputLatencySamples); - if( osErr != noErr ) - deviceInputLatencySamples = 0; - - stream->deviceInputLatencySamples = deviceInputLatencySamples; + OSStatus osErr = noErr; + PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties; + + // Start with the current values for the device properties. + UpdateSampleRateFromDeviceProperty( stream, deviceID, isInput ); + + if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput, + kAudioDevicePropertyLatency, &deviceProperties->deviceLatency )) != noErr ) return osErr; + if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput, + kAudioDevicePropertyBufferFrameSize, &deviceProperties->bufferFrameSize )) != noErr ) return osErr; + if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput, + kAudioDevicePropertySafetyOffset, &deviceProperties->safetyOffset )) != noErr ) return osErr; + + AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate, + AudioDevicePropertyActualSampleRateListenerProc, stream ); + + AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioStreamPropertyLatency, + AudioDevicePropertyGenericListenerProc, stream ); + AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize, + AudioDevicePropertyGenericListenerProc, stream ); + AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset, + AudioDevicePropertyGenericListenerProc, stream ); + + return osErr; } -static OSStatus AudioDevicePropertyInputLatencySamplesListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)inClientData; - - pthread_mutex_lock( &stream->timingInformationMutex ); - UpdateInputLatencySamplesFromDeviceProperty( stream ); - pthread_mutex_unlock( &stream->timingInformationMutex ); - - return noErr; +static void CleanupDevicePropertyListeners( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput ) +{ + AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate, + AudioDevicePropertyActualSampleRateListenerProc ); + + AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyLatency, + AudioDevicePropertyGenericListenerProc ); + AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize, + AudioDevicePropertyGenericListenerProc ); + AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset, + AudioDevicePropertyGenericListenerProc ); } - +/* ================================================================================= */ static PaError OpenAndSetupOneAudioUnit( const PaMacCoreStream *stream, const PaStreamParameters *inStreamParams, @@ -1299,11 +1547,17 @@ static PaError OpenAndSetupOneAudioUnit( ERR_WRAP( AudioUnitInitialize(*audioUnit) ); if( inStreamParams && outStreamParams ) - VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) ); + { + VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) ); + } else if( inStreamParams ) - VDBUG( ("Opened device %ld for input.\n", *audioDevice ) ); + { + VDBUG( ("Opened device %ld for input.\n", *audioDevice ) ); + } else if( outStreamParams ) - VDBUG( ("Opened device %ld for output.\n", *audioDevice ) ); + { + VDBUG( ("Opened device %ld for output.\n", *audioDevice ) ); + } return paNoError; #undef ERR_WRAP @@ -1315,13 +1569,74 @@ static PaError OpenAndSetupOneAudioUnit( return paResult; } +/* =================================================================================================== */ + +static UInt32 CalculateOptimalBufferSize( PaMacAUHAL *auhalHostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + UInt32 fixedInputLatency, + UInt32 fixedOutputLatency, + double sampleRate, + UInt32 requestedFramesPerBuffer ) +{ + UInt32 suggested = 0; + // Use maximum of suggested input and output latencies. + if( inputParameters ) + { + UInt32 suggestedLatencyFrames = inputParameters->suggestedLatency * sampleRate; + // Calculate a buffer size assuming we are double buffered. + SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedInputLatency; + // Prevent negative latency. + variableLatencyFrames = MAX( variableLatencyFrames, 0 ); + suggested = MAX( suggested, (UInt32) variableLatencyFrames ); + } + if( outputParameters ) + { + UInt32 suggestedLatencyFrames = outputParameters->suggestedLatency * sampleRate; + SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedOutputLatency; + variableLatencyFrames = MAX( variableLatencyFrames, 0 ); + suggested = MAX( suggested, (UInt32) variableLatencyFrames ); + } + + VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n", + suggested ) ); + + if( requestedFramesPerBuffer != paFramesPerBufferUnspecified ) + { + if( suggested > (requestedFramesPerBuffer + 1) ) + { + // If the user asks for higher latency than the requested buffer size would provide + // then put multiple user buffers in one host buffer. + UInt32 userBuffersPerHostBuffer = (suggested + (requestedFramesPerBuffer - 1)) / requestedFramesPerBuffer; + suggested = userBuffersPerHostBuffer * requestedFramesPerBuffer; + } + } + + // Clip to the capabilities of the device. + if( inputParameters ) + { + ClipToDeviceBufferSize( auhalHostApi->devIds[inputParameters->device], + true, // In the old code isInput was false! + suggested, &suggested ); + } + if( outputParameters ) + { + ClipToDeviceBufferSize( auhalHostApi->devIds[outputParameters->device], + false, suggested, &suggested ); + } + VDBUG(("After querying hardware, setting block size to %ld.\n", suggested)); + + return suggested; +} + +/* =================================================================================================== */ /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaStream** s, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate, - unsigned long framesPerBuffer, + unsigned long requestedFramesPerBuffer, PaStreamFlags streamFlags, PaStreamCallback *streamCallback, void *userData ) @@ -1332,21 +1647,34 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, int inputChannelCount, outputChannelCount; PaSampleFormat inputSampleFormat, outputSampleFormat; PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + UInt32 fixedInputLatency = 0; + UInt32 fixedOutputLatency = 0; + // Accumulate contributions to latency in these variables. + UInt32 inputLatencyFrames = 0; + UInt32 outputLatencyFrames = 0; + UInt32 suggestedLatencyFramesPerBuffer = requestedFramesPerBuffer; + VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n", inputParameters ? inputParameters->channelCount : -1, inputParameters ? inputParameters->sampleFormat : -1, outputParameters ? outputParameters->channelCount : -1, outputParameters ? outputParameters->sampleFormat : -1, (float) sampleRate, - framesPerBuffer )); + requestedFramesPerBuffer )); VDBUG( ("Opening Stream.\n") ); - - /*These first few bits of code are from paSkeleton with few modifications.*/ + + /* These first few bits of code are from paSkeleton with few modifications. */ if( inputParameters ) { inputChannelCount = inputParameters->channelCount; inputSampleFormat = inputParameters->sampleFormat; + /* @todo Blocking read/write on Mac is not yet supported. */ + if( !streamCallback && inputSampleFormat & paNonInterleaved ) + { + return paSampleFormatNotSupported; + } + /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ @@ -1371,6 +1699,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, outputChannelCount = outputParameters->channelCount; outputSampleFormat = outputParameters->sampleFormat; + /* @todo Blocking read/write on Mac is not yet supported. */ + if( !streamCallback && outputSampleFormat & paNonInterleaved ) + { + return paSampleFormatNotSupported; + } + /* unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification */ @@ -1442,70 +1776,25 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - /* -- handle paFramesPerBufferUnspecified -- */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) { - long requested = 64; - if( inputParameters ) - requested = MAX( requested, inputParameters->suggestedLatency * sampleRate / 2 ); - if( outputParameters ) - requested = MAX( requested, outputParameters->suggestedLatency *sampleRate / 2 ); - VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n", - requested ) ); - if( requested <= 64 ) { - /*requested a realtively low latency. make sure this is in range of devices */ - /*try to get the device's min natural buffer size and use that (but no smaller than 64).*/ - AudioValueRange audioRange; - UInt32 size = sizeof( audioRange ); - if( inputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MAX( requested, audioRange.mMinimum ); - } - size = sizeof( audioRange ); - if( outputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MAX( requested, audioRange.mMinimum ); - } - } else { - /* requested a realtively high latency. make sure this is in range of devices */ - /*try to get the device's max natural buffer size and use that (but no larger than 1024).*/ - AudioValueRange audioRange; - UInt32 size = sizeof( audioRange ); - requested = MIN( requested, 1024 ); - if( inputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MIN( requested, audioRange.mMaximum ); - } - size = sizeof( audioRange ); - if( outputParameters ) { - WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device], - 0, - false, - kAudioDevicePropertyBufferFrameSizeRange, - &size, &audioRange ) ); - if( result ) - requested = MIN( requested, audioRange.mMaximum ); - } - } - /* -- double check ranges -- */ - if( requested > 1024 ) requested = 1024; - if( requested < 64 ) requested = 64; - VDBUG(("After querying hardware, setting block size to %ld.\n", requested)); - framesPerBuffer = requested; + + if( inputParameters ) + { + CalculateFixedDeviceLatency( auhalHostApi->devIds[inputParameters->device], true, &fixedInputLatency ); + inputLatencyFrames += fixedInputLatency; + } + if( outputParameters ) + { + CalculateFixedDeviceLatency( auhalHostApi->devIds[outputParameters->device], false, &fixedOutputLatency ); + outputLatencyFrames += fixedOutputLatency; + + } + + suggestedLatencyFramesPerBuffer = CalculateOptimalBufferSize( auhalHostApi, inputParameters, outputParameters, + fixedInputLatency, fixedOutputLatency, + sampleRate, requestedFramesPerBuffer ); + if( requestedFramesPerBuffer == paFramesPerBufferUnspecified ) + { + requestedFramesPerBuffer = suggestedLatencyFramesPerBuffer; } /* -- Now we actually open and setup streams. -- */ @@ -1516,7 +1805,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, result = OpenAndSetupOneAudioUnit( stream, inputParameters, outputParameters, - framesPerBuffer, + suggestedLatencyFramesPerBuffer, &inputFramesPerBuffer, &outputFramesPerBuffer, auhalHostApi, @@ -1539,7 +1828,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, result = OpenAndSetupOneAudioUnit( stream, NULL, outputParameters, - framesPerBuffer, + suggestedLatencyFramesPerBuffer, NULL, &outputFramesPerBuffer, auhalHostApi, @@ -1553,7 +1842,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, result = OpenAndSetupOneAudioUnit( stream, inputParameters, NULL, - framesPerBuffer, + suggestedLatencyFramesPerBuffer, &inputFramesPerBuffer, NULL, auhalHostApi, @@ -1567,7 +1856,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, stream->inputFramesPerBuffer = inputFramesPerBuffer; stream->outputFramesPerBuffer = outputFramesPerBuffer; } - + + inputLatencyFrames += stream->inputFramesPerBuffer; + outputLatencyFrames += stream->outputFramesPerBuffer; + if( stream->inputUnit ) { const size_t szfl = sizeof(float); /* setup the AudioBufferList used for input */ @@ -1593,7 +1885,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, * ring buffer to store inpt data while waiting for output * data. */ - if( (stream->outputUnit && stream->inputUnit != stream->outputUnit) + if( (stream->outputUnit && (stream->inputUnit != stream->outputUnit)) || stream->inputSRConverter ) { /* May want the ringSize ot initial position in @@ -1624,6 +1916,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, middle of the buffer */ if( stream->outputUnit ) PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer, ringSize / RING_BUFFER_ADVANCE_DENOMINATOR ); + + // Just adds to input latency between input device and PA full duplex callback. + inputLatencyFrames += ringSize; } } @@ -1646,6 +1941,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, outputParameters?outputChannelCount:0 ) ; if( result != paNoError ) goto error; + + inputLatencyFrames += ringSize; + outputLatencyFrames += ringSize; + } /* -- initialize Buffer Processor -- */ @@ -1660,7 +1959,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, hostOutputSampleFormat, sampleRate, streamFlags, - framesPerBuffer, + requestedFramesPerBuffer, /* If sample rate conversion takes place, the buffer size will not be known. */ maxHostFrames, @@ -1674,16 +1973,27 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } stream->bufferProcessorIsInitialized = TRUE; - /* - IMPLEMENT ME: initialise the following fields with estimated or actual - values. - I think this is okay the way it is br 12/1/05 - maybe need to change input latency estimate if IO devs differ - */ - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)/sampleRate; - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)/sampleRate; + // Calculate actual latency from the sum of individual latencies. + if( inputParameters ) + { + inputLatencyFrames += PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor); + stream->streamRepresentation.streamInfo.inputLatency = inputLatencyFrames / sampleRate; + } + else + { + stream->streamRepresentation.streamInfo.inputLatency = 0.0; + } + + if( outputParameters ) + { + outputLatencyFrames += PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor); + stream->streamRepresentation.streamInfo.outputLatency = outputLatencyFrames / sampleRate; + } + else + { + stream->streamRepresentation.streamInfo.outputLatency = 0.0; + } + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; stream->sampleRate = sampleRate; @@ -1716,39 +2026,27 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, stream->userInChan = inputChannelCount; stream->userOutChan = outputChannelCount; + // Setup property listeners for timestamp and latency calculations. pthread_mutex_init( &stream->timingInformationMutex, NULL ); stream->timingInformationMutexIsInitialized = 1; - - if( stream->outputUnit ) { - UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( stream ); - stream->recipricalOfActualOutputSampleRate_ioProcCopy = stream->recipricalOfActualOutputSampleRate; - - AudioDeviceAddPropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate, - AudioDevicePropertyActualSampleRateListenerProc, stream ); - - UpdateOutputLatencySamplesFromDeviceProperty( stream ); - stream->deviceOutputLatencySamples_ioProcCopy = stream->deviceOutputLatencySamples; - - AudioDeviceAddPropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyLatency, - AudioDevicePropertyOutputLatencySamplesListenerProc, stream ); - - }else{ - stream->recipricalOfActualOutputSampleRate = 1.; - stream->recipricalOfActualOutputSampleRate_ioProcCopy = 0.; - stream->deviceOutputLatencySamples_ioProcCopy = 0; + InitializeDeviceProperties( &stream->inputProperties ); + InitializeDeviceProperties( &stream->outputProperties ); + if( stream->outputUnit ) + { + Boolean isInput = FALSE; + SetupDevicePropertyListeners( stream, stream->outputDevice, isInput ); + } + if( stream->inputUnit ) + { + Boolean isInput = TRUE; + SetupDevicePropertyListeners( stream, stream->inputDevice, isInput ); } - - if( stream->inputUnit ) { - UpdateInputLatencySamplesFromDeviceProperty( stream ); - stream->deviceInputLatencySamples_ioProcCopy = stream->deviceInputLatencySamples; - - AudioDeviceAddPropertyListener( stream->inputDevice, 0, /* isInput = */ TRUE, kAudioDevicePropertyLatency, - AudioDevicePropertyInputLatencySamplesListenerProc, stream ); - }else{ - stream->deviceInputLatencySamples = 0; - stream->deviceInputLatencySamples_ioProcCopy = 0; - } - + UpdateTimeStampOffsets( stream ); + // Setup copies to be used by audio callback. + stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined; + stream->timestampOffsetInputDevice_ioProcCopy = stream->timestampOffsetInputDevice; + stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice; + stream->state = STOPPED; stream->xrunFlags = 0; @@ -1817,6 +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); VVDBUG(("AudioIOProc()\n")); @@ -1853,9 +2152,9 @@ static OSStatus AudioIOProc( void *inRefCon, if( pthread_mutex_trylock( &stream->timingInformationMutex ) == 0 ){ /* snapshot the ioproc copy of timing information */ - stream->deviceOutputLatencySamples_ioProcCopy = stream->deviceOutputLatencySamples; - stream->recipricalOfActualOutputSampleRate_ioProcCopy = stream->recipricalOfActualOutputSampleRate; - stream->deviceInputLatencySamples_ioProcCopy = stream->deviceInputLatencySamples; + stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined; + stream->timestampOffsetInputDevice_ioProcCopy = stream->timestampOffsetInputDevice; + stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice; pthread_mutex_unlock( &stream->timingInformationMutex ); } @@ -1882,32 +2181,30 @@ static OSStatus AudioIOProc( void *inRefCon, { if( stream->inputUnit == stream->outputUnit ) /* full duplex AUHAL IOProc */ { - /* FIXME: review. i'm not sure this computation of inputBufferAdcTime is correct for a full-duplex AUHAL */ - timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) - - stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here? - timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) - + stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; + // Ross and Phil agreed that the following calculation is correct based on an email from Jeff Moore: + // http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html + // Basically the difference between the Apple output timestamp and the PA timestamp is kAudioDevicePropertyLatency. + timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - + (stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy); + timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy; } else /* full duplex with ring-buffer from a separate input AUHAL ioproc */ { - /* FIXME: review. this computation of inputBufferAdcTime is definitely wrong since it doesn't take the ring buffer latency into account */ - timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) - - stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here? - timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) - + stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; + /* FIXME: take the ring buffer latency into account */ + timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - + (stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy); + timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy; } } else /* output only */ { timeInfo.inputBufferAdcTime = 0; - timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) - + stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; + timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy; } } else /* input only */ { - timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) - - stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here? + timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - stream->timestampOffsetInputDevice_ioProcCopy; timeInfo.outputBufferDacTime = 0; } @@ -2249,16 +2546,16 @@ static PaError CloseStream( PaStream* s ) if( stream ) { - if( stream->outputUnit ) { - AudioDeviceRemovePropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate, - AudioDevicePropertyActualSampleRateListenerProc ); - AudioDeviceRemovePropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyLatency, - AudioDevicePropertyOutputLatencySamplesListenerProc ); + if( stream->outputUnit ) + { + Boolean isInput = FALSE; + CleanupDevicePropertyListeners( stream, stream->outputDevice, isInput ); } - if( stream->inputUnit ) { - AudioDeviceRemovePropertyListener( stream->inputDevice, 0, /* isInput = */ TRUE, kAudioDevicePropertyLatency, - AudioDevicePropertyInputLatencySamplesListenerProc ); + if( stream->inputUnit ) + { + Boolean isInput = TRUE; + CleanupDevicePropertyListeners( stream, stream->inputDevice, isInput ); } if( stream->outputUnit ) { diff --git a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_blocking.c b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_blocking.c index 6d31a713d..5a98826b6 100644 --- a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_blocking.c +++ b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_blocking.c @@ -71,12 +71,12 @@ #endif /* - * This fnuction determines the size of a particular sample format. + * This function determines the size of a particular sample format. * if the format is not recognized, this returns zero. */ static size_t computeSampleSizeFromFormat( PaSampleFormat format ) { - switch( format ) { + switch( format & (~paNonInterleaved) ) { case paFloat32: return 4; case paInt32: return 4; case paInt24: return 3; @@ -91,7 +91,7 @@ static size_t computeSampleSizeFromFormat( PaSampleFormat format ) */ static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format ) { - switch( format ) { + switch( format & (~paNonInterleaved) ) { case paFloat32: return 4; case paInt32: return 4; case paInt24: return 4; diff --git a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_internal.h b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_internal.h index 9277321b3..462240bb4 100644 --- a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_internal.h +++ b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_internal.h @@ -113,7 +113,18 @@ typedef struct } PaMacAUHAL; - +typedef struct PaMacCoreDeviceProperties +{ + /* Values in Frames from property queries. */ + UInt32 safetyOffset; + UInt32 bufferFrameSize; + // UInt32 streamLatency; // Seems to be the same as deviceLatency!? + UInt32 deviceLatency; + /* Current device sample rate. May change! */ + Float64 sampleRate; + Float64 samplePeriod; // reciprocal +} +PaMacCoreDeviceProperties; /* stream data structure specifically for this implementation */ typedef struct PaMacCoreStream @@ -141,7 +152,7 @@ typedef struct PaMacCoreStream AudioBufferList inputAudioBufferList; AudioTimeStamp startTime; /* FIXME: instead of volatile, these should be properly memory barriered */ - volatile PaStreamCallbackFlags xrunFlags; + volatile uint32_t xrunFlags; /*PaStreamCallbackFlags*/ volatile enum { STOPPED = 0, /* playback is completely stopped, and the user has called StopStream(). */ @@ -159,17 +170,24 @@ typedef struct PaMacCoreStream double outDeviceSampleRate; double inDeviceSampleRate; + PaMacCoreDeviceProperties inputProperties; + PaMacCoreDeviceProperties outputProperties; + /* data updated by main thread and notifications, protected by timingInformationMutex */ int timingInformationMutexIsInitialized; pthread_mutex_t timingInformationMutex; - Float64 recipricalOfActualOutputSampleRate; - UInt32 deviceOutputLatencySamples; - UInt32 deviceInputLatencySamples; + + /* These are written by the PA thread or from CoreAudio callbacks. Protected by the mutex. */ + Float64 timestampOffsetCombined; + Float64 timestampOffsetInputDevice; + Float64 timestampOffsetOutputDevice; - /* while the io proc is active, the following values are only accessed and manipulated by the ioproc */ - Float64 recipricalOfActualOutputSampleRate_ioProcCopy; - UInt32 deviceOutputLatencySamples_ioProcCopy; - UInt32 deviceInputLatencySamples_ioProcCopy; + /* Offsets in seconds to be applied to Apple timestamps to convert them to PA timestamps. + * While the io proc is active, the following values are only accessed and manipulated by the ioproc */ + Float64 timestampOffsetCombined_ioProcCopy; + Float64 timestampOffsetInputDevice_ioProcCopy; + Float64 timestampOffsetOutputDevice_ioProcCopy; + } PaMacCoreStream; diff --git a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_utilities.c b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_utilities.c index 251bbe84c..63e616f05 100644 --- a/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_utilities.c +++ b/bazaar/plugin/portaudio/hostapi/coreaudio/pa_mac_core_utilities.c @@ -524,84 +524,60 @@ PaError setBestFramesPerBuffer( const AudioDeviceID device, UInt32 requestedFramesPerBuffer, UInt32 *actualFramesPerBuffer ) { - UInt32 afpb; - const bool isInput = !isOutput; - UInt32 propsize = sizeof(UInt32); - OSErr err; - Float64 min = -1; /*the min blocksize*/ - Float64 best = -1; /*the best blocksize*/ - int i=0; - AudioValueRange *ranges; + UInt32 afpb; + const bool isInput = !isOutput; + UInt32 propsize = sizeof(UInt32); + OSErr err; + AudioValueRange range; - if( actualFramesPerBuffer == NULL ) - actualFramesPerBuffer = &afpb; + if( actualFramesPerBuffer == NULL ) + { + actualFramesPerBuffer = &afpb; + } - - /* -- try and set exact FPB -- */ - err = AudioDeviceSetProperty( device, NULL, 0, isInput, + /* -- try and set exact FPB -- */ + err = AudioDeviceSetProperty( device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propsize, &requestedFramesPerBuffer); - err = AudioDeviceGetProperty( device, 0, isInput, + err = AudioDeviceGetProperty( device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propsize, actualFramesPerBuffer); - if( err ) + if( err ) + { + return ERR( err ); + } + // Did we get the size we asked for? + if( *actualFramesPerBuffer == requestedFramesPerBuffer ) + { + return paNoError; /* we are done */ + } + + // Clip requested value against legal range for the device. + propsize = sizeof(AudioValueRange); + err = AudioDeviceGetProperty( device, 0, isInput, + kAudioDevicePropertyBufferFrameSizeRange, + &propsize, &range ); + if( err ) + { return ERR( err ); - if( *actualFramesPerBuffer == requestedFramesPerBuffer ) - return paNoError; /* we are done */ - - /* -- fetch available block sizes -- */ - err = AudioDeviceGetPropertyInfo( device, 0, isInput, - kAudioDevicePropertyBufferSizeRange, - &propsize, NULL ); - if( err ) - return ERR( err ); - ranges = (AudioValueRange *)calloc( 1, propsize ); - if( !ranges ) - return paInsufficientMemory; - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferSizeRange, - &propsize, ranges ); - if( err ) - { - free( ranges ); - return ERR( err ); - } - VDBUG(("Requested block size of %lu was not available.\n", - requestedFramesPerBuffer )); - VDBUG(("%lu Available block sizes are:\n",propsize/sizeof(AudioValueRange))); -#ifdef MAC_CORE_VERBOSE_DEBUG - for( i=0; i best ) - best = ranges[i].mMaximum; - } - } - if( best == -1 ) - best = min; - VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) ); - free( ranges ); - + } + if( requestedFramesPerBuffer < range.mMinimum ) + { + requestedFramesPerBuffer = range.mMinimum; + } + else if( requestedFramesPerBuffer > range.mMaximum ) + { + requestedFramesPerBuffer = range.mMaximum; + } + /* --- set the buffer size (ignore errors) -- */ - requestedFramesPerBuffer = (UInt32) best ; - propsize = sizeof( UInt32 ); + propsize = sizeof( UInt32 ); err = AudioDeviceSetProperty( device, NULL, 0, isInput, - kAudioDevicePropertyBufferSize, + kAudioDevicePropertyBufferFrameSize, propsize, &requestedFramesPerBuffer ); /* --- read the property to check that it was set -- */ err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferSize, + kAudioDevicePropertyBufferFrameSize, &propsize, actualFramesPerBuffer ); if( err ) @@ -652,10 +628,10 @@ OSStatus xrunCallback( if( isInput ) { if( stream->inputDevice == inDevice ) - OSAtomicOr32( paInputOverflow, (uint32_t *)&(stream->xrunFlags) ); + OSAtomicOr32( paInputOverflow, &stream->xrunFlags ); } else { if( stream->outputDevice == inDevice ) - OSAtomicOr32( paOutputUnderflow, (uint32_t *)&(stream->xrunFlags) ); + OSAtomicOr32( paOutputUnderflow, &stream->xrunFlags ); } } diff --git a/bazaar/plugin/portaudio/hostapi/dsound/pa_win_ds.c b/bazaar/plugin/portaudio/hostapi/dsound/pa_win_ds.c index 913176071..b5e12b02a 100644 --- a/bazaar/plugin/portaudio/hostapi/dsound/pa_win_ds.c +++ b/bazaar/plugin/portaudio/hostapi/dsound/pa_win_ds.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_ds.c 1534 2010-08-03 21:02:52Z dmitrykos $ + * $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 @@ -39,45 +39,24 @@ /** @file @ingroup hostapi_src - - @todo implement paInputOverflow callback status flag - - @todo implement paNeverDropInput. - - @todo implement host api specific extension to set i/o buffer sizes in frames - - @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.) - - @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable - - @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into - a native portaudio error code. Standard DirectSound result codes are documented at msdn. - - @todo implement IsFormatSupported - - @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error"). - - @todo make sure all buffers have been played before stopping the stream - when the stream callback returns paComplete - - @todo retrieve default devices using the DRVM_MAPPER_PREFERRED_GET functions used in the wmme api - these wave device ids can be aligned with the directsound devices either by retrieving - the system interface device name using DRV_QUERYDEVICEINTERFACE or by using the wave device - id retrieved in KsPropertySetEnumerateCallback. - - old TODOs from phil, need to work out if these have been done: - O- fix "patest_stop.c" */ +/* Until May 2011 PA/DS has used a multimedia timer to perform the callback. + 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 #include #include #include /* strlen() */ +#define _WIN32_WINNT 0x0400 /* required to get waitable timer APIs */ #include /* make sure ds guids get defined */ #include #include + /* Use the earliest version of DX required, no need to polute the namespace */ @@ -90,6 +69,11 @@ #ifdef PAWIN_USE_WDMKS_DEVICE_INFO #include #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ +#ifndef PA_WIN_DS_USE_WMME_TIMER +#ifndef UNDER_CE +#include +#endif +#endif #include "pa_util.h" #include "pa_allocation.h" @@ -103,13 +87,44 @@ #include "pa_win_ds_dynlink.h" #include "pa_win_waveformat.h" #include "pa_win_wdmks_utils.h" - +#include "pa_win_coinitialize.h" #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ #pragma comment( lib, "dsound.lib" ) #pragma comment( lib, "winmm.lib" ) +#pragma comment( lib, "kernel32.lib" ) #endif +/* use CreateThread for CYGWIN, _beginthreadex for all others */ +#ifndef PA_WIN_DS_USE_WMME_TIMER + +#if !defined(__CYGWIN__) && !defined(UNDER_CE) +#define CREATE_THREAD (HANDLE)_beginthreadex +#undef CLOSE_THREAD_HANDLE /* as per documentation we don't call CloseHandle on a thread created with _beginthreadex */ +#define PA_THREAD_FUNC static unsigned WINAPI +#define PA_THREAD_ID unsigned +#else +#define CREATE_THREAD CreateThread +#define CLOSE_THREAD_HANDLE CloseHandle +#define PA_THREAD_FUNC static DWORD WINAPI +#define PA_THREAD_ID DWORD +#endif + +#if (defined(UNDER_CE)) +#pragma comment(lib, "Coredll.lib") +#elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ +#pragma comment(lib, "winmm.lib") +#endif + +PA_THREAD_FUNC ProcessingThreadProc( void *pArg ); + +#if !defined(UNDER_CE) +#define PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT /* use waitable timer where possible, otherwise we use a WaitForSingleObject timeout */ +#endif + +#endif /* !PA_WIN_DS_USE_WMME_TIMER */ + + /* provided in newer platform sdks and x64 */ @@ -128,17 +143,17 @@ #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 */ @@ -216,7 +231,7 @@ typedef struct /* implementation specific data goes here */ - char comWasInitialized; + PaWinUtilComInitializationResult comInitializationResult; } PaWinDsHostApiRepresentation; @@ -240,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; @@ -252,15 +267,15 @@ typedef struct PaWinDsStream /* Input */ LPDIRECTSOUNDCAPTURE pDirectSoundCapture; LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer; - INT bytesPerInputFrame; + INT inputFrameSizeBytes; UINT readOffset; /* last read position */ - UINT inputSize; + UINT inputBufferSizeBytes; - MMRESULT timerID; - 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; @@ -273,9 +288,95 @@ typedef struct PaWinDsStream volatile int isActive; volatile int stopProcessing; /* stop thread once existing buffers have been returned */ volatile int abortProcessing; /* stop thread immediately */ + + UINT systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */ + +#ifdef PA_WIN_DS_USE_WMME_TIMER + MMRESULT timerID; +#else + +#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT + HANDLE waitableTimer; +#endif + HANDLE processingThread; + PA_THREAD_ID processingThreadId; + HANDLE processingThreadCompleted; +#endif + } 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. @@ -454,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 ) @@ -706,7 +808,7 @@ static PaError AddOutputDeviceInfoFromDirectSound( else { -#ifndef PA_NO_WMME +#if PA_USE_WMME if( caps.dwFlags & DSCAPS_EMULDRIVER ) { /* If WMME supported, then reject Emulated drivers because they are lousy. */ @@ -788,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 ) @@ -819,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 )); } @@ -834,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; } } @@ -922,7 +1024,7 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture( } else { -#ifndef PA_NO_WMME +#if PA_USE_WMME if( caps.dwFlags & DSCAPS_EMULDRIVER ) { /* If WMME supported, then reject Emulated drivers because they are lousy. */ @@ -950,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 @@ -996,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 ) { @@ -1011,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.; } } @@ -1041,32 +1144,15 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde int i, deviceCount; PaWinDsHostApiRepresentation *winDsHostApi; DSDeviceNamesAndGUIDs deviceNamesAndGUIDs; - PaWinDsDeviceInfo *deviceInfoArray; - char comWasInitialized = 0; - /* - If COM is already initialized CoInitialize will either return - FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different - threading mode. In either case we shouldn't consider it an error - but we need to be careful to not call CoUninitialize() if - RPC_E_CHANGED_MODE was returned. - */ - - HRESULT hr = CoInitialize(NULL); - if( FAILED(hr) && hr != RPC_E_CHANGED_MODE ) - return paUnanticipatedHostError; - - if( hr != RPC_E_CHANGED_MODE ) - comWasInitialized = 1; + PaWinDs_InitializeDSoundEntryPoints(); /* initialise guid vectors so they can be safely deleted on error */ deviceNamesAndGUIDs.winDsHostApi = NULL; deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL; deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL; - PaWinDs_InitializeDSoundEntryPoints(); - winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) ); if( !winDsHostApi ) { @@ -1074,7 +1160,11 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde goto error; } - winDsHostApi->comWasInitialized = comWasInitialized; + result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult ); + if( result != paNoError ) + { + goto error; + } winDsHostApi->allocations = PaUtil_CreateAllocationGroup(); if( !winDsHostApi->allocations ) @@ -1206,22 +1296,10 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde return result; error: - if( winDsHostApi ) - { - if( winDsHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winDsHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); - } - - PaUtil_FreeMemory( winDsHostApi ); - } - TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs ); TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs ); - if( comWasInitialized ) - CoUninitialize(); + Terminate( winDsHostApi ); return result; } @@ -1231,57 +1309,20 @@ error: static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) { PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; - char comWasInitialized = winDsHostApi->comWasInitialized; - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - */ + if( winDsHostApi ){ + if( winDsHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winDsHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); + } - if( winDsHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winDsHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); + PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult ); + + PaUtil_FreeMemory( winDsHostApi ); } - PaUtil_FreeMemory( winDsHostApi ); - PaWinDs_TerminateDSoundEntryPoints(); - - if( comWasInitialized ) - CoUninitialize(); -} - - -/* 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( @@ -1291,7 +1332,7 @@ static PaError ValidateWinDirectSoundSpecificStreamInfo( if( streamInfo ) { if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo ) - || streamInfo->version != 1 ) + || streamInfo->version != 2 ) { return paIncompatibleHostApiSpecificStreamInfo; } @@ -1396,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, @@ -1670,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 */ @@ -1696,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 ) { @@ -1835,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 ); @@ -1849,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 ); @@ -1863,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 ); @@ -1872,23 +1988,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, bufferProcessorIsInitialized = 1; - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ - 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); - - stream->timerID = 0; - + stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL ); if( stream->processingCompleted == NULL ) { @@ -1896,42 +2001,40 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, goto error; } - /* Get system minimum latency. */ - minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate ); +#ifdef PA_WIN_DS_USE_WMME_TIMER + stream->timerID = 0; +#endif - /* 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 ) +#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT + stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL ); + if( stream->waitableTimer == NULL ) { - /* App support variable framesPerBuffer */ - stream->framesPerDSBuffer = minLatencyFrames; - - stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate; + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() ); + goto error; } - else +#endif + +#ifndef PA_WIN_DS_USE_WMME_TIMER + stream->processingThreadCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL ); + if( stream->processingThreadCompleted == NULL ) { - /* 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 )); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() ); + goto error; } +#endif /* set up i/o parameters */ + CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames, + /* isFullDuplex = */ (inputParameters && outputParameters), + suggestedInputLatencyFrames, + suggestedOutputLatencyFrames, + sampleRate, framesPerBuffer ); + + stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate; + /* ------------------ OUTPUT */ if( outputParameters ) { @@ -1942,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; @@ -1960,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 { @@ -1984,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 */ @@ -2021,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 ); @@ -2047,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 ) @@ -2064,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 ) @@ -2077,6 +2178,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, } } + SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate ); + + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + *s = (PaStream*)stream; return result; @@ -2087,6 +2192,16 @@ error: if( stream->processingCompleted != NULL ) CloseHandle( stream->processingCompleted ); +#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT + if( stream->waitableTimer != NULL ) + CloseHandle( stream->waitableTimer ); +#endif + +#ifndef PA_WIN_DS_USE_WMME_TIMER + if( stream->processingThreadCompleted != NULL ) + CloseHandle( stream->processingThreadCompleted ); +#endif + if( stream->pDirectSoundOutputBuffer ) { IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer ); @@ -2227,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; @@ -2260,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 */ @@ -2276,21 +2391,21 @@ 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; } if( framesToXfer > 0 ) { - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); /* The outputBufferDacTime parameter should indicates the time at which @@ -2305,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, @@ -2319,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 ); } @@ -2334,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, @@ -2348,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 ); } @@ -2369,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); } @@ -2380,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: @@ -2495,6 +2610,74 @@ static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD } } +#ifndef PA_WIN_DS_USE_WMME_TIMER + +#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT + +static void CALLBACK WaitableTimerAPCProc( + LPVOID lpArg, // Data value + DWORD dwTimerLowValue, // Timer low value + DWORD dwTimerHighValue ) // Timer high value + +{ + (void)dwTimerLowValue; + (void)dwTimerHighValue; + + TimerCallback( 0, 0, (DWORD_PTR)lpArg, 0, 0 ); +} + +#endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */ + + +PA_THREAD_FUNC ProcessingThreadProc( void *pArg ) +{ + PaWinDsStream *stream = (PaWinDsStream *)pArg; + MMRESULT mmResult; + HANDLE hWaitableTimer; + LARGE_INTEGER dueTime; + int timerPeriodMs; + + 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 = timerPeriodMs * 1000 * 10; + dueTime.HighPart = 0; + + /* tick using waitable timer */ + 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, timerPeriodMs * 10, /* alertable = */ TRUE ); + + }while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION ); + } + + CancelWaitableTimer( stream->waitableTimer ); + +#else + + /* tick using WaitForSingleObject timout */ + while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT ) + { + TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 ); + } +#endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */ + + SetEvent( stream->processingThreadCompleted ); + + return 0; +} + +#endif /* !PA_WIN_DS_USE_WMME_TIMER */ + /*********************************************************************************** When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted. @@ -2506,6 +2689,15 @@ static PaError CloseStream( PaStream* s ) CloseHandle( stream->processingCompleted ); +#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT + if( stream->waitableTimer != NULL ) + CloseHandle( stream->waitableTimer ); +#endif + +#ifndef PA_WIN_DS_USE_WMME_TIMER + CloseHandle( stream->processingThreadCompleted ); +#endif + // Cleanup the sound buffers if( stream->pDirectSoundOutputBuffer ) { @@ -2600,6 +2792,10 @@ static PaError StartStream( PaStream *s ) ResetEvent( stream->processingCompleted ); +#ifndef PA_WIN_DS_USE_WMME_TIMER + ResetEvent( stream->processingThreadCompleted ); +#endif + if( stream->bufferProcessor.inputChannelCount > 0 ) { // Start the buffer capture @@ -2622,7 +2818,6 @@ static PaError StartStream( PaStream *s ) stream->abortProcessing = 0; stream->stopProcessing = 0; - stream->isActive = 1; if( stream->bufferProcessor.outputChannelCount > 0 ) { @@ -2665,28 +2860,90 @@ static PaError StartStream( PaStream *s ) if( stream->streamRepresentation.streamCallback ) { + TIMECAPS timecaps; + 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 + timeBeginPeriod() affects the scheduling granulatity of all timers + including Waitable Timer Objects. So we always call timeBeginPeriod, whether + we're using an MM timer callback via timeSetEvent or not. + */ + assert( stream->systemTimerResolutionPeriodMs == 0 ); + if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 ) ) + { + /* 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 */ + } + + +#ifdef PA_WIN_DS_USE_WMME_TIMER /* Create timer that will wake us up so we can fill the DSound buffer. */ - int resolution; - 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; - resolution = msecPerWakeup/4; - stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) TimerCallback, + /* We have deprecated timeSetEvent because all MM timer callbacks + 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( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback, (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS ); if( stream->timerID == 0 ) { stream->isActive = 0; result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() ); goto error; } +#else + /* Create processing thread which calls TimerCallback */ + + stream->processingThread = CREATE_THREAD( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ); + if( !stream->processingThread ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() ); + goto error; + } + + if( !SetThreadPriority( stream->processingThread, THREAD_PRIORITY_TIME_CRITICAL ) ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() ); + goto error; + } +#endif } - stream->isStarted = TRUE; + stream->isActive = 1; + stream->isStarted = 1; + + assert( result == paNoError ); + return result; error: + + if( stream->pDirectSoundOutputBuffer != NULL && stream->outputIsRunning ) + IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer ); + stream->outputIsRunning = FALSE; + +#ifndef PA_WIN_DS_USE_WMME_TIMER + if( stream->processingThread ) + { +#ifdef CLOSE_THREAD_HANDLE + CLOSE_THREAD_HANDLE( stream->processingThread ); /* Delete thread. */ +#endif + stream->processingThread = NULL; + } +#endif + return result; } @@ -2704,17 +2961,35 @@ 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 ); } +#ifdef PA_WIN_DS_USE_WMME_TIMER if( stream->timerID != 0 ) { timeKillEvent(stream->timerID); /* Stop callback timer. */ stream->timerID = 0; } +#else + if( stream->processingThread ) + { + if( WaitForSingleObject( stream->processingThreadCompleted, 30*100 ) == WAIT_TIMEOUT ) + return paUnanticipatedHostError; +#ifdef CLOSE_THREAD_HANDLE + CloseHandle( stream->processingThread ); /* Delete thread. */ + stream->processingThread = NULL; +#endif + + } +#endif + + if( stream->systemTimerResolutionPeriodMs > 0 ){ + timeEndPeriod( stream->systemTimerResolutionPeriodMs ); + stream->systemTimerResolutionPeriodMs = 0; + } if( stream->bufferProcessor.outputChannelCount > 0 ) { @@ -2740,7 +3015,7 @@ static PaError StopStream( PaStream *s ) } } - stream->isStarted = FALSE; + stream->isStarted = 0; return result; } @@ -2860,5 +3135,3 @@ static signed long GetStreamWriteAvailable( PaStream* s ) return 0; } - - diff --git a/bazaar/plugin/portaudio/hostapi/jack/pa_jack.c b/bazaar/plugin/portaudio/hostapi/jack/pa_jack.c index 8c23b4037..25b7fd2d7 100644 --- a/bazaar/plugin/portaudio/hostapi/jack/pa_jack.c +++ b/bazaar/plugin/portaudio/hostapi/jack/pa_jack.c @@ -1,5 +1,5 @@ /* - * $Id: pa_jack.c 1541 2010-09-22 06:33:47Z dmitrykos $ + * $Id: pa_jack.c 1668 2011-05-02 17:07:11Z rossb $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * JACK Implementation by Joshua Haberman @@ -1296,11 +1296,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if( stream->num_incoming_connections > 0 ) stream->streamRepresentation.streamInfo.inputLatency = (jack_port_get_latency( stream->remote_output_ports[0] ) - jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */ - + PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor )) / sampleRate; + + PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor )) / sampleRate; if( stream->num_outgoing_connections > 0 ) stream->streamRepresentation.streamInfo.outputLatency = (jack_port_get_latency( stream->remote_input_ports[0] ) - jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */ - + PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor )) / sampleRate; + + PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor )) / sampleRate; stream->streamRepresentation.streamInfo.sampleRate = jackSr; stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */ diff --git a/bazaar/plugin/portaudio/hostapi/oss/pa_unix_oss.c b/bazaar/plugin/portaudio/hostapi/oss/pa_unix_oss.c index 9bc972bf8..b91577bee 100644 --- a/bazaar/plugin/portaudio/hostapi/oss/pa_unix_oss.c +++ b/bazaar/plugin/portaudio/hostapi/oss/pa_unix_oss.c @@ -1,5 +1,5 @@ /* - * $Id: pa_unix_oss.c 1509 2010-06-06 17:36:33Z dmitrykos $ + * $Id: pa_unix_oss.c 1668 2011-05-02 17:07:11Z rossb $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * OSS implementation by: @@ -1121,7 +1121,7 @@ static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, un assert( component->hostChannelCount > 0 ); assert( component->hostFrames > 0 ); - *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; + *inputLatency = (component->hostFrames * (component->numBufs - 1)) / sampleRate; } if( stream->playback ) { @@ -1132,7 +1132,7 @@ static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, un assert( component->hostChannelCount > 0 ); assert( component->hostFrames > 0 ); - *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; + *outputLatency = (component->hostFrames * (component->numBufs - 1)) / sampleRate; } if( duplex ) @@ -1241,13 +1241,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, { inputHostFormat = stream->capture->hostFormat; stream->streamRepresentation.streamInfo.inputLatency = inLatency + - PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate; + PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) / sampleRate; } if( outputParameters ) { outputHostFormat = stream->playback->hostFormat; stream->streamRepresentation.streamInfo.outputLatency = outLatency + - PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate; + PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) / sampleRate; } /* Initialize buffer processor with fixed host buffer size. diff --git a/bazaar/plugin/portaudio/hostapi/skeleton/README.txt b/bazaar/plugin/portaudio/hostapi/skeleton/README.txt new file mode 100644 index 000000000..39d4c8d2b --- /dev/null +++ b/bazaar/plugin/portaudio/hostapi/skeleton/README.txt @@ -0,0 +1 @@ +pa_hostapi_skeleton.c provides a starting point for implementing support for a new host API with PortAudio. The idea is that you copy it to a new directory inside /hostapi and start editing. \ No newline at end of file diff --git a/bazaar/plugin/portaudio/common/pa_skeleton.c b/bazaar/plugin/portaudio/hostapi/skeleton/pa_hostapi_skeleton.c similarity index 98% rename from bazaar/plugin/portaudio/common/pa_skeleton.c rename to bazaar/plugin/portaudio/hostapi/skeleton/pa_hostapi_skeleton.c index d5cb52d82..24eb45009 100644 --- a/bazaar/plugin/portaudio/common/pa_skeleton.c +++ b/bazaar/plugin/portaudio/hostapi/skeleton/pa_hostapi_skeleton.c @@ -1,5 +1,5 @@ /* - * $Id: pa_skeleton.c 1339 2008-02-15 07:50:33Z rossb $ + * $Id: pa_hostapi_skeleton.c 1668 2011-05-02 17:07:11Z rossb $ * Portable Audio I/O Library skeleton implementation * demonstrates how to use the common functions to implement support * for a host API @@ -518,9 +518,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, values. */ stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); + (PaTime)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* inputLatency is specified in _seconds_ */ stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); + (PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* outputLatency is specified in _seconds_ */ stream->streamRepresentation.streamInfo.sampleRate = sampleRate; diff --git a/bazaar/plugin/portaudio/hostapi/wasapi/pa_win_wasapi.c b/bazaar/plugin/portaudio/hostapi/wasapi/pa_win_wasapi.c index 48778e837..d38243239 100644 --- a/bazaar/plugin/portaudio/hostapi/wasapi/pa_win_wasapi.c +++ b/bazaar/plugin/portaudio/hostapi/wasapi/pa_win_wasapi.c @@ -71,8 +71,11 @@ #include "pa_stream.h" #include "pa_cpuload.h" #include "pa_process.h" -#include "pa_debugprint.h" #include "pa_win_wasapi.h" +#include "pa_debugprint.h" +#include "pa_ringbuffer.h" + +#include "pa_win_coinitialize.h" #ifndef NTDDI_VERSION @@ -222,6 +225,11 @@ PA_THREAD_FUNC ProcThreadPoll(void *param); enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 }; +// Number of packets which compose single contignous buffer. With trial and error it was calculated +// that WASAPI Input sub-system uses 6 packets per whole buffer. Please provide more information +// or corrections if available. +enum { WASAPI_PACKETS_PER_INPUT_BUFFER = 6 }; + #define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) #define PRINT(x) PA_DEBUG(x); @@ -229,12 +237,16 @@ enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 }; #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText ) -#define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.client && (STREAM)->out.client) +#define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.clientProc && (STREAM)->out.clientProc) #ifndef IF_FAILED_JUMP #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label; #endif +#ifndef IF_FAILED_INTERNAL_ERROR_JUMP +#define IF_FAILED_INTERNAL_ERROR_JUMP(hr, error, label) if(FAILED(hr)) { error = paInternalError; goto label; } +#endif + #define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; } #define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; } @@ -353,13 +365,15 @@ PaWasapiDeviceInfo; typedef struct { PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; - PaUtilAllocationGroup *allocations; + PaUtilAllocationGroup *allocations; /* implementation specific data goes here */ + PaWinUtilComInitializationResult comInitializationResult; + //in case we later need the synch IMMDeviceEnumerator *enumerator; @@ -376,35 +390,53 @@ typedef struct } PaWasapiHostApiRepresentation; +// ------------------------------------------------------------------------------------------ +/* PaWasapiAudioClientParams - audio client parameters */ +typedef struct PaWasapiAudioClientParams +{ + PaWasapiDeviceInfo *device_info; + PaStreamParameters stream_params; + PaWasapiStreamInfo wasapi_params; + UINT32 frames_per_buffer; + double sample_rate; + BOOL blocking; + BOOL full_duplex; + BOOL wow64_workaround; +} +PaWasapiAudioClientParams; + // ------------------------------------------------------------------------------------------ /* PaWasapiStream - a stream data structure specifically for this implementation */ typedef struct PaWasapiSubStream { - IAudioClient *client; + IAudioClient *clientParent; + IStream *clientStream; + IAudioClient *clientProc; + WAVEFORMATEXTENSIBLE wavex; UINT32 bufferSize; - REFERENCE_TIME device_latency; + REFERENCE_TIME deviceLatency; REFERENCE_TIME period; - double latency_seconds; + double latencySeconds; UINT32 framesPerHostCallback; AUDCLNT_SHAREMODE shareMode; UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ... UINT32 flags; + PaWasapiAudioClientParams params; //!< parameters // Buffers UINT32 buffers; //!< number of buffers used (from host side) UINT32 framesPerBuffer; //!< number of frames per 1 buffer BOOL userBufferAndHostMatch; - // Used by blocking interface: - UINT32 prevTime; // time ms between calls of WriteStream - UINT32 prevSleep; // time ms to sleep from frames written in previous call - // Used for Mono >> Stereo workaround, if driver does not support it // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch) void *monoBuffer; //!< pointer to buffer UINT32 monoBufferSize; //!< buffer size in bytes MixMonoToStereoF monoMixer; //!< pointer to mixer function + + PaUtilRingBuffer *tailBuffer; //!< buffer with trailing sample for blocking mode operations (only for Input) + void *tailBufferMemory; //!< tail buffer memory region } PaWasapiSubStream; @@ -422,18 +454,22 @@ typedef struct PaWasapiStream { /* IMPLEMENT ME: rename this */ PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; // input - PaWasapiSubStream in; - IAudioCaptureClient *cclient; - IAudioEndpointVolume *inVol; + PaWasapiSubStream in; + IAudioCaptureClient *captureClientParent; + IStream *captureClientStream; + IAudioCaptureClient *captureClient; + IAudioEndpointVolume *inVol; // output - PaWasapiSubStream out; - IAudioRenderClient *rclient; - IAudioEndpointVolume *outVol; + PaWasapiSubStream out; + IAudioRenderClient *renderClientParent; + IStream *renderClientStream; + IAudioRenderClient *renderClient; + IAudioEndpointVolume *outVol; // event handles for event-driven processing mode HANDLE event[S_COUNT]; @@ -471,12 +507,15 @@ typedef struct PaWasapiStream PaWasapiStream; // Local stream methods -static void _OnStreamStop(PaWasapiStream *stream); -static void _FinishStream(PaWasapiStream *stream); +void _StreamOnStop(PaWasapiStream *stream); +void _StreamFinish(PaWasapiStream *stream); +void _StreamCleanup(PaWasapiStream *stream); +HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available); +HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available); +void *PaWasapi_ReallocateMemory(void *ptr, size_t size); +void PaWasapi_FreeMemory(void *ptr); // Local statics -static volatile BOOL g_WasapiCOMInit = FALSE; -static volatile DWORD g_WasapiInitThread = 0; // ------------------------------------------------------------------------------------------ #define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__) @@ -540,6 +579,46 @@ static PaError __LogPaError(PaError err, const char *func, const char *file, int return err; } +// ------------------------------------------------------------------------------------------ +/*! \class ThreadSleepScheduler + Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler + calculates number of times the thread must run untill next sleep of 1 millisecond. + It does not make thread sleeping for real number of microseconds but rather controls + how many of imaginary microseconds the thread task can allow thread to sleep. +*/ +typedef struct ThreadIdleScheduler +{ + UINT32 m_idle_microseconds; //!< number of microseconds to sleep + UINT32 m_next_sleep; //!< next sleep round + UINT32 m_i; //!< current round iterator position + UINT32 m_resolution; //!< resolution in number of milliseconds +} +ThreadIdleScheduler; +//! Setup scheduler. +static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds) +{ + assert(microseconds != 0); + assert(resolution != 0); + assert((resolution * 1000) >= microseconds); + + memset(sched, 0, sizeof(*sched)); + + sched->m_idle_microseconds = microseconds; + sched->m_resolution = resolution; + sched->m_next_sleep = (resolution * 1000) / microseconds; +} +//! Iterate and check if can sleep. +static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched) +{ + // advance and check if thread can sleep + if (++ sched->m_i == sched->m_next_sleep) + { + sched->m_i = 0; + return sched->m_resolution; + } + return 0; +} + // ------------------------------------------------------------------------------------------ /*static double nano100ToMillis(REFERENCE_TIME ref) { @@ -640,6 +719,16 @@ static UINT32 ALIGN_FWD(UINT32 v, UINT32 align) return v + (align - remainder); } +// ------------------------------------------------------------------------------------------ +// Get next value power of 2 +UINT32 ALIGN_NEXT_POW2(UINT32 v) +{ + UINT32 v2 = 1; + while (v > (v2 <<= 1)) { } + v = v2; + return v; +} + // ------------------------------------------------------------------------------------------ // Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer // is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED @@ -833,7 +922,7 @@ static BOOL UseWOW64Workaround() } // ------------------------------------------------------------------------------------------ -typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir; +typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerDir; // ------------------------------------------------------------------------------------------ #define _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(TYPE)\ @@ -848,7 +937,7 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir; } // ------------------------------------------------------------------------------------------ -#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(TYPE)\ +#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(TYPE)\ TYPE * __restrict to = (TYPE *)__to;\ TYPE * __restrict from = (TYPE *)__from;\ TYPE * __restrict end = to + count;\ @@ -858,6 +947,39 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir; from += 2;\ } +// ------------------------------------------------------------------------------------------ +#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(TYPE)\ + TYPE * __restrict to = (TYPE *)__to;\ + TYPE * __restrict from = (TYPE *)__from;\ + TYPE * __restrict end = to + count;\ + while (to != end)\ + {\ + *to ++ = (TYPE)(((INT32)from[0] + (INT32)from[1]) >> 1);\ + from += 2;\ + } + +// ------------------------------------------------------------------------------------------ +#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(TYPE)\ + TYPE * __restrict to = (TYPE *)__to;\ + TYPE * __restrict from = (TYPE *)__from;\ + TYPE * __restrict end = to + count;\ + while (to != end)\ + {\ + *to ++ = (TYPE)(((INT64)from[0] + (INT64)from[1]) >> 1);\ + from += 2;\ + } + +// ------------------------------------------------------------------------------------------ +#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(TYPE)\ + TYPE * __restrict to = (TYPE *)__to;\ + TYPE * __restrict from = (TYPE *)__from;\ + TYPE * __restrict end = to + count;\ + while (to != end)\ + {\ + *to ++ = from[0];\ + from += 2;\ + } + // ------------------------------------------------------------------------------------------ static void _MixMonoToStereo_1TO2_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); } static void _MixMonoToStereo_1TO2_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); } @@ -866,11 +988,18 @@ static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _ static void _MixMonoToStereo_1TO2_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); } // ------------------------------------------------------------------------------------------ -static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(BYTE); } -static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(short); } -static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(int); /* !!! int24 data is contained in 32-bit containers*/ } -static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(int); } -static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(float); } +static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); } +static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); } +static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ } +static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); } +static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); } + +// ------------------------------------------------------------------------------------------ +static void _MixMonoToStereo_2TO1_8_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); } +static void _MixMonoToStereo_2TO1_16_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(short); } +static void _MixMonoToStereo_2TO1_24_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); /* !!! int24 data is contained in 32-bit containers*/ } +static void _MixMonoToStereo_2TO1_32_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); } +static void _MixMonoToStereo_2TO1_32f_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(float); } // ------------------------------------------------------------------------------------------ static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir dir) @@ -898,6 +1027,17 @@ static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir d case paFloat32: return _MixMonoToStereo_2TO1_32f; } break; + + case MIX_DIR__2TO1_L: + switch (format & ~paNonInterleaved) + { + case paUInt8: return _MixMonoToStereo_2TO1_8_L; + case paInt16: return _MixMonoToStereo_2TO1_16_L; + case paInt24: return _MixMonoToStereo_2TO1_24_L; + case paInt32: return _MixMonoToStereo_2TO1_32_L; + case paFloat32: return _MixMonoToStereo_2TO1_32f_L; + } + break; } return NULL; @@ -919,26 +1059,6 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd return paNoError; } - /* - If COM is already initialized CoInitialize will either return - FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different - threading mode. In either case we shouldn't consider it an error - but we need to be careful to not call CoUninitialize() if - RPC_E_CHANGED_MODE was returned. - */ - hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); - if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE)) - { - PRINT(("WASAPI: failed CoInitialize")); - return paUnanticipatedHostError; - } - if (hr != RPC_E_CHANGED_MODE) - g_WasapiCOMInit = TRUE; - - // Memorize calling thread id and report warning on Uninitialize if calling thread is different - // as CoInitialize must match CoUninitialize in the same thread. - g_WasapiInitThread = GetCurrentThreadId(); - paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) ); if (paWasapi == NULL) { @@ -946,6 +1066,12 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd goto error; } + result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult ); + if( result != paNoError ) + { + goto error; + } + paWasapi->allocations = PaUtil_CreateAllocationGroup(); if (paWasapi->allocations == NULL) { @@ -964,7 +1090,10 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd paWasapi->enumerator = NULL; hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator); - IF_FAILED_JUMP(hr, error); + + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); // getting default device ids in the eMultimedia "role" { @@ -973,14 +1102,19 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer); if (hr != S_OK) { - if (hr != E_NOTFOUND) - IF_FAILED_JUMP(hr, error); + if (hr != E_NOTFOUND) { + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); + } } else { WCHAR *pszDeviceId = NULL; hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1); CoTaskMemFree(pszDeviceId); IMMDevice_Release(defaultRenderer); @@ -992,14 +1126,19 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer); if (hr != S_OK) { - if (hr != E_NOTFOUND) - IF_FAILED_JUMP(hr, error); + if (hr != E_NOTFOUND) { + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); + } } else { WCHAR *pszDeviceId = NULL; hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1); CoTaskMemFree(pszDeviceId); IMMDevice_Release(defaultCapturer); @@ -1008,12 +1147,16 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd } hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); - paWasapi->devInfo = (PaWasapiDeviceInfo *)malloc(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount); + paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_AllocateMemory(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount); for (i = 0; i < paWasapi->deviceCount; ++i) memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo)); @@ -1047,13 +1190,17 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd PA_DEBUG(("WASAPI: ---------------\n")); hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); // getting ID { WCHAR *pszDeviceId = NULL; hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hr, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1); CoTaskMemFree(pszDeviceId); @@ -1068,7 +1215,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd } hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE) { @@ -1078,7 +1227,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd { IPropertyStore *pProperty; hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); // "Friendly" Name { @@ -1086,7 +1237,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd PROPVARIANT value; PropVariantInit(&value); hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); deviceInfo->name = NULL; deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1); if (deviceName == NULL) @@ -1108,7 +1261,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd PROPVARIANT value; PropVariantInit(&value); hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize)); // cleanup PropVariantClear(&value); @@ -1119,7 +1274,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd PROPVARIANT value; PropVariantInit(&value); hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); // set #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION) // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1))); @@ -1156,12 +1313,16 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd hr = IMMDevice_Activate(paWasapi->devInfo[i].device, &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); hr = IAudioClient_GetDevicePeriod(tmpClient, &paWasapi->devInfo[i].DefaultDevicePeriod, &paWasapi->devInfo[i].MinimumDevicePeriod); - IF_FAILED_JUMP(hr, error); + // We need to set the result to a value otherwise we will return paNoError + // [IF_FAILED_JUMP(hResult, error);] + IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); //hr = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat); @@ -1174,6 +1335,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f} //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat LogHostError(hr); + // We need to set the result to a value otherwise we will return paNoError + result = paInternalError; goto error; } } @@ -1200,6 +1363,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd break; } default: PRINT(("WASAPI:%d| bad Data Flow!\n", i)); + // We need to set the result to a value otherwise we will return paNoError + result = paInternalError; //continue; // do not skip from list, allow to initialize break; } @@ -1242,6 +1407,11 @@ error: Terminate((PaUtilHostApiRepresentation *)paWasapi); + // Safety if error was not set so that we do not think initialize was a success + if (result == paNoError) { + result = paInternalError; + } + return result; } @@ -1264,7 +1434,7 @@ static void Terminate( PaUtilHostApiRepresentation *hostApi ) //if (info->MixFormat) // CoTaskMemFree(info->MixFormat); } - free(paWasapi->devInfo); + PaUtil_FreeMemory(paWasapi->devInfo); if (paWasapi->allocations) { @@ -1272,26 +1442,12 @@ static void Terminate( PaUtilHostApiRepresentation *hostApi ) PaUtil_DestroyAllocationGroup(paWasapi->allocations); } + PaWinUtil_CoUninitialize( paWASAPI, &paWasapi->comInitializationResult ); + PaUtil_FreeMemory(paWasapi); // Close AVRT CloseAVRT(); - - // Uninit COM (checking calling thread we won't unitialize user's COM if one is calling - // Pa_Unitialize by mistake from not initializing thread) - if (g_WasapiCOMInit) - { - DWORD calling_thread_id = GetCurrentThreadId(); - if (g_WasapiInitThread != calling_thread_id) - { - PRINT(("WASAPI: failed CoUninitializes calling thread[%d] does not match initializing thread[%d]\n", - calling_thread_id, g_WasapiInitThread)); - } - else - { - CoUninitialize(); - } - } } // ------------------------------------------------------------------------------------------ @@ -1627,8 +1783,8 @@ static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate, to 24-bit for user-space. The bug concerns Vista, if Windows 7 supports 24-bits for Input please report to PortAudio developers to exclude Windows 7. */ - if ((params.sampleFormat == paInt24) && (output == FALSE)) - params.sampleFormat = paFloat32; + /*if ((params.sampleFormat == paInt24) && (output == FALSE)) + params.sampleFormat = paFloat32;*/ // <<< The silence was due to missing Int32_To_Int24_Dither implementation MakeWaveFormatFromParams(outWavex, ¶ms, sampleRate); @@ -1955,12 +2111,18 @@ static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerL } // ------------------------------------------------------------------------------------------ -static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, - PaWasapiDeviceInfo *pInfo, const PaStreamParameters *params, UINT32 framesPerLatency, - double sampleRate, BOOL blocking, BOOL output, BOOL fullDuplex, PaError *pa_error) +static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, BOOL output, PaError *pa_error) { PaError error; HRESULT hr; + + const PaWasapiDeviceInfo *pInfo = pSub->params.device_info; + const PaStreamParameters *params = &pSub->params.stream_params; + UINT32 framesPerLatency = pSub->params.frames_per_buffer; + double sampleRate = pSub->params.sample_rate; + BOOL blocking = pSub->params.blocking; + BOOL fullDuplex = pSub->params.full_duplex; + const UINT32 userFramesPerBuffer = framesPerLatency; IAudioClient *audioClient = NULL; @@ -1991,14 +2153,14 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu // Check for Mono <<>> Stereo workaround if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2)) { - if (blocking) + /*if (blocking) { LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); goto done; // fail, blocking mode not supported - } + }*/ // select mixer - pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1)); + pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); if (pSub->monoMixer == NULL) { LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); @@ -2049,6 +2211,13 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu if (framesPerLatency == 0) framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec); + //! Exclusive Input stream renders data in 6 packets, we must set then the size of + //! single packet, total buffer size, e.g. required latency will be PacketSize * 6 + if (!output && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)) + { + framesPerLatency /= WASAPI_PACKETS_PER_INPUT_BUFFER; + } + // Calculate aligned period _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD); @@ -2230,14 +2399,14 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu // Check for Mono >> Stereo workaround if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2)) { - if (blocking) + /*if (blocking) { LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); goto done; // fail, blocking mode not supported - } + }*/ // Select mixer - pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1)); + pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L)); if (pSub->monoMixer == NULL) { LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); @@ -2270,8 +2439,8 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu } // Set client - pSub->client = audioClient; - IAudioClient_AddRef(pSub->client); + pSub->clientParent = audioClient; + IAudioClient_AddRef(pSub->clientParent); // Recalculate buffers count _RecalculateBuffersCount(pSub, @@ -2286,6 +2455,150 @@ done: return hr; } +// ------------------------------------------------------------------------------------------ +static PaError ActivateAudioClientOutput(PaWasapiStream *stream) +{ + HRESULT hr; + PaError result; + + UINT32 maxBufferSize = 0; + PaTime buffer_latency = 0; + UINT32 framesPerBuffer = stream->out.params.frames_per_buffer; + + // Create Audio client + hr = CreateAudioClient(stream, &stream->out, TRUE, &result); + if (hr != S_OK) + { + LogPaError(result = paInvalidDevice); + goto error; + } + LogWAVEFORMATEXTENSIBLE(&stream->out.wavex); + + // Activate volume + stream->outVol = NULL; + /*hr = info->device->Activate( + __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, + (void**)&stream->outVol); + if (hr != S_OK) + return paInvalidDevice;*/ + + // Get max possible buffer size to check if it is not less than that we request + hr = IAudioClient_GetBufferSize(stream->out.clientParent, &maxBufferSize); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + + // Correct buffer to max size if it maxed out result of GetBufferSize + stream->out.bufferSize = maxBufferSize; + + // Get interface latency (actually uneeded as we calculate latency from the size + // of maxBufferSize). + hr = IAudioClient_GetStreamLatency(stream->out.clientParent, &stream->out.deviceLatency); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + //stream->out.latencySeconds = nano100ToSeconds(stream->out.deviceLatency); + + // Number of frames that are required at each period + stream->out.framesPerHostCallback = maxBufferSize; + + // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer + stream->out.framesPerBuffer = + (stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer); + + // Calculate buffer latency + buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec; + + // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) + stream->out.latencySeconds = buffer_latency; + + PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latencySeconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->out.params.wow64_workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL"))); + + return paNoError; + +error: + + return result; +} + +// ------------------------------------------------------------------------------------------ +static PaError ActivateAudioClientInput(PaWasapiStream *stream) +{ + HRESULT hr; + PaError result; + + UINT32 maxBufferSize = 0; + PaTime buffer_latency = 0; + UINT32 framesPerBuffer = stream->in.params.frames_per_buffer; + + // Create Audio client + hr = CreateAudioClient(stream, &stream->in, FALSE, &result); + if (hr != S_OK) + { + LogPaError(result = paInvalidDevice); + goto error; + } + LogWAVEFORMATEXTENSIBLE(&stream->in.wavex); + + // Create volume mgr + stream->inVol = NULL; + /*hr = info->device->Activate( + __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, + (void**)&stream->inVol); + if (hr != S_OK) + return paInvalidDevice;*/ + + // Get max possible buffer size to check if it is not less than that we request + hr = IAudioClient_GetBufferSize(stream->in.clientParent, &maxBufferSize); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + + // Correct buffer to max size if it maxed out result of GetBufferSize + stream->in.bufferSize = maxBufferSize; + + // Get interface latency (actually uneeded as we calculate latency from the size + // of maxBufferSize). + hr = IAudioClient_GetStreamLatency(stream->in.clientParent, &stream->in.deviceLatency); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + //stream->in.latencySeconds = nano100ToSeconds(stream->in.deviceLatency); + + // Number of frames that are required at each period + stream->in.framesPerHostCallback = maxBufferSize; + + // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer + stream->in.framesPerBuffer = + (stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer); + + // Calculate buffer latency + buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec; + + // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) + stream->in.latencySeconds = buffer_latency; + + PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latencySeconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->in.params.wow64_workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL"))); + + return paNoError; + +error: + + return result; +} + // ------------------------------------------------------------------------------------------ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaStream** s, @@ -2306,8 +2619,6 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL; PaWasapiDeviceInfo *info = NULL; - UINT32 maxBufferSize; - PaTime buffer_latency; ULONG framesPerHostCallback; PaUtilHostBufferSizeMode bufferMode; const BOOL fullDuplex = ((inputParameters != NULL) && (outputParameters != NULL)); @@ -2394,27 +2705,31 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if (fullDuplex) stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also - // Create Audio client - hr = CreateAudioClient(stream, &stream->in, info, inputParameters, framesPerBuffer/*framesPerLatency*/, - sampleRate, (streamCallback == NULL), FALSE, fullDuplex, &result); + // Fill parameters for Audio Client creation + stream->in.params.device_info = info; + stream->in.params.stream_params = (*inputParameters); + if (inputStreamInfo != NULL) + { + stream->in.params.wasapi_params = (*inputStreamInfo); + stream->in.params.stream_params.hostApiSpecificStreamInfo = &stream->in.params.wasapi_params; + } + stream->in.params.frames_per_buffer = framesPerBuffer; + stream->in.params.sample_rate = sampleRate; + stream->in.params.blocking = (streamCallback == NULL); + stream->in.params.full_duplex = fullDuplex; + stream->in.params.wow64_workaround = paWasapi->useWOW64Workaround; + + // Create and activate audio client + hr = ActivateAudioClientInput(stream); if (hr != S_OK) { LogPaError(result = paInvalidDevice); goto error; } - LogWAVEFORMATEXTENSIBLE(&stream->in.wavex); // Get closest format hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->in.wavex), inputSampleFormat ); - // Create volume mgr - stream->inVol = NULL; - /*hr = info->device->Activate( - __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, - (void**)&stream->inVol); - if (hr != S_OK) - return paInvalidDevice;*/ - // Set user-side custom host processor if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor)) @@ -2423,43 +2738,45 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, stream->hostProcessOverrideInput.userData = userData; } - // Get max possible buffer size to check if it is not less than that we request - hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize); - if (hr != S_OK) + // Only get IAudioCaptureClient input once here instead of getting it at multiple places based on the use + hr = IAudioClient_GetService(stream->in.clientParent, &pa_IID_IAudioCaptureClient, (void **)&stream->captureClientParent); + if (hr != S_OK) { LogHostError(hr); - LogPaError(result = paInvalidDevice); + LogPaError(result = paUnanticipatedHostError); goto error; } - // Correct buffer to max size if it maxed out result of GetBufferSize - stream->in.bufferSize = maxBufferSize; - - // Get interface latency (actually uneeded as we calculate latency from the size - // of maxBufferSize). - hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency); - if (hr != S_OK) + // Create ring buffer for blocking mode (It is needed because we fetch Input packets, not frames, + // and thus we have to save partial packet if such remains unread) + if (stream->in.params.blocking == TRUE) { - LogHostError(hr); - LogPaError(result = paInvalidDevice); - goto error; + UINT32 bufferFrames = ALIGN_NEXT_POW2((stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER) * 2); + UINT32 frameSize = stream->in.wavex.Format.nBlockAlign; + + // buffer + if ((stream->in.tailBuffer = PaUtil_AllocateMemory(sizeof(PaUtilRingBuffer))) == NULL) + { + LogPaError(result = paInsufficientMemory); + goto error; + } + memset(stream->in.tailBuffer, 0, sizeof(PaUtilRingBuffer)); + + // buffer memory region + stream->in.tailBufferMemory = PaUtil_AllocateMemory(frameSize * bufferFrames); + if (stream->in.tailBufferMemory == NULL) + { + LogPaError(result = paInsufficientMemory); + goto error; + } + + // initialize + if (PaUtil_InitializeRingBuffer(stream->in.tailBuffer, frameSize, bufferFrames, stream->in.tailBufferMemory) != 0) + { + LogPaError(result = paInternalError); + goto error; + } } - //stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency); - - // Number of frames that are required at each period - stream->in.framesPerHostCallback = maxBufferSize; - - // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer - stream->in.framesPerBuffer = - (stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer); - - // Calculate buffer latency - buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec; - - // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) - stream->in.latency_seconds += buffer_latency; - - PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL"))); } else { @@ -2508,28 +2825,32 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if (fullDuplex) stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also - // Create Audio client - hr = CreateAudioClient(stream, &stream->out, info, outputParameters, framesPerBuffer/*framesPerLatency*/, - sampleRate, (streamCallback == NULL), TRUE, fullDuplex, &result); + // Fill parameters for Audio Client creation + stream->out.params.device_info = info; + stream->out.params.stream_params = (*outputParameters); + if (inputStreamInfo != NULL) + { + stream->out.params.wasapi_params = (*outputStreamInfo); + stream->out.params.stream_params.hostApiSpecificStreamInfo = &stream->out.params.wasapi_params; + } + stream->out.params.frames_per_buffer = framesPerBuffer; + stream->out.params.sample_rate = sampleRate; + stream->out.params.blocking = (streamCallback == NULL); + stream->out.params.full_duplex = fullDuplex; + stream->out.params.wow64_workaround = paWasapi->useWOW64Workaround; + + // Create and activate audio client + hr = ActivateAudioClientOutput(stream); if (hr != S_OK) { LogPaError(result = paInvalidDevice); goto error; } - LogWAVEFORMATEXTENSIBLE(&stream->out.wavex); - // Get closest format + // Get closest format hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->out.wavex), outputSampleFormat ); - // Activate volume - stream->outVol = NULL; - /*hr = info->device->Activate( - __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, - (void**)&stream->outVol); - if (hr != S_OK) - return paInvalidDevice;*/ - - // Set user-side custom host processor + // Set user-side custom host processor if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor)) { @@ -2537,43 +2858,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, stream->hostProcessOverrideOutput.userData = userData; } - // Get max possible buffer size to check if it is not less than that we request - hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize); - if (hr != S_OK) + // Only get IAudioCaptureClient output once here instead of getting it at multiple places based on the use + hr = IAudioClient_GetService(stream->out.clientParent, &pa_IID_IAudioRenderClient, (void **)&stream->renderClientParent); + if (hr != S_OK) { LogHostError(hr); - LogPaError(result = paInvalidDevice); + LogPaError(result = paUnanticipatedHostError); goto error; } - - // Correct buffer to max size if it maxed out result of GetBufferSize - stream->out.bufferSize = maxBufferSize; - - // Get interface latency (actually uneeded as we calculate latency from the size - // of maxBufferSize). - hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency); - if (hr != S_OK) - { - LogHostError(hr); - LogPaError(result = paInvalidDevice); - goto error; - } - //stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency); - - // Number of frames that are required at each period - stream->out.framesPerHostCallback = maxBufferSize; - - // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer - stream->out.framesPerBuffer = - (stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer); - - // Calculate buffer latency - buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec; - - // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) - stream->out.latency_seconds += buffer_latency; - - PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL"))); } else { @@ -2686,13 +2978,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, // Set Input latency stream->streamRepresentation.streamInfo.inputLatency = - ((double)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate) - + ((inputParameters)?stream->in.latency_seconds : 0); + ((double)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate) + + ((inputParameters)?stream->in.latencySeconds : 0); // Set Output latency stream->streamRepresentation.streamInfo.outputLatency = - ((double)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate) - + ((outputParameters)?stream->out.latency_seconds : 0); + ((double)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate) + + ((outputParameters)?stream->out.latencySeconds : 0); // Set SR stream->streamRepresentation.streamInfo.sampleRate = sampleRate; @@ -2717,29 +3009,29 @@ static PaError CloseStream( PaStream* s ) // abort active stream if (IsStreamActive(s)) { - if ((result = AbortStream(s)) != paNoError) - return result; + result = AbortStream(s); } - SAFE_RELEASE(stream->cclient); - SAFE_RELEASE(stream->rclient); - SAFE_RELEASE(stream->out.client); - SAFE_RELEASE(stream->in.client); + SAFE_RELEASE(stream->captureClientParent); + SAFE_RELEASE(stream->renderClientParent); + SAFE_RELEASE(stream->out.clientParent); + SAFE_RELEASE(stream->in.clientParent); SAFE_RELEASE(stream->inVol); SAFE_RELEASE(stream->outVol); CloseHandle(stream->event[S_INPUT]); CloseHandle(stream->event[S_OUTPUT]); - SAFE_CLOSE(stream->hThread); - SAFE_CLOSE(stream->hThreadStart); - SAFE_CLOSE(stream->hThreadExit); - SAFE_CLOSE(stream->hCloseRequest); - SAFE_CLOSE(stream->hBlockingOpStreamRD); - SAFE_CLOSE(stream->hBlockingOpStreamWR); + _StreamCleanup(stream); - free(stream->in.monoBuffer); - free(stream->out.monoBuffer); + PaWasapi_FreeMemory(stream->in.monoBuffer); + PaWasapi_FreeMemory(stream->out.monoBuffer); + + PaUtil_FreeMemory(stream->in.tailBuffer); + PaUtil_FreeMemory(stream->in.tailBufferMemory); + + PaUtil_FreeMemory(stream->out.tailBuffer); + PaUtil_FreeMemory(stream->out.tailBufferMemory); PaUtil_TerminateBufferProcessor(&stream->bufferProcessor); PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation); @@ -2748,11 +3040,163 @@ static PaError CloseStream( PaStream* s ) return result; } +// ------------------------------------------------------------------------------------------ +HRESULT UnmarshalSubStreamComPointers(PaWasapiSubStream *substream) +{ + HRESULT hResult = S_OK; + HRESULT hFirstBadResult = S_OK; + substream->clientProc = NULL; + + // IAudioClient + hResult = CoGetInterfaceAndReleaseStream(substream->clientStream, &pa_IID_IAudioClient, (LPVOID*)&substream->clientProc); + substream->clientStream = NULL; + if (hResult != S_OK) + { + hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult; + } + + return hFirstBadResult; +} + +// ------------------------------------------------------------------------------------------ +HRESULT UnmarshalStreamComPointers(PaWasapiStream *stream) +{ + HRESULT hResult = S_OK; + HRESULT hFirstBadResult = S_OK; + stream->captureClient = NULL; + stream->renderClient = NULL; + stream->in.clientProc = NULL; + stream->out.clientProc = NULL; + + if (NULL != stream->in.clientParent) + { + // SubStream pointers + hResult = UnmarshalSubStreamComPointers(&stream->in); + if (hResult != S_OK) + { + hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult; + } + + // IAudioCaptureClient + hResult = CoGetInterfaceAndReleaseStream(stream->captureClientStream, &pa_IID_IAudioCaptureClient, (LPVOID*)&stream->captureClient); + stream->captureClientStream = NULL; + if (hResult != S_OK) + { + hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult; + } + } + + if (NULL != stream->out.clientParent) + { + // SubStream pointers + hResult = UnmarshalSubStreamComPointers(&stream->out); + if (hResult != S_OK) + { + hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult; + } + + // IAudioRenderClient + hResult = CoGetInterfaceAndReleaseStream(stream->renderClientStream, &pa_IID_IAudioRenderClient, (LPVOID*)&stream->renderClient); + stream->renderClientStream = NULL; + if (hResult != S_OK) + { + hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult; + } + } + + return hFirstBadResult; +} + +// ----------------------------------------------------------------------------------------- +void ReleaseUnmarshaledSubComPointers(PaWasapiSubStream *substream) +{ + SAFE_RELEASE(substream->clientProc); +} + +// ----------------------------------------------------------------------------------------- +void ReleaseUnmarshaledComPointers(PaWasapiStream *stream) +{ + // Release AudioClient services first + SAFE_RELEASE(stream->captureClient); + SAFE_RELEASE(stream->renderClient); + + // Release AudioClients + ReleaseUnmarshaledSubComPointers(&stream->in); + ReleaseUnmarshaledSubComPointers(&stream->out); +} + +// ------------------------------------------------------------------------------------------ +HRESULT MarshalSubStreamComPointers(PaWasapiSubStream *substream) +{ + HRESULT hResult; + substream->clientStream = NULL; + + // IAudioClient + hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioClient, (LPUNKNOWN)substream->clientParent, &substream->clientStream); + if (hResult != S_OK) + goto marshal_sub_error; + + return hResult; + + // If marshaling error occurred, make sure to release everything. +marshal_sub_error: + + UnmarshalSubStreamComPointers(substream); + ReleaseUnmarshaledSubComPointers(substream); + return hResult; +} + +// ------------------------------------------------------------------------------------------ +HRESULT MarshalStreamComPointers(PaWasapiStream *stream) +{ + HRESULT hResult = S_OK; + stream->captureClientStream = NULL; + stream->in.clientStream = NULL; + stream->renderClientStream = NULL; + stream->out.clientStream = NULL; + + if (NULL != stream->in.clientParent) + { + // SubStream pointers + hResult = MarshalSubStreamComPointers(&stream->in); + if (hResult != S_OK) + goto marshal_error; + + // IAudioCaptureClient + hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioCaptureClient, (LPUNKNOWN)stream->captureClientParent, &stream->captureClientStream); + if (hResult != S_OK) + goto marshal_error; + } + + if (NULL != stream->out.clientParent) + { + // SubStream pointers + hResult = MarshalSubStreamComPointers(&stream->out); + if (hResult != S_OK) + goto marshal_error; + + // IAudioRenderClient + hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioRenderClient, (LPUNKNOWN)stream->renderClientParent, &stream->renderClientStream); + if (hResult != S_OK) + goto marshal_error; + } + + return hResult; + + // If marshaling error occurred, make sure to release everything. +marshal_error: + + UnmarshalStreamComPointers(stream); + ReleaseUnmarshaledComPointers(stream); + return hResult; +} + // ------------------------------------------------------------------------------------------ static PaError StartStream( PaStream *s ) { HRESULT hr; PaWasapiStream *stream = (PaWasapiStream*)s; + PaError result = paNoError; // check if stream is active already if (IsStreamActive(s)) @@ -2760,8 +3204,15 @@ static PaError StartStream( PaStream *s ) PaUtil_ResetBufferProcessor(&stream->bufferProcessor); + // Cleanup handles (may be necessary if stream was stopped by itself due to error) + _StreamCleanup(stream); + // Create close event - stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL); + if ((stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) + { + result = paInsufficientMemory; + goto start_error; + } // Create thread if (!stream->bBlocking) @@ -2769,78 +3220,118 @@ static PaError StartStream( PaStream *s ) // Create thread events stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL); stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL); - - if ((stream->in.client && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) || - (stream->out.client && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))) + if ((stream->hThreadStart == NULL) || (stream->hThreadExit == NULL)) { - if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL) - return paUnanticipatedHostError; + result = paInsufficientMemory; + goto start_error; + } + + // Marshal WASAPI interface pointers for safe use in thread created below. + if ((hr = MarshalStreamComPointers(stream)) != S_OK) + { + PRINT(("Failed marshaling stream COM pointers.")); + result = paUnanticipatedHostError; + goto nonblocking_start_error; + } + + if ((stream->in.clientParent && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) || + (stream->out.clientParent && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))) + { + if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL) + { + PRINT(("Failed creating thread: ProcThreadEvent.")); + result = paUnanticipatedHostError; + goto nonblocking_start_error; + } } else { - if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL) - return paUnanticipatedHostError; + if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL) + { + PRINT(("Failed creating thread: ProcThreadPoll.")); + result = paUnanticipatedHostError; + goto nonblocking_start_error; + } } // Wait for thread to start - if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT) - return paUnanticipatedHostError; + if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT) + { + PRINT(("Failed starting thread: timeout.")); + result = paUnanticipatedHostError; + goto nonblocking_start_error; + } } else { // Create blocking operation events (non-signaled event means - blocking operation is pending) - if (stream->out.client) - stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL); - if (stream->in.client) - stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL); + if (stream->out.clientParent != NULL) + { + if ((stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL) + { + result = paInsufficientMemory; + goto start_error; + } + } + if (stream->in.clientParent != NULL) + { + if ((stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL) + { + result = paInsufficientMemory; + goto start_error; + } + } // Initialize event & start INPUT stream - if (stream->in.client) + if (stream->in.clientParent != NULL) { - if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) + if ((hr = IAudioClient_Start(stream->in.clientParent)) != S_OK) { LogHostError(hr); - return paUnanticipatedHostError; - } - - if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; + result = paUnanticipatedHostError; + goto start_error; } } - // Initialize event & start OUTPUT stream - if (stream->out.client) + if (stream->out.clientParent != NULL) { - if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; - } - // Start - if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) + if ((hr = IAudioClient_Start(stream->out.clientParent)) != S_OK) { LogHostError(hr); - return paUnanticipatedHostError; + result = paUnanticipatedHostError; + goto start_error; } } - // Signal: stream running - stream->running = TRUE; + // Set parent to working pointers to use shared functions. + stream->captureClient = stream->captureClientParent; + stream->renderClient = stream->renderClientParent; + stream->in.clientProc = stream->in.clientParent; + stream->out.clientProc = stream->out.clientParent; - // Set current time - stream->out.prevTime = timeGetTime(); - stream->out.prevSleep = 0; + // Signal: stream running. + stream->running = TRUE; } - return paNoError; + return result; + +nonblocking_start_error: + + // Set hThreadExit event to prevent blocking during cleanup + SetEvent(stream->hThreadExit); + UnmarshalStreamComPointers(stream); + ReleaseUnmarshaledComPointers(stream); + +start_error: + + StopStream(s); + return result; } // ------------------------------------------------------------------------------------------ -static void _FinishStream(PaWasapiStream *stream) +void _StreamFinish(PaWasapiStream *stream) { // Issue command to thread to stop processing and wait for thread exit if (!stream->bBlocking) @@ -2851,15 +3342,24 @@ static void _FinishStream(PaWasapiStream *stream) // Blocking mode does not own thread { // Signal close event and wait for each of 2 blocking operations to complete - if (stream->out.client) + if (stream->out.clientParent) SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE); - if (stream->out.client) + if (stream->out.clientParent) SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE); // Process stop - _OnStreamStop(stream); + _StreamOnStop(stream); } + // Cleanup handles + _StreamCleanup(stream); + + stream->running = FALSE; +} + +// ------------------------------------------------------------------------------------------ +void _StreamCleanup(PaWasapiStream *stream) +{ // Close thread handles to allow restart SAFE_CLOSE(stream->hThread); SAFE_CLOSE(stream->hThreadStart); @@ -2867,15 +3367,13 @@ static void _FinishStream(PaWasapiStream *stream) SAFE_CLOSE(stream->hCloseRequest); SAFE_CLOSE(stream->hBlockingOpStreamRD); SAFE_CLOSE(stream->hBlockingOpStreamWR); - - stream->running = FALSE; } // ------------------------------------------------------------------------------------------ static PaError StopStream( PaStream *s ) { // Finish stream - _FinishStream((PaWasapiStream *)s); + _StreamFinish((PaWasapiStream *)s); return paNoError; } @@ -2883,7 +3381,7 @@ static PaError StopStream( PaStream *s ) static PaError AbortStream( PaStream *s ) { // Finish stream - _FinishStream((PaWasapiStream *)s); + _StreamFinish((PaWasapiStream *)s); return paNoError; } @@ -2907,11 +3405,6 @@ static PaTime GetStreamTime( PaStream *s ) /* suppress unused variable warnings */ (void) stream; - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - //this is lame ds and mme does the same thing, quite useless method imho - //why dont we fetch the time in the pa callbacks? - //at least its doing to be clocked to something return PaUtil_GetTime(); } @@ -2922,28 +3415,32 @@ static double GetStreamCpuLoad( PaStream* s ) } // ------------------------------------------------------------------------------------------ -/* NOT TESTED */ -static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames ) +static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames ) { PaWasapiStream *stream = (PaWasapiStream*)s; HRESULT hr = S_OK; - UINT32 frames; BYTE *user_buffer = (BYTE *)_buffer; BYTE *wasapi_buffer = NULL; DWORD flags = 0; - UINT32 i; + UINT32 i, available, sleep = 0; + unsigned long processed; + ThreadIdleScheduler sched; // validate if (!stream->running) return paStreamIsStopped; - if (stream->cclient == NULL) + if (stream->captureClient == NULL) return paBadStreamPtr; // Notify blocking op has begun ResetEvent(stream->hBlockingOpStreamRD); - // make a local copy of the user buffer pointer(s), this is necessary + // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than + // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting + ThreadIdleScheduler_Setup(&sched, 1, 250/* microseconds */); + + // Make a local copy of the user buffer pointer(s), this is necessary // because PaUtil_CopyOutput() advances these pointers every time it is called if (!stream->bufferProcessor.userInputIsInterleaved) { @@ -2955,64 +3452,140 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames ) ((BYTE **)user_buffer)[i] = ((BYTE **)_buffer)[i]; } - while (_frames != 0) + // Findout if there are tail frames, flush them all before reading hardware + if ((available = PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer)) != 0) { - UINT32 processed, processed_size; + ring_buffer_size_t buf1_size = 0, buf2_size = 0, read, desired; + void *buf1 = NULL, *buf2 = NULL; - // Get the available data in the shared buffer. - if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &wasapi_buffer, &frames, &flags, NULL, NULL)) != S_OK) + // Limit desired to amount of requested frames + desired = available; + if (desired > frames) + desired = frames; + + // Get pointers to read regions + read = PaUtil_GetRingBufferReadRegions(stream->in.tailBuffer, desired, &buf1, &buf1_size, &buf2, &buf2_size); + + if (buf1 != NULL) { - if (hr == AUDCLNT_S_BUFFER_EMPTY) - { - // Check if blocking call must be interrupted - if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT) - break; - } + // Register available frames to processor + PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf1_size); - return LogHostError(hr); - goto stream_rd_end; + // Register host buffer pointer to processor + PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf1, stream->bufferProcessor.inputChannelCount); + + // Copy user data to host buffer (with conversion if applicable) + processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf1_size); + frames -= processed; } - // Detect silence - // if (flags & AUDCLNT_BUFFERFLAGS_SILENT) - // data = NULL; + if (buf2 != NULL) + { + // Register available frames to processor + PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf2_size); - // Check if frames <= _frames - if (frames > _frames) - frames = _frames; + // Register host buffer pointer to processor + PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf2, stream->bufferProcessor.inputChannelCount); + + // Copy user data to host buffer (with conversion if applicable) + processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf2_size); + frames -= processed; + } + + // Advance + PaUtil_AdvanceRingBufferReadIndex(stream->in.tailBuffer, read); + } + + // Read hardware + while (frames != 0) + { + // Check if blocking call must be interrupted + if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT) + break; + + // Get available frames (must be finding out available frames before call to IAudioCaptureClient_GetBuffer + // othervise audio glitches will occur inExclusive mode as it seems that WASAPI has some scheduling/ + // processing problems when such busy polling with IAudioCaptureClient_GetBuffer occurs) + if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK) + { + LogHostError(hr); + return paUnanticipatedHostError; + } + + // Wait for more frames to become available + if (available == 0) + { + // Exclusive mode may require latency of 1 millisecond, thus we shall sleep + // around 500 microseconds (emulated) to collect packets in time + if (stream->in.shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) + { + UINT32 sleep_frames = (frames < stream->in.framesPerHostCallback ? frames : stream->in.framesPerHostCallback); + + sleep = GetFramesSleepTime(sleep_frames, stream->in.wavex.Format.nSamplesPerSec); + sleep /= 4; // wait only for 1/4 of the buffer + + // WASAPI input provides packets, thus expiring packet will result in bad audio + // limit waiting time to 2 seconds (will always work for smallest buffer in Shared) + if (sleep > 2) + sleep = 2; + + // Avoid busy waiting, schedule next 1 millesecond wait + if (sleep == 0) + sleep = ThreadIdleScheduler_NextSleep(&sched); + } + else + { + if ((sleep = ThreadIdleScheduler_NextSleep(&sched)) != 0) + { + Sleep(sleep); + sleep = 0; + } + } + + continue; + } + + // Get the available data in the shared buffer. + if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &wasapi_buffer, &available, &flags, NULL, NULL)) != S_OK) + { + // Buffer size is too small, waiting + if (hr != AUDCLNT_S_BUFFER_EMPTY) + { + LogHostError(hr); + goto end; + } + + continue; + } // Register available frames to processor - PaUtil_SetInputFrameCount(&stream->bufferProcessor, frames); + PaUtil_SetInputFrameCount(&stream->bufferProcessor, available); // Register host buffer pointer to processor - PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount); + PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount); // Copy user data to host buffer (with conversion if applicable) processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames); + frames -= processed; - // Advance user buffer to consumed portion - processed_size = processed * stream->in.wavex.Format.nBlockAlign; - if (stream->bufferProcessor.userInputIsInterleaved) + // Save tail into buffer + if ((frames == 0) && (available > processed)) { - user_buffer += processed_size; - } - else - { - for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i) - ((BYTE **)user_buffer)[i] = ((BYTE **)user_buffer)[i] + processed_size; + UINT32 bytes_processed = processed * stream->in.wavex.Format.nBlockAlign; + UINT32 frames_to_save = available - processed; + + PaUtil_WriteRingBuffer(stream->in.tailBuffer, wasapi_buffer + bytes_processed, frames_to_save); } // Release host buffer - if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, processed)) != S_OK) + if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, available)) != S_OK) { LogHostError(hr); - goto stream_rd_end; + goto end; } - - _frames -= processed; } -stream_rd_end: +end: // Notify blocking op has ended SetEvent(stream->hBlockingOpStreamRD); @@ -3021,58 +3594,32 @@ stream_rd_end: } // ------------------------------------------------------------------------------------------ -static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames ) +static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long frames ) { PaWasapiStream *stream = (PaWasapiStream*)s; - UINT32 frames; + //UINT32 frames; const BYTE *user_buffer = (const BYTE *)_buffer; BYTE *wasapi_buffer; HRESULT hr = S_OK; - UINT32 next_rev_sleep, blocks, block_sleep_ms; - UINT32 i; + UINT32 i, available, sleep = 0; + unsigned long processed; + ThreadIdleScheduler sched; // validate if (!stream->running) return paStreamIsStopped; - if (stream->rclient == NULL) + if (stream->renderClient == NULL) return paBadStreamPtr; // Notify blocking op has begun ResetEvent(stream->hBlockingOpStreamWR); - // Calculate sleep time for next call - { - UINT32 remainder = 0; - UINT32 sleep_ms = 0; - DWORD elapsed_ms; - blocks = _frames / stream->out.framesPerHostCallback; - block_sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec); - if (blocks == 0) - { - blocks = 1; - sleep_ms = GetFramesSleepTime(_frames, stream->out.wavex.Format.nSamplesPerSec); // partial - } - else - { - remainder = _frames - blocks * stream->out.framesPerHostCallback; - sleep_ms = block_sleep_ms; // full - } + // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than + // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting + ThreadIdleScheduler_Setup(&sched, 1, 500/* microseconds */); - // Sleep for remainder - elapsed_ms = timeGetTime() - stream->out.prevTime; - if (sleep_ms >= elapsed_ms) - sleep_ms -= elapsed_ms; - - next_rev_sleep = sleep_ms; - } - - // Sleep diff from last call - if (stream->out.prevSleep) - Sleep(stream->out.prevSleep); - stream->out.prevSleep = next_rev_sleep; - - // make a local copy of the user buffer pointer(s), this is necessary + // Make a local copy of the user buffer pointer(s), this is necessary // because PaUtil_CopyOutput() advances these pointers every time it is called if (!stream->bufferProcessor.userOutputIsInterleaved) { @@ -3084,91 +3631,75 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra ((const BYTE **)user_buffer)[i] = ((const BYTE **)_buffer)[i]; } - // Feed engine - for (i = 0; i < blocks; ++i) + // Blocking (potentially, untill 'frames' are consumed) loop + while (frames != 0) { - UINT32 available, processed; + // Check if blocking call must be interrupted + if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT) + break; - // Get block frames - frames = stream->out.framesPerHostCallback; - if (frames > _frames) - frames = _frames; - - if (i) - Sleep(block_sleep_ms); - - while (frames != 0) + // Get frames available + if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK) { - UINT32 padding = 0; - UINT32 processed_size; - - // Check if blocking call must be interrupted - if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT) - break; - - // Get Read position - hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); - if (hr != S_OK) - { - LogHostError(hr); - goto stream_wr_end; - } - - // Calculate frames available - if (frames >= padding) - available = frames - padding; - else - available = frames; - - // Get pointer to host buffer - if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &wasapi_buffer)) != S_OK) - { - // Buffer size is too big, waiting - if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) - continue; - LogHostError(hr); - goto stream_wr_end; - } - - // Register available frames to processor - PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available); - - // Register host buffer pointer to processor - PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount); - - // Copy user data to host buffer (with conversion if applicable) - processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, available); - - // Advance user buffer to consumed portion - processed_size = processed * stream->out.wavex.Format.nBlockAlign; - if (stream->bufferProcessor.userOutputIsInterleaved) - { - user_buffer += processed_size; - } - else - { - for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i) - ((const BYTE **)user_buffer)[i] = ((const BYTE **)user_buffer)[i] + processed_size; - } - - // Release host buffer - if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, processed, 0)) != S_OK) - { - LogHostError(hr); - goto stream_wr_end; - } - - // Deduct frames - frames -= processed; + LogHostError(hr); + goto end; } - _frames -= frames; + // Wait for more frames to become available + if (available == 0) + { + UINT32 sleep_frames = (frames < stream->out.framesPerHostCallback ? frames : stream->out.framesPerHostCallback); + + sleep = GetFramesSleepTime(sleep_frames, stream->out.wavex.Format.nSamplesPerSec); + sleep /= 2; // wait only for half of the buffer + + // Avoid busy waiting, schedule next 1 millesecond wait + if (sleep == 0) + sleep = ThreadIdleScheduler_NextSleep(&sched); + + continue; + } + + // Keep in 'frmaes' range + if (available > frames) + available = frames; + + // Get pointer to host buffer + if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, available, &wasapi_buffer)) != S_OK) + { + // Buffer size is too big, waiting + if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) + continue; + + LogHostError(hr); + goto end; + } + + // Keep waiting again (on Vista it was noticed that WASAPI could SOMETIMES return NULL pointer + // to buffer without returning AUDCLNT_E_BUFFER_TOO_LARGE instead) + if (wasapi_buffer == NULL) + continue; + + // Register available frames to processor + PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available); + + // Register host buffer pointer to processor + PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount); + + // Copy user data to host buffer (with conversion if applicable), this call will advance + // pointer 'user_buffer' to consumed portion of data + processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, frames); + frames -= processed; + + // Release host buffer + if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, available, 0)) != S_OK) + { + LogHostError(hr); + goto end; + } } -stream_wr_end: - - // Set prev time - stream->out.prevTime = timeGetTime(); +end: // Notify blocking op has ended SetEvent(stream->hBlockingOpStreamWR); @@ -3176,58 +3707,61 @@ stream_wr_end: return (hr != S_OK ? paUnanticipatedHostError : paNoError); } +unsigned long PaUtil_GetOutputFrameCount( PaUtilBufferProcessor* bp ) +{ + return bp->hostOutputFrameCount[0]; +} + // ------------------------------------------------------------------------------------------ -/* NOT TESTED */ static signed long GetStreamReadAvailable( PaStream* s ) { PaWasapiStream *stream = (PaWasapiStream*)s; + HRESULT hr; - UINT32 pending = 0; + UINT32 available = 0; // validate if (!stream->running) return paStreamIsStopped; - if (stream->cclient == NULL) + if (stream->captureClient == NULL) return paBadStreamPtr; - hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending); - if (hr != S_OK) + // available in hardware buffer + if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK) { LogHostError(hr); return paUnanticipatedHostError; } - return (long)pending; + // available in software tail buffer + available += PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer); + + return available; } // ------------------------------------------------------------------------------------------ static signed long GetStreamWriteAvailable( PaStream* s ) { PaWasapiStream *stream = (PaWasapiStream*)s; - - UINT32 frames = stream->out.framesPerHostCallback; HRESULT hr; - UINT32 padding = 0; + UINT32 available = 0; // validate if (!stream->running) return paStreamIsStopped; - if (stream->rclient == NULL) + if (stream->renderClient == NULL) return paBadStreamPtr; - hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); - if (hr != S_OK) + if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK) { LogHostError(hr); return paUnanticipatedHostError; } - // Calculate - frames -= padding; - - return frames; + return (signed long)available; } + // ------------------------------------------------------------------------------------------ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames, void *outputBuffer, long outputFrames, @@ -3250,24 +3784,24 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames, */ timeInfo.currentTime = PaUtil_GetTime(); // Query input latency - if (stream->in.client != NULL) + if (stream->in.clientProc != NULL) { PaTime pending_time; - if ((hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending)) == S_OK) + if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, &pending)) == S_OK) pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec; else - pending_time = (PaTime)stream->in.latency_seconds; + pending_time = (PaTime)stream->in.latencySeconds; timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time; } // Query output current latency - if (stream->out.client != NULL) + if (stream->out.clientProc != NULL) { PaTime pending_time; - if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &pending)) == S_OK) + if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &pending)) == S_OK) pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec; else - pending_time = (PaTime)stream->out.latency_seconds; + pending_time = (PaTime)stream->out.latencySeconds; timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time; } @@ -3657,13 +4191,48 @@ error: } // ------------------------------------------------------------------------------------------ -static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames) +HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available) +{ + HRESULT hr; + UINT32 frames = stream->out.framesPerHostCallback, + padding = 0; + + (*available) = 0; + + // get read position + if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding)) != S_OK) + return LogHostError(hr); + + // get available + frames -= padding; + + // set + (*available) = frames; + return hr; +} + +// ------------------------------------------------------------------------------------------ +HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available) +{ + HRESULT hr; + + (*available) = 0; + + // GetCurrentPadding() has opposite meaning to Output stream + if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, available)) != S_OK) + return LogHostError(hr); + + return hr; +} + +// ------------------------------------------------------------------------------------------ +HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames) { HRESULT hr; BYTE *data = NULL; // Get buffer - if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, frames, &data)) != S_OK) + if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK) { if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED) { @@ -3674,7 +4243,7 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor #if 0 // Get Read position UINT32 padding = 0; - hr = stream->out.client->GetCurrentPadding(&padding); + hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding); if (hr != S_OK) return LogHostError(hr); @@ -3683,7 +4252,7 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor if (frames == 0) return S_OK; - if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK) + if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK) return LogHostError(hr); #else if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) @@ -3697,20 +4266,16 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor // Process data if (stream->out.monoMixer != NULL) { -#define __DIV_8(v) ((v) >> 3) //!< (v / 8) - - // expand buffer (one way only for better performancedue to no calls to realloc) - UINT32 mono_frames_size = frames * __DIV_8(stream->out.wavex.Format.wBitsPerSample); + // expand buffer + UINT32 mono_frames_size = frames * (stream->out.wavex.Format.wBitsPerSample / 8); if (mono_frames_size > stream->out.monoBufferSize) - stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size)); + stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size)); // process processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData); // mix 1 to 2 channels stream->out.monoMixer(data, stream->out.monoBuffer, frames); - -#undef __DIV_8 } else { @@ -3718,14 +4283,14 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor } // Release buffer - if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, frames, 0)) != S_OK) + if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, frames, 0)) != S_OK) LogHostError(hr); return hr; } // ------------------------------------------------------------------------------------------ -static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor) +HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor) { HRESULT hr = S_OK; UINT32 frames; @@ -3738,13 +4303,22 @@ static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT) break; + // Findout if any frames available + frames = 0; + if ((hr = _PollGetInputFramesAvailable(stream, &frames)) != S_OK) + return hr; + + // Empty/consumed buffer + if (frames == 0) + break; + // Get the available data in the shared buffer. - if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK) + if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &data, &frames, &flags, NULL, NULL)) != S_OK) { if (hr == AUDCLNT_S_BUFFER_EMPTY) { hr = S_OK; - break; // capture buffer exhausted + break; // Empty/consumed buffer } return LogHostError(hr); @@ -3758,20 +4332,16 @@ static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor // Process data if (stream->in.monoMixer != NULL) { -#define __DIV_8(v) ((v) >> 3) //!< (v / 8) - - // expand buffer (one way only for better performancedue to no calls to realloc) - UINT32 mono_frames_size = frames * __DIV_8(stream->in.wavex.Format.wBitsPerSample); + // expand buffer + UINT32 mono_frames_size = frames * (stream->in.wavex.Format.wBitsPerSample / 8); if (mono_frames_size > stream->in.monoBufferSize) - stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size)); + stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size)); // mix 1 to 2 channels stream->in.monoMixer(stream->in.monoBuffer, data, frames); // process processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData); - -#undef __DIV_8 } else { @@ -3779,23 +4349,33 @@ static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor } // Release buffer - if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK) + if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, frames)) != S_OK) return LogHostError(hr); + + //break; } return hr; } // ------------------------------------------------------------------------------------------ -void _OnStreamStop(PaWasapiStream *stream) +void _StreamOnStop(PaWasapiStream *stream) { - // Stop INPUT client - if (stream->in.client != NULL) - IAudioClient_Stop(stream->in.client); - - // Stop OUTPUT client - if (stream->out.client != NULL) - IAudioClient_Stop(stream->out.client); + // Stop INPUT/OUTPUT clients + if (!stream->bBlocking) + { + if (stream->in.clientProc != NULL) + IAudioClient_Stop(stream->in.clientProc); + if (stream->out.clientProc != NULL) + IAudioClient_Stop(stream->out.clientProc); + } + else + { + if (stream->in.clientParent != NULL) + IAudioClient_Stop(stream->in.clientParent); + if (stream->out.clientParent != NULL) + IAudioClient_Stop(stream->out.clientParent); + } // Restore thread priority if (stream->hAvTask != NULL) @@ -3818,10 +4398,34 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) PaWasapiStream *stream = (PaWasapiStream *)param; PaWasapiHostProcessor defaultProcessor; BOOL set_event[S_COUNT] = { FALSE, FALSE }; + BOOL bWaitAllEvents = FALSE; + BOOL bThreadComInitialized = FALSE; + + /* + If COM is already initialized CoInitialize will either return + FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different + threading mode. In either case we shouldn't consider it an error + but we need to be careful to not call CoUninitialize() if + RPC_E_CHANGED_MODE was returned. + */ + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE)) + { + PRINT(("WASAPI: failed ProcThreadEvent CoInitialize")); + return paUnanticipatedHostError; + } + if (hr != RPC_E_CHANGED_MODE) + bThreadComInitialized = TRUE; + + // Unmarshal stream pointers for safe COM operation + hr = UnmarshalStreamComPointers(stream); + if (hr != S_OK) { + PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr)); + goto thread_end; + } // Waiting on all events in case of Full-Duplex/Exclusive mode. - BOOL bWaitAllEvents = FALSE; - if ((stream->in.client != NULL) && (stream->out.client != NULL)) + if ((stream->in.clientProc != NULL) && (stream->out.clientProc != NULL)) { bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE); @@ -3854,22 +4458,12 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) } // Initialize event & start INPUT stream - if (stream->in.client) + if (stream->in.clientProc) { // Create & set handle if (set_event[S_INPUT]) { - if ((hr = IAudioClient_SetEventHandle(stream->in.client, stream->event[S_INPUT])) != S_OK) - { - LogHostError(hr); - goto thread_error; - } - } - - // Create Capture client - if (stream->cclient == NULL) - { - if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) + if ((hr = IAudioClient_SetEventHandle(stream->in.clientProc, stream->event[S_INPUT])) != S_OK) { LogHostError(hr); goto thread_error; @@ -3877,7 +4471,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) } // Start - if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) + if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK) { LogHostError(hr); goto thread_error; @@ -3885,22 +4479,12 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) } // Initialize event & start OUTPUT stream - if (stream->out.client) + if (stream->out.clientProc) { // Create & set handle if (set_event[S_OUTPUT]) { - if ((hr = IAudioClient_SetEventHandle(stream->out.client, stream->event[S_OUTPUT])) != S_OK) - { - LogHostError(hr); - goto thread_error; - } - } - - // Create Render client - if (stream->rclient == NULL) - { - if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) + if ((hr = IAudioClient_SetEventHandle(stream->out.clientProc, stream->event[S_OUTPUT])) != S_OK) { LogHostError(hr); goto thread_error; @@ -3915,7 +4499,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) } // Start - if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) + if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK) { LogHostError(hr); goto thread_error; @@ -3932,8 +4516,8 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) // Processing Loop for (;;) { - // 2 sec timeout - dwResult = WaitForMultipleObjects(S_COUNT, stream->event, bWaitAllEvents, 10000); + // 10 sec timeout (on timeout stream will auto-stop when processed by WAIT_TIMEOUT case) + dwResult = WaitForMultipleObjects(S_COUNT, stream->event, bWaitAllEvents, 10*1000); // Check for close event (after wait for buffers to avoid any calls to user // callback when hCloseRequest was set) @@ -3950,24 +4534,30 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) // Input stream case WAIT_OBJECT_0 + S_INPUT: { - if (stream->cclient == NULL) + + if (stream->captureClient == NULL) break; + if ((hr = ProcessInputBuffer(stream, processor)) != S_OK) { LogHostError(hr); goto thread_error; } + break; } // Output stream case WAIT_OBJECT_0 + S_OUTPUT: { - if (stream->rclient == NULL) + + if (stream->renderClient == NULL) break; + if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK) { LogHostError(hr); goto thread_error; } + break; } } } @@ -3975,14 +4565,21 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) thread_end: // Process stop - _OnStreamStop(stream); + _StreamOnStop(stream); - // Notify: thread exited - SetEvent(stream->hThreadExit); + // Release unmarshaled COM pointers + ReleaseUnmarshaledComPointers(stream); + + // Cleanup COM for this thread + if (bThreadComInitialized == TRUE) + CoUninitialize(); // Notify: not running stream->running = FALSE; + // Notify: thread exited + SetEvent(stream->hThreadExit); + return 0; thread_error: @@ -3994,67 +4591,6 @@ thread_error: goto thread_end; } -// ------------------------------------------------------------------------------------------ -static HRESULT PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available) -{ - HRESULT hr; - UINT32 frames = stream->out.framesPerHostCallback, - padding = 0; - - (*available) = 0; - - // get read position - if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding)) != S_OK) - return LogHostError(hr); - - // get available - frames -= padding; - - // set - (*available) = frames; - return hr; -} - -// ------------------------------------------------------------------------------------------ -/*! \class ThreadSleepScheduler - Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler - calculates number of times the thread must run untill next sleep of 1 millisecond. - It does not make thread sleeping for real number of microseconds but rather controls - how many of imaginary microseconds the thread task can allow thread to sleep. -*/ -typedef struct ThreadIdleScheduler -{ - UINT32 m_idle_microseconds; //!< number of microseconds to sleep - UINT32 m_next_sleep; //!< next sleep round - UINT32 m_i; //!< current round iterator position - UINT32 m_resolution; //!< resolution in number of milliseconds -} -ThreadIdleScheduler; -//! Setup scheduler. -static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds) -{ - assert(microseconds != 0); - assert(resolution != 0); - assert((resolution * 1000) >= microseconds); - - memset(sched, 0, sizeof(*sched)); - - sched->m_idle_microseconds = microseconds; - sched->m_resolution = resolution; - sched->m_next_sleep = (resolution * 1000) / microseconds; -} -//! Iterate and check if can sleep. -static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched) -{ - // advance and check if thread can sleep - if (++ sched->m_i == sched->m_next_sleep) - { - sched->m_i = 0; - return sched->m_resolution; - } - return 0; -} - // ------------------------------------------------------------------------------------------ PA_THREAD_FUNC ProcThreadPoll(void *param) { @@ -4067,13 +4603,49 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) // Calculate the actual duration of the allocated buffer. DWORD sleep_ms = 0; - DWORD sleep_ms_in = GetFramesSleepTime(stream->in.framesPerBuffer, stream->in.wavex.Format.nSamplesPerSec); - DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec); + DWORD sleep_ms_in; + DWORD sleep_ms_out; - // Adjust polling time + BOOL bThreadComInitialized = FALSE; + + /* + If COM is already initialized CoInitialize will either return + FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different + threading mode. In either case we shouldn't consider it an error + but we need to be careful to not call CoUninitialize() if + RPC_E_CHANGED_MODE was returned. + */ + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE)) + { + PRINT(("WASAPI: failed ProcThreadPoll CoInitialize")); + return paUnanticipatedHostError; + } + if (hr != RPC_E_CHANGED_MODE) + bThreadComInitialized = TRUE; + + // Unmarshal stream pointers for safe COM operation + hr = UnmarshalStreamComPointers(stream); + if (hr != S_OK) + { + PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr)); + return 0; + } + + // Calculate timeout for next polling attempt. + sleep_ms_in = GetFramesSleepTime(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec); + sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec); + + // WASAPI Input packets tend to expire very easily, let's limit sleep time to 2 milliseconds + // for all cases. Please propose better solution if any. + if (sleep_ms_in > 2) + sleep_ms_in = 2; + + // Adjust polling time for non-paUtilFixedHostBufferSize. Input stream is not adjustable as it is being + // polled according its packet length. if (stream->bufferMode != paUtilFixedHostBufferSize) { - sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec); + //sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec); sleep_ms_out = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec); } @@ -4087,7 +4659,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) // Make sure not 0, othervise use ThreadIdleScheduler if (sleep_ms == 0) { - sleep_ms_in = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec); + sleep_ms_in = GetFramesSleepTimeMicroseconds(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec); sleep_ms_out = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec); // Choose smallest @@ -4099,7 +4671,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) } // Setup thread sleep scheduler - ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here actually */); + ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here */); sleep_ms = 0; } @@ -4113,57 +4685,47 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority); // Initialize event & start INPUT stream - if (stream->in.client) + if (stream->in.clientProc) { - if (stream->cclient == NULL) - { - if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) - { - LogHostError(hr); - goto thread_error; - } - } - - if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) + if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK) { LogHostError(hr); goto thread_error; } } - // Initialize event & start OUTPUT stream - if (stream->out.client) + if (stream->out.clientProc) { - if (stream->rclient == NULL) - { - if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) - { - LogHostError(hr); - goto thread_error; - } - } - // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing // when in full-duplex mode as it requires input processing as well if (!PA_WASAPI__IS_FULLDUPLEX(stream)) { UINT32 frames = 0; - if ((hr = PollGetOutputFramesAvailable(stream, &frames)) == S_OK) + if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) == S_OK) { if (stream->bufferMode == paUtilFixedHostBufferSize) { if (frames >= stream->out.framesPerBuffer) + { frames = stream->out.framesPerBuffer; - } - if (frames != 0) - { - if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK) - { - LogHostError(hr); // not fatal, just log - } - } + if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK) + { + LogHostError(hr); // not fatal, just log + } + } + } + else + { + if (frames != 0) + { + if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK) + { + LogHostError(hr); // not fatal, just log + } + } + } } else { @@ -4172,7 +4734,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) } // Start - if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) + if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK) { LogHostError(hr); goto thread_error; @@ -4204,24 +4766,27 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) { // Input stream case S_INPUT: { - if (stream->cclient == NULL) + + if (stream->captureClient == NULL) break; + if ((hr = ProcessInputBuffer(stream, processor)) != S_OK) { LogHostError(hr); goto thread_error; } + break; } // Output stream case S_OUTPUT: { UINT32 frames; - if (stream->rclient == NULL) + if (stream->renderClient == NULL) break; // get available frames - if ((hr = PollGetOutputFramesAvailable(stream, &frames)) != S_OK) + if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) != S_OK) { LogHostError(hr); goto thread_error; @@ -4268,7 +4833,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) UINT32 o_frames = 0; // get host input buffer - if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK) + if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK) { if (hr == AUDCLNT_S_BUFFER_EMPTY) continue; // no data in capture buffer @@ -4278,8 +4843,11 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) } // get available frames - if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK) + if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK) { + // release input buffer + IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0); + LogHostError(hr); break; } @@ -4291,7 +4859,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) UINT32 o_processed = i_frames; // get host output buffer - if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK) + if ((hr = IAudioRenderClient_GetBuffer(stream->procRCClient, o_processed, &o_data)) == S_OK) { // processed amount of i_frames i_processed = i_frames; @@ -4300,15 +4868,18 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) // convert output mono if (stream->out.monoMixer) { - #define __DIV_8(v) ((v) >> 3) //!< (v / 8) - UINT32 mono_frames_size = o_processed * __DIV_8(stream->out.wavex.Format.wBitsPerSample); - #undef __DIV_8 - // expand buffer (one way only for better performance due to no calls to realloc) + UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8); + // expand buffer if (mono_frames_size > stream->out.monoBufferSize) { - stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size)); + stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size)); if (stream->out.monoBuffer == NULL) { + // release input buffer + IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0); + // release output buffer + IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0); + LogPaError(paInsufficientMemory); break; } @@ -4321,15 +4892,18 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) // convert input mono if (stream->in.monoMixer) { - #define __DIV_8(v) ((v) >> 3) //!< (v / 8) - UINT32 mono_frames_size = i_processed * __DIV_8(stream->in.wavex.Format.wBitsPerSample); - #undef __DIV_8 - // expand buffer (one way only for better performance due to no calls to realloc) + UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8); + // expand buffer if (mono_frames_size > stream->in.monoBufferSize) { - stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size)); + stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size)); if (stream->in.monoBuffer == NULL) { + // release input buffer + IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0); + // release output buffer + IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0); + LogPaError(paInsufficientMemory); break; } @@ -4350,7 +4924,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed); // release host output buffer - if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK) + if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK) LogHostError(hr); } else @@ -4361,7 +4935,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) } // release host input buffer - if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK) + if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK) { LogHostError(hr); break; @@ -4376,11 +4950,6 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL; DWORD i_flags = 0; UINT32 o_frames = 0; - //BOOL repeat = FALSE; - - // going below 1 msec resolution, switching between 1 ms and no waiting - //if (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) - // sleep_ms = !sleep_ms; // Get next sleep time if (sleep_ms == 0) @@ -4389,7 +4958,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) } // get available frames - if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK) + if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK) { LogHostError(hr); break; @@ -4398,7 +4967,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) while (o_frames != 0) { // get host input buffer - if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK) + if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK) { if (hr == AUDCLNT_S_BUFFER_EMPTY) break; // no data in capture buffer @@ -4407,9 +4976,6 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) break; } - //PA_DEBUG(("full-duplex: o_frames[%d] i_frames[%d] repeat[%d]\n", o_frames, i_frames, repeat)); - //repeat = TRUE; - // process equal ammount of frames if (o_frames >= i_frames) { @@ -4417,7 +4983,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) UINT32 o_processed = i_frames; // get host output buffer - if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK) + if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, o_processed, &o_data)) == S_OK) { // processed amount of i_frames i_processed = i_frames; @@ -4426,15 +4992,18 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) // convert output mono if (stream->out.monoMixer) { - #define __DIV_8(v) ((v) >> 3) //!< (v / 8) - UINT32 mono_frames_size = o_processed * __DIV_8(stream->out.wavex.Format.wBitsPerSample); - #undef __DIV_8 - // expand buffer (one way only for better performance due to no calls to realloc) + UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8); + // expand buffer if (mono_frames_size > stream->out.monoBufferSize) { - stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size)); + stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size)); if (stream->out.monoBuffer == NULL) { + // release input buffer + IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0); + // release output buffer + IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0); + LogPaError(paInsufficientMemory); goto thread_error; } @@ -4447,15 +5016,18 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) // convert input mono if (stream->in.monoMixer) { - #define __DIV_8(v) ((v) >> 3) //!< (v / 8) - UINT32 mono_frames_size = i_processed * __DIV_8(stream->in.wavex.Format.wBitsPerSample); - #undef __DIV_8 - // expand buffer (one way only for better performance due to no calls to realloc) + UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8); + // expand buffer if (mono_frames_size > stream->in.monoBufferSize) { - stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size)); + stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size)); if (stream->in.monoBuffer == NULL) { + // release input buffer + IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0); + // release output buffer + IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0); + LogPaError(paInsufficientMemory); goto thread_error; } @@ -4476,7 +5048,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed); // release host output buffer - if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK) + if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK) LogHostError(hr); o_frames -= o_processed; @@ -4496,7 +5068,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) fd_release_buffer_in: // release host input buffer - if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK) + if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK) { LogHostError(hr); break; @@ -4513,14 +5085,21 @@ fd_release_buffer_in: thread_end: // Process stop - _OnStreamStop(stream); + _StreamOnStop(stream); - // Notify: thread exited - SetEvent(stream->hThreadExit); + // Release unmarshaled COM pointers + ReleaseUnmarshaledComPointers(stream); + + // Cleanup COM for this thread + if (bThreadComInitialized == TRUE) + CoUninitialize(); // Notify: not running stream->running = FALSE; + // Notify: thread exited + SetEvent(stream->hThreadExit); + return 0; thread_error: @@ -4532,6 +5111,18 @@ thread_error: goto thread_end; } +// ------------------------------------------------------------------------------------------ +void *PaWasapi_ReallocateMemory(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +// ------------------------------------------------------------------------------------------ +void PaWasapi_FreeMemory(void *ptr) +{ + free(ptr); +} + //#endif //VC 2005 diff --git a/bazaar/plugin/portaudio/hostapi/wdmks/pa_win_wdmks.c b/bazaar/plugin/portaudio/hostapi/wdmks/pa_win_wdmks.c index caddfd52d..60a61e474 100644 --- a/bazaar/plugin/portaudio/hostapi/wdmks/pa_win_wdmks.c +++ b/bazaar/plugin/portaudio/hostapi/wdmks/pa_win_wdmks.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_wdmks.c 1411 2009-05-14 14:37:37Z rossb $ + * $Id: pa_win_wdmks.c 1606 2011-02-17 15:56:04Z rob_bielik $ * PortAudio Windows WDM-KS interface * * Author: Andrew Baldwin @@ -106,6 +106,10 @@ #define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data) #endif +#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ +#pragma comment( lib, "setupapi.lib" ) +#endif + /* use CreateThread for CYGWIN, _beginthreadex for all others */ #ifndef __CYGWIN__ #define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ) diff --git a/bazaar/plugin/portaudio/hostapi/wmme/pa_win_wmme.c b/bazaar/plugin/portaudio/hostapi/wmme/pa_win_wmme.c index 2436d8de1..ec891a94e 100644 --- a/bazaar/plugin/portaudio/hostapi/wmme/pa_win_wmme.c +++ b/bazaar/plugin/portaudio/hostapi/wmme/pa_win_wmme.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_wmme.c 1432 2009-12-09 01:31:44Z 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) * @@ -65,35 +65,6 @@ @ingroup hostapi_src @brief Win32 host API implementation for the Windows MultiMedia Extensions (WMME) audio API. - - @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now, - needs to be reviewed and tested.) - - @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput. - - @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may - be called asynchronously from the callback thread. this is bad. - - @todo implement inputBufferAdcTime in callback thread - - @todo review/fix error recovery and cleanup in marked functions - - @todo implement timeInfo for stream priming - - @todo handle the case where the callback returns paAbort or paComplete during stream priming. - - @todo review input overflow and output underflow handling in ReadStream and WriteStream - -Non-critical stuff for the future: - - @todo Investigate supporting host buffer formats > 16 bits - - @todo define UNICODE and _UNICODE in the project settings and see what breaks - - @todo refactor conversion of MMSYSTEM errors into PA arrors into a single function. - - @todo cleanup WAVEFORMATEXTENSIBLE retry in InitializeWaveHandles to not use a for loop - */ /* @@ -102,7 +73,7 @@ Non-critical stuff for the future: For both callback and blocking read/write streams we open the MME devices in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever it has finished with a buffer (either filled it for input, or played it - for output). Where necessary we block waiting for Event objects using + for output). Where necessary, we block waiting for Event objects using WaitMultipleObjects(). When implementing a PA callback stream, we set up a high priority thread @@ -183,29 +154,47 @@ Non-critical stuff for the future: #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 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_MIN_TIMEOUT_MSEC_ (1000) @@ -820,7 +809,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; @@ -1353,126 +1344,194 @@ 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 value (hostBufferSizeFrames - 1) below is to ensure the buffer count is rounded up. */ - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); + unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 1)) / hostBufferSizeFrames); - if( latency > requestedLatency ) - { + /* We always need one extra buffer for processing while the rest are queued/playing. + i.e. latency is framesPerBuffer * (bufferCount - 1) + */ + resultBufferCount += 1; - /* reduce number of buffers without falling below suggested latency */ + if( resultBufferCount < minimumBufferCount ) /* clamp to minimum buffer count */ + resultBufferCount = minimumBufferCount; - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) - { - --bufferCount; - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - } - - }else if( latency < requestedLatency ){ - - baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1))); - if( baseBufferSizeIsPowerOfTwo ){ - - /* 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); - } - - }else{ - - /* increase size of buffers upto first excess of requestedLatency */ - - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - while( nextBufferSize <= maximumBufferSize - && nextLatency < requestedLatency ) - { - ++sizeMultiplier; - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - } - - if( nextLatency < requestedLatency ) - ++sizeMultiplier; - } - - /* increase number of buffers until requestedLatency is reached */ - - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - while( latency < requestedLatency ) - { - ++bufferCount; - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - } - } - - *hostBufferSize = baseBufferSize * sizeMultiplier; - *hostBufferCount = bufferCount; + return resultBufferCount; } -static void ReselectBufferCount( unsigned long bufferSize, - unsigned long requestedLatency, - unsigned long baseBufferCount, unsigned long minimumBufferCount, - unsigned long *hostBufferCount ) +static unsigned long ComputeHostBufferSizeGivenHardUpperLimit( + unsigned long userFramesPerBuffer, + unsigned long absoluteMaximumBufferSizeFrames ) { - unsigned long bufferCount, latency; - unsigned long nextLatency; + 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 */ - bufferCount = baseBufferCount; + unsigned long result = userFramesPerBuffer; + int i; - /* count-1 below because latency is always determined by one less - than the total number of buffers. - */ - latency = bufferSize * (bufferCount-1); + assert( absoluteMaximumBufferSizeFrames > 67 ); /* assume maximum is large and we're only factoring by small primes */ - if( latency > requestedLatency ) - { - /* reduce number of buffers without falling below suggested latency */ + /* search for the largest integer factor of userFramesPerBuffer less + than or equal to absoluteMaximumBufferSizeFrames */ - nextLatency = bufferSize * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) + /* 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 ) { - --bufferCount; - nextLatency = bufferSize * (bufferCount-2); + 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; } - - }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; + return result; +} + + +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 ) +{ + unsigned long effectiveUserFramesPerBuffer; + unsigned long numberOfUserBuffersPerHostBuffer; + + + if( userFramesPerBuffer == paFramesPerBufferUnspecified ){ + + effectiveUserFramesPerBuffer = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_; + + }else{ + + if( userFramesPerBuffer > absoluteMaximumBufferSizeFrames ){ + + /* 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 ); + + 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. + + 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( numberOfUserBuffersPerHostBuffer > 1 ) + { + unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* minimum of our limits */ + ? absoluteMaximumBufferSizeFrames + : preferredMaximumBufferSizeFrames; + + unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / effectiveUserFramesPerBuffer; /* don't coalesce more than this */ + + if( numberOfUserBuffersPerHostBuffer > maxUserBuffersPerHostBuffer ) + numberOfUserBuffersPerHostBuffer = maxUserBuffersPerHostBuffer; + + *hostBufferSizeFrames = effectiveUserFramesPerBuffer * numberOfUserBuffersPerHostBuffer; + + /* recompute hostBufferCount to approximate suggestedLatencyFrames now that hostBufferSizeFrames is larger */ + *hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount ); + } + } + + return paNoError; +} + + +static PaError CalculateMaxHostSampleFrameSizeBytes( + int channelCount, + PaSampleFormat hostSampleFormat, + const PaWinMmeStreamInfo *streamInfo, + int *hostSampleFrameSizeBytes ) +{ + unsigned int i; + /* PA WMME streams may aggregate multiple WMME devices. When the stream addresses + more than one device in a single direction, maxDeviceChannelCount is the maximum + number of channels used by a single device. + */ + int maxDeviceChannelCount = channelCount; + int hostSampleSizeBytes = Pa_GetSampleSize( hostSampleFormat ); + if( hostSampleSizeBytes < 0 ) + { + return hostSampleSizeBytes; /* the value of hostSampleSize here is an error code, not a sample size */ + } + + if( streamInfo && ( streamInfo->flags & paWinMmeUseMultipleDevices ) ) + { + maxDeviceChannelCount = streamInfo->devices[0].channelCount; + for( i=1; i< streamInfo->deviceCount; ++i ) + { + if( streamInfo->devices[i].channelCount > maxDeviceChannelCount ) + maxDeviceChannelCount = streamInfo->devices[i].channelCount; + } + } + + *hostSampleFrameSizeBytes = hostSampleSizeBytes * maxDeviceChannelCount; + + return paNoError; } @@ -1482,51 +1541,29 @@ static void ReselectBufferCount( unsigned long bufferSize, */ static PaError CalculateBufferSettings( - unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount, - unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount, + 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, - double sampleRate, unsigned long framesPerBuffer ) + 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; + 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 ) { @@ -1534,46 +1571,45 @@ static PaError CalculateBufferSettings( goto error; } - *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer; + *hostFramesPerInputBuffer = inputStreamInfo->framesPerBuffer; *hostInputBufferCount = inputStreamInfo->bufferCount; } 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( - ((framesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize; - *hostInputBufferCount = hostBufferCount; + result = SelectHostBufferSizeFramesAndHostBufferCount( + (unsigned long)(suggestedInputLatency * sampleRate), /* (truncate) */ + 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 { - *framesPerHostInputBuffer = 0; + *hostFramesPerInputBuffer = 0; *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 ) { @@ -1581,24 +1617,25 @@ static PaError CalculateBufferSettings( goto error; } - *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer; + *hostFramesPerOutputBuffer = outputStreamInfo->framesPerBuffer; *hostOutputBufferCount = outputStreamInfo->bufferCount; - if( inputChannelCount > 0 ) /* full duplex */ { - if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer ) + /* 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( *framesPerHostInputBuffer < *framesPerHostOutputBuffer ) + if( *hostFramesPerInputBuffer < *hostFramesPerOutputBuffer ) { - if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 ) + if( *hostFramesPerOutputBuffer % *hostFramesPerInputBuffer != 0 ) { result = paIncompatibleHostApiSpecificStreamInfo; goto error; @@ -1606,8 +1643,8 @@ static PaError CalculateBufferSettings( } else { - assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer ); - if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 ) + assert( *hostFramesPerInputBuffer > *hostFramesPerOutputBuffer ); + if( *hostFramesPerInputBuffer % *hostFramesPerOutputBuffer != 0 ) { result = paIncompatibleHostApiSpecificStreamInfo; goto error; @@ -1617,111 +1654,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. */ - *framesPerHostInputBuffer = *framesPerHostOutputBuffer; - *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1; + *hostFramesPerInputBuffer = *hostFramesPerOutputBuffer; - 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; + /* output - no low level latency parameters, so compute hostFramesPerOutputBuffer and hostOutputBufferCount + based on userFramesPerBuffer and suggestedOutputLatency. */ - hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat ); - if( hostOutputSampleSize < 0 ) - { - result = hostOutputSampleSize; + int hostOutputFrameSizeBytes; + result = CalculateMaxHostSampleFrameSizeBytes( + outputChannelCount, hostOutputSampleFormat, outputStreamInfo, &hostOutputFrameSizeBytes ); + if( result != paNoError ) goto error; - } - if( outputStreamInfo - && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) + /* compute the output buffer size and count */ + + result = SelectHostBufferSizeFramesAndHostBufferCount( + (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 */ + (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 ) /* full duplex */ { - /* 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; - } + /* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */ - 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_; - - - /* compute the following in bytes, then convert back to frames */ - - SelectBufferSizeAndCount( - ((framesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize; - *hostOutputBufferCount = hostBufferCount; - - - if( inputChannelCount > 0 ) - { /* 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( *framesPerHostOutputBuffer != *framesPerHostInputBuffer ) + if( *hostFramesPerOutputBuffer != *hostFramesPerInputBuffer ) { - if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) + if( hostFramesPerInputBuffer < hostFramesPerOutputBuffer ) { - unsigned long framesPerHostBuffer = *framesPerHostInputBuffer; - - minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; - ReselectBufferCount( - framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - &hostBufferCount ); + *hostFramesPerOutputBuffer = *hostFramesPerInputBuffer; - *framesPerHostOutputBuffer = framesPerHostBuffer; - *hostOutputBufferCount = hostBufferCount; + *hostOutputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + (unsigned long)(suggestedOutputLatency * sampleRate), + *hostOutputBufferCount, + PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ ); } else { - unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer; - - 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 = *hostFramesPerOutputBuffer; - *framesPerHostInputBuffer = framesPerHostBuffer; - *hostInputBufferCount = hostBufferCount; + *hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames( + (unsigned long)(suggestedInputLatency * sampleRate), + *hostFramesPerInputBuffer, + PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ); } } } @@ -1729,7 +1727,7 @@ static PaError CalculateBufferSettings( } else { - *framesPerHostOutputBuffer = 0; + *hostFramesPerOutputBuffer = 0; *hostOutputBufferCount = 0; } @@ -1832,21 +1830,21 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA for( j = 0; j < 2; ++j ) { - if( j == 0 ) - { - /* first, attempt to open the device using WAVEFORMATEXTENSIBLE, - if this fails we fall back to WAVEFORMATEX */ + switch(j){ + case 0: + /* first, attempt to open the device using WAVEFORMATEXTENSIBLE, + if this fails we fall back to WAVEFORMATEX */ - PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount, - sampleFormat, waveFormatTag, sampleRate, channelMask ); + PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount, + sampleFormat, waveFormatTag, sampleRate, channelMask ); + break; + + case 1: + /* retry with WAVEFORMATEX */ - } - else - { - /* retry with WAVEFORMATEX */ - - PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount, - sampleFormat, waveFormatTag, sampleRate ); + PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount, + sampleFormat, waveFormatTag, sampleRate ); + break; } /* REVIEW: consider not firing an event for input when a full duplex @@ -2505,12 +2503,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_GetBufferProcessorInputLatency(&stream->bufferProcessor) - +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate; + (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) + + framesPerHostInputBuffer) / sampleRate; stream->streamRepresentation.streamInfo.outputLatency = - (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) - +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; + (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) + + (framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; stream->streamRepresentation.streamInfo.sampleRate = sampleRate; stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0; @@ -2839,7 +2838,7 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg ) if( waitResult == WAIT_FAILED ) { result = paUnanticipatedHostError; - /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */ + /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread. see http://www.portaudio.com/trac/ticket/143 */ done = 1; } else if( waitResult == WAIT_TIMEOUT ) @@ -2892,7 +2891,7 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg ) we discard all but the most recent. This is an input buffer overflow. FIXME: these buffers should be passed to the callback in a paNeverDropInput - stream. + stream. http://www.portaudio.com/trac/ticket/142 note that it is also possible for an input overflow to happen while the callback is processing a buffer. @@ -3051,7 +3050,9 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg ) { stream->abortProcessing = 1; done = 1; - /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */ + /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort + see: http://www.portaudio.com/trac/ticket/141 + */ result = paNoError; } else @@ -3719,7 +3720,9 @@ static PaError ReadStream( PaStream* s, { /** @todo REVIEW: consider what to do if the input overflows. do we requeue all of the buffers? should we be running - a thread to make sure they are always queued? */ + a thread to make sure they are always queued? + see: http://www.portaudio.com/trac/ticket/117 + */ result = paInputOverflowed; } @@ -3824,7 +3827,9 @@ static PaError WriteStream( PaStream* s, /** @todo REVIEW: consider what to do if the output underflows. do we requeue all the existing buffers with zeros? should we run a separate thread to keep the buffers - enqueued at all times? */ + enqueued at all times? + see: http://www.portaudio.com/trac/ticket/117 + */ result = paOutputUnderflowed; } @@ -4000,8 +4005,3 @@ HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex ) else return 0; } - - - - - diff --git a/bazaar/plugin/portaudio/os/unix/pa_unix_hostapis.c b/bazaar/plugin/portaudio/os/unix/pa_unix_hostapis.c index 339e1b148..4399b875b 100644 --- a/bazaar/plugin/portaudio/os/unix/pa_unix_hostapis.c +++ b/bazaar/plugin/portaudio/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/bazaar/plugin/portaudio/os/win/pa_win_coinitialize.c b/bazaar/plugin/portaudio/os/win/pa_win_coinitialize.c new file mode 100644 index 000000000..e15b145e2 --- /dev/null +++ b/bazaar/plugin/portaudio/os/win/pa_win_coinitialize.c @@ -0,0 +1,144 @@ +/* + * Microsoft COM initialization routines + * Copyright (c) 1999-2011 Ross Bencina, Dmitry Kostjuchenko + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2011 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup win_src + + @brief Microsoft COM initialization routines. +*/ + +#include +#include + +#include "portaudio.h" +#include "pa_util.h" +#include "pa_debugprint.h" + +#include "pa_win_coinitialize.h" + + +#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) && !defined(_WIN32_WCE) /* MSC version 6 and above */ +#pragma comment( lib, "ole32.lib" ) +#endif + + +/* use some special bit patterns here to try to guard against uninitialized memory errors */ +#define PAWINUTIL_COM_INITIALIZED (0xb38f) +#define PAWINUTIL_COM_NOT_INITIALIZED (0xf1cd) + + +PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult ) +{ + HRESULT hr; + + comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED; + + /* + If COM is already initialized CoInitialize will either return + FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different + threading mode. In either case we shouldn't consider it an error + but we need to be careful to not call CoUninitialize() if + RPC_E_CHANGED_MODE was returned. + */ + + hr = CoInitialize(0); /* use legacy-safe equivalent to CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) */ + if( FAILED(hr) && hr != RPC_E_CHANGED_MODE ) + { + PA_DEBUG(("CoInitialize(0) failed. hr=%d\n", hr)); + + if( hr == E_OUTOFMEMORY ) + return paInsufficientMemory; + + { + char *lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + hr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + PaUtil_SetLastHostErrorInfo( hostApiType, hr, lpMsgBuf ); + LocalFree( lpMsgBuf ); + } + + return paUnanticipatedHostError; + } + + if( hr != RPC_E_CHANGED_MODE ) + { + comInitializationResult->state = PAWINUTIL_COM_INITIALIZED; + + /* + Memorize calling thread id and report warning on Uninitialize if + calling thread is different as CoInitialize must match CoUninitialize + in the same thread. + */ + comInitializationResult->initializingThreadId = GetCurrentThreadId(); + } + + return paNoError; +} + + +void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult ) +{ + if( comInitializationResult->state != PAWINUTIL_COM_NOT_INITIALIZED + && comInitializationResult->state != PAWINUTIL_COM_INITIALIZED ){ + + PA_DEBUG(("ERROR: PaWinUtil_CoUninitialize called without calling PaWinUtil_CoInitialize\n")); + } + + if( comInitializationResult->state == PAWINUTIL_COM_INITIALIZED ) + { + DWORD currentThreadId = GetCurrentThreadId(); + if( comInitializationResult->initializingThreadId != currentThreadId ) + { + PA_DEBUG(("ERROR: failed PaWinUtil_CoUninitialize calling thread[%d] does not match initializing thread[%d]\n", + currentThreadId, comInitializationResult->initializingThreadId)); + } + else + { + CoUninitialize(); + + comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED; + } + } +} \ No newline at end of file diff --git a/bazaar/plugin/portaudio/os/win/pa_win_coinitialize.h b/bazaar/plugin/portaudio/os/win/pa_win_coinitialize.h new file mode 100644 index 000000000..a76337c08 --- /dev/null +++ b/bazaar/plugin/portaudio/os/win/pa_win_coinitialize.h @@ -0,0 +1,94 @@ +/* + * Microsoft COM initialization routines + * Copyright (c) 1999-2011 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup win_src + + @brief Microsoft COM initialization routines. +*/ + +#ifndef PA_WIN_COINITIALIZE_H +#define PA_WIN_COINITIALIZE_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** + @brief Data type used to hold the result of an attempt to initialize COM + using PaWinUtil_CoInitialize. Must be retained between a call to + PaWinUtil_CoInitialize and a matching call to PaWinUtil_CoUninitialize. +*/ +typedef struct PaWinUtilComInitializationResult{ + int state; + int initializingThreadId; +} PaWinUtilComInitializationResult; + + +/** + @brief Initialize Microsoft COM subsystem on the current thread. + + @param hostApiType the host API type id of the caller. Used for error reporting. + + @param comInitializationResult An output parameter. The value pointed to by + this parameter stores information required by PaWinUtil_CoUninitialize + to correctly uninitialize COM. The value should be retained and later + passed to PaWinUtil_CoUninitialize. + + If PaWinUtil_CoInitialize returns paNoError, the caller must later call + PaWinUtil_CoUninitialize once. +*/ +PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult ); + + +/** + @brief Uninitialize the Microsoft COM subsystem on the current thread using + the result of a previous call to PaWinUtil_CoInitialize. Must be called on the same + thread as PaWinUtil_CoInitialize. + + @param hostApiType the host API type id of the caller. Used for error reporting. + + @param comInitializationResult An input parameter. A pointer to a value previously + initialized by a call to PaWinUtil_CoInitialize. +*/ +void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_WIN_COINITIALIZE_H */ diff --git a/bazaar/plugin/portaudio/os/win/pa_win_hostapis.c b/bazaar/plugin/portaudio/os/win/pa_win_hostapis.c index df084cbfa..5d2243879 100644 --- a/bazaar/plugin/portaudio/os/win/pa_win_hostapis.c +++ b/bazaar/plugin/portaudio/os/win/pa_win_hostapis.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_hostapis.c 1453 2010-02-16 09:46:08Z dmitrykos $ + * $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 @@ -40,14 +40,18 @@ @ingroup win_src @brief Win32 host API initialization function table. - - @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what - the Unix version does, we should consider being consistent. */ +/* This is needed to make this source file depend on CMake option changes + and at the same time make it transparent for clients not using CMake. +*/ +#ifdef PORTAUDIO_CMAKE_GENERATED +#include "options_cmake.h" +#endif #include "pa_hostapi.h" + #ifdef __cplusplus extern "C" { @@ -68,33 +72,31 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd PaUtilHostApiInitializer *paHostApiInitializers[] = { -#ifndef PA_NO_WMME +#if PA_USE_WMME PaWinMme_Initialize, #endif -#ifndef PA_NO_DS +#if PA_USE_DS PaWinDs_Initialize, #endif -#ifndef PA_NO_ASIO +#if PA_USE_ASIO PaAsio_Initialize, #endif -#ifndef PA_NO_WASAPI +#if PA_USE_WASAPI PaWasapi_Initialize, #endif -/* -#ifndef PA_NO_WDMKS - PaWinWdm_Initialize, +#if PA_USE_WDMKS + PaWinWdm_Initialize, #endif -*/ - //PaSkeleton_Initialize, /* just for testing */ +#if PA_USE_SKELETON + PaSkeleton_Initialize, /* just for testing. last in list so it isn't marked as default. */ +#endif 0 /* NULL terminated array */ }; -int paDefaultHostApiIndex = 0; - diff --git a/bazaar/plugin/portaudio/os/win/pa_win_util.c b/bazaar/plugin/portaudio/os/win/pa_win_util.c index 45eb26867..a9c55d0f8 100644 --- a/bazaar/plugin/portaudio/os/win/pa_win_util.c +++ b/bazaar/plugin/portaudio/os/win/pa_win_util.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_util.c 1437 2009-12-10 08:10:08Z rossb $ + * $Id: pa_win_util.c 1584 2011-02-02 18:58:17Z rossb $ * Portable Audio I/O Library * Win32 platform-specific support functions * @@ -41,9 +41,6 @@ @ingroup win_src @brief Win32 implementation of platform-specific PaUtil support functions. - - @todo Implement workaround for QueryPerformanceCounter() skipping forward - bug. (see msdn kb Q274323). */ #include @@ -129,13 +126,18 @@ double PaUtil_GetTime( void ) if( usePerformanceCounter_ ) { - /* FIXME: - according to this knowledge-base article, QueryPerformanceCounter - can skip forward by seconds! + /* + Note: QueryPerformanceCounter has a known issue where it can skip forward + by a few seconds (!) due to a hardware bug on some PCI-ISA bridge hardware. + This is documented here: http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323& - it may be better to use the rtdsc instruction using inline asm, - however then a method is needed to calculate a ticks/seconds ratio. + The work-arounds are not very paletable and involve querying GetTickCount + at every time step. + + Using rdtsc is not a good option on multi-core systems. + + For now we just use QueryPerformanceCounter(). It's good, most of the time. */ QueryPerformanceCounter( &time ); return time.QuadPart * secondsPerTick_; diff --git a/bazaar/plugin/portaudio/os/win/pa_x86_plain_converters.h b/bazaar/plugin/portaudio/os/win/pa_x86_plain_converters.h index 96b41b857..1623115d6 100644 --- a/bazaar/plugin/portaudio/os/win/pa_x86_plain_converters.h +++ b/bazaar/plugin/portaudio/os/win/pa_x86_plain_converters.h @@ -47,7 +47,7 @@ extern "C" /** - @brief Install optimised converter functions suitable for all IA32 processors + @brief Install optimized converter functions suitable for all IA32 processors It is recommended to call PaUtil_InitializeX86PlainConverters prior to calling Pa_Initialize */ diff --git a/bazaar/plugin/portaudio/pa_upp_ds.c b/bazaar/plugin/portaudio/pa_upp_ds.c index 3217d7472..2bcfd01b6 100644 --- a/bazaar/plugin/portaudio/pa_upp_ds.c +++ b/bazaar/plugin/portaudio/pa_upp_ds.c @@ -1,6 +1,6 @@ -#include "pa_upp.h" - -#if !defined(flagPOSIX) && !defined(PA_NO_DS) - #include "hostapi/dsound/pa_win_ds.c" - //#include "hostapi/dsound/pa_win_ds_dynlink.c" -#endif +#include "pa_upp.h" + +#if !defined(flagPOSIX) && defined(PA_USE_DS) + #include "hostapi/dsound/pa_win_ds.c" + //#include "hostapi/dsound/pa_win_ds_dynlink.c" +#endif diff --git a/bazaar/plugin/portaudio/pa_upp_platform.c b/bazaar/plugin/portaudio/pa_upp_platform.c index da7b53500..b1b44abf9 100644 --- a/bazaar/plugin/portaudio/pa_upp_platform.c +++ b/bazaar/plugin/portaudio/pa_upp_platform.c @@ -1,7 +1,7 @@ #include "pa_upp.h" #ifdef __clang__ - #warning "Clang does not compile PortAudio correctly. You might experience hang ups when using setStreamFinishedCallback." + #warning "Clang has sometimes problems to compile PortAudio correctly. Even after succesfull compilation, you might experience hang ups when using setStreamFinishedCallback." #endif #ifdef flagPOSIX @@ -11,37 +11,39 @@ #include "os/win/pa_win_hostapis.c" #include "os/win/pa_win_util.c" #include "os/win/pa_win_waveformat.c" -// #include "os/win/pa_x86_plain_converters.c" + #include "os/win/pa_win_coinitialize.c" + #include "os/win/pa_x86_plain_converters.c" #endif const char* PortAudioCompileFlags(){ - return + static const char* flags= #ifdef flagPOSIX - #if defined(PA_USE_ALSA) - "ALSA " + #if PA_USE_ALSA + " ALSA" #endif - #if defined(PA_USE_ASIHPI) - "ASIHPI " + #if PA_USE_ASIHPI + " ASIHPI" #endif - #if defined(PA_USE_JACK) - "JACK " + #if PA_USE_JACK + " JACK" #endif - #if defined(PA_USE_OSS) - "OSS " + #if PA_USE_OSS + " OSS" #endif #else - #if !defined(PA_NO_DS) - "DSOUND " + #if PA_USE_DS + " DSOUND" #endif - #if !defined(PA_NO_WASAPI) - "WASAPI " + #if PA_USE_WASAPI + " WASAPI" #endif - #if !defined(PA_NO_WDMKS) - "WDMKS " + #if PA_USE_WDMKS + " WDMKS" #endif - #if !defined(PA_NO_WMME) - "WMME " + #if PA_USE_WMME + " WMME" #endif #endif ; + return flags+1; } diff --git a/bazaar/plugin/portaudio/pa_upp_wasapi.c b/bazaar/plugin/portaudio/pa_upp_wasapi.c index e6ead3419..add74eebc 100644 --- a/bazaar/plugin/portaudio/pa_upp_wasapi.c +++ b/bazaar/plugin/portaudio/pa_upp_wasapi.c @@ -1,5 +1,5 @@ -#include "pa_upp.h" - -#if !defined(flagPOSIX) && !defined(PA_NO_WASAPI) - #include "hostapi/wasapi/pa_win_wasapi.c" -#endif +#include "pa_upp.h" + +#if !defined(flagPOSIX) && defined(PA_USE_WASAPI) + #include "hostapi/wasapi/pa_win_wasapi.c" +#endif diff --git a/bazaar/plugin/portaudio/pa_upp_wdmks.c b/bazaar/plugin/portaudio/pa_upp_wdmks.c index 6dfb4117f..211e9a2e3 100644 --- a/bazaar/plugin/portaudio/pa_upp_wdmks.c +++ b/bazaar/plugin/portaudio/pa_upp_wdmks.c @@ -1,6 +1,6 @@ -#include "pa_upp.h" - -#if !defined(flagPOSIX) & !defined(PA_NO_WDMKS) - #include "hostapi/wdmks/pa_win_wdmks.c" - #include "os/win/pa_win_wdmks_utils.c" -#endif +#include "pa_upp.h" + +#if !defined(flagPOSIX) & defined(PA_USE_WDMKS) + #include "hostapi/wdmks/pa_win_wdmks.c" + #include "os/win/pa_win_wdmks_utils.c" +#endif diff --git a/bazaar/plugin/portaudio/pa_upp_wmme.c b/bazaar/plugin/portaudio/pa_upp_wmme.c index d40d3ae96..e93f775f3 100644 --- a/bazaar/plugin/portaudio/pa_upp_wmme.c +++ b/bazaar/plugin/portaudio/pa_upp_wmme.c @@ -1,5 +1,5 @@ -#include "pa_upp.h" - -#if !defined(flagPOSIX) && !defined(PA_NO_WMME) - #include "hostapi/wmme/pa_win_wmme.c" -#endif +#include "pa_upp.h" + +#if !defined(flagPOSIX) && defined(PA_USE_WMME) + #include "hostapi/wmme/pa_win_wmme.c" +#endif diff --git a/bazaar/plugin/portaudio/portaudio.upp b/bazaar/plugin/portaudio/portaudio.upp index a07ef03f4..d659c73bc 100644 --- a/bazaar/plugin/portaudio/portaudio.upp +++ b/bazaar/plugin/portaudio/portaudio.upp @@ -41,17 +41,17 @@ options(POSIX & OSS) -DPA_USE_OSS=1; options(POSIX & (!ALSA & !JACK & !OSS & !ASIHPI)) "-DPA_USE_ALSA=1 -DPA_USE_OSS=1"; -options(!POSIX & !DSOUND & (WASAPI | WDMKS | WMME | DSOUND)) -DPA_NO_DS=1; +options(!POSIX & DSOUND) -DPA_USE_DS=1; -options(!POSIX & !WASAPI & (WASAPI | WDMKS | WMME | DSOUND)) -DPA_NO_WASAPI=1; +options(!POSIX & WASAPI) -DPA_USE_WASAPI=1; -options(!POSIX & !WDMKS & (WASAPI | WDMKS | WMME | DSOUND)) -DPA_NO_WDMKS=1; +options(!POSIX & WDMKS) -DPA_USE_WDMKS=1; -options(!POSIX & !WMME & (WASAPI | WDMKS | WMME | DSOUND)) -DPA_NO_WMME=1; +options(!POSIX & WMME) -DPA_USE_WMME=1; -options(!POSIX & !ASIO) -DPA_NO_ASIO=1; +options(!POSIX & ASIO) -DPA_USE_ASIO=1; -options(!POSIX & (!DSOUND & !WASAPI & !WDMKS & !WMME)) "-DPA_NO_DS=1 -DPA_NO_WDMKS=1"; +options(!POSIX & (!DSOUND & !WASAPI & !WDMKS & !WMME)) "-DPA_USE_WMME=1 -DPA_USE_WASAPI=1"; include ../portaudio, @@ -95,6 +95,7 @@ file pa_jack.h, pa_win_wmme.h, pa_win_waveformat.h, + os\win\pa_win_coinitialize.h, common/pa_allocation.c, common/pa_converters.c, common/pa_cpuload.c,