mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
.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:
parent
6be898dd04
commit
ebfccff488
38 changed files with 4311 additions and 2139 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
||||
/*@}*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
1
bazaar/plugin/portaudio/hostapi/skeleton/README.txt
Normal file
1
bazaar/plugin/portaudio/hostapi/skeleton/README.txt
Normal 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.
|
||||
|
|
@ -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
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
144
bazaar/plugin/portaudio/os/win/pa_win_coinitialize.c
Normal file
144
bazaar/plugin/portaudio/os/win/pa_win_coinitialize.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
94
bazaar/plugin/portaudio/os/win/pa_win_coinitialize.h
Normal file
94
bazaar/plugin/portaudio/os/win/pa_win_coinitialize.h
Normal 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 */
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue