.bazaar/plugin/portaudio: sync with upstream

git-svn-id: svn://ultimatepp.org/upp/trunk@3833 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
dolik 2011-09-06 16:54:35 +00:00
parent 6be898dd04
commit ebfccff488
38 changed files with 4311 additions and 2139 deletions

View file

@ -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;
}
}
/* -------------------------------------------------------------------------- */

View file

@ -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)

View file

@ -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 <stdio.h>

View file

@ -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 )

View file

@ -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_<APINAME> is no longer supported, please remove definition and use PA_USE_<APINAME> 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_<APINAME> is no longer supported, please remove definition and use PA_USE_<APINAME> 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 */

View file

@ -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()

View file

@ -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 */
}

View file

@ -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 );
/*@}*/

View file

@ -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;
}

View file

@ -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.

View file

@ -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().
*/

File diff suppressed because it is too large Load diff

View file

@ -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 );
}

View file

@ -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 <stdio.h>
#include <assert.h>
#include <string.h>
@ -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<char*>(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<char*>(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;
}

View file

@ -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; i<numStreams; i++ )
{
printf("Stream #%d = %d---------------------- \n", i, streamIDs[i] );
propSize = sizeof(UInt32);
err = WARNING(AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
printf(" kAudioStreamPropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, streamLatency );
}
}
#endif
/* =================================================================================================== */
/**
* @internal
* Calculate the fixed latency from the system and the device.
* Sum of kAudioStreamPropertyLatency +
* kAudioDevicePropertySafetyOffset +
* kAudioDevicePropertyLatency
*
* Some useful info from Jeff Moore on latency.
* http://osdir.com/ml/coreaudio-api/2010-01/msg00046.html
* http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html
*/
static PaError CalculateFixedDeviceLatency( AudioDeviceID macCoreDeviceId, int isInput, UInt32 *fixedLatencyPtr )
{
PaError err;
UInt32 propSize;
UInt32 deviceLatency;
UInt32 streamLatency;
UInt32 safetyOffset;
AudioStreamID streamIDs[1];
// To get stream latency we have to get a streamID from the device.
// We are only going to look at the first stream so only fetch one stream.
propSize = sizeof(streamIDs);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0]));
if( err != paNoError ) goto error;
if( propSize == sizeof(AudioStreamID) )
{
propSize = sizeof(UInt32);
err = WARNING(AudioStreamGetProperty(streamIDs[0], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
}
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset));
if( err != paNoError ) goto error;
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency));
if( err != paNoError ) goto error;
*fixedLatencyPtr = deviceLatency + streamLatency + safetyOffset;
return err;
error:
return err;
}
/* =================================================================================================== */
static PaError CalculateDefaultDeviceLatencies( AudioDeviceID macCoreDeviceId,
int isInput, UInt32 *lowLatencyFramesPtr,
UInt32 *highLatencyFramesPtr )
{
UInt32 propSize;
UInt32 bufferFrames = 0;
UInt32 fixedLatency = 0;
UInt32 clippedMinBufferSize = 0;
//DumpDeviceProperties( macCoreDeviceId, isInput );
PaError err = CalculateFixedDeviceLatency( macCoreDeviceId, isInput, &fixedLatency );
if( err != paNoError ) goto error;
// For low latency use a small fixed size buffer clipped to the device range.
err = ClipToDeviceBufferSize( macCoreDeviceId, isInput, PA_MAC_SMALL_BUFFER_SIZE, &clippedMinBufferSize );
if( err != paNoError ) goto error;
// For high latency use the default device buffer size.
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames));
if( err != paNoError ) goto error;
*lowLatencyFramesPtr = fixedLatency + clippedMinBufferSize;
*highLatencyFramesPtr = fixedLatency + bufferFrames;
return err;
error:
return err;
}
/* =================================================================================================== */
static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
PaDeviceInfo *deviceInfo,
AudioDeviceID macCoreDeviceId,
@ -407,8 +556,7 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
UInt32 i;
int numChannels = 0;
AudioBufferList *buflist = NULL;
UInt32 frameLatency;
VVDBUG(("GetChannelInfo()\n"));
/* Get the number of channels from the stream configuration.
@ -433,39 +581,33 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
else
deviceInfo->maxOutputChannels = 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 <seb@plogue.com> 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 ) {

View file

@ -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;

View file

@ -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;

View file

@ -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<propsize/sizeof(AudioValueRange); ++i )
VDBUG( ("\t%g-%g\n",
(float) ranges[i].mMinimum,
(float) ranges[i].mMaximum ) );
#endif
VDBUG(("-----\n"));
/* --- now pick the best available framesPerBuffer -- */
for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
{
if( min == -1 || ranges[i].mMinimum < min ) min = ranges[i].mMinimum;
if( ranges[i].mMaximum < requestedFramesPerBuffer ) {
if( best < 0 )
best = ranges[i].mMaximum;
else if( ranges[i].mMaximum > 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 );
}
}

File diff suppressed because it is too large Load diff

View file

@ -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 */

View file

@ -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.

View file

@ -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.

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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 )

View file

@ -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;
}

View file

@ -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;

View file

@ -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 <windows.h>
#include <objbase.h>
#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;
}
}
}

View file

@ -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 */

View file

@ -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;

View file

@ -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 <windows.h>
@ -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_;

View file

@ -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
*/

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,