aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portaudio')
-rw-r--r--pd/portaudio/pa_asio/pa_asio.cpp69
-rw-r--r--pd/portaudio/pa_common/pa_converters.c134
-rw-r--r--pd/portaudio/pa_common/pa_dither.c7
-rw-r--r--pd/portaudio/pa_common/pa_endianness.h6
-rw-r--r--pd/portaudio/pa_common/pa_front.c19
-rw-r--r--pd/portaudio/pa_common/pa_process.c27
-rw-r--r--pd/portaudio/pa_common/pa_trace.c8
-rw-r--r--pd/portaudio/pa_common/pa_util.h4
-rw-r--r--pd/portaudio/pa_common/portaudio.h13
-rw-r--r--pd/portaudio/pa_jack/pa_jack.c6
-rw-r--r--pd/portaudio/pa_linux_alsa/pa_linux_alsa.c1150
-rw-r--r--pd/portaudio/pa_mac_core/notes.txt117
-rw-r--r--pd/portaudio/pa_mac_core/pa_mac_core.c2551
-rw-r--r--pd/portaudio/pa_win_ds/pa_win_ds.c58
-rw-r--r--pd/portaudio/pa_win_wdmks/pa_win_wdmks.c1668
-rw-r--r--pd/portaudio/pa_win_wdmks/readme.txt11
-rw-r--r--pd/portaudio/pa_win_wmme/pa_win_wmme.c17
-rw-r--r--pd/portaudio/pablio/ringbuffer.h2
18 files changed, 3909 insertions, 1958 deletions
diff --git a/pd/portaudio/pa_asio/pa_asio.cpp b/pd/portaudio/pa_asio/pa_asio.cpp
index f95f39af..21987c71 100644
--- a/pd/portaudio/pa_asio/pa_asio.cpp
+++ b/pd/portaudio/pa_asio/pa_asio.cpp
@@ -1,5 +1,5 @@
/*
- * $Id: pa_asio.cpp,v 1.7.2.65 2005/02/21 08:07:10 rossbencina Exp $
+ * $Id: pa_asio.cpp,v 1.7.2.68 2005/12/05 04:55:28 rossbencina Exp $
* Portable Audio I/O Library for ASIO Drivers
*
* Author: Stephane Letz
@@ -1081,6 +1081,26 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
for( i=0; i < driverCount; ++i )
{
+
+ PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i]));
+
+ // Since portaudio opens ALL ASIO drivers, and no one else does that,
+ // we face fact that some drivers were not meant for it, drivers which act
+ // like shells on top of REAL drivers, for instance.
+ // so we get duplicate handles, locks and other problems.
+ // so lets NOT try to load any such wrappers.
+ // The ones i [davidv] know of so far are:
+
+ if ( strcmp (names[i],"ASIO DirectX Full Duplex Driver") == 0
+ || strcmp (names[i],"ASIO Multimedia Driver") == 0
+ || strncmp(names[i],"Premiere",8) == 0 //"Premiere Elements Windows Sound 1.0"
+ || strncmp(names[i],"Adobe",5) == 0 ) //"Adobe Default Windows Sound 1.5"
+ {
+ PA_DEBUG(("BLACKLISTED!!!\n"));
+ continue;
+ }
+
+
/* Attempt to load the asio driver... */
if( LoadAsioDriver( names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError )
{
@@ -1733,16 +1753,52 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
}
- PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
- asioError = ASIOSetSampleRate( sampleRate );
- /* Set sample rate */
+
+ // check that the device supports the requested sample rate
+
+ asioError = ASIOCanSampleRate( sampleRate );
+ PA_DEBUG(("ASIOCanSampleRate(%f):%d\n",sampleRate, asioError ));
+
if( asioError != ASE_OK )
{
result = paInvalidSampleRate;
- PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
goto error;
}
- PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
+
+
+ // retrieve the current sample rate, we only change to the requested
+ // sample rate if the device is not already in that rate.
+
+ ASIOSampleRate oldRate;
+ asioError = ASIOGetSampleRate(&oldRate);
+ if( asioError != ASE_OK )
+ {
+ result = paInvalidSampleRate;
+ PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ goto error;
+ }
+ PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate));
+
+ if (oldRate != sampleRate){
+
+ PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
+ asioError = ASIOSetSampleRate( sampleRate );
+ /* Set sample rate */
+ if( asioError != ASE_OK )
+ {
+ result = paInvalidSampleRate;
+ PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ goto error;
+ }
+ PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
+ }
+ else
+ {
+ PA_DEBUG(("No Need to change SR\n"));
+ }
+
+
/*
IMPLEMENT ME:
- if a full duplex stream is requested, check that the combination
@@ -2035,6 +2091,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
return result;
error:
+ PA_DEBUG(("goto errored\n"));
if( stream )
{
if( completedBuffersPlayedEventInited )
diff --git a/pd/portaudio/pa_common/pa_converters.c b/pd/portaudio/pa_common/pa_converters.c
index 4ee73c9a..a7e3a06c 100644
--- a/pd/portaudio/pa_common/pa_converters.c
+++ b/pd/portaudio/pa_common/pa_converters.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $
+ * $Id: pa_converters.c,v 1.1.2.27 2005/11/02 12:14:07 rossbencina Exp $
* Portable Audio I/O Library sample conversion mechanism
*
* Based on the Open Source API proposed by Ross Bencina
@@ -325,7 +325,7 @@ static void Float32_To_Int32(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
+ PaInt32 *dest = (PaInt32*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
@@ -336,7 +336,7 @@ static void Float32_To_Int32(
*dest = lrintf(scaled-0.5f);
#else
double scaled = *src * 0x7FFFFFFF;
- *dest = (signed long) scaled;
+ *dest = (PaInt32) scaled;
#endif
src += sourceStride;
@@ -352,7 +352,7 @@ static void Float32_To_Int32_Dither(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
+ PaInt32 *dest = (PaInt32*)destinationBuffer;
while( count-- )
{
@@ -366,7 +366,7 @@ static void Float32_To_Int32_Dither(
double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
/* use smaller scaler to prevent overflow when we add the dither */
double dithered = ((double)*src * (2147483646.0)) + dither;
- *dest = (signed long) dithered;
+ *dest = (PaInt32) dithered;
#endif
src += sourceStride;
dest += destinationStride;
@@ -381,7 +381,7 @@ static void Float32_To_Int32_Clip(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
+ PaInt32 *dest = (PaInt32*)destinationBuffer;
(void) ditherGenerator; /* unused parameter */
while( count-- )
@@ -394,7 +394,7 @@ static void Float32_To_Int32_Clip(
#else
double scaled = *src * 0x7FFFFFFF;
PA_CLIP_( scaled, -2147483648., 2147483647. );
- *dest = (signed long) scaled;
+ *dest = (PaInt32) scaled;
#endif
src += sourceStride;
@@ -410,7 +410,7 @@ static void Float32_To_Int32_DitherClip(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
+ PaInt32 *dest = (PaInt32*)destinationBuffer;
while( count-- )
{
@@ -426,7 +426,7 @@ static void Float32_To_Int32_DitherClip(
/* use smaller scaler to prevent overflow when we add the dither */
double dithered = ((double)*src * (2147483646.0)) + dither;
PA_CLIP_( dithered, -2147483648., 2147483647. );
- *dest = (signed long) dithered;
+ *dest = (PaInt32) dithered;
#endif
src += sourceStride;
@@ -443,7 +443,7 @@ static void Float32_To_Int24(
{
float *src = (float*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
+ PaInt32 temp;
(void) ditherGenerator; /* unused parameter */
@@ -451,7 +451,7 @@ static void Float32_To_Int24(
{
/* convert to 32 bit and drop the low 8 bits */
double scaled = *src * 0x7FFFFFFF;
- temp = (signed long) scaled;
+ temp = (PaInt32) scaled;
#if defined(PA_LITTLE_ENDIAN)
dest[0] = (unsigned char)(temp >> 8);
@@ -477,7 +477,7 @@ static void Float32_To_Int24_Dither(
{
float *src = (float*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
+ PaInt32 temp;
while( count-- )
{
@@ -487,7 +487,7 @@ static void Float32_To_Int24_Dither(
/* use smaller scaler to prevent overflow when we add the dither */
double dithered = ((double)*src * (2147483646.0)) + dither;
- temp = (signed long) dithered;
+ temp = (PaInt32) dithered;
#if defined(PA_LITTLE_ENDIAN)
dest[0] = (unsigned char)(temp >> 8);
@@ -513,7 +513,7 @@ static void Float32_To_Int24_Clip(
{
float *src = (float*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
+ PaInt32 temp;
(void) ditherGenerator; /* unused parameter */
@@ -522,7 +522,7 @@ static void Float32_To_Int24_Clip(
/* convert to 32 bit and drop the low 8 bits */
double scaled = *src * 0x7FFFFFFF;
PA_CLIP_( scaled, -2147483648., 2147483647. );
- temp = (signed long) scaled;
+ temp = (PaInt32) scaled;
#if defined(PA_LITTLE_ENDIAN)
dest[0] = (unsigned char)(temp >> 8);
@@ -548,7 +548,7 @@ static void Float32_To_Int24_DitherClip(
{
float *src = (float*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
+ PaInt32 temp;
while( count-- )
{
@@ -559,7 +559,7 @@ static void Float32_To_Int24_DitherClip(
double dithered = ((double)*src * (2147483646.0)) + dither;
PA_CLIP_( dithered, -2147483648., 2147483647. );
- temp = (signed long) dithered;
+ temp = (PaInt32) dithered;
#if defined(PA_LITTLE_ENDIAN)
dest[0] = (unsigned char)(temp >> 8);
@@ -584,7 +584,7 @@ static void Float32_To_Int16(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
@@ -610,7 +610,7 @@ static void Float32_To_Int16_Dither(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
while( count-- )
{
@@ -622,7 +622,7 @@ static void Float32_To_Int16_Dither(
#ifdef PA_USE_C99_LRINTF
*dest = lrintf(dithered-0.5f);
#else
- *dest = (signed short) dithered;
+ *dest = (PaInt16) dithered;
#endif
src += sourceStride;
@@ -638,7 +638,7 @@ static void Float32_To_Int16_Clip(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
@@ -646,10 +646,10 @@ static void Float32_To_Int16_Clip(
#ifdef PA_USE_C99_LRINTF
long samp = lrintf((*src * (32767.0f)) -0.5f);
#else
- long samp = (signed long) (*src * (32767.0f));
+ long samp = (PaInt32) (*src * (32767.0f));
#endif
PA_CLIP_( samp, -0x8000, 0x7FFF );
- *dest = (signed short) samp;
+ *dest = (PaInt16) samp;
src += sourceStride;
dest += destinationStride;
@@ -664,7 +664,7 @@ static void Float32_To_Int16_DitherClip(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
@@ -673,12 +673,12 @@ static void Float32_To_Int16_DitherClip(
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
/* use smaller scaler to prevent overflow when we add the dither */
float dithered = (*src * (32766.0f)) + dither;
- signed long samp = (signed long) dithered;
+ PaInt32 samp = (PaInt32) dithered;
PA_CLIP_( samp, -0x8000, 0x7FFF );
#ifdef PA_USE_C99_LRINTF
*dest = lrintf(samp-0.5f);
#else
- *dest = (signed short) samp;
+ *dest = (PaInt16) samp;
#endif
src += sourceStride;
@@ -723,7 +723,7 @@ static void Float32_To_Int8_Dither(
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
/* use smaller scaler to prevent overflow when we add the dither */
float dithered = (*src * (126.0f)) + dither;
- signed long samp = (signed long) dithered;
+ PaInt32 samp = (PaInt32) dithered;
*dest = (signed char) samp;
src += sourceStride;
@@ -744,7 +744,7 @@ static void Float32_To_Int8_Clip(
while( count-- )
{
- signed long samp = (signed long)(*src * (127.0f));
+ PaInt32 samp = (PaInt32)(*src * (127.0f));
PA_CLIP_( samp, -0x80, 0x7F );
*dest = (signed char) samp;
@@ -769,7 +769,7 @@ static void Float32_To_Int8_DitherClip(
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
/* use smaller scaler to prevent overflow when we add the dither */
float dithered = (*src * (126.0f)) + dither;
- signed long samp = (signed long) dithered;
+ PaInt32 samp = (PaInt32) dithered;
PA_CLIP_( samp, -0x80, 0x7F );
*dest = (signed char) samp;
@@ -866,7 +866,7 @@ static void Int32_To_Float32(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
float *dest = (float*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -886,7 +886,7 @@ static void Int32_To_Int24(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
(void) ditherGenerator; /* unused parameter */
@@ -930,13 +930,13 @@ static void Int32_To_Int16(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
{
- *dest = (signed short) ((*src) >> 16);
+ *dest = (PaInt16) ((*src) >> 16);
src += sourceStride;
dest += destinationStride;
@@ -950,15 +950,15 @@ static void Int32_To_Int16_Dither(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- signed long dither;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
+ PaInt32 dither;
while( count-- )
{
/* REVIEW */
dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
- *dest = (signed short) ((((*src)>>1) + dither) >> 15);
+ *dest = (PaInt16) ((((*src)>>1) + dither) >> 15);
src += sourceStride;
dest += destinationStride;
@@ -972,7 +972,7 @@ static void Int32_To_Int8(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
signed char *dest = (signed char*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -992,9 +992,9 @@ static void Int32_To_Int8_Dither(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
signed char *dest = (signed char*)destinationBuffer;
- signed long dither;
+ PaInt32 dither;
while( count-- )
{
@@ -1014,7 +1014,7 @@ static void Int32_To_UInt8(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -1034,7 +1034,7 @@ static void Int32_To_UInt8_Dither(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed long *src = (signed long*)sourceBuffer;
+ PaInt32 *src = (PaInt32*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -1056,7 +1056,7 @@ static void Int24_To_Float32(
{
unsigned char *src = (unsigned char*)sourceBuffer;
float *dest = (float*)destinationBuffer;
- signed long temp;
+ PaInt32 temp;
(void) ditherGenerator; /* unused parameter */
@@ -1088,8 +1088,8 @@ static void Int24_To_Int32(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
unsigned char *src = (unsigned char*)sourceBuffer;
- signed long *dest = (signed long*) destinationBuffer;
- signed long temp;
+ PaInt32 *dest = (PaInt32*) destinationBuffer;
+ PaInt32 temp;
(void) ditherGenerator; /* unused parameter */
@@ -1121,9 +1121,9 @@ static void Int24_To_Int16(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
unsigned char *src = (unsigned char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
- signed short temp;
+ PaInt16 temp;
(void) ditherGenerator; /* unused parameter */
@@ -1132,12 +1132,12 @@ static void Int24_To_Int16(
#if defined(PA_LITTLE_ENDIAN)
/* src[0] is discarded */
- temp = (((signed short)src[1]));
- temp = temp | (signed short)(((signed short)src[2]) << 8);
+ temp = (((PaInt16)src[1]));
+ temp = temp | (PaInt16)(((PaInt16)src[2]) << 8);
#elif defined(PA_BIG_ENDIAN)
/* src[2] is discarded */
- temp = (signed short)(((signed short)src[0]) << 8);
- temp = temp | (((signed short)src[1]));
+ temp = (PaInt16)(((PaInt16)src[0]) << 8);
+ temp = temp | (((PaInt16)src[1]));
#endif
*dest = temp;
@@ -1262,7 +1262,7 @@ static void Int16_To_Float32(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed short *src = (signed short*)sourceBuffer;
+ PaInt16 *src = (PaInt16*)sourceBuffer;
float *dest = (float*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -1283,8 +1283,8 @@ static void Int16_To_Int32(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed short *src = (signed short*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
+ PaInt16 *src = (PaInt16*)sourceBuffer;
+ PaInt32 *dest = (PaInt32*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
@@ -1307,9 +1307,9 @@ static void Int16_To_Int24(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed short *src = (signed short*) sourceBuffer;
+ PaInt16 *src = (PaInt16*) sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
- signed short temp;
+ PaInt16 temp;
(void) ditherGenerator; /* unused parameter */
@@ -1339,7 +1339,7 @@ static void Int16_To_Int8(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed short *src = (signed short*)sourceBuffer;
+ PaInt16 *src = (PaInt16*)sourceBuffer;
signed char *dest = (signed char*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -1359,7 +1359,7 @@ static void Int16_To_Int8_Dither(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed short *src = (signed short*)sourceBuffer;
+ PaInt16 *src = (PaInt16*)sourceBuffer;
signed char *dest = (signed char*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -1379,7 +1379,7 @@ static void Int16_To_UInt8(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed short *src = (signed short*)sourceBuffer;
+ PaInt16 *src = (PaInt16*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -1399,7 +1399,7 @@ static void Int16_To_UInt8_Dither(
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
- signed short *src = (signed short*)sourceBuffer;
+ PaInt16 *src = (PaInt16*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
@@ -1441,7 +1441,7 @@ static void Int8_To_Int32(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
signed char *src = (signed char*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
+ PaInt32 *dest = (PaInt32*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
@@ -1490,12 +1490,12 @@ static void Int8_To_Int16(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
signed char *src = (signed char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
{
- (*dest) = (signed short)((*src) << 8);
+ (*dest) = (PaInt16)((*src) << 8);
src += sourceStride;
dest += destinationStride;
@@ -1551,7 +1551,7 @@ static void UInt8_To_Int32(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
unsigned char *src = (unsigned char*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
+ PaInt32 *dest = (PaInt32*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
@@ -1600,12 +1600,12 @@ static void UInt8_To_Int16(
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
{
unsigned char *src = (unsigned char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
+ PaInt16 *dest = (PaInt16*)destinationBuffer;
(void)ditherGenerator; /* unused parameter */
while( count-- )
{
- (*dest) = (signed short)((*src - 128) << 8);
+ (*dest) = (PaInt16)((*src - 128) << 8);
src += sourceStride;
dest += destinationStride;
diff --git a/pd/portaudio/pa_common/pa_dither.c b/pd/portaudio/pa_common/pa_dither.c
index eb6bec38..0600db62 100644
--- a/pd/portaudio/pa_common/pa_dither.c
+++ b/pd/portaudio/pa_common/pa_dither.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_dither.c,v 1.1.2.5 2003/09/20 21:06:19 rossbencina Exp $
+ * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $
* Portable Audio I/O Library triangular dither generator
*
* Based on the Open Source API proposed by Ross Bencina
@@ -35,6 +35,7 @@
#include "pa_dither.h"
+#include "pa_types.h"
#define PA_DITHER_BITS_ (15)
@@ -59,7 +60,7 @@ signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerato
* Shift before adding to prevent overflow which would skew the distribution.
* Also shift an extra bit for the high pass filter.
*/
-#define DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)
+#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
(((signed long)state->randSeed2)>>DITHER_SHIFT_);
@@ -86,7 +87,7 @@ float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *sta
* Shift before adding to prevent overflow which would skew the distribution.
* Also shift an extra bit for the high pass filter.
*/
-#define DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)
+#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
(((signed long)state->randSeed2)>>DITHER_SHIFT_);
diff --git a/pd/portaudio/pa_common/pa_endianness.h b/pd/portaudio/pa_common/pa_endianness.h
index 052bced6..cb6f8ad5 100644
--- a/pd/portaudio/pa_common/pa_endianness.h
+++ b/pd/portaudio/pa_common/pa_endianness.h
@@ -1,7 +1,7 @@
#ifndef PA_ENDIANNESS_H
#define PA_ENDIANNESS_H
/*
- * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $
+ * $Id: pa_endianness.h,v 1.1.2.5 2006/02/16 16:26:07 bjornroche Exp $
* Portable Audio I/O Library current platform endianness macros
*
* Based on the Open Source API proposed by Ross Bencina
@@ -67,11 +67,13 @@ extern "C"
/* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
- #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(LITTLE_ENDIAN) || defined(__i386) || defined(_M_IX86)
#define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
#else
+
+ #define PA_BIG_ENDIAN
#endif
diff --git a/pd/portaudio/pa_common/pa_front.c b/pd/portaudio/pa_common/pa_front.c
index d61c3008..696df8b2 100644
--- a/pd/portaudio/pa_common/pa_front.c
+++ b/pd/portaudio/pa_common/pa_front.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $
+ * $Id: pa_front.c,v 1.1.2.53 2006/03/20 18:11:09 aknudsen Exp $
* Portable Audio I/O Library Multi-Host API front end
* Validate function parameters and manage multiple host APIs.
*
@@ -256,6 +256,9 @@ static PaError InitializeHostApis( void )
if( hostApis_[hostApisCount_] )
{
+ PaUtilHostApiRepresentation* hostApi = hostApis_[hostApisCount_];
+ assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount );
+ assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount );
hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
@@ -905,8 +908,8 @@ static int SampleFormatIsValid( PaSampleFormat format )
- at least one of inputParameters & outputParmeters is valid (not NULL)
- - if inputParameters & outputParmeters are both valid, that
- inputParameters->device & outputParmeters->device both use the same host api
+ - if inputParameters & outputParameters are both valid, that
+ inputParameters->device & outputParameters->device both use the same host api
PaDeviceIndex inputParameters->device
- is within range (0 to Pa_GetDeviceCount-1) Or:
@@ -1786,11 +1789,12 @@ PaError Pa_ReadStream( PaStream* stream,
{
if( frames == 0 )
{
- result = paInternalError; /** @todo should return a different error code */
+ /* XXX: Should we not allow the implementation to signal any overflow condition? */
+ result = paNoError;
}
else if( buffer == 0 )
{
- result = paInternalError; /** @todo should return a different error code */
+ result = paBadBufferPtr;
}
else
{
@@ -1830,11 +1834,12 @@ PaError Pa_WriteStream( PaStream* stream,
{
if( frames == 0 )
{
- result = paInternalError; /** @todo should return a different error code */
+ /* XXX: Should we not allow the implementation to signal any underflow condition? */
+ result = paNoError;
}
else if( buffer == 0 )
{
- result = paInternalError; /** @todo should return a different error code */
+ result = paBadBufferPtr;
}
else
{
diff --git a/pd/portaudio/pa_common/pa_process.c b/pd/portaudio/pa_common/pa_process.c
index cf711d41..4a52165b 100644
--- a/pd/portaudio/pa_common/pa_process.c
+++ b/pd/portaudio/pa_common/pa_process.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $
+ * $Id: pa_process.c,v 1.1.2.51 2005/10/27 23:28:48 aknudsen Exp $
* Portable Audio I/O Library
* streamCallback <-> host buffer processing adapter
*
@@ -131,6 +131,14 @@ PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
PaError bytesPerSample;
unsigned long tempInputBufferSize, tempOutputBufferSize;
+ if( streamFlags & paNeverDropInput )
+ {
+ /* paNeverDropInput is only valid for full-duplex callback streams, with an unspecified number of frames per buffer. */
+ if( !streamCallback || !(inputChannelCount > 0 && outputChannelCount > 0) ||
+ framesPerUserBuffer != paFramesPerBufferUnspecified )
+ return paInvalidFlag;
+ }
+
/* initialize buffer ptrs to zero so they can be freed if necessary in error */
bp->tempInputBuffer = 0;
bp->tempInputBufferPtrs = 0;
@@ -571,7 +579,8 @@ void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp,
unsigned int channel, void *data, unsigned int stride )
{
assert( channel < bp->outputChannelCount );
-
+ assert( data != NULL );
+
bp->hostOutputChannels[0][channel].data = data;
bp->hostOutputChannels[0][channel].stride = stride;
}
@@ -592,9 +601,8 @@ void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp,
for( i=0; i< channelCount; ++i )
{
- bp->hostOutputChannels[0][channel+i].data = p;
+ PaUtil_SetOutputChannel( bp, channel + i, p, channelCount );
p += bp->bytesPerHostOutputSample;
- bp->hostOutputChannels[0][channel+i].stride = channelCount;
}
}
@@ -604,8 +612,7 @@ void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
{
assert( channel < bp->outputChannelCount );
- bp->hostOutputChannels[0][channel].data = data;
- bp->hostOutputChannels[0][channel].stride = 1;
+ PaUtil_SetOutputChannel( bp, channel, data, 1 );
}
@@ -620,6 +627,7 @@ void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp,
unsigned int channel, void *data, unsigned int stride )
{
assert( channel < bp->outputChannelCount );
+ assert( data != NULL );
bp->hostOutputChannels[1][channel].data = data;
bp->hostOutputChannels[1][channel].stride = stride;
@@ -641,9 +649,8 @@ void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp,
for( i=0; i< channelCount; ++i )
{
- bp->hostOutputChannels[1][channel+i].data = p;
+ PaUtil_Set2ndOutputChannel( bp, channel + i, p, channelCount );
p += bp->bytesPerHostOutputSample;
- bp->hostOutputChannels[1][channel+i].stride = channelCount;
}
}
@@ -653,8 +660,7 @@ void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
{
assert( channel < bp->outputChannelCount );
- bp->hostOutputChannels[1][channel].data = data;
- bp->hostOutputChannels[1][channel].stride = 1;
+ PaUtil_Set2ndOutputChannel( bp, channel, data, 1 );
}
@@ -1180,6 +1186,7 @@ static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp)
for( i=0; i<bp->outputChannelCount; ++i )
{
+ assert( hostOutputChannels[i].data != NULL );
bp->outputConverter( hostOutputChannels[i].data,
hostOutputChannels[i].stride,
srcBytePtr, srcSampleStrideSamples,
diff --git a/pd/portaudio/pa_common/pa_trace.c b/pd/portaudio/pa_common/pa_trace.c
index 83514a05..e36329fd 100644
--- a/pd/portaudio/pa_common/pa_trace.c
+++ b/pd/portaudio/pa_common/pa_trace.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_trace.c,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
+ * $Id: pa_trace.c,v 1.1.1.1.2.4 2005/11/02 12:06:44 rossbencina Exp $
* Portable Audio I/O Library Trace Facility
* Store trace information in real-time for later printing.
*
@@ -42,8 +42,8 @@
#if PA_TRACE_REALTIME_EVENTS
-static char *traceTextArray[MAX_TRACE_RECORDS];
-static int traceIntArray[MAX_TRACE_RECORDS];
+static char *traceTextArray[PA_MAX_TRACE_RECORDS];
+static int traceIntArray[PA_MAX_TRACE_RECORDS];
static int traceIndex = 0;
static int traceBlock = 0;
@@ -65,7 +65,7 @@ void PaUtil_DumpTraceMessages()
printf("%3d: %s = 0x%08X\n",
i, traceTextArray[i], traceIntArray[i] );
}
- ResetTraceMessages();
+ PaUtil_ResetTraceMessages();
fflush(stdout);
}
diff --git a/pd/portaudio/pa_common/pa_util.h b/pd/portaudio/pa_common/pa_util.h
index 149fbca3..d20badd2 100644
--- a/pd/portaudio/pa_common/pa_util.h
+++ b/pd/portaudio/pa_common/pa_util.h
@@ -1,7 +1,7 @@
#ifndef PA_UTIL_H
#define PA_UTIL_H
/*
- * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $
+ * $Id: pa_util.h,v 1.1.2.13 2005/11/09 06:31:42 aknudsen Exp $
* Portable Audio I/O Library implementation utilities header
* common implementation utilities and interfaces
*
@@ -114,7 +114,7 @@ void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
void PaUtil_DebugPrint( const char *format, ... );
-#if (0) /* set to 1 to print debug messages */
+#ifdef PA_ENABLE_DEBUG_OUTPUT
#define PA_DEBUG(x) PaUtil_DebugPrint x ;
#else
#define PA_DEBUG(x)
diff --git a/pd/portaudio/pa_common/portaudio.h b/pd/portaudio/pa_common/portaudio.h
index 341d92c9..c5967b0b 100644
--- a/pd/portaudio/pa_common/portaudio.h
+++ b/pd/portaudio/pa_common/portaudio.h
@@ -2,7 +2,7 @@
#ifndef PORTAUDIO_H
#define PORTAUDIO_H
/*
- * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $
+ * $Id: portaudio.h,v 1.5.2.53 2006/03/20 17:49:38 aknudsen Exp $
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.portaudio.com/
@@ -92,7 +92,8 @@ typedef enum PaErrorCode
paCanNotWriteToACallbackStream, /**< @todo review error code name */
paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */
paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */
- paIncompatibleStreamHostApi
+ paIncompatibleStreamHostApi,
+ paBadBufferPtr
} PaErrorCode;
@@ -247,13 +248,13 @@ typedef struct PaHostApiInfo
*/
int deviceCount;
- /** The the default input device for this host API. The value will be a
+ /** The default input device for this host API. The value will be a
device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
if no default input device is available.
*/
PaDeviceIndex defaultInputDevice;
- /** The the default output device for this host API. The value will be a
+ /** The default output device for this host API. The value will be a
device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
if no default output device is available.
*/
@@ -496,8 +497,8 @@ typedef struct PaStreamParameters
configure their latency based on these parameters, otherwise they may
choose the closest viable latency instead. Unless the suggested latency
is greater than the absolute upper limit for the device implementations
- shouldround the suggestedLatency up to the next practial value - ie to
- provide an equal or higher latency than suggestedLatency whereever possibe.
+ should round the suggestedLatency up to the next practial value - ie to
+ provide an equal or higher latency than suggestedLatency wherever possibe.
Actual latency values for an open stream may be retrieved using the
inputLatency and outputLatency fields of the PaStreamInfo structure
returned by Pa_GetStreamInfo().
diff --git a/pd/portaudio/pa_jack/pa_jack.c b/pd/portaudio/pa_jack/pa_jack.c
index d50ad210..2199365f 100644
--- a/pd/portaudio/pa_jack/pa_jack.c
+++ b/pd/portaudio/pa_jack/pa_jack.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_jack.c,v 1.1.2.19 2004/12/23 18:30:09 aknudsen Exp $
+ * $Id: pa_jack.c,v 1.1.2.20 2005/10/02 22:02:26 aknudsen Exp $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* JACK Implementation by Joshua Haberman
@@ -573,7 +573,6 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi )
jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] );
curDevInfo->defaultLowInputLatency = curDevInfo->defaultHighInputLatency =
jack_port_get_latency( p ) / globalSampleRate;
- free( p );
for( i = 0; clientPorts[i] != NULL; i++)
{
@@ -595,7 +594,6 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi )
jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] );
curDevInfo->defaultLowOutputLatency = curDevInfo->defaultHighOutputLatency =
jack_port_get_latency( p ) / globalSampleRate;
- free( p );
for( i = 0; clientPorts[i] != NULL; i++)
{
@@ -961,13 +959,11 @@ static void CleanUpStream( PaJackStream *stream, int terminateStreamRepresentati
{
if( stream->local_input_ports[i] )
ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_input_ports[i] ), 0 );
- free( stream->remote_output_ports[i] );
}
for( i = 0; i < stream->num_outgoing_connections; ++i )
{
if( stream->local_output_ports[i] )
ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_output_ports[i] ), 0 );
- free( stream->remote_input_ports[i] );
}
if( terminateStreamRepresentation )
diff --git a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c
index 0d0aa1ce..2f88fc2f 100644
--- a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c
+++ b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c
@@ -1,11 +1,11 @@
/*
- * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $
+ * $Id: pa_linux_alsa.c,v 1.1.2.92 2006/03/22 19:36:04 aknudsen Exp $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen
*
* Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
- * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
+ * Copyright (c) 2005-2006 Arve Knudsen <aknuds-1@broadpark.no>
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@@ -71,15 +71,32 @@
{ \
PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
} \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \
+ if( (code) == paUnanticipatedHostError ) \
+ PA_DEBUG(( "Host error description: %s\n", snd_strerror( aErr_ ) )); \
result = (code); \
goto error; \
} \
} while( 0 );
+#define ENSURE_SYSTEM_(expr, success) \
+ do { \
+ if( UNLIKELY( (aErr_ = (expr)) != success ) ) \
+ { \
+ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+ if( pthread_self() != callbackThread_ ) \
+ { \
+ PaUtil_SetLastHostErrorInfo( paALSA, aErr_, strerror( aErr_ ) ); \
+ } \
+ PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \
+ result = paUnanticipatedHostError; \
+ goto error; \
+ } \
+ } while( 0 );
+
#define ASSERT_CALL_(expr, success) \
aErr_ = (expr); \
- assert( aErr_ == success );
+ assert( success == aErr_ );
static int aErr_; /* Used with ENSURE_ */
static pthread_t callbackThread_;
@@ -132,7 +149,7 @@ typedef struct PaAlsaStream
PaUtilBufferProcessor bufferProcessor;
PaAlsaThreading threading;
- unsigned long framesPerUserBuffer;
+ unsigned long framesPerUserBuffer, maxFramesPerHostBuffer;
int primeBuffers;
int callbackMode; /* bool: are we running in callback mode? */
@@ -140,7 +157,7 @@ typedef struct PaAlsaStream
/* the callback thread uses these to poll the sound device(s), waiting
* for data to be ready/available */
- struct pollfd *pfds;
+ struct pollfd* pfds;
int pollTimeout;
/* Used in communication between threads */
@@ -165,7 +182,7 @@ PaAlsaStream;
typedef struct PaAlsaHostApiRepresentation
{
- PaUtilHostApiRepresentation commonHostApiRep;
+ PaUtilHostApiRepresentation baseHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
PaUtilStreamInterface blockingStreamInterface;
@@ -177,7 +194,7 @@ PaAlsaHostApiRepresentation;
typedef struct PaAlsaDeviceInfo
{
- PaDeviceInfo commonDeviceInfo;
+ PaDeviceInfo baseDeviceInfo;
char *alsaName;
int isPlug;
int minInputChannels;
@@ -214,7 +231,7 @@ static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitR
if( th->watchdogRunning )
{
pthread_cancel( th->watchdogThread );
- ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
+ ENSURE_SYSTEM_( pthread_join( th->watchdogThread, &pret ), 0 );
if( pret && pret != PTHREAD_CANCELED )
{
@@ -227,8 +244,12 @@ static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitR
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
/* TODO: Make join time out */
if( !wait )
+ {
+ PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, th->callbackThread ));
pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
- ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
+ }
+ PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, th->callbackThread ));
+ ENSURE_SYSTEM_( pthread_join( th->callbackThread, &pret ), 0 );
if( pret && pret != PTHREAD_CANCELED )
{
@@ -237,6 +258,40 @@ static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitR
free( pret );
}
+error:
+ return result;
+}
+
+/** Lock a pthread_mutex_t.
+ *
+ * @concern ThreadCancellation We're disabling thread cancellation while the thread is holding a lock, so mutexes are
+ * properly unlocked at termination time.
+ */
+static PaError LockMutex( pthread_mutex_t *mtx )
+{
+ PaError result = paNoError;
+ int oldState;
+
+ ENSURE_SYSTEM_( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 );
+ ENSURE_SYSTEM_( pthread_mutex_lock( mtx ), 0 );
+
+error:
+ return result;
+}
+
+/** Unlock a pthread_mutex_t.
+ *
+ * @concern ThreadCancellation Thread cancellation is enabled again after the mutex is properly unlocked.
+ */
+static PaError UnlockMutex( pthread_mutex_t *mtx )
+{
+ PaError result = paNoError;
+ int oldState;
+
+ ENSURE_SYSTEM_( pthread_mutex_unlock( mtx ), 0 );
+ ENSURE_SYSTEM_( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 );
+
+error:
return result;
}
@@ -283,13 +338,15 @@ static void *WatchdogFunc( void *userData )
assert( th );
- pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */
+ /* Execute OnWatchdogExit when exiting */
+ pthread_cleanup_push( &OnWatchdogExit, th );
/* Boost priority of callback thread */
PA_ENSURE( result = BoostPriority( th ) );
if( !result )
{
- pthread_exit( NULL ); /* Boost failed, might as well exit */
+ /* Boost failed, might as well exit */
+ pthread_exit( NULL );
}
cpuTimeThen = th->callbackCpuTime;
@@ -445,7 +502,7 @@ static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThread
{
int policy;
th->watchdogRunning = 1;
- ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
+ ENSURE_SYSTEM_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
/* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
if( wdSpm.sched_priority != prio )
{
@@ -516,6 +573,10 @@ static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation
return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
}
+static void AlsaErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...)
+{
+}
+
PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
@@ -535,6 +596,8 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
+ ENSURE_( snd_lib_error_set_handler(AlsaErrorHandler), paUnanticipatedHostError );
+
PA_ENSURE( BuildDeviceList( alsaHostApi ) );
PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
@@ -588,28 +651,49 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
snd_config_update_free_global();
}
-/*! Determine max channels and default latencies.
+/** Determine max channels and default latencies.
*
* This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for
* traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
* and a suitable result returned. The device is closed before returning.
*/
-static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
- double *defaultHighLatency, double *defaultSampleRate, int isPlug )
+static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, int openBlocking,
+ PaAlsaDeviceInfo* devInfo, int* canMmap )
{
PaError result = paNoError;
snd_pcm_hw_params_t *hwParams;
snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
unsigned int minChans, maxChans;
+ int* minChannels, * maxChannels;
+ double * defaultLowLatency, * defaultHighLatency, * defaultSampleRate =
+ &devInfo->baseDeviceInfo.defaultSampleRate;
double defaultSr = *defaultSampleRate;
assert( pcm );
+ if( StreamDirection_In == mode )
+ {
+ minChannels = &devInfo->minInputChannels;
+ maxChannels = &devInfo->baseDeviceInfo.maxInputChannels;
+ defaultLowLatency = &devInfo->baseDeviceInfo.defaultLowInputLatency;
+ defaultHighLatency = &devInfo->baseDeviceInfo.defaultHighInputLatency;
+ }
+ else
+ {
+ minChannels = &devInfo->minOutputChannels;
+ maxChannels = &devInfo->baseDeviceInfo.maxOutputChannels;
+ defaultLowLatency = &devInfo->baseDeviceInfo.defaultLowOutputLatency;
+ defaultHighLatency = &devInfo->baseDeviceInfo.defaultHighOutputLatency;
+ }
+
ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
snd_pcm_hw_params_alloca( &hwParams );
snd_pcm_hw_params_any( pcm, hwParams );
+ *canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_INTERLEAVED ) >= 0 ||
+ snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED ) >= 0;
+
if( defaultSr >= 0 )
{
/* Could be that the device opened in one mode supports samplerates that the other mode wont have,
@@ -624,10 +708,14 @@ static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels,
if( defaultSr < 0. ) /* Default sample rate not set */
{
unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */
- ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
+ if( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ) < 0)
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
}
-
+
ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
assert( maxChans <= INT_MAX );
@@ -723,22 +811,22 @@ error:
return result;
}
-/* Disregard standard plugins
- * XXX: Might want to make the "default" plugin available, if we can make it work
+/* Disregard some standard plugins
*/
static int IgnorePlugin( const char *pluginId )
{
-#define numIgnored 10
- static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
- "file", "null", "shm"};
- int i;
-
- for( i = 0; i < numIgnored; ++i )
+ /* XXX: dmix and default ignored because after opening and closing, they seem to keep hogging resources.
+ */
+ static const char *ignoredPlugins[] = {"hw", "plughw", "plug", "dsnoop", "tee",
+ "file", "null", "shm", "cards", "dmix", "default", NULL};
+ int i = 0;
+ while( ignoredPlugins[i] )
{
if( !strcmp( pluginId, ignoredPlugins[i] ) )
{
return 1;
}
+ ++i;
}
return 0;
@@ -747,7 +835,7 @@ static int IgnorePlugin( const char *pluginId )
/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
{
- PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
+ PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep;
PaAlsaDeviceInfo *deviceInfoArray;
int cardIdx = -1, devIdx = 0;
snd_ctl_card_info_t *cardInfo;
@@ -763,8 +851,8 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
blocking = 0;
/* These two will be set to the first working input and output device, respectively */
- commonApi->info.defaultInputDevice = paNoDevice;
- commonApi->info.defaultOutputDevice = paNoDevice;
+ baseApi->info.defaultInputDevice = paNoDevice;
+ baseApi->info.defaultOutputDevice = paNoDevice;
/* count the devices by enumerating all the card numbers */
@@ -788,7 +876,10 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
/* Acquire name of card */
if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
- continue; /* Unable to open card :( */
+ {
+ /* Unable to open card :( */
+ continue;
+ }
snd_ctl_card_info( ctl, cardInfo );
PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
@@ -805,11 +896,15 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
snd_pcm_info_set_subdevice( pcmInfo, 0 );
snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+ {
hasCapture = 1;
+ }
snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+ {
hasPlayback = 1;
+ }
if( !hasPlayback && !hasCapture )
{
@@ -843,29 +938,42 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
}
/* Iterate over plugin devices */
- snd_config_update();
+ if( NULL == snd_config )
+ {
+ /* snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */
+ ENSURE_( snd_config_update(), paUnanticipatedHostError );
+ PA_DEBUG(( "Updating snd_config\n" ));
+ }
+ assert( snd_config );
if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
{
snd_config_iterator_t i, next;
snd_config_for_each( i, next, topNode )
{
- const char *tpStr = NULL, *idStr = NULL;
- char *alsaDeviceName, *deviceName;
- snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
- if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
- continue;
+ const char *tpStr = "unknown", *idStr = NULL;
+ int err = 0;
- ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
- ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
+ char *alsaDeviceName, *deviceName;
+ snd_config_t *n = snd_config_iterator_entry( i ), * tp = NULL;;
+ if( (err = snd_config_search( n, "type", &tp )) < 0 )
+ {
+ if( -ENOENT != err )
+ {
+ ENSURE_(err, paUnanticipatedHostError);
+ }
+ }
+ else
+ {
+ ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
+ }
ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
if( IgnorePlugin( idStr ) )
{
PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
continue;
}
-
PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
@@ -894,7 +1002,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
/* allocate deviceInfo memory based on the number of devices */
- PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
/* allocate all device info structs in a contiguous block */
@@ -909,10 +1017,11 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
{
snd_pcm_t *pcm;
PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
- PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
+ PaDeviceInfo *baseDeviceInfo = &deviceInfo->baseDeviceInfo;
+ int canMmap = -1;
/* Zero fields */
- InitializeDeviceInfo( commonDeviceInfo );
+ InitializeDeviceInfo( baseDeviceInfo );
/* to determine device capabilities, we must open the device and query the
* hardware parameter configuration space */
@@ -921,50 +1030,63 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
if( deviceNames[i].hasCapture &&
snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
{
- if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
- &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
- &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
- continue; /* Error */
+ if( GropeDevice( pcm, deviceNames[i].isPlug, StreamDirection_In, blocking, deviceInfo,
+ &canMmap ) != paNoError )
+ {
+ /* Error */
+ PA_DEBUG(("%s: Failed groping %s for capture\n", __FUNCTION__, deviceNames[i].alsaName));
+ continue;
+ }
}
/* Query playback */
if( deviceNames[i].hasPlayback &&
snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
{
- if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
- &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
- &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
- continue; /* Error */
+ if( GropeDevice( pcm, deviceNames[i].isPlug, StreamDirection_Out, blocking, deviceInfo,
+ &canMmap ) != paNoError )
+ {
+ /* Error */
+ PA_DEBUG(("%s: Failed groping %s for playback\n", __FUNCTION__, deviceNames[i].alsaName));
+ continue;
+ }
+ }
+
+ if( 0 == canMmap )
+ {
+ PA_DEBUG(("%s: Device %s doesn't support mmap\n", __FUNCTION__, deviceNames[i].alsaName));
+ continue;
}
- commonDeviceInfo->structVersion = 2;
- commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
- commonDeviceInfo->name = deviceNames[i].name;
+ baseDeviceInfo->structVersion = 2;
+ baseDeviceInfo->hostApi = alsaApi->hostApiIndex;
+ baseDeviceInfo->name = deviceNames[i].name;
deviceInfo->alsaName = deviceNames[i].alsaName;
deviceInfo->isPlug = deviceNames[i].isPlug;
/* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
* Should now be safe to add device info, unless the device supports neither capture nor playback
*/
- if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
+ if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 )
{
- if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
- commonApi->info.defaultInputDevice = devIdx;
- if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
- commonApi->info.defaultOutputDevice = devIdx;
-
- commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
+ if( baseApi->info.defaultInputDevice == paNoDevice && baseDeviceInfo->maxInputChannels > 0 )
+ baseApi->info.defaultInputDevice = devIdx;
+ if( baseApi->info.defaultOutputDevice == paNoDevice && baseDeviceInfo->maxOutputChannels > 0 )
+ baseApi->info.defaultOutputDevice = devIdx;
+ PA_DEBUG(("%s: Adding device %s\n", __FUNCTION__, deviceNames[i].name));
+ baseApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
}
}
free( deviceNames );
- commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
+ baseApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
end:
return result;
error:
- goto end; /* No particular action */
+ /* No particular action */
+ goto end;
}
/* Check against known device capabilities */
@@ -988,14 +1110,16 @@ static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilH
PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
paIncompatibleHostApiSpecificStreamInfo );
+ PA_UNLESS( streamInfo->deviceString != NULL, paInvalidDevice );
- return paNoError; /* Skip further checking */
+ /* Skip further checking */
+ return paNoError;
}
assert( deviceInfo );
assert( parameters->hostApiSpecificStreamInfo == NULL );
- maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels :
- deviceInfo->commonDeviceInfo.maxOutputChannels);
+ maxChans = (StreamDirection_In == mode ? deviceInfo->baseDeviceInfo.maxInputChannels :
+ deviceInfo->baseDeviceInfo.maxOutputChannels);
PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
error:
@@ -1003,7 +1127,6 @@ error:
}
/* Given an open stream, what sample formats are available? */
-
static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
{
PaSampleFormat available = 0;
@@ -1091,10 +1214,12 @@ static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStr
else
deviceName = streamInfo->deviceString;
+ PA_DEBUG(( "%s: Opening device %s\n", __FUNCTION__, deviceName ));
if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK )) < 0 )
{
- *pcm = NULL; /* Not to be closed */
+ /* Not to be closed */
+ *pcm = NULL;
ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination );
}
ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
@@ -1148,11 +1273,20 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const
PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+ {
+ /* It happens that this call fails because the device is busy */
+ int ret = 0;
+ if( (ret = snd_pcm_hw_params( pcm, hwParams )) < 0)
+ {
+ ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paUnanticipatedHostError );
+ }
+ }
end:
if( pcm )
+ {
snd_pcm_close( pcm );
+ }
return result;
error:
@@ -1215,7 +1349,7 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa
if( NULL == params->hostApiSpecificStreamInfo )
{
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device );
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->baseHostApiRep, params->device );
self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
: devInfo->minOutputChannels );
}
@@ -1225,7 +1359,7 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa
self->numHostChannels = params->channelCount;
}
- PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) );
+ PA_ENSURE( AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm ) );
self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
@@ -1253,25 +1387,18 @@ static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
PaUtil_FreeMemory( self->userBuffers );
}
-/** Configure the associated ALSA pcm.
+int nearbyint_(float value) {
+ if( value - (int)value > .5 )
+ return (int)ceil( value );
+ return (int)floor( value );
+}
+
+/** Initiate configuration, preparing for determining a period size suitable for both capture and playback components.
*
*/
-static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long
- framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency )
+static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *self, const PaStreamParameters *params,
+ int primeBuffers, snd_pcm_hw_params_t *hwParams, double *sampleRate )
{
- /*
- int numPeriods;
-
- if( getenv("PA_NUMPERIODS") != NULL )
- numPeriods = atoi( getenv("PA_NUMPERIODS") );
- else
- numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1;
-
- PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer ));
- if( numPeriods <= 1 )
- numPeriods = 2;
- */
-
/* Configuration consists of setting all of ALSA's parameters.
* These parameters come in two flavors: hardware parameters
* and software paramters. Hardware parameters will affect
@@ -1279,28 +1406,23 @@ static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, con
* affect the way ALSA interacts with me, the user-level client.
*/
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_sw_params_t *swParams;
PaError result = paNoError;
snd_pcm_access_t accessMode, alternateAccessMode;
- unsigned int numPeriods, minPeriods = 2;
int dir = 0;
snd_pcm_t *pcm = self->pcm;
- PaTime latency = params->suggestedLatency;
double sr = *sampleRate;
- *returnedLatency = -1.;
+ unsigned int minPeriods = 2;
- snd_pcm_hw_params_alloca( &hwParams );
- snd_pcm_sw_params_alloca( &swParams );
-
- self->framesPerBuffer = framesPerHostBuffer;
+ /* self->framesPerBuffer = framesPerHostBuffer; */
/* ... fill up the configuration space with all possibile
* combinations of parameters this device will accept */
ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+ /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
if( self->userInterleaved )
{
@@ -1312,11 +1434,23 @@ static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, con
accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
}
-
/* If requested access mode fails, try alternate mode */
if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
{
- ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError );
+ int err = 0;
+ if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0)
+ {
+ result = paUnanticipatedHostError;
+ if( -EINVAL == err )
+ {
+ PaUtil_SetLastHostErrorInfo( paALSA, err, "PA ALSA requires that a device supports mmap access" );
+ }
+ else
+ {
+ PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
+ }
+ goto error;
+ }
/* Flip mode */
self->hostInterleaved = !self->userInterleaved;
}
@@ -1334,56 +1468,64 @@ static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, con
ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
- /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
-
- /* Find an acceptable number of periods */
- numPeriods = (latency * sr) / self->framesPerBuffer + 1;
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
- /* Minimum of periods should already be 2 */
- PA_UNLESS( numPeriods >= 2, paInternalError );
+ *sampleRate = sr;
+
+end:
+ return result;
+
+error:
+ /* No particular action */
+ goto end;
+}
+
+/** Finish the configuration of the component's ALSA device.
+ *
+ * As part of this method, the component's bufferSize attribute will be set.
+ * @param latency: The latency for this component.
+ */
+static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *self, snd_pcm_hw_params_t* hwParams,
+ const PaStreamParameters *params, int primeBuffers, double sampleRate, PaTime* latency )
+{
+ PaError result = paNoError;
+ snd_pcm_sw_params_t* swParams;
+ snd_pcm_uframes_t bufSz = 0;
+ *latency = -1.;
+
+ snd_pcm_sw_params_alloca( &swParams );
+
+ bufSz = (params->suggestedLatency * sampleRate) + self->framesPerBuffer; /* One period does not count as latency */
+ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError );
/* Set the parameters! */
- ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params( self->pcm, hwParams ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
-
/* Latency in seconds, one period is not counted as latency */
- latency = (numPeriods - 1) * self->framesPerBuffer / sr;
+ *latency = (self->bufferSize - self->framesPerBuffer) / sampleRate;
/* Now software parameters... */
- ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_start_threshold( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_stop_threshold( self->pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
/* Silence buffer in the case of underrun */
if( !primeBuffers ) /* XXX: Make sense? */
{
snd_pcm_uframes_t boundary;
ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_silence_threshold( self->pcm, swParams, 0 ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_silence_size( self->pcm, swParams, boundary ), paUnanticipatedHostError );
}
- ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
/* Set the parameters! */
- ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
-
- *sampleRate = sr;
- *returnedLatency = latency;
-
-end:
- return result;
+ ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError );
error:
- goto end; /* No particular action */
+ return result;
}
static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
@@ -1475,78 +1617,171 @@ static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frame
return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
}
-/** Set up ALSA stream parameters.
+/** Determine size per host buffer.
*
+ * During this method call, the component's framesPerBuffer attribute gets computed, and the corresponding period size
+ * gets configured for the device.
+ * @param accurate: If the configured period size is non-integer, this will be set to 0.
*/
-static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
- *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
- unsigned long *maxHostBufferSize )
+static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamComponent* self, const PaStreamParameters* params,
+ unsigned long framesPerUserBuffer, double sampleRate, snd_pcm_hw_params_t* hwParams, int* accurate )
{
PaError result = paNoError;
- double realSr = sampleRate;
-
- if( self->capture.pcm )
- PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
- self->callbackMode, &realSr, inputLatency ) );
- if( self->playback.pcm )
- PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
- self->callbackMode, &realSr, outputLatency ) );
+ unsigned long bufferSize = params->suggestedLatency * sampleRate, framesPerHostBuffer;
+ int dir = 0;
+
+ {
+ snd_pcm_uframes_t tmp;
+ snd_pcm_hw_params_get_buffer_size_min( hwParams, &tmp );
+ bufferSize = PA_MAX( bufferSize, tmp );
+ snd_pcm_hw_params_get_buffer_size_max( hwParams, &tmp );
+ bufferSize = PA_MIN( bufferSize, tmp );
+ }
- /* Should be exact now */
- self->streamRepresentation.streamInfo.sampleRate = realSr;
+ assert( bufferSize > 0 );
- /* this will cause the two streams to automatically start/stop/prepare in sync.
- * We only need to execute these operations on one of the pair.
- * A: We don't want to do this on a blocking stream.
- */
- if( self->callbackMode && self->capture.pcm && self->playback.pcm )
+ if( framesPerUserBuffer != paFramesPerBufferUnspecified )
{
- int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
- if( err >= 0 )
- self->pcmsSynced = 1;
- else
- PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
- }
+ /* Preferably the host buffer size should be a multiple of the user buffer size */
- /* Frames per host buffer for the stream is set as a compromise between the two directions */
- framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
- self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
- self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */
+ if( bufferSize > framesPerUserBuffer )
+ {
+ snd_pcm_uframes_t remainder = bufferSize % framesPerUserBuffer;
+ if( remainder > framesPerUserBuffer / 2. )
+ bufferSize += framesPerUserBuffer - remainder;
+ else
+ bufferSize -= remainder;
- *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
- self->playback.pcm ? self->playback.bufferSize : 0 );
+ assert( bufferSize % framesPerUserBuffer == 0 );
+ }
+ else if( framesPerUserBuffer % bufferSize != 0 )
+ {
+ /* Find a good compromise between user specified latency and buffer size */
+ if( bufferSize > framesPerUserBuffer * .75 )
+ {
+ bufferSize = framesPerUserBuffer;
+ }
+ else
+ {
+ snd_pcm_uframes_t newSz = framesPerUserBuffer;
+ while( newSz / 2 >= bufferSize )
+ {
+ if( framesPerUserBuffer % (newSz / 2) != 0 )
+ {
+ /* No use dividing any further */
+ break;
+ }
+ newSz /= 2;
+ }
+ bufferSize = newSz;
+ }
- /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
- self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
+ assert( framesPerUserBuffer % bufferSize == 0 );
+ }
+ }
- if( self->callbackMode )
+ /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
+ finding a combination of period/buffer size which best fits these constraints */
{
- /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
- * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
- * of host buffer frames with what the user specified */
- if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
+ unsigned numPeriods = 4, maxPeriods = 0;
+ /* It may be that the device only supports 2 periods for instance */
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError );
+ assert( maxPeriods > 1 );
+ /* One period is not counted as latency */
+ maxPeriods -= 1;
+ numPeriods = PA_MIN( maxPeriods, numPeriods );
+
+ if( framesPerUserBuffer != paFramesPerBufferUnspecified )
{
- /* self->alignFrames = 1; */
+ framesPerHostBuffer = framesPerUserBuffer;
+ if( framesPerHostBuffer < bufferSize )
+ {
+ while( bufferSize / framesPerHostBuffer > numPeriods )
+ {
+ framesPerHostBuffer *= 2;
+ }
+ }
+ else
+ {
+ while( bufferSize / framesPerHostBuffer < numPeriods )
+ {
+ if( framesPerUserBuffer % (framesPerHostBuffer / 2) != 0 )
+ {
+ /* Can't be divided any further */
+ break;
+ }
+ framesPerHostBuffer /= 2;
+ }
+ }
- /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
- * on block adaption */
- /*
- if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
- self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
- self->useBlockAdaption = 1;
+ if( framesPerHostBuffer < framesPerUserBuffer )
+ {
+ assert( framesPerUserBuffer % framesPerHostBuffer == 0 );
+ if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
+ {
+ if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 )
+ framesPerHostBuffer *= 2;
+ else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 )
+ framesPerHostBuffer /= 2;
+ }
+ }
else
- self->alignFrames = 1;
- */
+ {
+ assert( framesPerHostBuffer % framesPerUserBuffer == 0 );
+ if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
+ {
+ if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 )
+ framesPerHostBuffer += framesPerUserBuffer;
+ else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 )
+ framesPerHostBuffer -= framesPerUserBuffer;
+ }
+ }
+ }
+ else
+ {
+ framesPerHostBuffer = bufferSize / numPeriods;
+ }
+ }
+
+ assert( framesPerHostBuffer > 0 );
+ {
+ snd_pcm_uframes_t min = 0, max = 0;
+ ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParams, &min, NULL ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParams, &max, NULL ), paUnanticipatedHostError );
+
+ if( framesPerHostBuffer < min )
+ {
+ framesPerHostBuffer = min;
+ PA_DEBUG(( "%s: The determined period size (%lu) is less than minimum (%lu)\n", __FUNCTION__,
+ framesPerHostBuffer, min ));
+ }
+ else if( framesPerHostBuffer > max )
+ {
+ framesPerHostBuffer = max;
+ PA_DEBUG(( "%s: The determined period size (%lu) is greater than maximum (%lu)\n", __FUNCTION__,
+ framesPerHostBuffer, max ));
+ }
+
+ assert( framesPerHostBuffer >= min && framesPerHostBuffer <= max );
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ),
+ paUnanticipatedHostError );
+ if( dir != 0 )
+ {
+ PA_DEBUG(( "%s: The configured period size is non-integer.\n", __FUNCTION__, dir ));
+ *accurate = 0;
}
}
+ self->framesPerBuffer = framesPerHostBuffer;
error:
return result;
}
-/* We need to determine how many frames per host buffer to use. Our
+/* We need to determine how many frames per host buffer (period) to use. Our
* goals are to provide the best possible performance, but also to
- * most closely honor the requested latency settings. Therefore this
+ * honor the requested latency settings as closely as we can. Therefore this
* decision is based on:
*
* - the period sizes that playback and/or capture support. The
@@ -1556,6 +1791,18 @@ error:
* We want to make period_size*(num_periods-1) to be as close as possible
* to latency*rate for both playback and capture.
*
+ * This method will determine suitable period sizes for capture and playback handles, and report the maximum number of
+ * frames per host buffer. The latter is relevant, in case we should be so unfortunate that the period size differs
+ * between capture and playback. If this should happen, the stream's hostBufferSizeMode attribute will be set to
+ * paUtilBoundedHostBufferSize, because the best we can do is limit the size of individual host buffers to the upper
+ * bound. The size of host buffers scheduled for processing should only matter if the user has specified a buffer size,
+ * but when he/she does we must strive for an optimal configuration. By default we'll opt for a fixed host buffer size,
+ * which should be fine if the period size is the same for capture and playback. In general, if there is a specified user
+ * buffer size, this method tries it best to determine a period size which is a multiple of the user buffer size.
+ *
+ * The framesPerBuffer attributes of the individual capture and playback components of the stream are set to corresponding
+ * values determined here. Since these should be reported as
+ *
* This is one of those blocks of code that will just take a lot of
* refinement to be any good.
*
@@ -1563,175 +1810,273 @@ error:
* to find a number of frames per buffer acceptable to both devices
* TODO: Implement an algorithm to find the value closest to acceptance
* by both devices, to minimize difference between period sizes?
+ *
+ * @param determinedFramesPerHostBuffer: The determined host buffer size.
*/
-static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
+static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double sampleRate, const PaStreamParameters* inputParameters,
+ const PaStreamParameters* outputParameters, unsigned long framesPerUserBuffer, snd_pcm_hw_params_t* hwParamsCapture,
+ snd_pcm_hw_params_t* hwParamsPlayback, PaUtilHostBufferSizeMode* hostBufferSizeMode )
{
PaError result = paNoError;
- unsigned long framesPerBuffer = 0;
- int numHostInputChannels = 0, numHostOutputChannels = 0;
-
- /* XXX: Clean this up */
- if( stream->capture.pcm )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
- numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
- }
- if( stream->playback.pcm )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
- numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
- }
+ unsigned long framesPerHostBuffer = 0;
+ int dir = 0;
+ int accurate = 1;
- if( stream->capture.pcm && stream->playback.pcm )
+ if( self->capture.pcm && self->playback.pcm )
{
- snd_pcm_uframes_t desiredLatency, e;
- snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
- optimalPeriodSize, periodSize;
- int dir = 0;
- unsigned int minPeriods = 2;
+ if( framesPerUserBuffer == paFramesPerBufferUnspecified )
+ {
+ snd_pcm_uframes_t desiredLatency, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize,
+ minCapture, minPlayback, maxCapture, maxPlayback;
+
+ /* Come up with a common desired latency */
+
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
+ minPeriodSize = PA_MAX( minPlayback, minCapture );
+ maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
+ PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination );
+
+ desiredLatency = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
+ * sampleRate);
+ /* Clamp desiredLatency */
+ {
+ snd_pcm_uframes_t maxBufferSize;
+ snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback;
+ ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError );
+ maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback );
- snd_pcm_t *pcm;
- snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
+ desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
+ }
- snd_pcm_hw_params_alloca( &hwParamsPlayback );
- snd_pcm_hw_params_alloca( &hwParamsCapture );
+ /* Find the closest power of 2 */
+ e = ilogb( minPeriodSize );
+ if( minPeriodSize & (minPeriodSize - 1) )
+ e += 1;
+ periodSize = (snd_pcm_uframes_t)pow( 2, e );
- /* Come up with a common desired latency */
- pcm = stream->playback.pcm;
- snd_pcm_hw_params_any( pcm, hwParamsPlayback );
- ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
- paBadIODeviceCombination );
+ while( periodSize <= maxPeriodSize )
+ {
+ if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
+ snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
+ break; /* Ok! */
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
+ periodSize *= 2;
+ }
- pcm = stream->capture.pcm;
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
- paBadIODeviceCombination );
+ /* 4 periods considered optimal */
+ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+ optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
+ /* Find the closest power of 2 */
+ e = ilogb( optimalPeriodSize );
+ if( optimalPeriodSize & (optimalPeriodSize - 1) )
+ e += 1;
+ optimalPeriodSize = (snd_pcm_uframes_t)pow( 2, e );
- minPeriodSize = PA_MAX( minPlayback, minCapture );
- maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
+ while( optimalPeriodSize >= periodSize )
+ {
+ if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) < 0 )
+ continue;
+ if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 )
+ break;
+ optimalPeriodSize /= 2;
+ }
+ if( optimalPeriodSize > periodSize )
+ periodSize = optimalPeriodSize;
- desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
- * sampleRate);
- /* Clamp desiredLatency */
+ if( periodSize <= maxPeriodSize )
+ {
+ /* Looks good, the periodSize _should_ be acceptable by both devices */
+ ENSURE_( snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ),
+ paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ),
+ paUnanticipatedHostError );
+ self->capture.framesPerBuffer = self->playback.framesPerBuffer = periodSize;
+ framesPerHostBuffer = periodSize;
+ }
+ else
+ {
+ /* Unable to find a common period size, oh well */
+ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+ optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+ self->capture.framesPerBuffer = optimalPeriodSize;
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ),
+ paUnanticipatedHostError );
+ self->playback.framesPerBuffer = optimalPeriodSize;
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( self->playback.pcm, hwParamsPlayback, &self->playback.framesPerBuffer, &dir ),
+ paUnanticipatedHostError );
+ framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer );
+ *hostBufferSizeMode = paUtilBoundedHostBufferSize;
+ }
+ }
+ else
{
- snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
- ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
- maxBufferSize = PA_MIN( maxBufferSize, tmp );
+ /* We choose the simple route and determine a suitable number of frames per buffer for one component of
+ * the stream, then we hope that this will work for the other component too (it should!).
+ */
- desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
- }
+ unsigned maxPeriods = 0;
+ PaAlsaStreamComponent* first = &self->capture, * second = &self->playback;
+ const PaStreamParameters* firstStreamParams = inputParameters;
+ snd_pcm_hw_params_t* firstHwParams = hwParamsCapture, * secondHwParams = hwParamsPlayback;
- /* Find the closest power of 2 */
- e = ilogb( minPeriodSize );
- if( minPeriodSize & (minPeriodSize - 1) )
- e += 1;
- periodSize = (snd_pcm_uframes_t) pow( 2, e );
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError );
+ if( maxPeriods < 4 )
+ {
+ /* The playback component is trickier to get right, try that first */
+ first = &self->playback;
+ second = &self->capture;
+ firstStreamParams = outputParameters;
+ firstHwParams = hwParamsPlayback;
+ secondHwParams = hwParamsCapture;
+ }
- while( periodSize <= maxPeriodSize )
- {
- if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
- snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
- break; /* Ok! */
+ PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( first, firstStreamParams, framesPerUserBuffer,
+ sampleRate, firstHwParams, &accurate ) );
- periodSize *= 2;
+ second->framesPerBuffer = first->framesPerBuffer;
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( second->pcm, secondHwParams, &second->framesPerBuffer, &dir ),
+ paUnanticipatedHostError );
+ if( self->capture.framesPerBuffer == self->playback.framesPerBuffer )
+ {
+ framesPerHostBuffer = self->capture.framesPerBuffer;
+ }
+ else
+ {
+ framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer );
+ *hostBufferSizeMode = paUtilBoundedHostBufferSize;
+ }
+ }
+ }
+ else /* half-duplex is a slightly simpler case */
+ {
+ if( self->capture.pcm )
+ {
+ PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->capture, inputParameters, framesPerUserBuffer,
+ sampleRate, hwParamsCapture, &accurate) );
+ framesPerHostBuffer = self->capture.framesPerBuffer;
+ }
+ else
+ {
+ assert( self->playback.pcm );
+ PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->playback, outputParameters, framesPerUserBuffer,
+ sampleRate, hwParamsPlayback, &accurate ) );
+ framesPerHostBuffer = self->playback.framesPerBuffer;
}
+ }
- /* 4 periods considered optimal */
- optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
- optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+ PA_UNLESS( framesPerHostBuffer != 0, paInternalError );
+ self->maxFramesPerHostBuffer = framesPerHostBuffer;
- /* Find the closest power of 2 */
- e = ilogb( optimalPeriodSize );
- if( optimalPeriodSize & (optimalPeriodSize - 1) )
- e += 1;
- optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e );
+ if( !accurate )
+ {
+ /* Don't know the exact size per host buffer */
+ *hostBufferSizeMode = paUtilBoundedHostBufferSize;
+ /* Raise upper bound */
+ ++self->maxFramesPerHostBuffer;
+ }
- while( optimalPeriodSize >= periodSize )
- {
- pcm = stream->playback.pcm;
- if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 )
- continue;
+error:
+ return result;
+}
- pcm = stream->capture.pcm;
- if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 )
- break;
+/** Set up ALSA stream parameters.
+ *
+ */
+static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters*
+ outParams, double sampleRate, unsigned long framesPerUserBuffer, double* inputLatency, double* outputLatency,
+ PaUtilHostBufferSizeMode* hostBufferSizeMode )
+{
+ PaError result = paNoError;
+ double realSr = sampleRate;
+ snd_pcm_hw_params_t* hwParamsCapture, * hwParamsPlayback;
- optimalPeriodSize /= 2;
- }
+ snd_pcm_hw_params_alloca( &hwParamsCapture );
+ snd_pcm_hw_params_alloca( &hwParamsPlayback );
- if( optimalPeriodSize > periodSize )
- periodSize = optimalPeriodSize;
+ if( self->capture.pcm )
+ PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture,
+ &realSr ) );
+ if( self->playback.pcm )
+ PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->playback, outParams, self->primeBuffers, hwParamsPlayback,
+ &realSr ) );
- if( periodSize <= maxPeriodSize )
- {
- /* Looks good */
- framesPerBuffer = periodSize;
- }
- else
- {
- /* Unable to find a common period size, oh well */
- optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
- optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+ PA_ENSURE( PaAlsaStream_DetermineFramesPerBuffer( self, realSr, inParams, outParams, framesPerUserBuffer,
+ hwParamsCapture, hwParamsPlayback, hostBufferSizeMode ) );
- /* ConfigureStream should find individual period sizes acceptable for each device */
- framesPerBuffer = optimalPeriodSize;
- /* PA_ENSURE( paBadIODeviceCombination ); */
- }
+ if( self->capture.pcm )
+ {
+ assert( self->capture.framesPerBuffer != 0 );
+ PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->capture, hwParamsCapture, inParams, self->primeBuffers, realSr,
+ inputLatency ) );
+ PA_DEBUG(( "%s: Capture period size: %lu, latency: %f\n", __FUNCTION__, self->capture.framesPerBuffer, *inputLatency ));
}
- else /* half-duplex is a slightly simpler case */
+ if( self->playback.pcm )
{
- unsigned long bufferSize, channels;
- snd_pcm_t *pcm;
- snd_pcm_hw_params_t *hwParams;
+ assert( self->playback.framesPerBuffer != 0 );
+ PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->playback, hwParamsPlayback, outParams, self->primeBuffers, realSr,
+ outputLatency ) );
+ PA_DEBUG(( "%s: Playback period size: %lu, latency: %f\n", __FUNCTION__, self->playback.framesPerBuffer, *outputLatency ));
+ }
- snd_pcm_hw_params_alloca( &hwParams );
+ /* Should be exact now */
+ self->streamRepresentation.streamInfo.sampleRate = realSr;
- if( stream->capture.pcm )
- {
- pcm = stream->capture.pcm;
- bufferSize = inputParameters->suggestedLatency * sampleRate;
- channels = numHostInputChannels;
- }
+ /* this will cause the two streams to automatically start/stop/prepare in sync.
+ * We only need to execute these operations on one of the pair.
+ * A: We don't want to do this on a blocking stream.
+ */
+ if( self->callbackMode && self->capture.pcm && self->playback.pcm )
+ {
+ int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
+ if( err == 0 )
+ self->pcmsSynced = 1;
else
- {
- pcm = stream->playback.pcm;
- bufferSize = outputParameters->suggestedLatency * sampleRate;
- channels = numHostOutputChannels;
- }
-
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination );
+ PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
+ }
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+ {
+ unsigned long minFramesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
+ self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
+ self->pollTimeout = CalculatePollTimeout( self, minFramesPerHostBuffer ); /* Period in msecs, rounded up */
- /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
- finding a combination of period/buffer size which best fits these constraints */
- framesPerBuffer = bufferSize / 4;
- bufferSize += framesPerBuffer; /* One period doesn't count as latency */
- ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError );
+ /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
+ self->threading.throttledSleepTime = (unsigned long) (minFramesPerHostBuffer / sampleRate / 4 * 1000);
}
- PA_UNLESS( framesPerBuffer != 0, paInternalError );
- *determinedFrames = framesPerBuffer;
+ if( self->callbackMode )
+ {
+ /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
+ * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
+ * of host buffer frames with what the user specified */
+ if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
+ {
+ /* self->alignFrames = 1; */
+
+ /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
+ * on block adaption */
+ /*
+ if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
+ self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
+ self->useBlockAdaption = 1;
+ else
+ self->alignFrames = 1;
+ */
+ }
+ }
error:
return result;
@@ -1744,7 +2089,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
double sampleRate,
unsigned long framesPerBuffer,
PaStreamFlags streamFlags,
- PaStreamCallback *callback,
+ PaStreamCallback* callback,
void *userData )
{
PaError result = paNoError;
@@ -1754,9 +2099,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
int numInputChannels = 0, numOutputChannels = 0;
PaTime inputLatency, outputLatency;
- unsigned long framesPerHostBuffer;
- PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize;
- unsigned long maxHostBufferSize; /* Upper bound of host buffer size */
+ /* Operate with fixed host buffer size by default, since other modes will invariably lead to block adaption */
+ /* XXX: Use Bounded by default? Output tends to get stuttery with Fixed ... */
+ PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilFixedHostBufferSize;
if( (streamFlags & paPlatformSpecificFlags) != 0 )
return paInvalidFlag;
@@ -1782,37 +2127,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
}
- framesPerHostBuffer = framesPerBuffer;
PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
framesPerBuffer, callback, streamFlags, userData ) );
- /* If the number of frames per buffer is unspecified, we have to come up with
- * one. This is both a blessing and a curse: a blessing because we can optimize
- * the number to best meet the requirements, but a curse because that's really
- * hard to do well. For this reason we also support an interface where the user
- * specifies these by setting environment variables. */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer,
- hostApi ) );
- }
-
- PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer,
- &inputLatency, &outputLatency, &maxHostBufferSize ) );
+ PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer,
+ &inputLatency, &outputLatency, &hostBufferSizeMode ) );
hostInputSampleFormat = stream->capture.hostSampleFormat;
hostOutputSampleFormat = stream->playback.hostSampleFormat;
- if( framesPerHostBuffer != framesPerBuffer )
- {
- PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ ));
- }
-
PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
numInputChannels, inputSampleFormat, hostInputSampleFormat,
numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize,
+ sampleRate, streamFlags, framesPerBuffer, stream->maxFramesPerHostBuffer,
hostBufferSizeMode, callback, userData ) );
/* Ok, buffer processor is initialized, now we can deduce it's latency */
@@ -1829,7 +2157,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
error:
if( stream )
+ {
+ PA_DEBUG(( "%s: Stream in error, terminating\n", __FUNCTION__ ));
PaAlsaStream_Terminate( stream );
+ }
return result;
}
@@ -1907,7 +2238,7 @@ static int IsRunning( PaAlsaStream *stream )
{
int result = 0;
- ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */
+ LockMutex( &stream->stateMtx );
if( stream->capture.pcm )
{
snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
@@ -1933,7 +2264,7 @@ static int IsRunning( PaAlsaStream *stream )
}
end:
- ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+ ASSERT_CALL_( UnlockMutex( &stream->stateMtx ), paNoError );
return result;
}
@@ -1966,14 +2297,14 @@ static PaError StartStream( PaStream *s )
/* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking
* stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely
* case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */
- ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+ PA_ENSURE( LockMutex( &stream->startMtx ) );
/* Due to possible spurious wakeups, we enclose in a loop */
while( !IsRunning( stream ) && IsStreamActive( s ) && !res )
{
res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts );
}
- ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+ PA_ENSURE( UnlockMutex( &stream->startMtx ) );
PA_UNLESS( !res || res == ETIMEDOUT, paInternalError );
PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt ));
@@ -2006,18 +2337,34 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort )
if( abort )
{
if( stream->playback.pcm )
+ {
ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
+ }
if( stream->capture.pcm && !stream->pcmsSynced )
+ {
ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
+ }
- PA_DEBUG(( "Dropped frames\n" ));
+ PA_DEBUG(( "%s: Dropped frames\n", __FUNCTION__ ));
}
else
{
if( stream->playback.pcm )
- ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError );
+ {
+ ENSURE_( snd_pcm_nonblock( stream->playback.pcm, 0 ), paUnanticipatedHostError );
+ if( snd_pcm_drain( stream->playback.pcm ) < 0 )
+ {
+ PA_DEBUG(( "%s: Draining playback handle failed!\n", __FUNCTION__ ));
+ }
+ }
if( stream->capture.pcm && !stream->pcmsSynced )
- ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError );
+ {
+ /* We don't need to retrieve any remaining frames */
+ if( snd_pcm_drop( stream->capture.pcm ) < 0 )
+ {
+ PA_DEBUG(( "%s: Draining capture handle failed!\n", __FUNCTION__ ));
+ }
+ }
}
end:
@@ -2108,7 +2455,7 @@ static PaTime GetStreamTime( PaStream *s )
PaAlsaStream *stream = (PaAlsaStream*)s;
snd_timestamp_t timestamp;
- snd_pcm_status_t *status;
+ snd_pcm_status_t* status;
snd_pcm_status_alloca( &status );
/* TODO: what if we have both? does it really matter? */
@@ -2127,7 +2474,7 @@ static PaTime GetStreamTime( PaStream *s )
}
snd_pcm_status_get_tstamp( status, &timestamp );
- return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0;
+ return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1e6;
}
static double GetStreamCpuLoad( PaStream* s )
@@ -2180,14 +2527,15 @@ static PaError AlsaRestart( PaAlsaStream *stream )
{
PaError result = paNoError;
- ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 );
+ PA_ENSURE( LockMutex( &stream->stateMtx ) );
PA_ENSURE( AlsaStop( stream, 0 ) );
PA_ENSURE( AlsaStart( stream, 0 ) );
PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
error:
- ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+ PA_ENSURE( UnlockMutex( &stream->stateMtx ) );
+
return result;
}
@@ -2300,10 +2648,11 @@ static void OnExit( void *data )
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */
+ PA_DEBUG(( "%s: Stopping ALSA handles\n", __FUNCTION__ ));
AlsaStop( stream, stream->callbackAbort );
stream->callbackAbort = 0; /* Clear state */
- PA_DEBUG(( "OnExit: Stoppage\n" ));
+ PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ ));
/* Eventually notify user all buffers have played */
if( stream->streamRepresentation.streamFinishedCallback )
@@ -2516,10 +2865,11 @@ error:
/** Fill in pollfd objects.
*/
-static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds )
+static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent* self, struct pollfd* pfds )
{
PaError result = paNoError;
int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
+ (void)ret; /* Prevent unused variable warning if asserts are turned off */
assert( ret == self->nfds );
self->ready = 0;
@@ -2533,7 +2883,7 @@ static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self,
* @param shouldPoll Should we continue to poll
* @param xrun Has an xrun occurred
*/
-static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun )
+static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, struct pollfd* pfds, int* shouldPoll, int* xrun )
{
PaError result = paNoError;
unsigned short revents;
@@ -2592,6 +2942,7 @@ static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCap
if( queryCapture && queryPlayback )
{
*available = PA_MIN( captureFrames, playbackFrames );
+ /*PA_DEBUG(("capture: %lu, playback: %lu, combined: %lu\n", captureFrames, playbackFrames, *available));*/
}
else if( queryCapture )
{
@@ -2668,11 +3019,13 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr
PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
totalFds += self->playback.nfds;
}
-
+
if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
{
/* XXX: Depend on preprocessor condition? */
- if( errno == EINTR ) { /* gdb */
+ if( errno == EINTR )
+ {
+ /* gdb */
continue;
}
@@ -2715,8 +3068,8 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr
{
/* Get the number of available frames for the pcms that are marked ready.
* @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
- * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput
- * is not specified.
+ * the other direction is returned. Output is normally preferred over capture however, so capture frames may be
+ * discarded to avoid overrun unless paNeverDropInput is specified.
*/
int captureReady = self->capture.pcm ? self->capture.ready : 0,
playbackReady = self->playback.pcm ? self->playback.ready : 0;
@@ -2726,9 +3079,18 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr
{
if( !self->playback.ready && !self->neverDropInput )
{
- /* TODO: Drop input */
+ /* Drop input, a period's worth */
+ assert( self->capture.ready );
+ PaAlsaStreamComponent_EndProcessing( &self->capture, PA_MIN( self->capture.framesPerBuffer,
+ *framesAvail ), &xrun );
+ *framesAvail = 0;
+ self->capture.ready = 0;
}
}
+ else if( self->capture.pcm )
+ assert( self->capture.ready );
+ else
+ assert( self->playback.ready );
}
end:
@@ -2739,6 +3101,14 @@ error:
PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
*framesAvail = 0;
}
+ else
+ {
+ if( 0 != *framesAvail )
+ {
+ /* If we're reporting frames eligible for processing, one of the handles better be ready */
+ PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError );
+ }
+ }
*xrunOccurred = xrun;
return result;
@@ -2751,8 +3121,8 @@ error:
*
* @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
*/
-static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp,
- unsigned long *numFrames, int *xrun )
+static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* self, PaUtilBufferProcessor* bp,
+ unsigned long* numFrames, int* xrun )
{
PaError result = paNoError;
const snd_pcm_channel_area_t *areas, *area;
@@ -2812,12 +3182,20 @@ error:
* @param numFrames On entrance the number of available frames, on exit the number of received frames
* @param xrunOccurred Return whether an xrun has occurred
*/
-static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred )
+static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream* self, unsigned long* numFrames, int* xrunOccurred )
{
PaError result = paNoError;
unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
int xrun = 0;
+ if( *xrunOccurred )
+ {
+ *numFrames = 0;
+ return result;
+ }
+ /* If we got here at least one of the pcm's should be marked ready */
+ PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError );
+
/* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
* It is possible that a direction is not marked ready however, because it is out of sync with the other.
*/
@@ -2841,7 +3219,24 @@ static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *num
}
commonFrames = PA_MIN( captureFrames, playbackFrames );
- assert( commonFrames <= *numFrames );
+ /* assert( commonFrames <= *numFrames ); */
+ if( commonFrames > *numFrames )
+ {
+ /* Hmmm ... how come there are more frames available than we requested!? Blah. */
+ PA_DEBUG(( "%s: Common available frames are reported to be more than number requested: %lu, %lu, callbackMode: %d\n", __FUNCTION__,
+ commonFrames, *numFrames, self->callbackMode ));
+ if( self->capture.pcm )
+ {
+ PA_DEBUG(( "%s: captureFrames: %lu, capture.ready: %d\n", __FUNCTION__, captureFrames, self->capture.ready ));
+ }
+ if( self->playback.pcm )
+ {
+ PA_DEBUG(( "%s: playbackFrames: %lu, playback.ready: %d\n", __FUNCTION__, playbackFrames, self->playback.ready ));
+ }
+
+ commonFrames = 0;
+ goto end;
+ }
/* Inform PortAudio of the number of frames we got.
* @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
@@ -2869,7 +3264,9 @@ static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *num
else
{
/* We have output underflow, but keeping input data (paNeverDropInput) */
- /* assert( self->neverDropInput ); */
+ assert( self->neverDropInput );
+ assert( self->capture.pcm != NULL );
+ PA_DEBUG(( "%s: Setting output buffers to NULL\n", __FUNCTION__ ));
PaUtil_SetNoOutput( &self->bufferProcessor );
}
}
@@ -2934,10 +3331,11 @@ static void *CallbackThreadFunc( void *userData )
}
else
{
- ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
- PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */
- ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 );
- ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+ PA_ENSURE( LockMutex( &stream->startMtx ) );
+ /* Buffer will be zeroed */
+ PA_ENSURE( AlsaStart( stream, 0 ) );
+ ENSURE_SYSTEM_( pthread_cond_signal( &stream->startCond ), 0 );
+ PA_ENSURE( UnlockMutex( &stream->startMtx ) );
streamStarted = 1;
}
@@ -2962,7 +3360,7 @@ static void *CallbackThreadFunc( void *userData )
{
stream->callbackAbort = (paAbort == callbackResult);
if( stream->callbackAbort ||
- /** @concern BlockAdaption Go on if adaption buffers are empty */
+ /** @concern BlockAdaption: Go on if adaption buffers are empty */
PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
goto end;
@@ -3031,23 +3429,31 @@ static void *CallbackThreadFunc( void *userData )
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
framesGot = framesAvail;
+ if( paUtilFixedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode )
+ {
+ /* We've committed to a fixed host buffer size, stick to that */
+ framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0;
+ }
+ else
+ {
+ /* We've committed to an upper bound on the size of host buffers */
+ assert( paUtilBoundedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode );
+ framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer );
+ }
PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+ /* Check the host buffer size against the buffer processor configuration */
framesAvail -= framesGot;
if( framesGot > 0 )
{
assert( !xrun );
-
PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
}
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
- if( framesGot == 0 )
+ if( 0 == framesGot )
{
- if( !xrun )
- PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ ));
-
/* Go back to polling for more frames */
break;
@@ -3062,6 +3468,7 @@ static void *CallbackThreadFunc( void *userData )
pthread_cleanup_pop( 1 );
end:
+ PA_DEBUG(( "%s: Thread %d exiting\n ", __FUNCTION__, pthread_self() ));
pthread_exit( pres );
error:
@@ -3096,7 +3503,9 @@ static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
}
if( stream->capture.userInterleaved )
+ {
userBuffer = buffer;
+ }
else
{
/* Copy channels into local array */
@@ -3178,12 +3587,13 @@ static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frame
frames -= framesGot;
}
+ /* Start stream after one period of samples worth */
+
/* Frames residing in buffer */
PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
framesAvail = err;
hwAvail = stream->playback.bufferSize - framesAvail;
- /* Start stream after one period of samples worth */
if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
hwAvail >= stream->playback.framesPerBuffer )
{
diff --git a/pd/portaudio/pa_mac_core/notes.txt b/pd/portaudio/pa_mac_core/notes.txt
index c79b90e6..ad66f358 100644
--- a/pd/portaudio/pa_mac_core/notes.txt
+++ b/pd/portaudio/pa_mac_core/notes.txt
@@ -1,8 +1,23 @@
-Notes on Core Audio Implementation of PortAudio
+Notes on status of CoreAudio Implementation of PortAudio
+
+Document Last Updated December 9, 2005
+
+There are currently two implementations of PortAudio for Mac Core Audio.
+
+The original is in pa_mac_core_old.c, and the newer, default implementation
+is in pa_mac_core.c.
+Only pa_mac_core.c is currently developed and supported as it uses apple's
+current core audio technology. To select use the old implementation, replace
+pa_mac_core.c with pa_mac_core_old.c (eg. "cp pa_mac_core_auhal.c
+pa_mac_core.c"), then run configure and make as usual.
+
+----------------------------------------
+
+Notes on Original implementation:
by Phil Burk and Darren Gibbs
-Document last updated March 20, 2002
+Last updated March 20, 2002
WHAT WORKS
@@ -18,7 +33,7 @@ KNOWN BUGS OR LIMITATIONS
We do not yet support simultaneous input and output on different
devices. Note that some CoreAudio devices like the Roland UH30 look
like one device but are actually two different CoreAudio devices. The
-BuiltIn audio is typically one CoreAudio device.
+Built-In audio is typically one CoreAudio device.
Mono doesn't work.
@@ -32,3 +47,99 @@ input, output or both.
When we query devices, we first get a list of CoreAudio devices. Then
we scan the list and add a PortAudio device for each CoreAudio device
that supports input. Then we make a scan for output devices.
+
+-------------------------------------------
+
+Notes on Newer/Default AUHAL implementation:
+
+by Bjorn Roche
+
+Last Updated December 9, 2005
+
+Principle of Operation:
+
+This implementation uses AUHAL for audio I/O. To some extent, it also
+operates at the "HAL" Layer, though this behavior can be limited by
+platform specific flags (see pa_mac_core.h for details). The default
+settings should be reasonable: they don't change the SR of the device and
+don't cause interruptions if other devices are using the device.
+
+Major Software Elements Used: Apple's HAL AUs provide output SR
+conversion transparently, however, only on output, so this
+implementation uses AudioConverters to convert the sample rate on input.
+A PortAudio ring buffer is used to buffer input when sample rate
+conversion is required or when separate audio units are used for duplex
+IO. Finally, a PortAudio buffer processor is used to convert formats and
+provide additional buffers if needed. Internally, interleaved floating
+point data streams are used exclusively - the audio unit converts from
+the audio hardware's native format to interleaved float PCM and
+PortAudio's Buffer processor is used for conversion to user formats.
+
+Simplex Input: Simplex input uses a single callback. If sample rate
+conversion is required, a ring buffer and AudioConverter are used as
+well.
+
+Simplex output: Simplex output uses a single callback. No ring buffer or
+audio converter is used because AUHAL does its own output SR conversion.
+
+Duplex, one device (no SR conversion): When one device is used, a single
+callback is used. This achieves very low latency.
+
+Duplex, separate devices or SR conversion: When SR conversion is
+required, data must be buffered before it is converted and data is not
+always available at the same times on input and output, so SR conversion
+requires the same treatment as separate devices. The input callback
+reads data and puts it in the ring buffer. The output callback reads the
+data off the ring buffer, into an audio converter and finally to the
+buffer processor.
+
+Platform Specific Options:
+
+By using the flags in pa_mac_core.h, the user may specify several options.
+For example, the user can specify the sample-rate conversion quality, and
+the extent to which PA will attempt to "play nice" and to what extent it
+will interrupt other apps to improve performance. For example, if 44100 Hz
+sample rate is requested but the device is set at 48000 Hz, PA can either
+change the device for optimal playback ("Pro" mode), which may interrupt
+other programs playing back audio, or simple use a sample-rate coversion,
+which allows for friendlier sharing of the device ("Play Nice" mode).
+
+
+Known issues:
+
+- Latency: Latency settings are ignored in most cases. Exceptions are when
+doing I/O between different devices and as a hint for selecting a realtively
+low or relatively high latency in conjunction with
+paHostFramesPerBufferUnspecified. Latency settings are always automatically
+bound to "safe" values, however, so setting extreme values here should not be
+an issue.
+
+- Buffer Size: paHostFramesPerBufferUnspecified and specific host buffer sizes
+are supported. paHostFramesPerBufferUnspecified works best in "pro" mode,
+where the buffer size and sample rate of the audio device is most likely
+to match the expected values.
+
+- Timing info. It reports on stream time, but I'm probably doing something
+wrong since patest_sine_time often reports negative latency numbers.
+
+- xrun detection: The only xrun detection performed is when reading
+and writing the ring buffer. There is probably more that can be done.
+
+- abort/stop issues: stopping a stream is always a complete operation,
+but latency should be low enough to make the lack of a separate abort
+unnecessary. Apple clarifies its AudioOutputUnitStop() call here:
+http://lists.apple.com/archives/coreaudio-api/2005/Dec/msg00055.html
+
+- blocking interface: Not implemented.
+
+- multichannel: It has been tested successfully on multichannel hardware
+from MOTU: traveler and 896HD.
+
+- sample rate conversion quality: By default, SR conversion is the maximum
+available. This can be tweaked using flags pa_mac_core.h. Note that the AU
+render quyality property is used to set the sample rat conversion quality
+as "documented" here:
+http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html
+
+- x86: I haven't tested it on an x86 Mac myself, but users have reported
+being able to comiple and run it.
diff --git a/pd/portaudio/pa_mac_core/pa_mac_core.c b/pd/portaudio/pa_mac_core/pa_mac_core.c
index 77451a5d..16eb0824 100644
--- a/pd/portaudio/pa_mac_core/pa_mac_core.c
+++ b/pd/portaudio/pa_mac_core/pa_mac_core.c
@@ -1,13 +1,19 @@
/*
- * $Id: pa_mac_core.c,v 1.8.2.3 2005/06/19 22:12:38 tgrill Exp $
- * pa_mac_core.c
- * Implementation of PortAudio for Mac OS X CoreAudio
- *
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com
+ * This is the AUHAL implementation of portaudio. Hopefully this will
+ * one day replace pa_mac_core.
*
- * Authors: Ross Bencina and Phil Burk
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
+ * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
+ *
+ * Dominic's code was based on code by Phil Burk, Darren Gibbs,
+ * Gord Peters, Stephane Letz, and Greg Pfiel.
+ *
+ * Bjorn Roche and XO Audio LLC reserve no rights to this code.
+ * The maintainers of PortAudio may redistribute and modify the code and
+ * licenses as they deam appropriate.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
@@ -31,600 +37,1009 @@
* 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.
- *
*/
-#include <CoreAudio/CoreAudio.h>
+/**
+ @file pa_mac_core
+ @author Bjorn Roche
+ @brief AUHAL implementation of PortAudio
+*/
+
+#include <string.h> /* strlen(), memcmp() etc. */
+
+#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <assert.h>
-#include "portaudio.h"
-#include "pa_trace.h"
+
#include "pa_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
+#include "../pablio/ringbuffer.h"
+#include "pa_mac_core.h"
-// ===== constants =====
+#ifndef MIN
+#define MIN(a, b) (((a)<(b))?(a):(b))
+#endif
-// ===== structs =====
-#pragma mark structs
+#ifndef MAX
+#define MAX(a, b) (((a)<(b))?(b):(a))
+#endif
-// PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation
-typedef struct PaMacCore_HAR
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#define ERR(mac_error) PaMacCore_SetError(mac_error, __LINE__, 1 )
+#define WARNING(mac_error) PaMacCore_SetError(mac_error, __LINE__, 0 )
+
+/* Help keep track of AUHAL element numbers */
+#define INPUT_ELEMENT (1)
+#define OUTPUT_ELEMENT (0)
+
+/* Normal level of debugging: fine for most apps that don't mind the occational warning being printf'ed */
+/*
+ */
+#define MAC_CORE_DEBUG
+#ifdef MAC_CORE_DEBUG
+# define DBUG(MSG) do { printf("||PaMacCore (AUHAL)|| "); printf MSG ; fflush(stdout); } while(0)
+#else
+# define DBUG(MSG)
+#endif
+
+/* Verbose Debugging: useful for developement */
+/*
+#define MAC_CORE_VERBOSE_DEBUG
+ */
+#ifdef MAC_CORE_VERBOSE_DEBUG
+# define VDBUG(MSG) do { printf("||PaMacCore (v )|| "); printf MSG ; fflush(stdout); } while(0)
+#else
+# define VDBUG(MSG)
+#endif
+
+/* Very Verbose Debugging: Traces every call. */
+/*
+#define MAC_CORE_VERY_VERBOSE_DEBUG
+ */
+#ifdef MAC_CORE_VERY_VERBOSE_DEBUG
+# define VVDBUG(MSG) do { printf("||PaMacCore (vv)|| "); printf MSG ; fflush(stdout); } while(0)
+#else
+# define VVDBUG(MSG)
+#endif
+
+#define RING_BUFFER_ADVANCE_DENOMINATOR (4)
+
+/* Some utilities that sort of belong here, but were getting too unweildy */
+#include "pa_mac_core_utilities.c"
+/* Special purpose ring buffer just for pa_mac_core input processing. */
+/* #include "pa_mac_core_input_ring_buffer.c" */
+#include "../pablio/ringbuffer.c"
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static void setStreamStartTime( PaStream *stream );
+static OSStatus AudioIOProc( void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+/* PaMacAUHAL - host api datastructure specific to this implementation */
+typedef struct
{
PaUtilHostApiRepresentation inheritedHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
PaUtilStreamInterface blockingStreamInterface;
-
+
PaUtilAllocationGroup *allocations;
- AudioDeviceID *macCoreDeviceIds;
-}
-PaMacCoreHostApiRepresentation;
-typedef struct PaMacCore_DI
-{
- PaDeviceInfo inheritedDeviceInfo;
+ /* implementation specific data goes here */
+ long devCount;
+ AudioDeviceID *devIds; /*array of all audio devices*/
+ AudioDeviceID defaultIn;
+ AudioDeviceID defaultOut;
}
-PaMacCoreDeviceInfo;
+PaMacAUHAL;
-// PaMacCoreStream - a stream data structure specifically for this implementation
-typedef struct PaMacCore_S
+/* stream data structure specifically for this implementation */
+typedef struct PaMacCoreStream
{
PaUtilStreamRepresentation streamRepresentation;
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
PaUtilBufferProcessor bufferProcessor;
-
- int primeStreamUsingCallback;
-
+
+ /* implementation specific data goes here */
+ bool bufferProcessorIsInitialized;
+ AudioUnit inputUnit;
+ AudioUnit outputUnit;
AudioDeviceID inputDevice;
AudioDeviceID outputDevice;
-
- // Processing thread management --------------
-// HANDLE abortEvent;
-// HANDLE processingThread;
-// DWORD processingThreadId;
-
- char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle
- int processingThreadPriority;
- int highThreadPriority;
- int throttledThreadPriority;
- unsigned long throttledSleepMsecs;
-
- int isStopped;
- volatile int isActive;
- volatile int stopProcessing; // stop thread once existing buffers have been returned
- volatile int abortProcessing; // stop thread immediately
-
-// DWORD allBuffersDurationMs; // used to calculate timeouts
+ size_t userInChan;
+ size_t userOutChan;
+ size_t inputFramesPerBuffer;
+ size_t outputFramesPerBuffer;
+ /* We use this ring buffer when input and out devs are different. */
+ RingBuffer inputRingBuffer;
+ /* We may need to do SR conversion on input. */
+ AudioConverterRef inputSRConverter;
+ /* We need to preallocate an inputBuffer for reading data. */
+ AudioBufferList inputAudioBufferList;
+ AudioTimeStamp startTime;
+ //volatile bool isTimeSet;
+ volatile PaStreamCallbackFlags xrunFlags;
+ volatile enum {
+ STOPPED = 0, /* playback is completely stopped,
+ and the user has called StopStream(). */
+ CALLBACK_STOPPED = 1, /* callback has requested stop,
+ but user has not yet called StopStream(). */
+ STOPPING = 2, /* The stream is in the process of closing.
+ This state is just used internally;
+ externally it is indistinguishable from
+ ACTIVE.*/
+ ACTIVE = 3 /* The stream is active and running. */
+ } state;
+ double sampleRate;
}
PaMacCoreStream;
-// Data needed by the CoreAudio callback functions
-typedef struct PaMacCore_CD
-{
- PaMacCoreStream *stream;
- PaStreamCallback *callback;
- void *userData;
- PaUtilConverter *inputConverter;
- PaUtilConverter *outputConverter;
- void *inputBuffer;
- void *outputBuffer;
- int inputChannelCount;
- int outputChannelCount;
- PaSampleFormat inputSampleFormat;
- PaSampleFormat outputSampleFormat;
- PaUtilTriangularDitherGenerator *ditherGenerator;
-}
-PaMacClientData;
+static PaError OpenAndSetupOneAudioUnit(
+ const PaStreamParameters *inStreamParams,
+ const PaStreamParameters *outStreamParams,
+ const unsigned long requestedFramesPerBuffer,
+ unsigned long *actualInputFramesPerBuffer,
+ unsigned long *actualOutputFramesPerBuffer,
+ const PaMacAUHAL *auhalHostApi,
+ AudioUnit *audioUnit,
+ AudioConverterRef *srConverter,
+ AudioDeviceID *audioDevice,
+ const double sampleRate,
+ void *refCon );
-// ===== CoreAudio-PortAudio bridge functions =====
-#pragma mark CoreAudio-PortAudio bridge functions
+/* for setting errors. */
+#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+ PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
-// Maps CoreAudio OSStatus codes to PortAudio PaError codes
-static PaError conv_err(OSStatus error)
-{
- PaError result;
-
- switch (error) {
- case kAudioHardwareNoError:
- result = paNoError; break;
- case kAudioHardwareNotRunningError:
- result = paInternalError; break;
- case kAudioHardwareUnspecifiedError:
- result = paInternalError; break;
- case kAudioHardwareUnknownPropertyError:
- result = paInternalError; break;
- case kAudioHardwareBadPropertySizeError:
- result = paInternalError; break;
- case kAudioHardwareIllegalOperationError:
- result = paInternalError; break;
- case kAudioHardwareBadDeviceError:
- result = paInvalidDevice; break;
- case kAudioHardwareBadStreamError:
- result = paBadStreamPtr; break;
- case kAudioHardwareUnsupportedOperationError:
- result = paInternalError; break;
- case kAudioDeviceUnsupportedFormatError:
- result = paSampleFormatNotSupported; break;
- case kAudioDevicePermissionsError:
- result = paDeviceUnavailable; break;
- default:
- result = paInternalError;
- }
-
- return result;
-}
-
-static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate)
-{
- struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription));
- streamDescription->mSampleRate = sampleRate;
- streamDescription->mFormatID = kAudioFormatLinearPCM;
- streamDescription->mFormatFlags = 0;
- streamDescription->mFramesPerPacket = 1;
-
- if (parameters->sampleFormat & paNonInterleaved) {
- streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved;
- streamDescription->mChannelsPerFrame = 1;
- streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat);
- streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat);
- }
- else {
- streamDescription->mChannelsPerFrame = parameters->channelCount;
- }
-
- streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame;
- streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket;
-
- if (parameters->sampleFormat & paFloat32) {
- streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
- streamDescription->mBitsPerChannel = 32;
- }
- else if (parameters->sampleFormat & paInt32) {
- streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
- streamDescription->mBitsPerChannel = 32;
- }
- else if (parameters->sampleFormat & paInt24) {
- streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
- streamDescription->mBitsPerChannel = 24;
- }
- else if (parameters->sampleFormat & paInt16) {
- streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
- streamDescription->mBitsPerChannel = 16;
- }
- else if (parameters->sampleFormat & paInt8) {
- streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
- streamDescription->mBitsPerChannel = 8;
- }
- else if (parameters->sampleFormat & paInt32) {
- streamDescription->mBitsPerChannel = 8;
- }
-
- return streamDescription;
-}
-static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime)
-{
- PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo));
-
- timeInfo->inputBufferAdcTime = inputTime->mSampleTime;
- timeInfo->currentTime = now->mSampleTime;
- timeInfo->outputBufferDacTime = outputTime->mSampleTime;
-
- return timeInfo;
-}
-// ===== support functions =====
-#pragma mark support functions
-static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi)
+/*currently, this is only used in initialization, but it might be modified
+ to be used when the list of devices changes.*/
+static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
{
- if( macCoreHostApi->allocations )
+ UInt32 size;
+ UInt32 propsize;
+ VVDBUG(("gatherDeviceInfo()\n"));
+ /* -- free any previous allocations -- */
+ if( auhalHostApi->devIds )
+ PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds);
+ auhalHostApi->devIds = NULL;
+
+ /* -- figure out how many devices there are -- */
+ AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
+ &propsize,
+ NULL );
+ auhalHostApi->devCount = propsize / sizeof( AudioDeviceID );
+
+ VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) );
+
+ /* -- copy the device IDs -- */
+ auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory(
+ auhalHostApi->allocations,
+ propsize );
+ if( !auhalHostApi->devIds )
+ return paInsufficientMemory;
+ AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
+ &propsize,
+ auhalHostApi->devIds );
+#ifdef MAC_CORE_VERBOSE_DEBUG
{
- PaUtil_FreeAllAllocations( macCoreHostApi->allocations );
- PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations );
+ int i;
+ for( i=0; i<auhalHostApi->devCount; ++i )
+ printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
}
-
- PaUtil_FreeMemory( macCoreHostApi );
+#endif
+
+ size = sizeof(AudioDeviceID);
+ auhalHostApi->defaultIn = kAudioDeviceUnknown;
+ auhalHostApi->defaultOut = kAudioDeviceUnknown;
+ /* FEEDBACK: these calls could fail, in which case default in and out will
+ be unknown devices or could be undefined. Do I need to be more
+ rigorous here? */
+ AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
+ &size,
+ &auhalHostApi->defaultIn);
+ AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+ &size,
+ &auhalHostApi->defaultOut);
+ VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) );
+ VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) );
+
+ return paNoError;
}
-static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput)
+static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
+ PaDeviceInfo *deviceInfo,
+ AudioDeviceID macCoreDeviceId,
+ int isInput)
{
UInt32 propSize;
PaError err = paNoError;
UInt32 i;
int numChannels = 0;
AudioBufferList *buflist;
+ UInt32 frameLatency;
+
+ VVDBUG(("GetChannelInfo()\n"));
+
+ /* Get the number of channels from the stream configuration.
+ Fail if we can't get this. */
+
+ err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
+ if (err)
+ return err;
- err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
buflist = PaUtil_AllocateMemory(propSize);
- err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
- if (!err) {
- for (i = 0; i < buflist->mNumberBuffers; ++i) {
- numChannels += buflist->mBuffers[i].mNumberChannels;
- }
-
- if (isInput)
- deviceInfo->maxInputChannels = numChannels;
- else
- deviceInfo->maxOutputChannels = numChannels;
-
- int frameLatency;
- propSize = sizeof(UInt32);
- err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
- if (!err) {
- double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
- if (isInput) {
- deviceInfo->defaultLowInputLatency = secondLatency;
- deviceInfo->defaultHighInputLatency = secondLatency;
- }
- else {
- deviceInfo->defaultLowOutputLatency = secondLatency;
- deviceInfo->defaultHighOutputLatency = secondLatency;
- }
- }
+ err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
+ if (err)
+ return err;
+
+ for (i = 0; i < buflist->mNumberBuffers; ++i)
+ numChannels += buflist->mBuffers[i].mNumberChannels;
+
+ if (isInput)
+ deviceInfo->maxInputChannels = numChannels;
+ else
+ deviceInfo->maxOutputChannels = numChannels;
+
+ if (numChannels > 0) // do not try to retrieve the latency if there is no channels.
+ {
+ /* Get the latency. Don't fail if we can't get this. */
+ /* default to something reasonable */
+ deviceInfo->defaultLowInputLatency = .01;
+ deviceInfo->defaultHighInputLatency = .01;
+ deviceInfo->defaultLowOutputLatency = .01;
+ deviceInfo->defaultHighOutputLatency = .01;
+ propSize = sizeof(UInt32);
+ err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
+ if (!err)
+ {
+ double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
+ if (isInput)
+ {
+ deviceInfo->defaultLowInputLatency = secondLatency;
+ deviceInfo->defaultHighInputLatency = secondLatency;
+ }
+ else
+ {
+ deviceInfo->defaultLowOutputLatency = secondLatency;
+ deviceInfo->defaultHighOutputLatency = secondLatency;
+ }
+ }
}
- PaUtil_FreeMemory(buflist);
-
- return err;
+ return paNoError;
}
-static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex )
+static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
+ PaDeviceInfo *deviceInfo,
+ AudioDeviceID macCoreDeviceId,
+ PaHostApiIndex hostApiIndex )
{
- PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
-
+ Float64 sampleRate;
+ char *name;
PaError err = paNoError;
UInt32 propSize;
- err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
- // FIXME: this allocation should be part of the allocations group
- char *name = PaUtil_AllocateMemory(propSize);
- err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
- if (!err) {
- deviceInfo->name = name;
- }
-
- Float64 sampleRate;
+ VVDBUG(("InitializeDeviceInfo(): macCoreDeviceId=%ld\n", macCoreDeviceId));
+
+ memset(deviceInfo, 0, sizeof(deviceInfo));
+
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ /* Get the device name. Fail if we can't get it. */
+ err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
+ if (err)
+ return err;
+
+ name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations,propSize);
+ if ( !name )
+ return paInsufficientMemory;
+ err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
+ if (err)
+ return err;
+ deviceInfo->name = name;
+
+ /* Try to get the default sample rate. Don't fail if we can't get this. */
propSize = sizeof(Float64);
- err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
- if (!err) {
+ err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
+ if (err)
+ deviceInfo->defaultSampleRate = 0.0;
+ else
deviceInfo->defaultSampleRate = sampleRate;
- }
+ /* Get the maximum number of input and output channels. Fail if we can't get this. */
+
+ err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 1);
+ if (err)
+ return err;
- // Get channel info
- err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1);
- err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0);
+ err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 0);
+ if (err)
+ return err;
- return err;
+ return paNoError;
}
-static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex )
+PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
- PaUtilHostApiRepresentation *hostApi;
- PaMacCoreDeviceInfo *deviceInfoArray;
-
- // initialise device counts and default devices under the assumption that there are no devices. These values are incremented below if and when devices are successfully initialized.
- hostApi = &macCoreHostApi->inheritedHostApiRep;
- hostApi->info.deviceCount = 0;
- hostApi->info.defaultInputDevice = paNoDevice;
- hostApi->info.defaultOutputDevice = paNoDevice;
-
- UInt32 propsize;
- AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL);
- int numDevices = propsize / sizeof(AudioDeviceID);
- hostApi->info.deviceCount = numDevices;
- if (numDevices > 0) {
- hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
- if( !hostApi->deviceInfos )
+ int i;
+ PaMacAUHAL *auhalHostApi;
+ PaDeviceInfo *deviceInfoArray;
+
+ VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
+
+ auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) );
+ if( !auhalHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ auhalHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !auhalHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ auhalHostApi->devIds = NULL;
+ auhalHostApi->devCount = 0;
+
+ /* get the info we need about the devices */
+ result = gatherDeviceInfo( auhalHostApi );
+ if( result != paNoError )
+ goto error;
+
+ *hostApi = &auhalHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paCoreAudio;
+ (*hostApi)->info.name = "Core Audio";
+
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+ (*hostApi)->info.deviceCount = 0;
+
+ if( auhalHostApi->devCount > 0 )
+ {
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount);
+ if( !(*hostApi)->deviceInfos )
{
- return paInsufficientMemory;
+ result = paInsufficientMemory;
+ goto error;
}
- // allocate all device info structs in a contiguous block
- deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory(
- macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices );
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount );
if( !deviceInfoArray )
{
- return paInsufficientMemory;
+ result = paInsufficientMemory;
+ goto error;
}
-
- macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize);
- AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds);
- AudioDeviceID defaultInputDevice, defaultOutputDevice;
- propsize = sizeof(AudioDeviceID);
- AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice);
- AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice);
-
- UInt32 i;
- for (i = 0; i < numDevices; ++i) {
- if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) {
- hostApi->info.defaultInputDevice = i;
+ for( i=0; i < auhalHostApi->devCount; ++i )
+ {
+ int err;
+ err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i],
+ auhalHostApi->devIds[i],
+ hostApiIndex );
+ if (err == paNoError)
+ { /* copy some info and set the defaults */
+ (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i];
+ if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn)
+ (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+ if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut)
+ (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+ (*hostApi)->info.deviceCount++;
}
- if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) {
- hostApi->info.defaultOutputDevice = i;
+ else
+ { /* there was an error. we need to shift the devices down, so we ignore this one */
+ int j;
+ auhalHostApi->devCount--;
+ for( j=i; j<auhalHostApi->devCount; ++j )
+ auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1];
+ i--;
}
- InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex);
- hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo);
}
}
- return result;
-}
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped,
+ IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable,
+ PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &auhalHostApi->blockingStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped,
+ IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream,
+ GetStreamReadAvailable,
+ GetStreamWriteAvailable );
-static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput)
-{
- UInt32 propSize = sizeof(AudioStreamBasicDescription);
- AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize);
-
- streamDescription->mSampleRate = sampleRate;
- streamDescription->mFormatID = 0;
- streamDescription->mFormatFlags = 0;
- streamDescription->mBytesPerPacket = 0;
- streamDescription->mFramesPerPacket = 0;
- streamDescription->mBytesPerFrame = 0;
- streamDescription->mChannelsPerFrame = 0;
- streamDescription->mBitsPerChannel = 0;
- streamDescription->mReserved = 0;
-
- OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription);
- PaUtil_FreeMemory(streamDescription);
return result;
-}
-static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount)
-{
- int frameSpacing, channelSpacing;
- if (destination->inputSampleFormat & paNonInterleaved) {
- frameSpacing = 1;
- channelSpacing = destination->inputChannelCount;
- }
- else {
- frameSpacing = destination->inputChannelCount;
- channelSpacing = 1;
- }
-
- AudioBuffer const *inputBuffer = &source->mBuffers[0];
- void *coreAudioBuffer = inputBuffer->mData;
- void *portAudioBuffer = destination->inputBuffer;
- UInt32 i, streamNumber, streamChannel;
- for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) {
- if (streamChannel >= inputBuffer->mNumberChannels) {
- ++streamNumber;
- inputBuffer = &source->mBuffers[streamNumber];
- coreAudioBuffer = inputBuffer->mData;
- streamChannel = 0;
+error:
+ if( auhalHostApi )
+ {
+ if( auhalHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( auhalHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
}
- destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator);
- coreAudioBuffer += sizeof(Float32);
- portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing;
+
+ PaUtil_FreeMemory( auhalHostApi );
}
+ return result;
}
-static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount)
-{
- int frameSpacing, channelSpacing;
- if (source->outputSampleFormat & paNonInterleaved) {
- frameSpacing = 1;
- channelSpacing = source->outputChannelCount;
- }
- else {
- frameSpacing = source->outputChannelCount;
- channelSpacing = 1;
- }
-
- AudioBuffer *outputBuffer = &destination->mBuffers[0];
- void *coreAudioBuffer = outputBuffer->mData;
- void *portAudioBuffer = source->outputBuffer;
- UInt32 i, streamNumber, streamChannel;
- for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) {
- if (streamChannel >= outputBuffer->mNumberChannels) {
- ++streamNumber;
- outputBuffer = &destination->mBuffers[streamNumber];
- coreAudioBuffer = outputBuffer->mData;
- streamChannel = 0;
- }
- source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL);
- coreAudioBuffer += sizeof(Float32);
- portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing;
- }
-}
-static OSStatus AudioIOProc( AudioDeviceID inDevice,
- const AudioTimeStamp* inNow,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* inInputTime,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* inOutputTime,
- void* inClientData)
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
- PaMacClientData *clientData = (PaMacClientData *)inClientData;
- PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
-
- PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
-
- AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
- unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
+ PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
- if (clientData->inputBuffer) {
- CopyInputData(clientData, inInputData, frameCount);
- }
- PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
- if (clientData->outputBuffer) {
- CopyOutputData(outOutputData, clientData, frameCount);
- }
+ VVDBUG(("Terminate()\n"));
- PaUtil_FreeMemory(timeInfo);
- PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
-
- if (result == paComplete || result == paAbort) {
- Pa_StopStream(clientData->stream);
- }
-}
+ /*
+ IMPLEMENT ME:
+ - clean up any resources not handled by the allocation group
+ TODO: Double check that everything is handled by alloc group
+ */
-// This is not for input-only streams, this is for streams where the input device is different from the output device
-// TODO: This needs to store the output data in a buffer, to be written to the device the next time AudioOutputProc is called
-static OSStatus AudioInputProc( AudioDeviceID inDevice,
- const AudioTimeStamp* inNow,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* inInputTime,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* inOutputTime,
- void* inClientData)
-{
- PaMacClientData *clientData = (PaMacClientData *)inClientData;
- PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
-
- PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
-
- AudioBuffer const *inputBuffer = &inInputData->mBuffers[0];
- unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32));
+ if( auhalHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( auhalHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
+ }
- CopyInputData(clientData, inInputData, frameCount);
- clientData->callback(clientData->inputBuffer, NULL, frameCount, timeInfo, paNoFlag, clientData->userData);
-
- PaUtil_FreeMemory(timeInfo);
- PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+ PaUtil_FreeMemory( auhalHostApi );
}
-// This is not for output-only streams, this is for streams where the input device is different from the output device
-static OSStatus AudioOutputProc( AudioDeviceID inDevice,
- const AudioTimeStamp* inNow,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* inInputTime,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* inOutputTime,
- void* inClientData)
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
{
- PaMacClientData *clientData = (PaMacClientData *)inClientData;
- PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ VVDBUG(("IsFormatSupported(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld sampleRate=%g\n",
+ inputParameters ? inputParameters->channelCount : -1,
+ inputParameters ? inputParameters->sampleFormat : -1,
+ outputParameters ? outputParameters->channelCount : -1,
+ outputParameters ? outputParameters->sampleFormat : -1,
+ (float) sampleRate ));
+
+ /** These first checks are standard PA checks. We do some fancier checks
+ later. */
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( inputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
- PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
- AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
- unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( outputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
- clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
- CopyOutputData(outOutputData, clientData, frameCount);
- PaUtil_FreeMemory(timeInfo);
- PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
-}
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
-static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput)
-{
- PaError result = paNoError;
-
- double actualSampleRate;
- UInt32 propSize = sizeof(double);
- result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate));
-
- result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate));
-
- if (result == paNoError && actualSampleRate != sampleRate) {
- result = paInvalidSampleRate;
}
-
- return result;
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ /* FEEDBACK */
+ /* I think the only way to check a given format SR combo is */
+ /* to try opening it. This could be disruptive, is that Okay? */
+ /* The alternative is to just read off available sample rates, */
+ /* but this will not work %100 of the time (eg, a device that */
+ /* supports N output at one rate but only N/2 at a higher rate.)*/
+
+ /* The following code opens the device with the requested parameters to
+ see if it works. */
+ {
+ PaError err;
+ PaStream *s;
+ err = OpenStream( hostApi, &s, inputParameters, outputParameters,
+ sampleRate, 1024, 0, (PaStreamCallback *)1, NULL );
+ if( err != paNoError && err != paInvalidSampleRate )
+ DBUG( ( "OpenStream @ %g returned: %d: %s\n",
+ (float) sampleRate, err, Pa_GetErrorText( err ) ) );
+ if( err )
+ return err;
+ err = CloseStream( s );
+ if( err ) {
+ /* FEEDBACK: is this more serious? should we assert? */
+ DBUG( ( "WARNING: could not close Stream. %d: %s\n",
+ err, Pa_GetErrorText( err ) ) );
+ }
+ }
+
+ return paFormatIsSupported;
}
-static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput)
+static PaError OpenAndSetupOneAudioUnit(
+ const PaStreamParameters *inStreamParams,
+ const PaStreamParameters *outStreamParams,
+ const unsigned long requestedFramesPerBuffer,
+ unsigned long *actualInputFramesPerBuffer,
+ unsigned long *actualOutputFramesPerBuffer,
+ const PaMacAUHAL *auhalHostApi,
+ AudioUnit *audioUnit,
+ AudioConverterRef *srConverter,
+ AudioDeviceID *audioDevice,
+ const double sampleRate,
+ void *refCon )
{
- PaError result = paNoError;
- UInt32 preferredFramesPerBuffer = framesPerBuffer;
- // while (preferredFramesPerBuffer > UINT32_MAX) {
- // preferredFramesPerBuffer /= 2;
- // }
-
- UInt32 actualFramesPerBuffer;
- UInt32 propSize = sizeof(UInt32);
- result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer));
-
- result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer));
-
- if (result != paNoError) {
- // do nothing
+ ComponentDescription desc;
+ Component comp;
+ /*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
+ AudioStreamBasicDescription desiredFormat;
+ OSErr result = noErr;
+ PaError paResult = paNoError;
+ int line;
+ UInt32 callbackKey;
+ AURenderCallbackStruct rcbs;
+ unsigned long macInputStreamFlags = paMacCorePlayNice;
+ unsigned long macOutputStreamFlags = paMacCorePlayNice;
+
+ VVDBUG(("OpenAndSetupOneAudioUnit(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld, requestedFramesPerBuffer=%ld\n",
+ inStreamParams ? inStreamParams->channelCount : -1,
+ inStreamParams ? inStreamParams->sampleFormat : -1,
+ outStreamParams ? outStreamParams->channelCount : -1,
+ outStreamParams ? outStreamParams->sampleFormat : -1,
+ requestedFramesPerBuffer ));
+
+ /* -- handle the degenerate case -- */
+ if( !inStreamParams && !outStreamParams ) {
+ *audioUnit = NULL;
+ *audioDevice = kAudioDeviceUnknown;
+ return paNoError;
}
- else if (actualFramesPerBuffer > framesPerBuffer) {
- result = paBufferTooSmall;
+
+ /* -- get the user's api specific info, if they set any -- */
+ if( inStreamParams && inStreamParams->hostApiSpecificStreamInfo )
+ macInputStreamFlags=
+ ((paMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
+ ->flags;
+ if( outStreamParams && outStreamParams->hostApiSpecificStreamInfo )
+ macOutputStreamFlags=
+ ((paMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
+ ->flags;
+ /* Override user's flags here, if desired for testing. */
+
+ /*
+ * The HAL AU is a Mac OS style "component".
+ * the first few steps deal with that.
+ * Later steps work on a combination of Mac OS
+ * components and the slightly lower level
+ * HAL.
+ */
+
+ /* -- describe the output type AudioUnit -- */
+ /* Note: for the default AudioUnit, we could use the
+ * componentSubType value kAudioUnitSubType_DefaultOutput;
+ * but I don't think that's relevant here.
+ */
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_HALOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ /* -- find the component -- */
+ comp = FindNextComponent( NULL, &desc );
+ if( !comp )
+ {
+ DBUG( ( "AUHAL component not found." ) );
+ *audioUnit = NULL;
+ *audioDevice = kAudioDeviceUnknown;
+ return paUnanticipatedHostError;
}
- else if (actualFramesPerBuffer < framesPerBuffer) {
- result = paBufferTooBig;
+ /* -- open it -- */
+ result = OpenAComponent( comp, audioUnit );
+ if( result )
+ {
+ DBUG( ( "Failed to open AUHAL component." ) );
+ *audioUnit = NULL;
+ *audioDevice = kAudioDeviceUnknown;
+ return ERR( result );
}
-
- return result;
-}
-
-static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput)
-{
- PaError err = paNoError;
- err = SetSampleRate(device, sampleRate, isInput);
- if( err == paNoError )
- err = SetFramesPerBuffer(device, framesPerBuffer, isInput);
- return err;
-}
-
-// ===== PortAudio functions =====
-#pragma mark PortAudio functions
+ /* -- prepare a little error handling logic / hackery -- */
+#define ERR_WRAP(mac_err) do { result = mac_err ; line = __LINE__ ; if ( result != noErr ) goto error ; } while(0)
-#ifdef __cplusplus
-extern "C"
-{
-#endif // __cplusplus
-
- PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
+ /* -- if there is input, we have to explicitly enable input -- */
+ if( inStreamParams )
+ {
+ UInt32 enableIO;
+ enableIO = 1;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Input,
+ INPUT_ELEMENT,
+ &enableIO,
+ sizeof(enableIO) ) );
+ }
+ /* -- if there is no output, we must explicitly disable output -- */
+ if( !outStreamParams )
+ {
+ UInt32 enableIO;
+ enableIO = 0;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Output,
+ OUTPUT_ELEMENT,
+ &enableIO,
+ sizeof(enableIO) ) );
+ }
+ /* -- set the devices -- */
+ /* make sure input and output are the same device if we are doing input and
+ output. */
+ if( inStreamParams && outStreamParams )
+ assert( outStreamParams->device == inStreamParams->device );
+ if( inStreamParams )
+ {
+ *audioDevice = auhalHostApi->devIds[inStreamParams->device] ;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ INPUT_ELEMENT,
+ audioDevice,
+ sizeof(AudioDeviceID) ) );
+ }
+ if( outStreamParams )
+ {
+ *audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ OUTPUT_ELEMENT,
+ audioDevice,
+ sizeof(AudioDeviceID) ) );
+ }
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
-
- CleanUp(macCoreHostApi);
-}
+ /* -- set format -- */
+ bzero( &desiredFormat, sizeof(desiredFormat) );
+ desiredFormat.mFormatID = kAudioFormatLinearPCM ;
+ desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
+ desiredFormat.mFramesPerPacket = 1;
+ desiredFormat.mBitsPerChannel = sizeof( float ) * 8;
+
+ result = 0;
+ /* set device format first, but only touch the device if the user asked */
+ if( inStreamParams ) {
+ /*The callback never calls back if we don't set the FPB */
+ /*This seems wierd, because I would think setting anything on the device
+ would be disruptive.*/
+ paResult = setBestFramesPerBuffer( *audioDevice, FALSE,
+ requestedFramesPerBuffer,
+ actualInputFramesPerBuffer );
+ if( paResult ) goto error;
+ if( macInputStreamFlags & paMacCore_ChangeDeviceParameters ) {
+ bool requireExact;
+ requireExact=macInputStreamFlags&paMacCore_FailIfConversionRequired;
+ paResult = setBestSampleRateForDevice( *audioDevice, FALSE,
+ requireExact, sampleRate );
+ if( paResult ) goto error;
+ }
+ if( actualInputFramesPerBuffer && actualOutputFramesPerBuffer )
+ *actualOutputFramesPerBuffer = *actualInputFramesPerBuffer ;
+ }
+ if( outStreamParams && !inStreamParams ) {
+ /*The callback never calls back if we don't set the FPB */
+ /*This seems wierd, because I would think setting anything on the device
+ would be disruptive.*/
+ paResult = setBestFramesPerBuffer( *audioDevice, TRUE,
+ requestedFramesPerBuffer,
+ actualOutputFramesPerBuffer );
+ if( paResult ) goto error;
+ if( macOutputStreamFlags & paMacCore_ChangeDeviceParameters ) {
+ bool requireExact;
+ requireExact=macOutputStreamFlags&paMacCore_FailIfConversionRequired;
+ paResult = setBestSampleRateForDevice( *audioDevice, TRUE,
+ requireExact, sampleRate );
+ if( paResult ) goto error;
+ }
+ }
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
- PaDeviceInfo *deviceInfo;
-
- PaError result = paNoError;
- if (inputParameters) {
- deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
- if (inputParameters->channelCount > deviceInfo->maxInputChannels)
- result = paInvalidChannelCount;
- else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) {
- result = paInvalidSampleRate;
- }
+ /* -- set the quality of the output converter -- */
+ if( outStreamParams ) {
+ UInt32 value = kAudioConverterQuality_Max;
+ switch( macOutputStreamFlags & 0x0700 ) {
+ case 0x0100: /*paMacCore_ConversionQualityMin:*/
+ value=kRenderQuality_Min;
+ break;
+ case 0x0200: /*paMacCore_ConversionQualityLow:*/
+ value=kRenderQuality_Low;
+ break;
+ case 0x0300: /*paMacCore_ConversionQualityMedium:*/
+ value=kRenderQuality_Medium;
+ break;
+ case 0x0400: /*paMacCore_ConversionQualityHigh:*/
+ value=kRenderQuality_High;
+ break;
+ }
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_RenderQuality,
+ kAudioUnitScope_Global,
+ OUTPUT_ELEMENT,
+ &value,
+ sizeof(value) ) );
}
- if (outputParameters && result == paNoError) {
- deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
- if (outputParameters->channelCount > deviceInfo->maxOutputChannels)
- result = paInvalidChannelCount;
- else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) {
- result = paInvalidSampleRate;
- }
+ /* now set the format on the Audio Units. */
+ if( outStreamParams )
+ {
+ desiredFormat.mSampleRate =sampleRate;
+ desiredFormat.mBytesPerPacket=sizeof(float)*outStreamParams->channelCount;
+ desiredFormat.mBytesPerFrame =sizeof(float)*outStreamParams->channelCount;
+ desiredFormat.mChannelsPerFrame = outStreamParams->channelCount;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ OUTPUT_ELEMENT,
+ &desiredFormat,
+ sizeof(AudioStreamBasicDescription) ) );
+ }
+ if( inStreamParams )
+ {
+ AudioStreamBasicDescription sourceFormat;
+ UInt32 size = sizeof( AudioStreamBasicDescription );
+
+ /* keep the sample rate of the device, or we confuse AUHAL */
+ ERR_WRAP( AudioUnitGetProperty( *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ INPUT_ELEMENT,
+ &sourceFormat,
+ &size ) );
+ desiredFormat.mSampleRate = sourceFormat.mSampleRate;
+ desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ &desiredFormat,
+ sizeof(AudioStreamBasicDescription) ) );
+ }
+ /* set the maximumFramesPerSlice */
+ /* not doing this causes real problems
+ (eg. the callback might not be called). The idea of setting both this
+ and the frames per buffer on the device is that we'll be most likely
+ to actually get the frame size we requested in the callback with the
+ minimum latency. */
+ if( outStreamParams ) {
+ UInt32 size = sizeof( *actualOutputFramesPerBuffer );
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Input,
+ OUTPUT_ELEMENT,
+ actualOutputFramesPerBuffer,
+ sizeof(unsigned long) ) );
+ ERR_WRAP( AudioUnitGetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Global,
+ OUTPUT_ELEMENT,
+ actualOutputFramesPerBuffer,
+ &size ) );
+ }
+ if( inStreamParams ) {
+ /*UInt32 size = sizeof( *actualInputFramesPerBuffer );*/
+ ERR_WRAP( AudioUnitSetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ actualInputFramesPerBuffer,
+ sizeof(unsigned long) ) );
+/* Don't know why this causes problems
+ ERR_WRAP( AudioUnitGetProperty( *audioUnit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Global, //Output,
+ INPUT_ELEMENT,
+ actualInputFramesPerBuffer,
+ &size ) );
+*/
}
- return result;
+ /* -- if we have input, we may need to setup an SR converter -- */
+ /* even if we got the sample rate we asked for, we need to do
+ the conversion in case another program changes the underlying SR. */
+ /* FIXME: I think we need to monitor stream and change the converter if the incoming format changes. */
+ if( inStreamParams ) {
+ AudioStreamBasicDescription desiredFormat;
+ AudioStreamBasicDescription sourceFormat;
+ UInt32 sourceSize = sizeof( sourceFormat );
+ bzero( &desiredFormat, sizeof(desiredFormat) );
+ desiredFormat.mSampleRate = sampleRate;
+ desiredFormat.mFormatID = kAudioFormatLinearPCM ;
+ desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
+ desiredFormat.mFramesPerPacket = 1;
+ desiredFormat.mBitsPerChannel = sizeof( float ) * 8;
+ desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
+ desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
+
+ /* get the source format */
+ ERR_WRAP( AudioUnitGetProperty(
+ *audioUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ &sourceFormat,
+ &sourceSize ) );
+
+ if( desiredFormat.mSampleRate != sourceFormat.mSampleRate )
+ {
+ UInt32 value = kAudioConverterQuality_Max;
+ switch( macInputStreamFlags & 0x0700 ) {
+ case 0x0100: /*paMacCore_ConversionQualityMin:*/
+ value=kAudioConverterQuality_Min;
+ break;
+ case 0x0200: /*paMacCore_ConversionQualityLow:*/
+ value=kAudioConverterQuality_Low;
+ break;
+ case 0x0300: /*paMacCore_ConversionQualityMedium:*/
+ value=kAudioConverterQuality_Medium;
+ break;
+ case 0x0400: /*paMacCore_ConversionQualityHigh:*/
+ value=kAudioConverterQuality_High;
+ break;
+ }
+ VDBUG(( "Creating sample rate converter for input"
+ " to convert from %g to %g\n",
+ (float)sourceFormat.mSampleRate,
+ (float)desiredFormat.mSampleRate ) );
+ /* create our converter */
+ ERR_WRAP( AudioConverterNew(
+ &sourceFormat,
+ &desiredFormat,
+ srConverter ) );
+ /* Set quality */
+ ERR_WRAP( AudioConverterSetProperty(
+ *srConverter,
+ kAudioConverterSampleRateConverterQuality,
+ sizeof( value ),
+ &value ) );
+ }
+ }
+ /* -- set IOProc (callback) -- */
+ callbackKey = outStreamParams ? kAudioUnitProperty_SetRenderCallback
+ : kAudioOutputUnitProperty_SetInputCallback ;
+ rcbs.inputProc = AudioIOProc;
+ rcbs.inputProcRefCon = refCon;
+ ERR_WRAP( AudioUnitSetProperty(
+ *audioUnit,
+ callbackKey,
+ kAudioUnitScope_Output,
+ outStreamParams ? OUTPUT_ELEMENT : INPUT_ELEMENT,
+ &rcbs,
+ sizeof(rcbs)) );
+
+ if( inStreamParams && outStreamParams && *srConverter )
+ ERR_WRAP( AudioUnitSetProperty(
+ *audioUnit,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Output,
+ INPUT_ELEMENT,
+ &rcbs,
+ sizeof(rcbs)) );
+
+ /*IMPLEMENTME: may need to worry about channel mapping.*/
+
+ /* initialize the audio unit */
+ ERR_WRAP( AudioUnitInitialize(*audioUnit) );
+
+ if( inStreamParams && outStreamParams )
+ VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) );
+ else if( inStreamParams )
+ VDBUG( ("Opened device %ld for input.\n", *audioDevice ) );
+ else if( outStreamParams )
+ VDBUG( ("Opened device %ld for output.\n", *audioDevice ) );
+ return paNoError;
+#undef ERR_WRAP
+
+ error:
+ CloseComponent( *audioUnit );
+ *audioUnit = NULL;
+ if( result )
+ return PaMacCore_SetError( result, line, 1 );
+ return paResult;
}
+/* 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,
@@ -635,192 +1050,1012 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStreamCallback *streamCallback,
void *userData )
{
- PaError err = paNoError;
- PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi;
- PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream));
- stream->isActive = 0;
- stream->isStopped = 1;
- stream->inputDevice = kAudioDeviceUnknown;
- stream->outputDevice = kAudioDeviceUnknown;
-
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- ( (streamCallback)
- ? &macCoreHostApi->callbackStreamInterface
- : &macCoreHostApi->blockingStreamInterface ),
- streamCallback, userData );
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
- *s = (PaStream*)stream;
- PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData));
- clientData->stream = stream;
- clientData->callback = streamCallback;
- clientData->userData = userData;
- clientData->inputBuffer = 0;
- clientData->outputBuffer = 0;
- clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator));
- PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator);
-
- if (inputParameters != NULL) {
- stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device];
- clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags);
- clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount);
- clientData->inputChannelCount = inputParameters->channelCount;
- clientData->inputSampleFormat = inputParameters->sampleFormat;
- err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1);
+ PaError result = paNoError;
+ PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
+ PaMacCoreStream *stream = 0;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+ 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 ));
+ VDBUG( ("Opening Stream.\n") );
+
+ /*These first few bits of code are from paSkeleton with few modifications.*/
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* Host supports interleaved float32 */
+ hostInputSampleFormat = paFloat32;
}
-
- if (err == paNoError && outputParameters != NULL) {
- stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device];
- clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags);
- clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount);
- clientData->outputChannelCount = outputParameters->channelCount;
- clientData->outputSampleFormat = outputParameters->sampleFormat;
- err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0);
+ else
+ {
+ inputChannelCount = 0;
+ inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* Host supports interleaved float32 */
+ hostOutputSampleFormat = paFloat32;
+ }
+ else
+ {
+ outputChannelCount = 0;
+ outputSampleFormat = hostOutputSampleFormat = paFloat32; /* Surpress 'uninitialized var' warnings. */
}
- if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) {
- AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice;
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
- AudioDeviceAddIOProc(device, AudioIOProc, clientData);
+ stream = (PaMacCoreStream*)PaUtil_AllocateMemory( sizeof(PaMacCoreStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* If we fail after this point, we my be left in a bad state, with
+ some data structures setup and others not. So, first thing we
+ do is initialize everything so that if we fail, we know what hasn't
+ been touched.
+ */
+
+ stream->inputAudioBufferList.mBuffers[0].mData = NULL;
+ stream->inputRingBuffer.buffer = NULL;
+ stream->inputSRConverter = NULL;
+ stream->inputUnit = NULL;
+ stream->outputUnit = NULL;
+ stream->inputFramesPerBuffer = 0;
+ stream->outputFramesPerBuffer = 0;
+ stream->bufferProcessorIsInitialized = FALSE;
+
+ assert( streamCallback ) ; /* only callback mode is implemented */
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &auhalHostApi->callbackStreamInterface, streamCallback, userData );
}
- else {
- // using different devices for input and output
- AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData);
- AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData);
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &auhalHostApi->blockingStreamInterface, streamCallback, userData );
}
-
- return err;
+
+ 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;
+ size_t size = sizeof( audioRange );
+ if( inputParameters ) {
+ WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &size, &audioRange ) );
+ if( result )
+ requested = MAX( requested, audioRange.mMinimum );
+ }
+ 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;
+ size_t 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 );
+ }
+ 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;
+ }
+
+ /* -- Now we actually open and setup streams. -- */
+ if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
+ { /* full duplex. One device. */
+ result = OpenAndSetupOneAudioUnit( inputParameters,
+ outputParameters,
+ framesPerBuffer,
+ &(stream->inputFramesPerBuffer),
+ &(stream->outputFramesPerBuffer),
+ auhalHostApi,
+ &(stream->inputUnit),
+ &(stream->inputSRConverter),
+ &(stream->inputDevice),
+ sampleRate,
+ stream );
+ stream->outputUnit = stream->inputUnit;
+ stream->outputDevice = stream->inputDevice;
+ if( result != paNoError )
+ goto error;
+ }
+ else
+ { /* full duplex, different devices OR simplex */
+ result = OpenAndSetupOneAudioUnit( NULL,
+ outputParameters,
+ framesPerBuffer,
+ NULL,
+ &(stream->outputFramesPerBuffer),
+ auhalHostApi,
+ &(stream->outputUnit),
+ NULL,
+ &(stream->outputDevice),
+ sampleRate,
+ stream );
+ if( result != paNoError )
+ goto error;
+ result = OpenAndSetupOneAudioUnit( inputParameters,
+ NULL,
+ framesPerBuffer,
+ &(stream->inputFramesPerBuffer),
+ NULL,
+ auhalHostApi,
+ &(stream->inputUnit),
+ &(stream->inputSRConverter),
+ &(stream->inputDevice),
+ sampleRate,
+ stream );
+ if( result != paNoError )
+ goto error;
+ }
+
+ if( stream->inputUnit ) {
+ const size_t szfl = sizeof(float);
+ /* setup the AudioBufferList used for input */
+ bzero( &stream->inputAudioBufferList, sizeof( AudioBufferList ) );
+ stream->inputAudioBufferList.mNumberBuffers = 1;
+ stream->inputAudioBufferList.mBuffers[0].mNumberChannels
+ = inputChannelCount;
+ stream->inputAudioBufferList.mBuffers[0].mDataByteSize
+ = stream->inputFramesPerBuffer*inputChannelCount*szfl;
+ stream->inputAudioBufferList.mBuffers[0].mData
+ = (float *) calloc(
+ stream->inputFramesPerBuffer*inputChannelCount,
+ szfl );
+ if( !stream->inputAudioBufferList.mBuffers[0].mData )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /*
+ * If input and output devs are different or we are doing SR conversion,
+ * we also need a
+ * ring buffer to store inpt data while waiting for output
+ * data.
+ */
+ if( (stream->outputUnit && stream->inputUnit != stream->outputUnit)
+ || stream->inputSRConverter )
+ {
+ /* May want the ringSize ot initial position in
+ ring buffer to depend somewhat on sample rate change */
+ /* Calculate an appropriate ring buffer size. It must be at least
+ 3x framesPerBuffer and 2x suggested latency and it must be a
+ power of 2. FEEDBACK: too liberal/conservative/another way?*/
+ double latency;
+ int index, i;
+ void *data;
+ long ringSize;
+ if( !outputParameters )
+ latency = inputParameters->suggestedLatency;
+ else
+ latency = MAX( inputParameters->suggestedLatency,
+ outputParameters->suggestedLatency );
+ ringSize = latency * sampleRate * 2 * inputChannelCount;
+ VDBUG( ( "suggested latency: %d\n", (int) (latency*sampleRate) ) );
+ if( ringSize < stream->inputFramesPerBuffer * 3 )
+ ringSize = stream->inputFramesPerBuffer * 3 * inputChannelCount;
+ if( outputParameters && ringSize < stream->outputFramesPerBuffer * 3 )
+ ringSize = stream->outputFramesPerBuffer * 3 * inputChannelCount;
+ VDBUG(("inFramesPerBuffer:%d\n",(int)stream->inputFramesPerBuffer));
+ if( outputParameters )
+ VDBUG(("outFramesPerBuffer:%d\n",
+ (int)stream->outputFramesPerBuffer));
+ VDBUG(("Ringbuffer size (1): %d\n", (int)ringSize ));
+
+ /* round up to the next power of 2 */
+ index = -1;
+ for( i=0; i<sizeof(long)*8; ++i )
+ if( ringSize >> i & 0x01 )
+ index = i;
+ assert( index > 0 );
+ if( ringSize <= ( 0x01 << index ) )
+ ringSize = 0x01 << index ;
+ else
+ ringSize = 0x01 << ( index + 1 );
+
+ /*ringSize <<= 4; *//*16x bigger, for testing */
+
+ VDBUG(( "Final Ringbuffer size (2): %d\n", (int)ringSize ));
+
+ /*now, we need to allocate memory for the ring buffer*/
+ data = calloc( ringSize, szfl );
+ if( !data )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* now we can initialize the ring buffer */
+ assert( 0 ==
+ RingBuffer_Init( &stream->inputRingBuffer,
+ ringSize*szfl, data ) );
+ /* advance the read point a little, so we are reading from the
+ middle of the buffer */
+ if( stream->outputUnit )
+ RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, ringSize*szfl / RING_BUFFER_ADVANCE_DENOMINATOR );
+ }
+ }
+
+ /* -- initialize Buffer Processor -- */
+ {
+ unsigned long maxHostFrames = stream->inputFramesPerBuffer;
+ if( stream->outputFramesPerBuffer > maxHostFrames )
+ maxHostFrames = stream->outputFramesPerBuffer;
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat,
+ hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat,
+ hostOutputSampleFormat,
+ sampleRate,
+ streamFlags,
+ framesPerBuffer,
+ /* If sample rate conversion takes place, the buffer size
+ will not be known. */
+ maxHostFrames,
+ stream->inputSRConverter
+ ? paUtilUnknownHostBufferSize
+ : paUtilBoundedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+ }
+ 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);
+ stream->streamRepresentation.streamInfo.outputLatency =
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ stream->sampleRate = sampleRate;
+ stream->userInChan = inputChannelCount;
+ stream->userOutChan = outputChannelCount;
+
+ //stream->isTimeSet = FALSE;
+ stream->state = STOPPED;
+ stream->xrunFlags = 0;
+
+ *s = (PaStream*)stream;
+
+ setStreamStartTime( stream );
+
+ return result;
+
+error:
+ CloseStream( stream );
+ return result;
}
-// Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted.
-static PaError CloseStream( PaStream* s )
+PaTime GetStreamTime( PaStream *s )
{
- PaError err = paNoError;
+ /* FIXME: I am not at all sure this timing info stuff is right.
+ patest_sine_time reports negative latencies, which is wierd.*/
PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ AudioTimeStamp timeStamp;
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+ VVDBUG(("GetStreamTime()\n"));
- if (stream->inputDevice != kAudioDeviceUnknown) {
- if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
- err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc));
- }
- else {
- err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc));
- err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc));
- }
- }
- else {
- err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc));
+ //if ( !stream->isTimeSet )
+ // return (PaTime)0;
+
+ if ( stream->outputDevice )
+ AudioDeviceGetCurrentTime( stream->outputDevice, &timeStamp);
+ else if ( stream->inputDevice )
+ AudioDeviceGetCurrentTime( stream->inputDevice, &timeStamp);
+ else
+ return (PaTime)0;
+
+ return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->sampleRate;
+}
+
+static void setStreamStartTime( PaStream *stream )
+{
+ /* FIXME: I am not at all sure this timing info stuff is right.
+ patest_sine_time reports negative latencies, which is wierd.*/
+ VVDBUG(("setStreamStartTime()\n"));
+ PaMacCoreStream *s = (PaMacCoreStream *) stream;
+ if( s->inputDevice )
+ AudioDeviceGetCurrentTime( s->inputDevice, &s->startTime);
+ else
+ AudioDeviceGetCurrentTime( s->outputDevice, &s->startTime);
+}
+
+
+static PaTime TimeStampToSecs(PaMacCoreStream *stream, const AudioTimeStamp* timeStamp)
+{
+ VVDBUG(("TimeStampToSecs()\n"));
+ if (timeStamp->mFlags & kAudioTimeStampSampleTimeValid)
+ return (timeStamp->mSampleTime / stream->sampleRate);
+ else
+ return 0;
+}
+
+#define RING_BUFFER_EMPTY (1000)
+
+static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter,
+ UInt32*ioDataSize,
+ void** outData,
+ void*inUserData )
+{
+ void *dummyData;
+ long dummySize;
+ RingBuffer *rb = (RingBuffer *) inUserData;
+
+ VVDBUG(("ringBufferIOProc()\n"));
+
+ assert( sizeof( UInt32 ) == sizeof( long ) );
+ if( RingBuffer_GetReadAvailable( rb ) == 0 ) {
+ *outData = NULL;
+ *ioDataSize = 0;
+ return RING_BUFFER_EMPTY;
+ }
+ RingBuffer_GetReadRegions( rb, *ioDataSize,
+ outData, (long *)ioDataSize,
+ &dummyData, &dummySize );
+
+ assert( *ioDataSize );
+ RingBuffer_AdvanceReadIndex( rb, *ioDataSize );
+
+ return noErr;
+}
+
+/*
+ * Called by the AudioUnit API to process audio from the sound card.
+ * This is where the magic happens.
+ */
+/* FEEDBACK: there is a lot of redundant code here because of how all the cases differ. This makes it hard to maintain, so if there are suggestinos for cleaning it up, I'm all ears. */
+static OSStatus AudioIOProc( void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData )
+{
+ unsigned long framesProcessed = 0;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0};
+ PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon;
+ const bool isRender = inBusNumber == OUTPUT_ELEMENT;
+ int callbackResult = paContinue ;
+
+ VVDBUG(("AudioIOProc()\n"));
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ //if( !stream->isTimeSet )
+ // setStreamStartTime( stream );
+ //stream->isTimeSet = TRUE;
+
+
+ /* -----------------------------------------------------------------*\
+ This output may be useful for debugging,
+ But printing durring the callback is a bad enough idea that
+ this is not enabled by enableing the usual debugging calls.
+ \* -----------------------------------------------------------------*/
+ /*
+ static int renderCount = 0;
+ static int inputCount = 0;
+ printf( "------------------- starting reder/input\n" );
+ if( isRender )
+ printf("Render callback (%d):\t", ++renderCount);
+ else
+ printf("Input callback (%d):\t", ++inputCount);
+ printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount );
+
+ printf( "--- inBusNumber: %lu\n", inBusNumber );
+ printf( "--- inNumberFrames: %lu\n", inNumberFrames );
+ printf( "--- %x ioData\n", (unsigned) ioData );
+ if( ioData )
+ {
+ int i=0;
+ printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers );
+ for( i=0; i<ioData->mNumberBuffers; ++i )
+ printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize );
+ }
+ ----------------------------------------------------------------- */
+
+ if( isRender ) {
+ AudioTimeStamp currentTime;
+ timeInfo.outputBufferDacTime = TimeStampToSecs(stream, inTimeStamp);
+ AudioDeviceGetCurrentTime(stream->outputDevice, &currentTime);
+ timeInfo.currentTime = TimeStampToSecs(stream, &currentTime);
+ }
+ if( isRender && stream->inputUnit == stream->outputUnit )
+ timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
+ if( !isRender ) {
+ AudioTimeStamp currentTime;
+ timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
+ AudioDeviceGetCurrentTime(stream->inputDevice, &currentTime);
+ timeInfo.currentTime = TimeStampToSecs(stream, &currentTime);
+ }
+
+
+ if( isRender && stream->inputUnit == stream->outputUnit
+ && !stream->inputSRConverter )
+ {
+ /* --------- Full Duplex, One Device, no SR Conversion -------
+ *
+ * This is the lowest latency case, and also the simplest.
+ * Input data and output data are available at the same time.
+ * we do not use the input SR converter or the input ring buffer.
+ *
+ */
+ OSErr err = 0;
+ unsigned long frames;
+
+ /* -- start processing -- */
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ stream->xrunFlags );
+ stream->xrunFlags = 0;
+
+ /* -- compute frames. do some checks -- */
+ assert( ioData->mNumberBuffers == 1 );
+ assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
+ frames = ioData->mBuffers[0].mDataByteSize;
+ frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
+ /* -- copy and process input data -- */
+ err= AudioUnitRender(stream->inputUnit,
+ ioActionFlags,
+ inTimeStamp,
+ INPUT_ELEMENT,
+ inNumberFrames,
+ &stream->inputAudioBufferList );
+ /* FEEDBACK: I'm not sure what to do when this call fails */
+ assert( !err );
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ stream->inputAudioBufferList.mBuffers[0].mData,
+ stream->inputAudioBufferList.mBuffers[0].mNumberChannels);
+ /* -- Copy and process output data -- */
+ PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
+ 0,
+ ioData->mBuffers[0].mData,
+ ioData->mBuffers[0].mNumberChannels);
+ /* -- complete processing -- */
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ else if( isRender )
+ {
+ /* -------- Output Side of Full Duplex (Separate Devices or SR Conversion)
+ * -- OR Simplex Output
+ *
+ * This case handles output data as in the full duplex case,
+ * and, if there is input data, reads it off the ring buffer
+ * and into the PA buffer processor. If sample rate conversion
+ * is required on input, that is done here as well.
+ */
+ unsigned long frames;
+
+ /* Sometimes, when stopping a duplex stream we get erroneous
+ xrun flags, so if this is our last run, clear the flags. */
+ int xrunFlags = stream->xrunFlags;
+ if( xrunFlags & paInputUnderflow )
+ printf( "input underflow.\n" );
+ if( xrunFlags & paInputOverflow )
+ printf( "input overflow.\n" );
+ if( stream->state == STOPPING || stream->state == CALLBACK_STOPPED )
+ xrunFlags = 0;
+
+ /* -- start processing -- */
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ xrunFlags );
+ stream->xrunFlags = 0; /* FEEDBACK: we only send flags to Buf Proc once */
+
+ /* -- Copy and process output data -- */
+ assert( ioData->mNumberBuffers == 1 );
+ frames = ioData->mBuffers[0].mDataByteSize;
+ frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
+ assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
+ PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
+ 0,
+ ioData->mBuffers[0].mData,
+ ioData->mBuffers[0].mNumberChannels);
+
+ /* -- copy and process input data, and complete processing -- */
+ if( stream->inputUnit ) {
+ const int flsz = sizeof( float );
+ /* Here, we read the data out of the ring buffer, through the
+ audio converter. */
+ int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels;
+ if( stream->inputSRConverter )
+ {
+ OSStatus err;
+ UInt32 size;
+ float data[ inChan * frames ];
+ size = sizeof( data );
+ err = AudioConverterFillBuffer(
+ stream->inputSRConverter,
+ ringBufferIOProc,
+ &stream->inputRingBuffer,
+ &size,
+ (void *)&data );
+ if( err == RING_BUFFER_EMPTY )
+ { /*the ring buffer callback underflowed */
+ err = 0;
+ bzero( ((char *)data) + size, sizeof(data)-size );
+ stream->xrunFlags |= paInputUnderflow;
+ }
+ ERR( err );
+ assert( !err );
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ else
+ {
+ /* Without the AudioConverter is actually a bit more complex
+ because we have to do a little buffer processing that the
+ AudioConverter would otherwise handle for us. */
+ void *data1, *data2;
+ long size1, size2;
+ RingBuffer_GetReadRegions( &stream->inputRingBuffer,
+ inChan*frames*flsz,
+ &data1, &size1,
+ &data2, &size2 );
+ if( size1 / ( flsz * inChan ) == frames ) {
+ /* simplest case: all in first buffer */
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data1,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1 );
+ } else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) {
+ /*we underflowed. take what data we can, zero the rest.*/
+ float data[frames*inChan];
+ if( size1 )
+ memcpy( data, data1, size1 );
+ if( size2 )
+ memcpy( data+size1, data2, size2 );
+ bzero( data+size1+size2, frames*flsz*inChan - size1 - size2 );
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ RingBuffer_AdvanceReadIndex( &stream->inputRingBuffer,
+ size1+size2 );
+ /* flag underflow */
+ stream->xrunFlags |= paInputUnderflow;
+ } else {
+ /*we got all the data, but split between buffers*/
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor),
+ size1 / ( flsz * inChan ) );
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data1,
+ inChan );
+ PaUtil_Set2ndInputFrameCount( &(stream->bufferProcessor),
+ size2 / ( flsz * inChan ) );
+ PaUtil_Set2ndInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data2,
+ inChan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1+size2 );
+ }
+ }
+ } else {
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+
+ }
+ else
+ {
+ /* ------------------ Input
+ *
+ * First, we read off the audio data and put it in the ring buffer.
+ * if this is an input-only stream, we need to process it more,
+ * otherwise, we let the output case deal with it.
+ */
+ OSErr err = 0;
+ int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ;
+ /* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */
+ do {
+ err= AudioUnitRender(stream->inputUnit,
+ ioActionFlags,
+ inTimeStamp,
+ INPUT_ELEMENT,
+ inNumberFrames,
+ &stream->inputAudioBufferList );
+ if( err == -10874 )
+ inNumberFrames /= 2;
+ } while( err == -10874 && inNumberFrames > 1 );
+ /* FEEDBACK: I'm not sure what to do when this call fails */
+ ERR( err );
+ assert( !err );
+ if( stream->inputSRConverter || stream->outputUnit )
+ {
+ /* If this is duplex or we use a converter, put the data
+ into the ring buffer. */
+ long bytesIn, bytesOut;
+ bytesIn = sizeof( float ) * inNumberFrames * chan;
+ bytesOut = RingBuffer_Write( &stream->inputRingBuffer,
+ stream->inputAudioBufferList.mBuffers[0].mData,
+ bytesIn );
+ if( bytesIn != bytesOut )
+ stream->xrunFlags |= paInputOverflow ;
+ }
+ else
+ {
+ /* for simplex input w/o SR conversion,
+ just pop the data into the buffer processor.*/
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ stream->xrunFlags );
+ stream->xrunFlags = 0;
+
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), inNumberFrames);
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ stream->inputAudioBufferList.mBuffers[0].mData,
+ chan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ if( !stream->outputUnit && stream->inputSRConverter )
+ {
+ /* ------------------ Simplex Input w/ SR Conversion
+ *
+ * if this is a simplex input stream, we need to read off the buffer,
+ * do our sample rate conversion and pass the results to the buffer
+ * processor.
+ * The logic here is complicated somewhat by the fact that we don't
+ * know how much data is available, so we loop on reasonably sized
+ * chunks, and let the BufferProcessor deal with the rest.
+ *
+ */
+ /*This might be too big or small depending on SR conversion*/
+ float data[ chan * inNumberFrames ];
+ OSStatus err;
+ do
+ { /*Run the buffer processor until we are out of data*/
+ UInt32 size;
+ long f;
+
+ size = sizeof( data );
+ err = AudioConverterFillBuffer(
+ stream->inputSRConverter,
+ ringBufferIOProc,
+ &stream->inputRingBuffer,
+ &size,
+ (void *)data );
+ if( err != RING_BUFFER_EMPTY )
+ ERR( err );
+ assert( err == 0 || err == RING_BUFFER_EMPTY );
+
+ f = size / ( chan * sizeof(float) );
+ PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f );
+ if( f )
+ {
+ PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
+ &timeInfo,
+ stream->xrunFlags );
+ stream->xrunFlags = 0;
+
+ PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
+ 0,
+ data,
+ chan );
+ framesProcessed =
+ PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
+ &callbackResult );
+ }
+ } while( callbackResult == paContinue && !err );
+ }
+ }
+
+ switch( callbackResult )
+ {
+ case paContinue: break;
+ case paComplete:
+ case paAbort:
+ stream->state = CALLBACK_STOPPED ;
+ if( stream->outputUnit )
+ AudioOutputUnitStop(stream->outputUnit);
+ if( stream->inputUnit )
+ AudioOutputUnitStop(stream->inputUnit);
+ break;
+ }
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+ return noErr;
+}
+
+
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ /* This may be called from a failed OpenStream.
+ Therefore, each piece of info is treated seperately. */
+ PaError result = paNoError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+ VVDBUG(("CloseStream()\n"));
+ VDBUG( ( "Closing stream.\n" ) );
+
+ if( stream ) {
+ if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
+ AudioUnitUninitialize( stream->outputUnit );
+ CloseComponent( stream->outputUnit );
+ }
+ stream->outputUnit = NULL;
+ if( stream->inputUnit )
+ {
+ AudioUnitUninitialize( stream->inputUnit );
+ CloseComponent( stream->inputUnit );
+ stream->inputUnit = NULL;
+ }
+ if( stream->inputRingBuffer.buffer )
+ free( stream->inputRingBuffer.buffer );
+ stream->inputRingBuffer.buffer = NULL;
+ /*TODO: is there more that needs to be done on error
+ from AudioConverterDispose?*/
+ if( stream->inputSRConverter )
+ ERR( AudioConverterDispose( stream->inputSRConverter ) );
+ stream->inputSRConverter = NULL;
+ if( stream->inputAudioBufferList.mBuffers[0].mData )
+ free( stream->inputAudioBufferList.mBuffers[0].mData );
+ stream->inputAudioBufferList.mBuffers[0].mData = NULL;
+
+ if( stream->bufferProcessorIsInitialized )
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
}
-
- return err;
+
+ return result;
}
static PaError StartStream( PaStream *s )
{
- PaError err = paNoError;
PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ OSErr result = noErr;
+ VVDBUG(("StartStream()\n"));
+ VDBUG( ( "Starting stream.\n" ) );
- if (stream->inputDevice != kAudioDeviceUnknown) {
- if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
- err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc));
- }
- else {
- err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc));
- err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc));
- }
+#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
+
+ /*FIXME: maybe want to do this on close/abort for faster start? */
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+ if( stream->inputSRConverter )
+ ERR_WRAP( AudioConverterReset( stream->inputSRConverter ) );
+
+ /* -- start -- */
+ stream->state = ACTIVE;
+ if( stream->inputUnit ) {
+ ERR_WRAP( AudioOutputUnitStart(stream->inputUnit) );
}
- else {
- err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc));
+ if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
+ ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) );
}
-
- stream->isActive = 1;
- stream->isStopped = 0;
- return err;
+
+ return paNoError;
+#undef ERR_WRAP
}
-static PaError AbortStream( PaStream *s )
+
+static PaError StopStream( PaStream *s )
{
- PaError err = paNoError;
PaMacCoreStream *stream = (PaMacCoreStream*)s;
-
- if (stream->inputDevice != kAudioDeviceUnknown) {
- if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
- err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc));
- }
- else {
- err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc));
- err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc));
- }
+ OSErr result = noErr;
+ VVDBUG(("StopStream()\n"));
+ VDBUG( ( "Stopping stream.\n" ) );
+
+ stream->state = STOPPING;
+
+#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
+ /* -- stop and reset -- */
+ if( stream->inputUnit == stream->outputUnit && stream->inputUnit )
+ {
+ ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) );
+ ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) );
+ ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) );
}
- else {
- err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc));
+ else
+ {
+ if( stream->inputUnit )
+ {
+ ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) );
+ ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1));
+ }
+ if( stream->outputUnit )
+ {
+ ERR_WRAP(AudioOutputUnitStop(stream->outputUnit));
+ ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0));
+ }
+ }
+ if( stream->inputRingBuffer.buffer ) {
+ RingBuffer_Flush( &stream->inputRingBuffer );
+ bzero(stream->inputRingBuffer.buffer,stream->inputRingBuffer.bufferSize);
+ /* advance the write point a little, so we are reading from the
+ middle of the buffer. We'll need extra at the end because
+ testing has shown that this helps. */
+ if( stream->outputUnit )
+ RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer,
+ stream->inputRingBuffer.bufferSize
+ / RING_BUFFER_ADVANCE_DENOMINATOR );
}
-
- stream->isActive = 0;
- stream->isStopped = 1;
- return err;
-}
-static PaError StopStream( PaStream *s )
-{
- // TODO: this should be nicer than abort
- return AbortStream(s);
+ //stream->isTimeSet = FALSE;
+ stream->xrunFlags = 0;
+ stream->state = STOPPED;
+
+ VDBUG( ( "Stream Stopped.\n" ) );
+ return paNoError;
+#undef ERR_WRAP
}
-static PaError IsStreamStopped( PaStream *s )
+static PaError AbortStream( PaStream *s )
{
- PaMacCoreStream *stream = (PaMacCoreStream*)s;
-
- return stream->isStopped;
+ VVDBUG(("AbortStream()->StopStream()\n"));
+ VDBUG( ( "Aborting stream.\n" ) );
+ /* We have nothing faster than StopStream. */
+ return StopStream(s);
}
-static PaError IsStreamActive( PaStream *s )
+static PaError IsStreamStopped( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("IsStreamStopped()\n"));
- return stream->isActive;
+ return stream->state == STOPPED ? 1 : 0;
}
-static PaTime GetStreamTime( PaStream *s )
+static PaError IsStreamActive( PaStream *s )
{
- OSStatus err;
- PaTime result;
PaMacCoreStream *stream = (PaMacCoreStream*)s;
-
- AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp));
- if (stream->inputDevice != kAudioDeviceUnknown) {
- err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp);
- }
- else {
- err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp);
- }
-
- result = err ? 0 : timeStamp->mSampleTime;
- PaUtil_FreeMemory(timeStamp);
-
- return result;
+ VVDBUG(("IsStreamActive()\n"));
+ return ( stream->state == ACTIVE || stream->state == STOPPING );
}
static double GetStreamCpuLoad( PaStream* s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
-
+ VVDBUG(("GetStreamCpuLoad()\n"));
+
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}
-// As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams.
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams. IMPLEMENTME: no blocking interface yet!
+*/
static PaError ReadStream( PaStream* s,
void *buffer,
unsigned long frames )
{
- return paInternalError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("ReadStream()\n"));
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
}
@@ -828,71 +2063,43 @@ static PaError WriteStream( PaStream* s,
const void *buffer,
unsigned long frames )
{
- return paInternalError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("WriteStream()\n"));
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
}
static signed long GetStreamReadAvailable( PaStream* s )
{
- return paInternalError;
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("GetStreamReadAvailable()\n"));
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
}
static signed long GetStreamWriteAvailable( PaStream* s )
{
- return paInternalError;
-}
+ PaMacCoreStream *stream = (PaMacCoreStream*)s;
+ VVDBUG(("GetStreamWriteAvailable()\n"));
-// HostAPI-specific initialization function
-PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) );
- if( !macCoreHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
+ /* suppress unused variable warnings */
+ (void) stream;
- macCoreHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !macCoreHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &macCoreHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paCoreAudio;
- (*hostApi)->info.name = "CoreAudio";
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
- result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex);
- if (result != paNoError) {
- goto error;
- }
-
- // Set up the proper callbacks to this HostApi's functions
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( macCoreHostApi ) {
- CleanUp(macCoreHostApi);
- }
-
- return result;
+ return 0;
}
diff --git a/pd/portaudio/pa_win_ds/pa_win_ds.c b/pd/portaudio/pa_win_ds/pa_win_ds.c
index d1bc698a..ef970906 100644
--- a/pd/portaudio/pa_win_ds/pa_win_ds.c
+++ b/pd/portaudio/pa_win_ds/pa_win_ds.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $
+ * $Id: pa_win_ds.c,v 1.1.2.51 2006/01/26 01:13:18 rossbencina Exp $
* Portable Audio I/O Library DirectSound implementation
*
* Based on the Open Source API proposed by Ross Bencina
@@ -75,11 +75,17 @@
#pragma comment( lib, "winmm.lib" )
#endif
+/*
+ provided in newer platform sdks and x64
+ */
+#ifndef DWORD_PTR
+#define DWORD_PTR DWORD
+#endif
-#define PRINT(x) /* { printf x; fflush(stdout); } */
+#define PRINT(x) PA_DEBUG(x);
#define ERR_RPT(x) PRINT(x)
-#define DBUG(x) /* PRINT(x) */
-#define DBUGX(x) /* PRINT(x) */
+#define DBUG(x) PRINT(x)
+#define DBUGX(x) PRINT(x)
#define PA_USE_HIGH_LATENCY (0)
#if PA_USE_HIGH_LATENCY
@@ -366,6 +372,15 @@ static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
}
+/*
+ GUIDs for emulated devices which we blacklist below.
+ are there more than two of them??
+*/
+
+GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
+GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
+
+
#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
static double defaultSampleRateSearchOrder_[] =
{ 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
@@ -404,6 +419,16 @@ static PaError AddOutputDeviceInfoFromDirectSound(
}
+ if( lpGUID )
+ {
+ if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
+ IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
+ {
+ PA_DEBUG(("BLACKLISTED: %s \n",name));
+ return paNoError;
+ }
+ }
+
/* Create a DirectSound object for the specified GUID
Note that using CoCreateInstance doesn't work on windows CE.
*/
@@ -427,7 +452,24 @@ static PaError AddOutputDeviceInfoFromDirectSound(
if( hr != DS_OK )
{
+ if (hr == DSERR_ALLOCATED)
+ PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
+ if (lpGUID)
+ DBUG(("%s's GUID: {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, 0x%x} \n",
+ name,
+ lpGUID->Data1,
+ lpGUID->Data2,
+ lpGUID->Data3,
+ lpGUID->Data4[0],
+ lpGUID->Data4[1],
+ lpGUID->Data4[2],
+ lpGUID->Data4[3],
+ lpGUID->Data4[4],
+ lpGUID->Data4[5],
+ lpGUID->Data4[6],
+ lpGUID->Data4[7]));
+
deviceOK = FALSE;
}
else
@@ -491,7 +533,7 @@ static PaError AddOutputDeviceInfoFromDirectSound(
*/
deviceInfo->defaultSampleRate = 44100.0f;
- DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
+ DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
}
else
{
@@ -505,7 +547,7 @@ static PaError AddOutputDeviceInfoFromDirectSound(
** So when we see a ridiculous set of rates, assume it is a range.
*/
deviceInfo->defaultSampleRate = 44100.0f;
- DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
+ DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
}
else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
@@ -1511,7 +1553,7 @@ error2:
return result;
}
/*******************************************************************/
-static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
{
PaWinDsStream *stream;
@@ -1635,7 +1677,7 @@ static PaError StartStream( PaStream *s )
else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
resolution = msecPerWakeup/4;
stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
- (DWORD) stream, TIME_PERIODIC );
+ (DWORD_PTR) stream, TIME_PERIODIC );
}
if( stream->timerID == 0 )
{
diff --git a/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c b/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c
index be7d8a7a..ff05e384 100644
--- a/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c
+++ b/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_win_wdmks.c,v 1.3 2005-12-31 20:55:25 millerpuckette Exp $
+ * $Id: pa_win_wdmks.c,v 1.4 2006-06-03 19:13:07 millerpuckette Exp $
* PortAudio Windows WDM-KS interface
*
* Author: Andrew Baldwin
@@ -51,8 +51,8 @@
#ifdef __GNUC__
#include <initguid.h>
- #define _WIN32_WINNT 0x0501
- #define WINVER 0x0501
+ #define _WIN32_WINNT 0x0501
+ #define WINVER 0x0501
#endif
#include <string.h> /* strlen() */
@@ -71,10 +71,10 @@
#ifdef __GNUC__
- #undef PA_LOGE_
- #define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
- #undef PA_LOGL_
- #define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__))
+ #undef PA_LOGE_
+ #define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
+ #undef PA_LOGL_
+ #define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__))
/* These defines are set in order to allow the WIndows DirectX
* headers to compile with a GCC compiler such as MinGW
* NOTE: The headers may generate a few warning in GCC, but
@@ -93,15 +93,15 @@
#define WAVE_FORMAT_MULAW 0x0007
#define WAVE_FORMAT_MPEG 0x0050
#define WAVE_FORMAT_DRM 0x0009
- #define DYNAMIC_GUID_THUNK(l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
- #define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data)
+ #define DYNAMIC_GUID_THUNK(l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
+ #define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data)
#endif
#ifdef _MSC_VER
- #define DYNAMIC_GUID(data) {data}
+ #define DYNAMIC_GUID(data) {data}
#define _INC_MMREG
#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
- #undef DEFINE_GUID
+ #undef DEFINE_GUID
#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
@@ -157,75 +157,75 @@ PaWinWdmPin;
* A filter has a number of pins and a "friendly name" */
struct __PaWinWdmFilter
{
- HANDLE handle;
- int pinCount;
- PaWinWdmPin** pins;
- TCHAR filterName[MAX_PATH];
- TCHAR friendlyName[MAX_PATH];
- int maxInputChannels;
- int maxOutputChannels;
- unsigned long formats;
- int usageCount;
- int bestSampleRate;
+ HANDLE handle;
+ int pinCount;
+ PaWinWdmPin** pins;
+ TCHAR filterName[MAX_PATH];
+ TCHAR friendlyName[MAX_PATH];
+ int maxInputChannels;
+ int maxOutputChannels;
+ unsigned long formats;
+ int usageCount;
+ int bestSampleRate;
};
/* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
typedef struct __PaWinWdmHostApiRepresentation
{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
- PaUtilAllocationGroup *allocations;
- PaWinWdmFilter** filters;
- int filterCount;
+ PaUtilAllocationGroup* allocations;
+ PaWinWdmFilter** filters;
+ int filterCount;
}
PaWinWdmHostApiRepresentation;
typedef struct __PaWinWdmDeviceInfo
{
- PaDeviceInfo inheritedDeviceInfo;
- PaWinWdmFilter* filter;
+ PaDeviceInfo inheritedDeviceInfo;
+ PaWinWdmFilter* filter;
}
PaWinWdmDeviceInfo;
typedef struct __DATAPACKET
{
- KSSTREAM_HEADER Header;
- OVERLAPPED Signal;
+ KSSTREAM_HEADER Header;
+ OVERLAPPED Signal;
} DATAPACKET;
/* PaWinWdmStream - a stream data structure specifically for this implementation */
typedef struct __PaWinWdmStream
{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
- PaWinWdmPin* recordingPin;
- PaWinWdmPin* playbackPin;
- char* hostBuffer;
- unsigned long framesPerHostIBuffer;
- unsigned long framesPerHostOBuffer;
- int bytesPerInputFrame;
- int bytesPerOutputFrame;
- int streamStarted;
- int streamActive;
- int streamStop;
- int streamAbort;
- int oldProcessPriority;
- HANDLE streamThread;
- HANDLE events[5]; /* 2 play + 2 record packets + abort events */
- DATAPACKET packets[4]; /* 2 play + 2 record */
- PaStreamFlags streamFlags;
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ PaWinWdmPin* recordingPin;
+ PaWinWdmPin* playbackPin;
+ char* hostBuffer;
+ unsigned long framesPerHostIBuffer;
+ unsigned long framesPerHostOBuffer;
+ int bytesPerInputFrame;
+ int bytesPerOutputFrame;
+ int streamStarted;
+ int streamActive;
+ int streamStop;
+ int streamAbort;
+ int oldProcessPriority;
+ HANDLE streamThread;
+ HANDLE events[5]; /* 2 play + 2 record packets + abort events */
+ DATAPACKET packets[4]; /* 2 play + 2 record */
+ PaStreamFlags streamFlags;
/* These values handle the case where the user wants to use fewer
* channels than the device has */
- int userInputChannels;
- int deviceInputChannels;
- int userOutputChannels;
- int deviceOutputChannels;
- int inputSampleSize;
- int outputSampleSize;
+ int userInputChannels;
+ int deviceInputChannels;
+ int userOutputChannels;
+ int deviceOutputChannels;
+ int inputSampleSize;
+ int outputSampleSize;
}
PaWinWdmStream;
@@ -326,19 +326,21 @@ static void FilterRelease(
/* Interface functions */
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
+static PaError IsFormatSupported(
+ struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream(
+ struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
@@ -347,8 +349,14 @@ static PaError IsStreamStopped( PaStream *s );
static PaError IsStreamActive( PaStream *stream );
static PaTime GetStreamTime( PaStream *stream );
static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static PaError ReadStream(
+ PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+static PaError WriteStream(
+ PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
static signed long GetStreamReadAvailable( PaStream* stream );
static signed long GetStreamWriteAvailable( PaStream* stream );
@@ -365,7 +373,7 @@ static DWORD WINAPI ProcessingThread(LPVOID pParam);
static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
{
- if ( wfex->wFormatTag == WAVE_FORMAT_PCM )
+ if( wfex->wFormatTag == WAVE_FORMAT_PCM )
{
return sizeof( WAVEFORMATEX );
}
@@ -378,7 +386,8 @@ static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
/*
Low level pin/filter access functions
*/
-static PaError WdmSyncIoctl(HANDLE handle,
+static PaError WdmSyncIoctl(
+ HANDLE handle,
unsigned long ioctlNumber,
void* inBuffer,
unsigned long inBufferCount,
@@ -392,39 +401,39 @@ static PaError WdmSyncIoctl(HANDLE handle,
unsigned long dummyBytesReturned;
unsigned long error;
- if (!bytesReturned)
+ if( !bytesReturned )
{
/* User a dummy as the caller hasn't supplied one */
bytesReturned = &dummyBytesReturned;
}
- FillMemory((void *)&overlapped,sizeof(overlapped),0);
+ FillMemory((void *)&overlapped,sizeof(overlapped),0);
overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
- if ( !overlapped.hEvent )
+ if( !overlapped.hEvent )
{
- result = paInsufficientMemory;
+ result = paInsufficientMemory;
goto error;
}
overlapped.hEvent = (HANDLE)((DWORD_PTR)overlapped.hEvent | 0x1);
boolResult = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount,
- outBuffer, outBufferCount, bytesReturned, &overlapped);
- if ( !boolResult )
+ outBuffer, outBufferCount, bytesReturned, &overlapped);
+ if( !boolResult )
{
error = GetLastError();
- if ( error == ERROR_IO_PENDING )
+ if( error == ERROR_IO_PENDING )
{
error = WaitForSingleObject(overlapped.hEvent,INFINITE);
- if ( error != WAIT_OBJECT_0 )
+ if( error != WAIT_OBJECT_0 )
{
result = paUnanticipatedHostError;
goto error;
}
}
- else if ((( error == ERROR_INSUFFICIENT_BUFFER ) ||
- ( error == ERROR_MORE_DATA )) &&
- ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
- ( outBufferCount == 0 ))
+ else if((( error == ERROR_INSUFFICIENT_BUFFER ) ||
+ ( error == ERROR_MORE_DATA )) &&
+ ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
+ ( outBufferCount == 0 ))
{
boolResult = TRUE;
}
@@ -433,47 +442,47 @@ static PaError WdmSyncIoctl(HANDLE handle,
result = paUnanticipatedHostError;
}
}
- if ( !boolResult )
+ if( !boolResult )
*bytesReturned = 0;
error:
- if ( overlapped.hEvent )
+ if( overlapped.hEvent )
{
- CloseHandle( overlapped.hEvent );
- }
- return result;
+ CloseHandle( overlapped.hEvent );
+ }
+ return result;
}
static PaError WdmGetPropertySimple(HANDLE handle,
const GUID* const guidPropertySet,
- unsigned long property,
- void* value,
- unsigned long valueCount,
- void* instance,
- unsigned long instanceCount)
+ unsigned long property,
+ void* value,
+ unsigned long valueCount,
+ void* instance,
+ unsigned long instanceCount)
{
PaError result;
KSPROPERTY* ksProperty;
unsigned long propertyCount;
propertyCount = sizeof(KSPROPERTY) + instanceCount;
- ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
+ ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
if( !ksProperty )
{
- return paInsufficientMemory;
+ return paInsufficientMemory;
}
- FillMemory((void*)ksProperty,sizeof(ksProperty),0);
+ FillMemory((void*)ksProperty,sizeof(ksProperty),0);
ksProperty->Set = *guidPropertySet;
ksProperty->Id = property;
ksProperty->Flags = KSPROPERTY_TYPE_GET;
- if ( instance )
- {
- memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount );
- }
+ if( instance )
+ {
+ memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount );
+ }
- result = WdmSyncIoctl(
+ result = WdmSyncIoctl(
handle,
IOCTL_KS_PROPERTY,
ksProperty,
@@ -482,12 +491,12 @@ static PaError WdmGetPropertySimple(HANDLE handle,
valueCount,
NULL);
- PaUtil_FreeMemory( ksProperty );
+ PaUtil_FreeMemory( ksProperty );
return result;
}
static PaError WdmSetPropertySimple(
- HANDLE handle,
+ HANDLE handle,
const GUID* const guidPropertySet,
unsigned long property,
void* value,
@@ -500,17 +509,17 @@ static PaError WdmSetPropertySimple(
unsigned long propertyCount = 0;
propertyCount = sizeof(KSPROPERTY) + instanceCount;
- ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
+ ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
if( !ksProperty )
{
- return paInsufficientMemory;
+ return paInsufficientMemory;
}
ksProperty->Set = *guidPropertySet;
ksProperty->Id = property;
ksProperty->Flags = KSPROPERTY_TYPE_SET;
- if ( instance )
+ if( instance )
{
memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
}
@@ -524,12 +533,12 @@ static PaError WdmSetPropertySimple(
valueCount,
NULL);
- PaUtil_FreeMemory( ksProperty );
+ PaUtil_FreeMemory( ksProperty );
return result;
}
static PaError WdmGetPinPropertySimple(
- HANDLE handle,
+ HANDLE handle,
unsigned long pinId,
const GUID* const guidPropertySet,
unsigned long property,
@@ -546,26 +555,26 @@ static PaError WdmGetPinPropertySimple(
ksPProp.Reserved = 0;
result = WdmSyncIoctl(
- handle,
- IOCTL_KS_PROPERTY,
- &ksPProp,
- sizeof(KSP_PIN),
- value,
- valueCount,
- NULL);
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksPProp,
+ sizeof(KSP_PIN),
+ value,
+ valueCount,
+ NULL);
return result;
}
static PaError WdmGetPinPropertyMulti(
- HANDLE handle,
+ HANDLE handle,
unsigned long pinId,
const GUID* const guidPropertySet,
unsigned long property,
KSMULTIPLE_ITEM** ksMultipleItem)
{
PaError result;
- unsigned long multipleItemSize = 0;
+ unsigned long multipleItemSize = 0;
KSP_PIN ksPProp;
ksPProp.Property.Set = *guidPropertySet;
@@ -575,36 +584,36 @@ static PaError WdmGetPinPropertyMulti(
ksPProp.Reserved = 0;
result = WdmSyncIoctl(
- handle,
- IOCTL_KS_PROPERTY,
- &ksPProp.Property,
- sizeof(KSP_PIN),
- NULL,
- 0,
- &multipleItemSize);
- if ( result != paNoError )
- {
- return result;
- }
-
- *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksPProp.Property,
+ sizeof(KSP_PIN),
+ NULL,
+ 0,
+ &multipleItemSize);
+ if( result != paNoError )
+ {
+ return result;
+ }
+
+ *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
if( !*ksMultipleItem )
{
- return paInsufficientMemory;
+ return paInsufficientMemory;
}
result = WdmSyncIoctl(
- handle,
- IOCTL_KS_PROPERTY,
- &ksPProp,
- sizeof(KSP_PIN),
- (void*)*ksMultipleItem,
- multipleItemSize,
- NULL);
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksPProp,
+ sizeof(KSP_PIN),
+ (void*)*ksMultipleItem,
+ multipleItemSize,
+ NULL);
- if ( result != paNoError )
+ if( result != paNoError )
{
- PaUtil_FreeMemory( ksMultipleItem );
+ PaUtil_FreeMemory( ksMultipleItem );
}
return result;
@@ -652,23 +661,23 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
}
/* Configure the connect structure with default values */
- pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard;
- pin->pinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
- pin->pinConnect->Interface.Flags = 0;
- pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard;
- pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
- pin->pinConnect->Medium.Flags = 0;
- pin->pinConnect->PinId = pinId;
- pin->pinConnect->PinToHandle = NULL;
- pin->pinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
- pin->pinConnect->Priority.PrioritySubClass = 1;
+ pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard;
+ pin->pinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
+ pin->pinConnect->Interface.Flags = 0;
+ pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard;
+ pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
+ pin->pinConnect->Medium.Flags = 0;
+ pin->pinConnect->PinId = pinId;
+ pin->pinConnect->PinToHandle = NULL;
+ pin->pinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
+ pin->pinConnect->Priority.PrioritySubClass = 1;
pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1);
- pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);
- pin->ksDataFormatWfx->DataFormat.Flags = 0;
- pin->ksDataFormatWfx->DataFormat.Reserved = 0;
+ pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);
+ pin->ksDataFormatWfx->DataFormat.Flags = 0;
+ pin->ksDataFormatWfx->DataFormat.Reserved = 0;
pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
- pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
+ pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
pin->frameSize = 0; /* Unknown until we instantiate pin */
@@ -680,10 +689,10 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
KSPROPERTY_PIN_COMMUNICATION,
&pin->communication,
sizeof(KSPIN_COMMUNICATION));
- if ( result != paNoError )
+ if( result != paNoError )
goto error;
- if ( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
+ if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
(pin->communication != KSPIN_COMMUNICATION_SINK) &&
(pin->communication != KSPIN_COMMUNICATION_BOTH) )
{
@@ -701,7 +710,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
&pin->dataFlow,
sizeof(KSPIN_DATAFLOW));
- if ( result != paNoError )
+ if( result != paNoError )
goto error;
/* Get the INTERFACE property list */
@@ -712,16 +721,16 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
KSPROPERTY_PIN_INTERFACES,
&item);
- if ( result != paNoError )
+ if( result != paNoError )
goto error;
identifier = (KSIDENTIFIER*)(item+1);
/* Check that at least one interface is STANDARD_STREAMING */
result = paUnanticipatedHostError;
- for ( i = 0; i < item->Count; i++ )
+ for( i = 0; i < item->Count; i++ )
{
- if ( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) &&
+ if( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) &&
( identifier[i].Id == KSINTERFACE_STANDARD_STREAMING ) )
{
result = paNoError;
@@ -729,7 +738,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
}
}
- if ( result != paNoError )
+ if( result != paNoError )
{
PA_DEBUG(("No standard streaming\n"));
goto error;
@@ -747,16 +756,16 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
KSPROPERTY_PIN_MEDIUMS,
&item);
- if ( result != paNoError )
+ if( result != paNoError )
goto error;
identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
/* Check that at least one medium is STANDARD_DEVIO */
result = paUnanticipatedHostError;
- for ( i = 0; i < item->Count; i++ )
+ for( i = 0; i < item->Count; i++ )
{
- if ( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) &&
+ if( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) &&
( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
{
result = paNoError;
@@ -764,7 +773,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
}
}
- if ( result != paNoError )
+ if( result != paNoError )
{
PA_DEBUG(("No standard devio\n"));
goto error;
@@ -781,7 +790,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
KSPROPERTY_PIN_DATARANGES,
&pin->dataRangesItem);
- if ( result != paNoError )
+ if( result != paNoError )
goto error;
pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
@@ -791,13 +800,12 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
dataRange = pin->dataRanges;
pin->maxChannels = 0;
pin->bestSampleRate = 0;
- pin->formats = 0;
- for ( i = 0; i <pin->dataRangesItem->Count; i++)
+ pin->formats = 0;
+ for( i = 0; i <pin->dataRangesItem->Count; i++)
{
PA_DEBUG(("DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
/* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
- if (
- IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
+ if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
!memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof ( GUID ) ) ||
( !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof ( GUID ) ) &&
( !memcmp((void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof ( GUID ) ) ) ) )
@@ -805,32 +813,30 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
result = paNoError;
/* Record the maximum possible channels with this pin */
PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));
- if ((int)((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels)
+ if( (int)((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels )
{
pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
/*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/
}
/* Record the formats (bit depths) that are supported */
- if (((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16)
+ if( ((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16 )
{
pin->formats |= paInt16;
PA_DEBUG(("Format 16 bit supported\n"));
}
- if (((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24)
+ if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24 )
{
pin->formats |= paInt24;
PA_DEBUG(("Format 24 bit supported\n"));
}
- if (
- ( pin->bestSampleRate != 48000) &&
+ if( ( pin->bestSampleRate != 48000) &&
(((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) &&
(((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) )
{
pin->bestSampleRate = 48000;
PA_DEBUG(("48kHz supported\n"));
}
- else if (
- ( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) &&
+ else if(( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) &&
(((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) &&
(((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) )
{
@@ -845,7 +851,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
}
- if ( result != paNoError )
+ if( result != paNoError )
goto error;
/* Get instance information */
@@ -857,7 +863,7 @@ static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, Pa
&pin->instances,
sizeof(KSPIN_CINSTANCES));
- if ( result != paNoError )
+ if( result != paNoError )
goto error;
/* Success */
@@ -871,7 +877,7 @@ error:
Error cleanup
*/
PaUtil_FreeMemory( item );
- if ( pin )
+ if( pin )
{
PaUtil_FreeMemory( pin->pinConnect );
PaUtil_FreeMemory( pin->dataRangesItem );
@@ -888,14 +894,14 @@ Safely free all resources associated with the pin
static void PinFree(PaWinWdmPin* pin)
{
PA_LOGE_;
- if ( pin )
+ if( pin )
{
PinClose(pin);
- if ( pin->pinConnect )
+ if( pin->pinConnect )
{
PaUtil_FreeMemory( pin->pinConnect );
}
- if ( pin->dataRangesItem )
+ if( pin->dataRangesItem )
{
PaUtil_FreeMemory( pin->dataRangesItem );
}
@@ -910,13 +916,13 @@ If the pin handle is open, close it
static void PinClose(PaWinWdmPin* pin)
{
PA_LOGE_;
- if ( pin == NULL )
+ if( pin == NULL )
{
PA_DEBUG(("Closing NULL pin!"));
PA_LOGL_;
return;
}
- if ( pin->handle != NULL )
+ if( pin->handle != NULL )
{
PinSetState( pin, KSSTATE_PAUSE );
PinSetState( pin, KSSTATE_STOP );
@@ -935,9 +941,9 @@ static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
PaError result;
PA_LOGE_;
- if ( pin == NULL )
+ if( pin == NULL )
return paInternalError;
- if ( pin->handle == NULL )
+ if( pin->handle == NULL )
return paInternalError;
result = WdmSetPropertySimple(
@@ -961,9 +967,9 @@ static PaError PinInstantiate(PaWinWdmPin* pin)
PA_LOGE_;
- if ( pin == NULL )
+ if( pin == NULL )
return paInternalError;
- if (!pin->pinConnect)
+ if(!pin->pinConnect)
return paInternalError;
FilterUse(pin->parentFilter);
@@ -976,7 +982,7 @@ static PaError PinInstantiate(PaWinWdmPin* pin)
);
PA_DEBUG(("Pin create result = %x\n",createResult));
- if ( createResult != ERROR_SUCCESS )
+ if( createResult != ERROR_SUCCESS )
{
FilterRelease(pin->parentFilter);
pin->handle = NULL;
@@ -992,7 +998,7 @@ static PaError PinInstantiate(PaWinWdmPin* pin)
NULL,
0);
- if ( result != paNoError )
+ if( result != paNoError )
{
result = WdmGetPropertySimple(
pin->handle,
@@ -1002,7 +1008,7 @@ static PaError PinInstantiate(PaWinWdmPin* pin)
sizeof(ksafex),
NULL,
0);
- if ( result == paNoError )
+ if( result == paNoError )
{
pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
}
@@ -1022,11 +1028,11 @@ static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state)
{
PaError result;
- if ( state == NULL )
+ if( state == NULL )
return paInternalError;
- if ( pin == NULL )
+ if( pin == NULL )
return paInternalError;
- if ( pin->handle == NULL )
+ if( pin->handle == NULL )
return paInternalError;
result = WdmGetPropertySimple(
@@ -1048,17 +1054,17 @@ static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
PA_LOGE_;
- if ( pin == NULL )
+ if( pin == NULL )
return paInternalError;
- if ( format == NULL )
+ if( format == NULL )
return paInternalError;
size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
- if ( pin->pinConnectSize != size )
+ if( pin->pinConnectSize != size )
{
newConnect = PaUtil_AllocateMemory( size );
- if ( newConnect == NULL )
+ if( newConnect == NULL )
return paInsufficientMemory;
memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) );
PaUtil_FreeMemory( pin->pinConnect );
@@ -1085,21 +1091,21 @@ static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format
PA_LOGE_;
- if ( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE )
+ if( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE )
{
guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat;
}
dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
- for (count = 0; count<pin->dataRangesItem->Count; count++)
+ for(count = 0; count<pin->dataRangesItem->Count; count++)
{
- if (( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) ||
+ if(( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) ||
( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) ))
{
/* This is an audio or wildcard datarange... */
- if (( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) ||
+ if(( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) ||
( !memcmp(&(dataRange->DataRange.SubFormat),&guid,sizeof(GUID)) ))
{
- if (( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) ||
+ if(( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) ||
( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,sizeof(GUID) )))
{
@@ -1109,27 +1115,27 @@ static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format
PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample));
PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency));
- if ( dataRange->MaximumChannels < format->nChannels )
+ if( dataRange->MaximumChannels < format->nChannels )
{
result = paInvalidChannelCount;
continue;
}
- if ( dataRange->MinimumBitsPerSample > format->wBitsPerSample )
+ if( dataRange->MinimumBitsPerSample > format->wBitsPerSample )
{
result = paSampleFormatNotSupported;
continue;
}
- if ( dataRange->MaximumBitsPerSample < format->wBitsPerSample )
+ if( dataRange->MaximumBitsPerSample < format->wBitsPerSample )
{
result = paSampleFormatNotSupported;
continue;
}
- if ( dataRange->MinimumSampleFrequency > format->nSamplesPerSec )
+ if( dataRange->MinimumSampleFrequency > format->nSamplesPerSec )
{
result = paInvalidSampleRate;
continue;
}
- if ( dataRange->MaximumSampleFrequency < format->nSamplesPerSec )
+ if( dataRange->MaximumSampleFrequency < format->nSamplesPerSec )
{
result = paInvalidSampleRate;
continue;
@@ -1178,7 +1184,7 @@ static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError
/* Open the filter handle */
result = FilterUse(filter);
- if ( result != paNoError )
+ if( result != paNoError )
{
goto error;
}
@@ -1194,7 +1200,7 @@ static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError
sizeof(filter->pinCount)
);
- if ( result != paNoError)
+ if( result != paNoError)
{
goto error;
}
@@ -1213,51 +1219,51 @@ static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError
filter->bestSampleRate = 0;
valid = 0;
- for (pinId = 0; pinId < filter->pinCount; pinId++)
+ for(pinId = 0; pinId < filter->pinCount; pinId++)
{
/* Create the pin with this Id */
PaWinWdmPin* newPin;
newPin = PinNew(filter, pinId, &result);
- if ( result == paInsufficientMemory )
+ if( result == paInsufficientMemory )
goto error;
- if ( newPin != NULL )
+ if( newPin != NULL )
{
filter->pins[pinId] = newPin;
valid = 1;
/* Get the max output channel count */
- if (( newPin->dataFlow == KSPIN_DATAFLOW_IN ) &&
+ if(( newPin->dataFlow == KSPIN_DATAFLOW_IN ) &&
(( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
{
- if (newPin->maxChannels > filter->maxOutputChannels)
+ if(newPin->maxChannels > filter->maxOutputChannels)
filter->maxOutputChannels = newPin->maxChannels;
filter->formats |= newPin->formats;
}
/* Get the max input channel count */
- if (( newPin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
+ if(( newPin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
(( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
{
- if (newPin->maxChannels > filter->maxInputChannels)
+ if(newPin->maxChannels > filter->maxInputChannels)
filter->maxInputChannels = newPin->maxChannels;
filter->formats |= newPin->formats;
}
- if (newPin->bestSampleRate > filter->bestSampleRate)
+ if(newPin->bestSampleRate > filter->bestSampleRate)
{
filter->bestSampleRate = newPin->bestSampleRate;
}
}
}
- if (( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0))
+ if(( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0))
{
/* No input or output... not valid */
valid = 0;
}
- if ( !valid )
+ if( !valid )
{
/* No valid pin was found on this filter so we destroy it */
result = paDeviceUnavailable;
@@ -1275,12 +1281,12 @@ error:
/*
Error cleanup
*/
- if ( filter )
+ if( filter )
{
- for ( pinId = 0; pinId < filter->pinCount; pinId++ )
+ for( pinId = 0; pinId < filter->pinCount; pinId++ )
PinFree(filter->pins[pinId]);
PaUtil_FreeMemory( filter->pins );
- if ( filter->handle )
+ if( filter->handle )
CloseHandle( filter->handle );
PaUtil_FreeMemory( filter );
}
@@ -1295,12 +1301,12 @@ static void FilterFree(PaWinWdmFilter* filter)
{
int pinId;
PA_LOGL_;
- if ( filter )
+ if( filter )
{
- for ( pinId = 0; pinId < filter->pinCount; pinId++ )
+ for( pinId = 0; pinId < filter->pinCount; pinId++ )
PinFree(filter->pins[pinId]);
PaUtil_FreeMemory( filter->pins );
- if ( filter->handle )
+ if( filter->handle )
CloseHandle( filter->handle );
PaUtil_FreeMemory( filter );
}
@@ -1315,7 +1321,7 @@ static PaError FilterUse(PaWinWdmFilter* filter)
assert( filter );
PA_LOGE_;
- if ( filter->handle == NULL )
+ if( filter->handle == NULL )
{
/* Open the filter */
filter->handle = CreateFile(
@@ -1327,7 +1333,7 @@ static PaError FilterUse(PaWinWdmFilter* filter)
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
- if ( filter->handle == NULL )
+ if( filter->handle == NULL )
{
return paDeviceUnavailable;
}
@@ -1347,9 +1353,9 @@ static void FilterRelease(PaWinWdmFilter* filter)
PA_LOGE_;
filter->usageCount--;
- if ( filter->usageCount == 0 )
+ if( filter->usageCount == 0 )
{
- if ( filter->handle != NULL )
+ if( filter->handle != NULL )
{
CloseHandle( filter->handle );
filter->handle = NULL;
@@ -1371,17 +1377,17 @@ static PaWinWdmPin* FilterCreateRenderPin(PaWinWdmFilter* filter,
assert( filter );
pin = FilterFindViableRenderPin(filter,wfex,&result);
- if (!pin)
+ if(!pin)
{
goto error;
}
result = PinSetFormat(pin,wfex);
- if ( result != paNoError )
+ if( result != paNoError )
{
goto error;
}
result = PinInstantiate(pin);
- if ( result != paNoError )
+ if( result != paNoError )
{
goto error;
}
@@ -1408,17 +1414,17 @@ static PaWinWdmPin* FilterFindViableRenderPin(PaWinWdmFilter* filter,
assert( filter );
- for ( pinId = 0; pinId<filter->pinCount; pinId++ )
+ for( pinId = 0; pinId<filter->pinCount; pinId++ )
{
pin = filter->pins[pinId];
- if ( pin != NULL )
+ if( pin != NULL )
{
- if (( pin->dataFlow == KSPIN_DATAFLOW_IN ) &&
+ if(( pin->dataFlow == KSPIN_DATAFLOW_IN ) &&
(( pin->communication == KSPIN_COMMUNICATION_SINK) ||
( pin->communication == KSPIN_COMMUNICATION_BOTH)))
{
result = PinIsFormatSupported( pin, wfex );
- if ( result == paNoError )
+ if( result == paNoError )
{
return pin;
}
@@ -1462,19 +1468,19 @@ static PaWinWdmPin* FilterCreateCapturePin(PaWinWdmFilter* filter,
assert( filter );
pin = FilterFindViableCapturePin(filter,wfex,&result);
- if (!pin)
+ if(!pin)
{
goto error;
}
result = PinSetFormat(pin,wfex);
- if ( result != paNoError )
+ if( result != paNoError )
{
goto error;
}
result = PinInstantiate(pin);
- if ( result != paNoError )
+ if( result != paNoError )
{
goto error;
}
@@ -1501,17 +1507,17 @@ static PaWinWdmPin* FilterFindViableCapturePin(PaWinWdmFilter* filter,
assert( filter );
- for ( pinId = 0; pinId<filter->pinCount; pinId++ )
+ for( pinId = 0; pinId<filter->pinCount; pinId++ )
{
pin = filter->pins[pinId];
- if ( pin != NULL )
+ if( pin != NULL )
{
- if (( pin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
+ if(( pin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
(( pin->communication == KSPIN_COMMUNICATION_SINK) ||
( pin->communication == KSPIN_COMMUNICATION_BOTH)))
{
result = PinIsFormatSupported( pin, wfex );
- if ( result == paNoError )
+ if( result == paNoError )
{
return pin;
}
@@ -1575,7 +1581,7 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
/* Open a handle to search for devices (filters) */
handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
- if ( handle == NULL )
+ if( handle == NULL )
{
return paUnanticipatedHostError;
}
@@ -1583,7 +1589,7 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
/* First let's count the number of devices so we can allocate a list */
invalidDevices = 0;
- for ( device = 0;;device++ )
+ for( device = 0;;device++ )
{
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
interfaceData.Reserved = 0;
@@ -1591,16 +1597,16 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
aliasData.Reserved = 0;
noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
PA_DEBUG(("Enum called\n"));
- if ( !noError )
+ if( !noError )
break; /* No more devices */
/* Check this one has the render or capture alias */
hasAlias = 0;
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
PA_DEBUG(("noError = %d\n",noError));
- if (noError)
+ if(noError)
{
- if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
{
PA_DEBUG(("Device %d has render alias\n",device));
hasAlias |= 1; /* Has render alias */
@@ -1611,9 +1617,9 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
}
}
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
- if (noError)
+ if(noError)
{
- if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
{
PA_DEBUG(("Device %d has capture alias\n",device));
hasAlias |= 2; /* Has capture alias */
@@ -1623,7 +1629,7 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
PA_DEBUG(("Device %d has no capture alias\n",device));
}
}
- if (!hasAlias)
+ if(!hasAlias)
invalidDevices++; /* This was not a valid capture or render audio device */
}
@@ -1636,14 +1642,14 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
wdmHostApi->filters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * device );
if( !wdmHostApi->filters )
{
- if (handle != NULL)
+ if(handle != NULL)
SetupDiDestroyDeviceInfoList(handle);
return paInsufficientMemory;
}
/* Now create filter objects for each interface found */
slot = 0;
- for ( device = 0;;device++ )
+ for( device = 0;;device++ )
{
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
interfaceData.Reserved = 0;
@@ -1653,34 +1659,34 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
devInfoData.Reserved = 0;
noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
- if ( !noError )
+ if( !noError )
break; /* No more devices */
/* Check this one has the render or capture alias */
hasAlias = 0;
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
- if (noError)
+ if(noError)
{
- if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
{
PA_DEBUG(("Device %d has render alias\n",device));
hasAlias |= 1; /* Has render alias */
}
}
noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
- if (noError)
+ if(noError)
{
- if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
{
PA_DEBUG(("Device %d has capture alias\n",device));
hasAlias |= 2; /* Has capture alias */
}
}
- if (!hasAlias)
+ if(!hasAlias)
continue; /* This was not a valid capture or render audio device */
noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
- if ( noError )
+ if( noError )
{
/* Try to get the "friendly name" for this interface */
sizeFriendlyName = sizeof(friendlyName);
@@ -1689,10 +1695,10 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
* as its causes failure when running without admin rights
* and it was not required */
hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
- if (hkey!=INVALID_HANDLE_VALUE)
+ if(hkey!=INVALID_HANDLE_VALUE)
{
noError = RegQueryValueEx(hkey,TEXT("FriendlyName"),0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
- if ( noError == ERROR_SUCCESS )
+ if( noError == ERROR_SUCCESS )
{
PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
RegCloseKey(hkey);
@@ -1703,7 +1709,7 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
}
}
newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result);
- if ( result == paNoError )
+ if( result == paNoError )
{
PA_DEBUG(("Filter created\n"));
wdmHostApi->filters[slot] = newFilter;
@@ -1720,7 +1726,7 @@ static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
}
/* Clean up */
- if (handle != NULL)
+ if(handle != NULL)
SetupDiDestroyDeviceInfoList(handle);
return paNoError;
@@ -1736,22 +1742,22 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
PaWinWdmDeviceInfo *wdmDeviceInfo;
PaDeviceInfo *deviceInfo;
- PA_LOGE_;
+ PA_LOGE_;
- /*
- Attempt to load the KSUSER.DLL without which we cannot create pins
- We will unload this on termination
- */
- if (DllKsUser == NULL)
- {
- DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
- if (DllKsUser == NULL)
- goto error;
- }
+ /*
+ Attempt to load the KSUSER.DLL without which we cannot create pins
+ We will unload this on termination
+ */
+ if(DllKsUser == NULL)
+ {
+ DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
+ if(DllKsUser == NULL)
+ goto error;
+ }
- FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
- if (FunctionKsCreatePin == NULL)
- goto error;
+ FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
+ if(FunctionKsCreatePin == NULL)
+ goto error;
wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
if( !wdmHostApi )
@@ -1768,7 +1774,7 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
}
result = BuildFilterList( wdmHostApi );
- if ( result != paNoError )
+ if( result != paNoError )
{
goto error;
}
@@ -1778,7 +1784,7 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
(*hostApi)->info.structVersion = 1;
(*hostApi)->info.type = paWDMKS;
(*hostApi)->info.name = "Windows WDM-KS";
-
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
(*hostApi)->info.defaultOutputDevice = paNoDevice;
if( deviceCount > 0 )
@@ -1804,8 +1810,8 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
{
wdmDeviceInfo = &deviceInfoArray[i];
deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
- pFilter = wdmHostApi->filters[i];
- if ( pFilter == NULL )
+ pFilter = wdmHostApi->filters[i];
+ if( pFilter == NULL )
continue;
wdmDeviceInfo->filter = pFilter;
deviceInfo->structVersion = 2;
@@ -1813,24 +1819,24 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->name = (char*)pFilter->friendlyName;
PA_DEBUG(("Device found name: %s\n",(char*)pFilter->friendlyName));
deviceInfo->maxInputChannels = pFilter->maxInputChannels;
- if (deviceInfo->maxInputChannels > 0)
+ if(deviceInfo->maxInputChannels > 0)
{
/* Set the default input device to the first device we find with
* more than zero input channels
**/
- if ((*hostApi)->info.defaultInputDevice == paNoDevice)
+ if((*hostApi)->info.defaultInputDevice == paNoDevice)
{
(*hostApi)->info.defaultInputDevice = i;
}
}
deviceInfo->maxOutputChannels = pFilter->maxOutputChannels;
- if (deviceInfo->maxOutputChannels > 0)
+ if(deviceInfo->maxOutputChannels > 0)
{
/* Set the default output device to the first device we find with
* more than zero output channels
**/
- if ((*hostApi)->info.defaultOutputDevice == paNoDevice)
+ if((*hostApi)->info.defaultOutputDevice == paNoDevice)
{
(*hostApi)->info.defaultOutputDevice = i;
}
@@ -1850,7 +1856,6 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->defaultSampleRate = (double)(pFilter->bestSampleRate);
(*hostApi)->deviceInfos[i] = deviceInfo;
- ++(*hostApi)->info.deviceCount;
}
}
@@ -1871,15 +1876,15 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
GetStreamTime, PaUtil_DummyGetCpuLoad,
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
- PA_LOGL_;
+ PA_LOGL_;
return result;
error:
- if ( DllKsUser != NULL )
- {
- FreeLibrary( DllKsUser );
- DllKsUser = NULL;
- }
+ if( DllKsUser != NULL )
+ {
+ FreeLibrary( DllKsUser );
+ DllKsUser = NULL;
+ }
if( wdmHostApi )
{
@@ -1900,19 +1905,19 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
int i;
- PA_LOGE_;
+ PA_LOGE_;
- if( wdmHostApi->filters )
- {
- for ( i=0; i<wdmHostApi->filterCount; i++)
+ if( wdmHostApi->filters )
+ {
+ for( i=0; i<wdmHostApi->filterCount; i++)
{
- if ( wdmHostApi->filters[i] != NULL )
+ if( wdmHostApi->filters[i] != NULL )
{
FilterFree( wdmHostApi->filters[i] );
wdmHostApi->filters[i] = NULL;
}
}
- }
+ }
PaUtil_FreeMemory( wdmHostApi->filters );
if( wdmHostApi->allocations )
{
@@ -1924,20 +1929,20 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
}
static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
- {
- PA_LOGE_;
- PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
- PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
- PA_DEBUG(( "chanelCount = %d\n", channelCount ));
+{
+ PA_LOGE_;
+ PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
+ PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
+ PA_DEBUG(( "chanelCount = %d\n", channelCount ));
pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
pwfext->Format.nChannels = channelCount;
pwfext->Format.nSamplesPerSec = (int)sampleRate;
- if (channelCount == 1)
- pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
+ if(channelCount == 1)
+ pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
else
- pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
- if (sampleFormat == paFloat32)
+ pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
+ if(sampleFormat == paFloat32)
{
pwfext->Format.nBlockAlign = channelCount * 4;
pwfext->Format.wBitsPerSample = 32;
@@ -1945,7 +1950,7 @@ static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat
pwfext->Samples.wValidBitsPerSample = 32;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
- else if (sampleFormat == paInt32)
+ else if(sampleFormat == paInt32)
{
pwfext->Format.nBlockAlign = channelCount * 4;
pwfext->Format.wBitsPerSample = 32;
@@ -1953,7 +1958,7 @@ static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat
pwfext->Samples.wValidBitsPerSample = 32;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
- else if (sampleFormat == paInt24)
+ else if(sampleFormat == paInt24)
{
pwfext->Format.nBlockAlign = channelCount * 3;
pwfext->Format.wBitsPerSample = 24;
@@ -1961,7 +1966,7 @@ static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat
pwfext->Samples.wValidBitsPerSample = 24;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
- else if (sampleFormat == paInt16)
+ else if(sampleFormat == paInt16)
{
pwfext->Format.nBlockAlign = channelCount * 2;
pwfext->Format.wBitsPerSample = 16;
@@ -1986,7 +1991,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
int result = paFormatIsSupported;
WAVEFORMATEXTENSIBLE wfx;
- PA_LOGE_;
+ PA_LOGE_;
if( inputParameters )
{
@@ -2016,8 +2021,8 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount);
pFilter = wdmHostApi->filters[inputParameters->device];
- result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
- if ( result != paNoError )
+ result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
+ if( result != paNoError )
{
/* Try a WAVE_FORMAT_PCM instead */
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
@@ -2026,7 +2031,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
wfx.dwChannelMask = 0;
wfx.SubFormat = GUID_NULL;
result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
- if ( result != paNoError )
+ if( result != paNoError )
return result;
}
}
@@ -2063,8 +2068,8 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount);
pFilter = wdmHostApi->filters[outputParameters->device];
- result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
- if ( result != paNoError )
+ result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
+ if( result != paNoError )
{
/* Try a WAVE_FORMAT_PCM instead */
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
@@ -2073,7 +2078,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
wfx.dwChannelMask = 0;
wfx.SubFormat = GUID_NULL;
result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
- if ( result != paNoError )
+ if( result != paNoError )
return result;
}
@@ -2103,11 +2108,11 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
we have the capability to convert from outputSampleFormat to
a native format
*/
- if ((inputChannelCount == 0)&&(outputChannelCount == 0))
- result = paSampleFormatNotSupported; /* Not right error */
+ if((inputChannelCount == 0)&&(outputChannelCount == 0))
+ result = paSampleFormatNotSupported; /* Not right error */
- PA_LOGL_;
- return result;
+ PA_LOGL_;
+ return result;
}
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
@@ -2129,13 +2134,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
int userInputChannels,userOutputChannels;
- int size;
+ int size;
PaWinWdmFilter* pFilter;
WAVEFORMATEXTENSIBLE wfx;
- PA_LOGE_;
- PA_DEBUG(("sampleRate = %f;",sampleRate));
- PA_DEBUG(("framesPerBuffer = %lu;",framesPerBuffer));
+ PA_LOGE_;
+ PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate));
+ PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerBuffer));
if( inputParameters )
{
@@ -2216,22 +2221,44 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
/* Instantiate the input pin if necessary */
- if (userInputChannels > 0)
+ if(userInputChannels > 0)
{
- stream->userInputChannels = userInputChannels;
+ result = paSampleFormatNotSupported;
pFilter = wdmHostApi->filters[inputParameters->device];
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat );
- if ( hostInputSampleFormat == paInt16 )
- stream->inputSampleSize = 2;
- else
- stream->inputSampleSize = 3;
- FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,stream->userInputChannels);
- stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
- stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
- stream->deviceInputChannels = stream->userInputChannels;
- if (result != paNoError)
- {
+ stream->userInputChannels = userInputChannels;
+
+ if(((inputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0)
+ { /* inputSampleFormat is supported, so try to use it */
+ hostInputSampleFormat = inputSampleFormat;
+ FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels);
+ stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+ stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result);
+ stream->deviceInputChannels = stream->userInputChannels;
+ }
+
+ if(result != paNoError)
+ { /* Search through all PaSampleFormats to find one that works */
+ hostInputSampleFormat = paFloat32;
+
+ do {
+ FillWFEXT(&wfx, hostInputSampleFormat, sampleRate, stream->userInputChannels);
+ stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+ stream->recordingPin = FilterCreateCapturePin(pFilter, (const WAVEFORMATEX*)&wfx, &result);
+ stream->deviceInputChannels = stream->userInputChannels;
+
+ if(stream->recordingPin == NULL) result = paSampleFormatNotSupported;
+ if(result != paNoError) hostInputSampleFormat <<= 1;
+ }
+ while(result != paNoError && hostInputSampleFormat <= paUInt8);
+ }
+
+ if(result != paNoError)
+ { /* None of the PaSampleFormats worked. Set the hostInputSampleFormat to the best fit
+ * and try a PCM format.
+ **/
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat );
+
/* Try a WAVE_FORMAT_PCM instead */
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
wfx.Format.cbSize = 0;
@@ -2239,36 +2266,49 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
wfx.dwChannelMask = 0;
wfx.SubFormat = GUID_NULL;
stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
- if ( result != paNoError )
+ if(stream->recordingPin == NULL) result = paSampleFormatNotSupported;
+ }
+
+ if( result != paNoError )
+ {
+ /* Some or all KS devices can only handle the exact number of channels
+ * they specify. But PortAudio clients expect to be able to
+ * at least specify mono I/O on a multi-channel device
+ * If this is the case, then we will do the channel mapping internally
+ **/
+ if( stream->userInputChannels < pFilter->maxInputChannels )
{
- /* Some or all KS devices can only handle the exact number of channels
- * they specify. But PortAudio clients expect to be able to
- * at least specify mono I/O on a multi-channel device
- * If this is the case, then we will do the channel mapping internally
- **/
- if ( stream->userInputChannels < pFilter->maxInputChannels )
+ FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,pFilter->maxInputChannels);
+ stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+ stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ stream->deviceInputChannels = pFilter->maxInputChannels;
+
+ if( result != paNoError )
{
- FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,pFilter->maxInputChannels);
- stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
- stream->deviceInputChannels = pFilter->maxInputChannels;
- if ( result != paNoError )
- {
- /* Try a WAVE_FORMAT_PCM instead */
- wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
- wfx.Format.cbSize = 0;
- wfx.Samples.wValidBitsPerSample = 0;
- wfx.dwChannelMask = 0;
- wfx.SubFormat = GUID_NULL;
- stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
- }
}
}
}
- if (stream->recordingPin == NULL)
+
+ if(stream->recordingPin == NULL)
{
goto error;
}
+
+ switch(hostInputSampleFormat)
+ {
+ case paInt16: stream->inputSampleSize = 2; break;
+ case paInt24: stream->inputSampleSize = 3; break;
+ case paInt32:
+ case paFloat32: stream->inputSampleSize = 4; break;
+ }
+
stream->recordingPin->frameSize /= stream->bytesPerInputFrame;
PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize));
}
@@ -2279,22 +2319,42 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
/* Instantiate the output pin if necessary */
- if (userOutputChannels > 0)
+ if(userOutputChannels > 0)
{
- stream->userOutputChannels = userOutputChannels;
+ result = paSampleFormatNotSupported;
pFilter = wdmHostApi->filters[outputParameters->device];
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( pFilter->formats/*paInt16*/, outputSampleFormat );
- if ( hostOutputSampleFormat == paInt16 )
- stream->outputSampleSize = 2;
- else
- stream->outputSampleSize = 3;
- FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
- stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
- stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
- stream->deviceOutputChannels = stream->userOutputChannels;
- if (result != paNoError)
+ stream->userOutputChannels = userOutputChannels;
+
+ if(((outputSampleFormat & ~paNonInterleaved) & pFilter->formats) != 0)
+ {
+ hostOutputSampleFormat = outputSampleFormat;
+ FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
+ stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+ stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
+ stream->deviceOutputChannels = stream->userOutputChannels;
+ }
+
+ if(result != paNoError)
{
+ hostOutputSampleFormat = paFloat32;
+
+ do {
+ FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
+ stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+ stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
+ stream->deviceOutputChannels = stream->userOutputChannels;
+
+ if(stream->playbackPin == NULL) result = paSampleFormatNotSupported;
+ if(result != paNoError) hostOutputSampleFormat <<= 1;
+ }
+ while(result != paNoError && hostOutputSampleFormat <= paUInt8);
+ }
+
+ if(result != paNoError)
+ {
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( pFilter->formats, outputSampleFormat );
+
/* Try a WAVE_FORMAT_PCM instead */
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
wfx.Format.cbSize = 0;
@@ -2302,36 +2362,48 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
wfx.dwChannelMask = 0;
wfx.SubFormat = GUID_NULL;
stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
- if ( result != paNoError )
+ if(stream->playbackPin == NULL) result = paSampleFormatNotSupported;
+ }
+
+ if( result != paNoError )
+ {
+ /* Some or all KS devices can only handle the exact number of channels
+ * they specify. But PortAudio clients expect to be able to
+ * at least specify mono I/O on a multi-channel device
+ * If this is the case, then we will do the channel mapping internally
+ **/
+ if( stream->userOutputChannels < pFilter->maxOutputChannels )
{
- /* Some or all KS devices can only handle the exact number of channels
- * they specify. But PortAudio clients expect to be able to
- * at least specify mono I/O on a multi-channel device
- * If this is the case, then we will do the channel mapping internally
- **/
- if ( stream->userOutputChannels < pFilter->maxOutputChannels )
+ FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,pFilter->maxOutputChannels);
+ stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+ stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ stream->deviceOutputChannels = pFilter->maxOutputChannels;
+ if( result != paNoError )
{
- FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,pFilter->maxOutputChannels);
- stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
- stream->deviceOutputChannels = pFilter->maxOutputChannels;
- if ( result != paNoError )
- {
- /* Try a WAVE_FORMAT_PCM instead */
- wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
- wfx.Format.cbSize = 0;
- wfx.Samples.wValidBitsPerSample = 0;
- wfx.dwChannelMask = 0;
- wfx.SubFormat = GUID_NULL;
- stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
- }
}
}
}
- if (stream->playbackPin == NULL)
+
+ if(stream->playbackPin == NULL)
{
goto error;
}
+
+ switch(hostOutputSampleFormat)
+ {
+ case paInt16: stream->outputSampleSize = 2; break;
+ case paInt24: stream->outputSampleSize = 3; break;
+ case paInt32:
+ case paFloat32: stream->outputSampleSize = 4; break;
+ }
+
stream->playbackPin->frameSize /= stream->bytesPerOutputFrame;
PA_DEBUG(("Pin output frames: %d\n",stream->playbackPin->frameSize));
}
@@ -2341,49 +2413,41 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->bytesPerOutputFrame = 0;
}
- /* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
+ /* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
- /* Record the buffer length */
- if (inputParameters)
- {
+ /* Record the buffer length */
+ if(inputParameters)
+ {
/* Calculate the frames from the user's value - add a bit to round up */
- stream->framesPerHostIBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
- if (stream->framesPerHostIBuffer > (unsigned long)sampleRate)
+ stream->framesPerHostIBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
+ if(stream->framesPerHostIBuffer > (unsigned long)sampleRate)
{ /* Upper limit is 1 second */
- stream->framesPerHostIBuffer = (unsigned long)sampleRate;
+ stream->framesPerHostIBuffer = (unsigned long)sampleRate;
}
- /* Uncomment the following code to make the device-reported
- * frame size the lower limit*/
- /*
- else if (stream->framesPerHostIBuffer < stream->recordingPin->frameSize)
- {
- stream->framesPerHostIBuffer = stream->recordingPin->frameSize;
+ else if(stream->framesPerHostIBuffer < stream->recordingPin->frameSize)
+ {
+ stream->framesPerHostIBuffer = stream->recordingPin->frameSize;
}
- */
PA_DEBUG(("Input frames chosen:%ld\n",stream->framesPerHostIBuffer));
}
- if (outputParameters)
- {
+ if(outputParameters)
+ {
/* Calculate the frames from the user's value - add a bit to round up */
stream->framesPerHostOBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
- if (stream->framesPerHostOBuffer > (unsigned long)sampleRate)
+ if(stream->framesPerHostOBuffer > (unsigned long)sampleRate)
{ /* Upper limit is 1 second */
- stream->framesPerHostOBuffer = (unsigned long)sampleRate;
+ stream->framesPerHostOBuffer = (unsigned long)sampleRate;
}
- /* Uncomment the following code to make the device-reported
- * frame size the lower limit*/
- /*
- else if (stream->framesPerHostOBuffer < stream->playbackPin->frameSize)
+ else if(stream->framesPerHostOBuffer < stream->playbackPin->frameSize)
{
- stream->framesPerHostOBuffer = stream->playbackPin->frameSize;
- }
- */
+ stream->framesPerHostOBuffer = stream->playbackPin->frameSize;
+ }
PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer));
- }
+ }
- /* Host buffer size is bounded to the largest of the input and output
- frame sizes */
+ /* Host buffer size is bounded to the largest of the input and output
+ frame sizes */
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
@@ -2401,14 +2465,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
((double)stream->framesPerHostOBuffer) / sampleRate;
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+ PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame));
+ PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame));
- PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame));
- PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame));
-
- /* Allocate all the buffers for host I/O */
- size = 2 * (stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame);
- PA_DEBUG(("Buffer size = %d\n",size));
- stream->hostBuffer = (char*)PaUtil_AllocateMemory(size);
+ /* Allocate all the buffers for host I/O */
+ size = 2 * (stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame);
+ PA_DEBUG(("Buffer size = %d\n",size));
+ stream->hostBuffer = (char*)PaUtil_AllocateMemory(size);
PA_DEBUG(("Buffer allocated\n"));
if( !stream->hostBuffer )
{
@@ -2419,7 +2482,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PA_DEBUG(("Buffer start = %p\n",stream->hostBuffer));
/* memset(stream->hostBuffer,0,size); */
- /* Set up the packets */
+ /* Set up the packets */
stream->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
ResetEvent(stream->events[0]); /* Record buffer 1 */
stream->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -2430,18 +2493,19 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
ResetEvent(stream->events[3]); /* Play buffer 2 */
stream->events[4] = CreateEvent(NULL, FALSE, FALSE, NULL);
ResetEvent(stream->events[4]); /* Abort event */
- if (stream->userInputChannels > 0)
- {
- DATAPACKET *p = &(stream->packets[0]);
- p->Signal.hEvent = stream->events[0];
+ if(stream->userInputChannels > 0)
+ {
+ DATAPACKET *p = &(stream->packets[0]);
+ p->Signal.hEvent = stream->events[0];
p->Header.Data = stream->hostBuffer;
p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
p->Header.DataUsed = 0;
p->Header.Size = sizeof(p->Header);
p->Header.PresentationTime.Numerator = 1;
p->Header.PresentationTime.Denominator = 1;
- p = &(stream->packets[1]);
- p->Signal.hEvent = stream->events[1];
+
+ p = &(stream->packets[1]);
+ p->Signal.hEvent = stream->events[1];
p->Header.Data = stream->hostBuffer + stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
p->Header.DataUsed = 0;
@@ -2449,60 +2513,61 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
p->Header.PresentationTime.Numerator = 1;
p->Header.PresentationTime.Denominator = 1;
}
- if (stream->userOutputChannels > 0)
- {
- DATAPACKET *p = &(stream->packets[2]);
- p->Signal.hEvent = stream->events[2];
+ if(stream->userOutputChannels > 0)
+ {
+ DATAPACKET *p = &(stream->packets[2]);
+ p->Signal.hEvent = stream->events[2];
p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
p->Header.Size = sizeof(p->Header);
p->Header.PresentationTime.Numerator = 1;
p->Header.PresentationTime.Denominator = 1;
- p = &(stream->packets[3]);
- p->Signal.hEvent = stream->events[3];
+
+ p = &(stream->packets[3]);
+ p->Signal.hEvent = stream->events[3];
p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
p->Header.Size = sizeof(p->Header);
p->Header.PresentationTime.Numerator = 1;
p->Header.PresentationTime.Denominator = 1;
- }
+ }
- stream->streamStarted = 0;
- stream->streamActive = 0;
- stream->streamStop = 0;
- stream->streamAbort = 0;
- stream->streamFlags = streamFlags;
- stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+ stream->streamStarted = 0;
+ stream->streamActive = 0;
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
+ stream->streamFlags = streamFlags;
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
*s = (PaStream*)stream;
- PA_LOGL_;
+ PA_LOGL_;
return result;
error:
- size = 5;
- while (size--)
- {
- if (stream->events[size] != NULL)
- {
- CloseHandle(stream->events[size]);
- stream->events[size] = NULL;
- }
- }
- if (stream->hostBuffer)
+ size = 5;
+ while(size--)
+ {
+ if(stream->events[size] != NULL)
+ {
+ CloseHandle(stream->events[size]);
+ stream->events[size] = NULL;
+ }
+ }
+ if(stream->hostBuffer)
PaUtil_FreeMemory( stream->hostBuffer );
- if (stream->playbackPin)
- PinClose(stream->playbackPin);
- if (stream->recordingPin)
- PinClose(stream->recordingPin);
+ if(stream->playbackPin)
+ PinClose(stream->playbackPin);
+ if(stream->recordingPin)
+ PinClose(stream->recordingPin);
if( stream )
PaUtil_FreeMemory( stream );
- PA_LOGL_;
+ PA_LOGL_;
return result;
}
@@ -2514,35 +2579,35 @@ static PaError CloseStream( PaStream* s )
{
PaError result = paNoError;
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- int size;
+ int size;
- PA_LOGE_;
+ PA_LOGE_;
- assert(!stream->streamStarted);
- assert(!stream->streamActive);
+ assert(!stream->streamStarted);
+ assert(!stream->streamActive);
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- size = 5;
- while (size--)
- {
- if (stream->events[size] != NULL)
- {
- CloseHandle(stream->events[size]);
- stream->events[size] = NULL;
- }
- }
- if (stream->hostBuffer)
+ size = 5;
+ while(size--)
+ {
+ if(stream->events[size] != NULL)
+ {
+ CloseHandle(stream->events[size]);
+ stream->events[size] = NULL;
+ }
+ }
+ if(stream->hostBuffer)
PaUtil_FreeMemory( stream->hostBuffer );
- if (stream->playbackPin)
- PinClose(stream->playbackPin);
- if (stream->recordingPin)
- PinClose(stream->recordingPin);
+ if(stream->playbackPin)
+ PinClose(stream->playbackPin);
+ if(stream->recordingPin)
+ PinClose(stream->recordingPin);
PaUtil_FreeMemory( stream );
- PA_LOGL_;
+ PA_LOGL_;
return result;
}
@@ -2555,7 +2620,7 @@ static BOOL PinWrite(HANDLE h, DATAPACKET* p)
{
unsigned long cbReturned = 0;
return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0,
- &p->Header,p->Header.Size,&cbReturned,&p->Signal);
+ &p->Header,p->Header.Size,&cbReturned,&p->Signal);
}
/*
@@ -2567,7 +2632,7 @@ static BOOL PinRead(HANDLE h, DATAPACKET* p)
{
unsigned long cbReturned = 0;
return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0,
- &p->Header,p->Header.Size,&cbReturned,&p->Signal);
+ &p->Header,p->Header.Size,&cbReturned,&p->Signal);
}
/*
@@ -2578,11 +2643,11 @@ static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
unsigned short* data = (unsigned short*)buffer;
int channel;
unsigned short sourceSample;
- while ( samples-- )
+ while( samples-- )
{
sourceSample = *data++;
channel = channels-1;
- while ( channel-- )
+ while( channel-- )
{
*data++ = sourceSample;
}
@@ -2597,14 +2662,14 @@ static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
unsigned char* data = (unsigned char*)buffer;
int channel;
unsigned char sourceSample[3];
- while ( samples-- )
+ while( samples-- )
{
sourceSample[0] = data[0];
sourceSample[1] = data[1];
sourceSample[2] = data[2];
data += 3;
channel = channels-1;
- while ( channel-- )
+ while( channel-- )
{
data[0] = sourceSample[0];
data[1] = sourceSample[1];
@@ -2614,6 +2679,25 @@ static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
}
}
+/*
+Copy the first interleaved channel of 32 bit data to the other channels
+*/
+static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples)
+{
+ unsigned long* data = (unsigned long*)buffer;
+ int channel;
+ unsigned long sourceSample;
+ while( samples-- )
+ {
+ sourceSample = *data++;
+ channel = channels-1;
+ while( channel-- )
+ {
+ *data++ = sourceSample;
+ }
+ }
+}
+
static DWORD WINAPI ProcessingThread(LPVOID pParam)
{
PaWinWdmStream *stream = (PaWinWdmStream*)pParam;
@@ -2623,12 +2707,12 @@ static DWORD WINAPI ProcessingThread(LPVOID pParam)
int outbuf = 0;
int pending = 0;
PaError result;
- unsigned long wait;
- unsigned long eventSignaled;
- int fillPlaybuf = 0;
- int emptyRecordbuf = 0;
- int framesProcessed;
- unsigned long timeout;
+ unsigned long wait;
+ unsigned long eventSignaled;
+ int fillPlaybuf = 0;
+ int emptyRecordbuf = 0;
+ int framesProcessed;
+ unsigned long timeout;
int i;
int doChannelCopy;
int priming = 0;
@@ -2640,56 +2724,55 @@ static DWORD WINAPI ProcessingThread(LPVOID pParam)
ti.currentTime = 0.0;
ti.outputBufferDacTime = 0.0;
- /* Get double buffering going */
+ /* Get double buffering going */
/* Submit buffers */
- if (stream->playbackPin)
+ if(stream->playbackPin)
{
- result = PinSetState(stream->playbackPin, KSSTATE_RUN);
+ result = PinSetState(stream->playbackPin, KSSTATE_RUN);
- PA_DEBUG(("play state run = %d;",(int)result));
- SetEvent(stream->events[outbuf+2]);
- outbuf = (outbuf+1)&1;
- SetEvent(stream->events[outbuf+2]);
- outbuf = (outbuf+1)&1;
- pending += 2;
+ PA_DEBUG(("play state run = %d;",(int)result));
+ SetEvent(stream->events[outbuf+2]);
+ outbuf = (outbuf+1)&1;
+ SetEvent(stream->events[outbuf+2]);
+ outbuf = (outbuf+1)&1;
+ pending += 2;
priming += 4;
}
- if (stream->recordingPin)
+ if(stream->recordingPin)
{
- result = PinSetState(stream->recordingPin, KSSTATE_RUN);
+ result = PinSetState(stream->recordingPin, KSSTATE_RUN);
- PA_DEBUG(("recording state run = %d;",(int)result));
- PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
- inbuf = (inbuf+1)&1; // Increment and wrap
- PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
- inbuf = (inbuf+1)&1; // Increment and wrap
- /* FIXME - do error checking */
- pending += 2;
+ PA_DEBUG(("recording state run = %d;",(int)result));
+ PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+ inbuf = (inbuf+1)&1; /* Increment and wrap */
+ PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+ inbuf = (inbuf+1)&1; /* Increment and wrap */
+ /* FIXME - do error checking */
+ pending += 2;
}
PA_DEBUG(("Out buffer len:%f\n",(2000*stream->framesPerHostOBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
PA_DEBUG(("In buffer len:%f\n",(2000*stream->framesPerHostIBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
timeout = max(
- ((2000*(DWORD)stream->framesPerHostOBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate),
- ((2000*(DWORD)stream->framesPerHostIBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate)
- );
+ ((2000*(DWORD)stream->framesPerHostOBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate),
+ ((2000*(DWORD)stream->framesPerHostIBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate));
timeout = max(timeout,1);
- PA_DEBUG(("Timeout = %ld ",timeout));
+ PA_DEBUG(("Timeout = %ld\n",timeout));
while(!stream->streamAbort)
{
- fillPlaybuf = 0;
- emptyRecordbuf = 0;
+ fillPlaybuf = 0;
+ emptyRecordbuf = 0;
- /* Wait for next input or output buffer to be finished with*/
- assert(pending>0);
+ /* Wait for next input or output buffer to be finished with*/
+ assert(pending>0);
- if (stream->streamStop)
- {
- PA_DEBUG(("ss1:pending=%d ",pending));
- }
+ if(stream->streamStop)
+ {
+ PA_DEBUG(("ss1:pending=%d ",pending));
+ }
wait = WaitForMultipleObjects(5, stream->events, FALSE, 0);
- if ( wait == WAIT_TIMEOUT )
+ if( wait == WAIT_TIMEOUT )
{
/* No (under|over)flow has ocurred */
wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout);
@@ -2698,96 +2781,97 @@ static DWORD WINAPI ProcessingThread(LPVOID pParam)
else
{
eventSignaled = wait - WAIT_OBJECT_0;
- if ( eventSignaled < 2 )
+ if( eventSignaled < 2 )
{
underover |= paInputOverflow;
PA_DEBUG(("Input overflow\n"));
}
- else if (( eventSignaled < 4 )&&(!priming))
+ else if(( eventSignaled < 4 )&&(!priming))
{
underover |= paOutputUnderflow;
PA_DEBUG(("Output underflow\n"));
}
}
- if (stream->streamStop)
- {
- PA_DEBUG(("ss2:wait=%ld",wait));
- }
- if (wait == WAIT_FAILED)
- {
- PA_DEBUG(("Wait fail = %ld! ",wait));
+ if(stream->streamStop)
+ {
+ PA_DEBUG(("ss2:wait=%ld",wait));
+ }
+ if(wait == WAIT_FAILED)
+ {
+ PA_DEBUG(("Wait fail = %ld! ",wait));
break;
- }
- if (wait == WAIT_TIMEOUT)
+ }
+ if(wait == WAIT_TIMEOUT)
{
continue;
}
- if (eventSignaled < 2)
- { /* Recording input buffer has been filled */
- PA_DEBUG(("R"));
- if (stream->playbackPin)
+ if(eventSignaled < 2)
+ { /* Recording input buffer has been filled */
+ if(stream->playbackPin)
{
/* First check if also the next playback buffer has been signaled */
- wait = WaitForSingleObject(stream->events[outbuf+2],0);
- if (wait == WAIT_OBJECT_0)
- {
+ wait = WaitForSingleObject(stream->events[outbuf+2],0);
+ if(wait == WAIT_OBJECT_0)
+ {
/* Yes, so do both buffers at same time */
fillPlaybuf = 1;
- pending--;
+ pending--;
/* Was this an underflow situation? */
- if ( underover )
+ if( underover )
underover |= paOutputUnderflow; /* Yes! */
- }
- }
+ }
+ }
emptyRecordbuf = 1;
pending--;
- }
- else if (eventSignaled < 4)
- { /* Playback output buffer has been emptied */
- if (stream->recordingPin)
- {
- /* First check if also the next recording buffer has been signaled */
- wait = WaitForSingleObject(stream->events[inbuf],0);
- if (wait == WAIT_OBJECT_0)
- { /* Yes, so do both buffers at same time */
- emptyRecordbuf = 1;
- pending--;
+ }
+ else if(eventSignaled < 4)
+ { /* Playback output buffer has been emptied */
+ if(stream->recordingPin)
+ {
+ /* First check if also the next recording buffer has been signaled */
+ wait = WaitForSingleObject(stream->events[inbuf],0);
+ if(wait == WAIT_OBJECT_0)
+ { /* Yes, so do both buffers at same time */
+ emptyRecordbuf = 1;
+ pending--;
/* Was this an overflow situation? */
- if ( underover )
+ if( underover )
underover |= paInputOverflow; /* Yes! */
- }
- }
- fillPlaybuf = 1;
- pending--;
- }
- else
- {
+ }
+ }
+ fillPlaybuf = 1;
+ pending--;
+ }
+ else
+ {
/* Abort event! */
assert(stream->streamAbort); /* Should have been set */
- PA_DEBUG(("ABORTING "));
- break;
- }
- ResetEvent(stream->events[eventSignaled]);
-
- if (stream->streamStop)
- {
- PA_DEBUG(("Stream stop! pending=%d",pending));
- cbResult = paComplete; /* Stop, but play remaining buffers */
- }
-
- /* Do necessary buffer processing (which will invoke user callback if necessary */
+ PA_DEBUG(("ABORTING "));
+ break;
+ }
+ ResetEvent(stream->events[eventSignaled]);
+
+ if(stream->streamStop)
+ {
+ PA_DEBUG(("Stream stop! pending=%d",pending));
+ cbResult = paComplete; /* Stop, but play remaining buffers */
+ }
+
+ /* Do necessary buffer processing (which will invoke user callback if necessary */
doChannelCopy = 0;
- if (cbResult==paContinue)
- {
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
- PaUtil_BeginBufferProcessing(&stream->bufferProcessor,&ti,underover);
+ if(cbResult==paContinue)
+ {
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+ if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) ==
+ (stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) )
+ PaUtil_BeginBufferProcessing(&stream->bufferProcessor,&ti,underover);
underover = 0; /* Reset the (under|over)flow status */
- if (fillPlaybuf)
- {
- PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0);
- if ( stream->userOutputChannels == 1 )
+ if(fillPlaybuf)
+ {
+ PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0);
+ if( stream->userOutputChannels == 1 )
{
/* Write the single user channel to the first interleaved block */
PaUtil_SetOutputChannel(&stream->bufferProcessor,0,stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels);
@@ -2796,87 +2880,97 @@ static DWORD WINAPI ProcessingThread(LPVOID pParam)
}
else
{
- for (i=0;i<stream->userOutputChannels;i++)
+ for(i=0;i<stream->userOutputChannels;i++)
{
- /* Only write the user output channels. Leave the rest blank */
- PaUtil_SetOutputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[outbuf+2].Header.Data))+(i*stream->outputSampleSize),stream->deviceOutputChannels);
+ /* Only write the user output channels. Leave the rest blank */
+ PaUtil_SetOutputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[outbuf+2].Header.Data))+(i*stream->outputSampleSize),stream->deviceOutputChannels);
}
}
- }
- if (emptyRecordbuf)
- {
- PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame);
- for (i=0;i<stream->userInputChannels;i++)
+ }
+ if(emptyRecordbuf)
+ {
+ PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame);
+ for(i=0;i<stream->userInputChannels;i++)
{
/* Only read as many channels as the user wants */
PaUtil_SetInputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[inbuf].Header.Data))+(i*stream->inputSampleSize),stream->deviceInputChannels);
}
- }
- framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
- if ( doChannelCopy )
+ }
+ /* Only call the EndBufferProcessing function is the total input frames == total output frames */
+ if((stream->bufferProcessor.hostInputFrameCount[0] + stream->bufferProcessor.hostInputFrameCount[1]) ==
+ (stream->bufferProcessor.hostOutputFrameCount[0] + stream->bufferProcessor.hostOutputFrameCount[1]) )
+ framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
+ else framesProcessed = 0;
+ if( doChannelCopy )
{
/* Copy the first output channel to the other channels */
- if ( stream->outputSampleSize == 2 )
+ switch(stream->outputSampleSize)
{
- DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+ case 2:
+ DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+ break;
+ case 3:
+ DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+ break;
+ case 4:
+ DuplicateFirstChannelInt32(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+ break;
+ default:
+ assert(0); /* Unsupported format! */
+ break;
}
- else if ( stream->outputSampleSize == 3 )
- {
- DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
- }
- else
- assert(0); /* Unsupported format! */
}
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
- }
- else
- {
- fillPlaybuf = 0;
- emptyRecordbuf = 0;
- }
- /*
- if (cbResult != paContinue)
- {
- PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending));
- }
- */
- /* Submit buffers */
- if ((fillPlaybuf)&&(cbResult!=paAbort))
- {
- if (!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2]))
- outbuf = (outbuf+1)&1; /* Increment and wrap */
- pending++;
- if ( priming )
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+ }
+ else
+ {
+ fillPlaybuf = 0;
+ emptyRecordbuf = 0;
+ }
+
+ /*
+ if(cbResult != paContinue)
+ {
+ PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending));
+ }
+ */
+ /* Submit buffers */
+ if((fillPlaybuf)&&(cbResult!=paAbort))
+ {
+ if(!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2]))
+ outbuf = (outbuf+1)&1; /* Increment and wrap */
+ pending++;
+ if( priming )
priming--; /* Have to prime twice */
- }
- if ((emptyRecordbuf)&&(cbResult==paContinue))
- {
- stream->packets[inbuf].Header.DataUsed = 0; /* Reset for reuse */
- PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
- inbuf = (inbuf+1)&1; /* Increment and wrap */
- pending++;
- }
- if (pending==0)
- {
- PA_DEBUG(("pending==0 finished...;"));
- break;
- }
- if ((!stream->playbackPin)&&(cbResult!=paContinue))
- {
- PA_DEBUG(("record only cbResult=%d...;",cbResult));
- break;
- }
+ }
+ if((emptyRecordbuf)&&(cbResult==paContinue))
+ {
+ stream->packets[inbuf].Header.DataUsed = 0; /* Reset for reuse */
+ PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+ inbuf = (inbuf+1)&1; /* Increment and wrap */
+ pending++;
+ }
+ if(pending==0)
+ {
+ PA_DEBUG(("pending==0 finished...;"));
+ break;
+ }
+ if((!stream->playbackPin)&&(cbResult!=paContinue))
+ {
+ PA_DEBUG(("record only cbResult=%d...;",cbResult));
+ break;
+ }
}
PA_DEBUG(("Finished thread"));
/* Finished, either normally or aborted */
- if (stream->playbackPin)
+ if(stream->playbackPin)
{
result = PinSetState(stream->playbackPin, KSSTATE_PAUSE);
result = PinSetState(stream->playbackPin, KSSTATE_STOP);
}
- if (stream->recordingPin)
+ if(stream->recordingPin)
{
result = PinSetState(stream->recordingPin, KSSTATE_PAUSE);
result = PinSetState(stream->recordingPin, KSSTATE_STOP);
@@ -2884,73 +2978,73 @@ static DWORD WINAPI ProcessingThread(LPVOID pParam)
stream->streamActive = 0;
- if ((!stream->streamStop)&&(!stream->streamAbort))
+ if((!stream->streamStop)&&(!stream->streamAbort))
{
- /* Invoke the user stream finished callback */
- /* Only do it from here if not being stopped/aborted by user */
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ /* Invoke the user stream finished callback */
+ /* Only do it from here if not being stopped/aborted by user */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
}
- stream->streamStop = 0;
- stream->streamAbort = 0;
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
- /* Reset process priority if necessary */
- if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
- {
- SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
- stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
- }
+ /* Reset process priority if necessary */
+ if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+ {
+ SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+ }
PA_LOGL_;
ExitThread(0);
- return 0;
+ return 0;
}
static PaError StartStream( PaStream *s )
{
PaError result = paNoError;
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- DWORD dwID;
- BOOL ret;
- int size;
-
- PA_LOGE_;
-
- stream->streamStop = 0;
- stream->streamAbort = 0;
- size = 5;
- while (size--)
- {
- if (stream->events[size] != NULL)
- {
- ResetEvent(stream->events[size]);
- }
- }
+ DWORD dwID;
+ BOOL ret;
+ int size;
+
+ PA_LOGE_;
+
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
+ size = 5;
+ while(size--)
+ {
+ if(stream->events[size] != NULL)
+ {
+ ResetEvent(stream->events[size]);
+ }
+ }
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
- stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
+ stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
/* Uncomment the following line to enable dynamic boosting of the process
* priority to real time for best low latency support
* Disabled by default because RT processes can easily block the OS */
- /*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
- PA_DEBUG(("Class ret = %d;",ret));*/
-
- stream->streamStarted = 1;
- stream->streamThread = CreateThread(NULL, 0, ProcessingThread, stream, 0, &dwID);
- if (stream->streamThread == NULL)
- {
- stream->streamStarted = 0;
- result = paInsufficientMemory;
- goto end;
- }
- ret = SetThreadPriority(stream->streamThread,THREAD_PRIORITY_TIME_CRITICAL);
- PA_DEBUG(("Priority ret = %d;",ret));
+ /*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
+ PA_DEBUG(("Class ret = %d;",ret));*/
+
+ stream->streamStarted = 1;
+ stream->streamThread = CreateThread(NULL, 0, ProcessingThread, stream, 0, &dwID);
+ if(stream->streamThread == NULL)
+ {
+ stream->streamStarted = 0;
+ result = paInsufficientMemory;
+ goto end;
+ }
+ ret = SetThreadPriority(stream->streamThread,THREAD_PRIORITY_TIME_CRITICAL);
+ PA_DEBUG(("Priority ret = %d;",ret));
/* Make the stream active */
stream->streamActive = 1;
end:
- PA_LOGL_;
+ PA_LOGL_;
return result;
}
@@ -2961,44 +3055,44 @@ static PaError StopStream( PaStream *s )
PaWinWdmStream *stream = (PaWinWdmStream*)s;
int doCb = 0;
- PA_LOGE_;
+ PA_LOGE_;
- if (stream->streamActive)
- {
- doCb = 1;
- stream->streamStop = 1;
- while (stream->streamActive)
- {
- PA_DEBUG(("W."));
- Sleep(10); /* Let thread sleep for 10 msec */
- }
- }
+ if(stream->streamActive)
+ {
+ doCb = 1;
+ stream->streamStop = 1;
+ while(stream->streamActive)
+ {
+ PA_DEBUG(("W."));
+ Sleep(10); /* Let thread sleep for 10 msec */
+ }
+ }
- PA_DEBUG(("Terminating thread"));
- if (stream->streamStarted && stream->streamThread)
- {
- TerminateThread(stream->streamThread,0);
- stream->streamThread = NULL;
- }
+ PA_DEBUG(("Terminating thread"));
+ if(stream->streamStarted && stream->streamThread)
+ {
+ TerminateThread(stream->streamThread,0);
+ stream->streamThread = NULL;
+ }
- stream->streamStarted = 0;
+ stream->streamStarted = 0;
- if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
- {
- SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
- stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
- }
+ if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+ {
+ SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+ }
- if (doCb)
+ if(doCb)
{
- /* Do user callback now after all state has been reset */
- /* This means it should be safe for the called function */
- /* to invoke e.g. StartStream */
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ /* Do user callback now after all state has been reset */
+ /* This means it should be safe for the called function */
+ /* to invoke e.g. StartStream */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
}
- PA_LOGL_;
+ PA_LOGL_;
return result;
}
@@ -3008,46 +3102,46 @@ static PaError AbortStream( PaStream *s )
PaWinWdmStream *stream = (PaWinWdmStream*)s;
int doCb = 0;
- PA_LOGE_;
-
- if (stream->streamActive)
- {
- doCb = 1;
- stream->streamAbort = 1;
- SetEvent(stream->events[4]); /* Signal immediately */
- while (stream->streamActive)
- {
- Sleep(10);
- }
- }
-
- if (stream->streamStarted && stream->streamThread)
- {
- TerminateThread(stream->streamThread,0);
- stream->streamThread = NULL;
- }
+ PA_LOGE_;
+
+ if(stream->streamActive)
+ {
+ doCb = 1;
+ stream->streamAbort = 1;
+ SetEvent(stream->events[4]); /* Signal immediately */
+ while(stream->streamActive)
+ {
+ Sleep(10);
+ }
+ }
+
+ if(stream->streamStarted && stream->streamThread)
+ {
+ TerminateThread(stream->streamThread,0);
+ stream->streamThread = NULL;
+ }
stream->streamStarted = 0;
- if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
- {
- SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
- stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
- }
+ if(stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+ {
+ SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+ }
- if (doCb)
+ if(doCb)
{
- /* Do user callback now after all state has been reset */
- /* This means it should be safe for the called function */
- /* to invoke e.g. StartStream */
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ /* Do user callback now after all state has been reset */
+ /* This means it should be safe for the called function */
+ /* to invoke e.g. StartStream */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
}
- stream->streamActive = 0;
- stream->streamStarted = 0;
+ stream->streamActive = 0;
+ stream->streamStarted = 0;
- PA_LOGL_;
+ PA_LOGL_;
return result;
}
@@ -3055,14 +3149,14 @@ static PaError AbortStream( PaStream *s )
static PaError IsStreamStopped( PaStream *s )
{
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- int result = 0;
+ int result = 0;
- PA_LOGE_;
+ PA_LOGE_;
- if (!stream->streamStarted)
- result = 1;
+ if(!stream->streamStarted)
+ result = 1;
- PA_LOGL_;
+ PA_LOGL_;
return result;
}
@@ -3070,21 +3164,21 @@ static PaError IsStreamStopped( PaStream *s )
static PaError IsStreamActive( PaStream *s )
{
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- int result = 0;
+ int result = 0;
- PA_LOGE_;
+ PA_LOGE_;
- if (stream->streamActive)
- result = 1;
+ if(stream->streamActive)
+ result = 1;
- PA_LOGL_;
+ PA_LOGL_;
return result;
}
static PaTime GetStreamTime( PaStream* s )
{
- PA_LOGE_;
+ PA_LOGE_;
PA_LOGL_;
(void)s;
return PaUtil_GetTime();
@@ -3094,11 +3188,11 @@ static PaTime GetStreamTime( PaStream* s )
static double GetStreamCpuLoad( PaStream* s )
{
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- double result;
- PA_LOGE_;
+ double result;
+ PA_LOGE_;
result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
- PA_LOGL_;
- return result;
+ PA_LOGL_;
+ return result;
}
@@ -3114,7 +3208,7 @@ static PaError ReadStream( PaStream* s,
{
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- PA_LOGE_;
+ PA_LOGE_;
/* suppress unused variable warnings */
(void) buffer;
@@ -3122,7 +3216,7 @@ static PaError ReadStream( PaStream* s,
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
- PA_LOGL_;
+ PA_LOGL_;
return paNoError;
}
@@ -3133,7 +3227,7 @@ static PaError WriteStream( PaStream* s,
{
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- PA_LOGE_;
+ PA_LOGE_;
/* suppress unused variable warnings */
(void) buffer;
@@ -3141,7 +3235,7 @@ static PaError WriteStream( PaStream* s,
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
- PA_LOGL_;
+ PA_LOGL_;
return paNoError;
}
@@ -3150,13 +3244,13 @@ static signed long GetStreamReadAvailable( PaStream* s )
{
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- PA_LOGE_;
+ PA_LOGE_;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
- PA_LOGL_;
+ PA_LOGL_;
return 0;
}
@@ -3165,15 +3259,11 @@ static signed long GetStreamWriteAvailable( PaStream* s )
{
PaWinWdmStream *stream = (PaWinWdmStream*)s;
- PA_LOGE_;
+ PA_LOGE_;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
- PA_LOGL_;
+ PA_LOGL_;
return 0;
-}
-
-
-
-
+} \ No newline at end of file
diff --git a/pd/portaudio/pa_win_wdmks/readme.txt b/pd/portaudio/pa_win_wdmks/readme.txt
index 2fc6c75c..1a381fe7 100644
--- a/pd/portaudio/pa_win_wdmks/readme.txt
+++ b/pd/portaudio/pa_win_wdmks/readme.txt
@@ -3,6 +3,17 @@ Notes about WDM-KS host API
Status history
--------------
+10th November 2005:
+Made following changes:
+ * OpenStream: Try all PaSampleFormats internally if the the chosen
+ format is not supported natively. This fixed several problems
+ with soundcards that soundcards that did not take kindly to
+ using 24-bit 3-byte formats.
+ * OpenStream: Make the minimum framesPerHostIBuffer (and framesPerHostOBuffer)
+ the default frameSize for the playback/recording pin.
+ * ProcessingThread: Added a switch to only call PaUtil_EndBufferProcessing
+ if the total input frames equals the total output frames
+
5th September 2004:
This is the first public version of the code. It should be considered
an alpha release with zero guarantee not to crash on any particular
diff --git a/pd/portaudio/pa_win_wmme/pa_win_wmme.c b/pd/portaudio/pa_win_wmme/pa_win_wmme.c
index c3d7fe6d..1a9ea59e 100644
--- a/pd/portaudio/pa_win_wmme/pa_win_wmme.c
+++ b/pd/portaudio/pa_win_wmme/pa_win_wmme.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $
+ * $Id: pa_win_wmme.c,v 1.6.2.88 2006/02/16 01:56:45 rossbencina Exp $
* pa_win_wmme.c
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
*
@@ -128,6 +128,13 @@ Non-critical stuff for the future:
#pragma comment(lib, "winmm.lib")
#endif
+/*
+ provided in newer platform sdks
+ */
+#ifndef DWORD_PTR
+#define DWORD_PTR DWORD
+#endif
+
/************************************************* Constants ********/
#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
@@ -1555,10 +1562,10 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
if( isInput )
mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
- (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+ (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
else
mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
- (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+ (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
if( mmresult != MMSYSERR_NOERROR )
{
@@ -1616,11 +1623,15 @@ static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *h
{
if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
+ else
+ mmresult = MMSYSERR_NOERROR;
}
else
{
if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
+ else
+ mmresult = MMSYSERR_NOERROR;
}
if( mmresult != MMSYSERR_NOERROR &&
diff --git a/pd/portaudio/pablio/ringbuffer.h b/pd/portaudio/pablio/ringbuffer.h
index 2d522277..8e16f2c7 100644
--- a/pd/portaudio/pablio/ringbuffer.h
+++ b/pd/portaudio/pablio/ringbuffer.h
@@ -6,7 +6,7 @@ extern "C"
#endif /* __cplusplus */
/*
- * $Id: ringbuffer.h,v 1.3 2006-03-10 11:04:58 ggeiger Exp $
+ * $Id: ringbuffer.h,v 1.4 2006-06-03 19:13:07 millerpuckette Exp $
* ringbuffer.h
* Ring Buffer utility..
*