aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/src/hostapi/coreaudio
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portaudio/src/hostapi/coreaudio')
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/notes.txt22
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c161
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c88
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h9
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h7
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c141
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h31
7 files changed, 378 insertions, 81 deletions
diff --git a/pd/portaudio/src/hostapi/coreaudio/notes.txt b/pd/portaudio/src/hostapi/coreaudio/notes.txt
index ffe96962..145afe15 100644
--- a/pd/portaudio/src/hostapi/coreaudio/notes.txt
+++ b/pd/portaudio/src/hostapi/coreaudio/notes.txt
@@ -73,17 +73,24 @@ the stream with it. See below for creating a channel map.
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
+- Buffering: No buffering beyond that provided by core audio is provided
+except where absolutely needed for the implementation to work. This may cause
+issues with large framesPerBuffer settings and it also means that no additional
+latency will be provided even if a large latency setting is selected.
+
+- Latency: Latency settings are generally ignored. They may be used as a
+hint for buffer size in paHostFramesPerBufferUnspecified, or the value may
+be used in cases where additional buffering is needed, such as doing input and
+output on seperate devices. 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.
+to match the expected values. In the case of paHostFramesPerBuffer, an
+appropriate framesPerBuffer value will be used that guarantees minimum
+requested latency if that's possible.
- Timing info. It reports on stream time, but I'm probably doing something
wrong since patest_sine_time often reports negative latency numbers. Also,
@@ -111,8 +118,7 @@ render quyality property is used to set the sample rate conversion quality
as "documented" here:
http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html
-- x86/Universal Binary: to build a universal binary, be sure to use
-the darwin makefile and not the usual configure && make combo.
+- x86/Universal Binary: Universal binaries can be build.
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c
index 7c887bd6..98cfbb51 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c
@@ -65,6 +65,7 @@
#include "pa_mac_core_internal.h"
#include <string.h> /* strlen(), memcmp() etc. */
+#include <libkern/OSAtomic.h>
#include "pa_mac_core.h"
#include "pa_mac_core_utilities.h"
@@ -127,6 +128,8 @@ const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input )
OSStatus error;
err = PaUtil_GetHostApiRepresentation( &hostApi, paCoreAudio );
assert(err == paNoError);
+ if( err != paNoError )
+ return NULL;
PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi;
AudioDeviceID hostApiDevice = macCoreHostApi->devIds[device];
@@ -261,11 +264,12 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
int isInput);
static PaError OpenAndSetupOneAudioUnit(
+ const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
- const unsigned long requestedFramesPerBuffer,
- unsigned long *actualInputFramesPerBuffer,
- unsigned long *actualOutputFramesPerBuffer,
+ const UInt32 requestedFramesPerBuffer,
+ UInt32 *actualInputFramesPerBuffer,
+ UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@@ -277,6 +281,37 @@ static PaError OpenAndSetupOneAudioUnit(
#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+/*
+ * Callback called when starting or stopping a stream.
+ */
+static void startStopCallback(
+ void * inRefCon,
+ AudioUnit ci,
+ AudioUnitPropertyID inID,
+ AudioUnitScope inScope,
+ AudioUnitElement inElement )
+{
+ PaMacCoreStream *stream = (PaMacCoreStream *) inRefCon;
+ UInt32 isRunning;
+ UInt32 size = sizeof( isRunning );
+ OSStatus err;
+ err = AudioUnitGetProperty( ci, kAudioOutputUnitProperty_IsRunning, inScope, inElement, &isRunning, &size );
+ assert( !err );
+ if( err )
+ isRunning = false; //it's very unclear what to do in case of error here. There's no real way to notify the user, and crashing seems unreasonable.
+ if( isRunning )
+ return; //We are only interested in when we are stopping
+ // -- if we are using 2 I/O units, we only need one notification!
+ if( stream->inputUnit && stream->outputUnit && stream->inputUnit != stream->outputUnit && ci == stream->inputUnit )
+ return;
+ PaStreamFinishedCallback *sfc = stream->streamRepresentation.streamFinishedCallback;
+ if( stream->state == STOPPING )
+ stream->state = STOPPED ;
+ if( sfc )
+ sfc( stream->streamRepresentation.userData );
+}
+
+
/*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)
@@ -497,9 +532,15 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn
int i;
PaMacAUHAL *auhalHostApi;
PaDeviceInfo *deviceInfoArray;
+ int unixErr;
VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
+ unixErr = initializeXRunListenerList();
+ if( 0 != unixErr ) {
+ return UNIX_ERR(unixErr);
+ }
+
auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) );
if( !auhalHostApi )
{
@@ -618,10 +659,16 @@ error:
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
+ int unixErr;
+
PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
VVDBUG(("Terminate()\n"));
+ unixErr = destroyXRunListenerList();
+ if( 0 != unixErr )
+ UNIX_ERR(unixErr);
+
/*
IMPLEMENT ME:
- clean up any resources not handled by the allocation group
@@ -737,11 +784,12 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
}
static PaError OpenAndSetupOneAudioUnit(
+ const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
- const unsigned long requestedFramesPerBuffer,
- unsigned long *actualInputFramesPerBuffer,
- unsigned long *actualOutputFramesPerBuffer,
+ const UInt32 requestedFramesPerBuffer,
+ UInt32 *actualInputFramesPerBuffer,
+ UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@@ -753,7 +801,7 @@ static PaError OpenAndSetupOneAudioUnit(
Component comp;
/*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
AudioStreamBasicDescription desiredFormat;
- OSErr result = noErr;
+ OSStatus result = noErr;
PaError paResult = paNoError;
int line = 0;
UInt32 callbackKey;
@@ -881,7 +929,7 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
- if( outStreamParams )
+ if( outStreamParams && outStreamParams != inStreamParams )
{
*audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
ERR_WRAP( AudioUnitSetProperty( *audioUnit,
@@ -891,6 +939,24 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
+ /* -- add listener for dropouts -- */
+ result = AudioDeviceAddPropertyListener( *audioDevice,
+ 0,
+ outStreamParams ? false : true,
+ kAudioDeviceProcessorOverload,
+ xrunCallback,
+ addToXRunListenerList( (void *)stream ) ) ;
+ if( result == kAudioHardwareIllegalOperationError ) {
+ // -- already registered, we're good
+ } else {
+ // -- not already registered, just check for errors
+ ERR_WRAP( result );
+ }
+ /* -- listen for stream start and stop -- */
+ ERR_WRAP( AudioUnitAddPropertyListener( *audioUnit,
+ kAudioOutputUnitProperty_IsRunning,
+ startStopCallback,
+ (void *)stream ) );
/* -- set format -- */
bzero( &desiredFormat, sizeof(desiredFormat) );
@@ -1010,7 +1076,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Input,
OUTPUT_ELEMENT,
actualOutputFramesPerBuffer,
- sizeof(unsigned long) ) );
+ sizeof(*actualOutputFramesPerBuffer) ) );
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
@@ -1025,7 +1091,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Output,
INPUT_ELEMENT,
actualInputFramesPerBuffer,
- sizeof(unsigned long) ) );
+ sizeof(*actualInputFramesPerBuffer) ) );
/* Don't know why this causes problems
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
@@ -1305,7 +1371,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/*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 );
+ UInt32 size = sizeof( audioRange );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
0,
@@ -1315,6 +1381,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MAX( requested, audioRange.mMinimum );
}
+ size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@@ -1328,7 +1395,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* 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 );
+ UInt32 size = sizeof( audioRange );
requested = MIN( requested, 1024 );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
@@ -1339,6 +1406,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MIN( requested, audioRange.mMaximum );
}
+ size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@@ -1359,17 +1427,22 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* -- Now we actually open and setup streams. -- */
if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
{ /* full duplex. One device. */
- result = OpenAndSetupOneAudioUnit( inputParameters,
+ UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
+ UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
+ result = OpenAndSetupOneAudioUnit( stream,
+ inputParameters,
outputParameters,
framesPerBuffer,
- &(stream->inputFramesPerBuffer),
- &(stream->outputFramesPerBuffer),
+ &inputFramesPerBuffer,
+ &outputFramesPerBuffer,
auhalHostApi,
&(stream->inputUnit),
&(stream->inputSRConverter),
&(stream->inputDevice),
sampleRate,
stream );
+ stream->inputFramesPerBuffer = inputFramesPerBuffer;
+ stream->outputFramesPerBuffer = outputFramesPerBuffer;
stream->outputUnit = stream->inputUnit;
stream->outputDevice = stream->inputDevice;
if( result != paNoError )
@@ -1377,11 +1450,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
else
{ /* full duplex, different devices OR simplex */
- result = OpenAndSetupOneAudioUnit( NULL,
+ UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
+ UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
+ result = OpenAndSetupOneAudioUnit( stream,
+ NULL,
outputParameters,
framesPerBuffer,
NULL,
- &(stream->outputFramesPerBuffer),
+ &outputFramesPerBuffer,
auhalHostApi,
&(stream->outputUnit),
NULL,
@@ -1390,10 +1466,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
- result = OpenAndSetupOneAudioUnit( inputParameters,
+ result = OpenAndSetupOneAudioUnit( stream,
+ inputParameters,
NULL,
framesPerBuffer,
- &(stream->inputFramesPerBuffer),
+ &inputFramesPerBuffer,
NULL,
auhalHostApi,
&(stream->inputUnit),
@@ -1403,6 +1480,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
+ stream->inputFramesPerBuffer = inputFramesPerBuffer;
+ stream->outputFramesPerBuffer = outputFramesPerBuffer;
}
if( stream->inputUnit ) {
@@ -1456,8 +1535,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
/* now we can initialize the ring buffer */
+ //FIXME: element size whould probably be szfl*inputchan
+ // but that will require some work all over the
+ // place to patch up. szfl may be sufficient and would
+ // be way easier to handle, but it seems clear from the
+ // discussion that buffer processor compatibility
+ // requires szfl*inputchan.
+ // See revision 1346 and discussion:
+ // http://techweb.rfa.org/pipermail/portaudio/2008-February/008295.html
PaUtil_InitializeRingBuffer( &stream->inputRingBuffer,
- ringSize*szfl, data ) ;
+ 1, ringSize*szfl, data ) ;
/* advance the read point a little, so we are reading from the
middle of the buffer */
if( stream->outputUnit )
@@ -1727,14 +1814,14 @@ static OSStatus AudioIOProc( void *inRefCon,
* we do not use the input SR converter or the input ring buffer.
*
*/
- OSErr err = 0;
+ OSStatus err = 0;
unsigned long frames;
/* -- start processing -- */
PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
&timeInfo,
stream->xrunFlags );
- stream->xrunFlags = 0;
+ stream->xrunFlags = 0; //FIXME: this flag also gets set outside by a callback, which calls the xrunCallback function. It should be in the same thread as the main audio callback, but the apple docs just use the word "usually" so it may be possible to loose an xrun notification, if that callback happens here.
/* -- compute frames. do some checks -- */
assert( ioData->mNumberBuffers == 1 );
@@ -1748,7 +1835,8 @@ static OSStatus AudioIOProc( void *inRefCon,
INPUT_ELEMENT,
inNumberFrames,
&stream->inputAudioBufferList );
- /* FEEDBACK: I'm not sure what to do when this call fails */
+ /* FEEDBACK: I'm not sure what to do when this call fails. There's nothing in the PA API to
+ * do about failures in the callback system. */
assert( !err );
PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
@@ -1868,7 +1956,7 @@ static OSStatus AudioIOProc( void *inRefCon,
PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1 );
} else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) {
/*we underflowed. take what data we can, zero the rest.*/
- float data[frames*inChan];
+ unsigned char data[frames*inChan*flsz];
if( size1 )
memcpy( data, data1, size1 );
if( size2 )
@@ -1922,7 +2010,7 @@ static OSStatus AudioIOProc( void *inRefCon,
* 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;
+ OSStatus 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 {
@@ -2054,6 +2142,24 @@ static PaError CloseStream( PaStream* s )
VDBUG( ( "Closing stream.\n" ) );
if( stream ) {
+ if( stream->outputUnit ) {
+ int count = removeFromXRunListenerList( stream );
+ if( count == 0 )
+ AudioDeviceRemovePropertyListener( stream->outputDevice,
+ 0,
+ false,
+ kAudioDeviceProcessorOverload,
+ xrunCallback );
+ }
+ if( stream->inputUnit && stream->outputUnit != stream->inputUnit ) {
+ int count = removeFromXRunListenerList( stream );
+ if( count == 0 )
+ AudioDeviceRemovePropertyListener( stream->inputDevice,
+ 0,
+ true,
+ kAudioDeviceProcessorOverload,
+ xrunCallback );
+ }
if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
AudioUnitUninitialize( stream->outputUnit );
CloseComponent( stream->outputUnit );
@@ -2089,11 +2195,10 @@ static PaError CloseStream( PaStream* s )
return result;
}
-
static PaError StartStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
- OSErr result = noErr;
+ OSStatus result = noErr;
VVDBUG(("StartStream()\n"));
VDBUG( ( "Starting stream.\n" ) );
@@ -2138,7 +2243,7 @@ static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioU
static PaError StopStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
- OSErr result = noErr;
+ OSStatus result = noErr;
PaError paErr;
VVDBUG(("StopStream()\n"));
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c
index 3b81389d..6d31a713 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c
@@ -53,7 +53,7 @@
/**
@file
- @ingroup hostaip_src
+ @ingroup hostapi_src
This file contains the implementation
required for blocking I/O. It is separated from pa_mac_core.c simply to ease
@@ -85,6 +85,22 @@ static size_t computeSampleSizeFromFormat( PaSampleFormat format )
default: return 0;
}
}
+/*
+ * Same as computeSampleSizeFromFormat, except that if
+ * the size is not a power of two, it returns the next power of two up
+ */
+static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format )
+{
+ switch( format ) {
+ case paFloat32: return 4;
+ case paInt32: return 4;
+ case paInt24: return 4;
+ case paInt16: return 2;
+ case paInt8: case paUInt8: return 1;
+ default: return 0;
+ }
+}
+
/*
@@ -105,6 +121,7 @@ PaError initializeBlioRingBuffers(
{
void *data;
int result;
+ OSStatus err;
/* zeroify things */
bzero( blio, sizeof( PaMacBlio ) );
@@ -114,10 +131,14 @@ PaError initializeBlioRingBuffers(
blio->outputRingBuffer.buffer = NULL;
/* initialize simple data */
+ blio->ringBufferFrames = ringBufferSize;
blio->inputSampleFormat = inputSampleFormat;
- blio->inputSampleSize = computeSampleSizeFromFormat(inputSampleFormat);
+ blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat);
+ blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat);
blio->outputSampleFormat = outputSampleFormat;
- blio->outputSampleSize = computeSampleSizeFromFormat(outputSampleFormat);
+ blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat);
+ blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat);
+
blio->framesPerBuffer = framesPerBuffer;
blio->inChan = inChan;
blio->outChan = outChan;
@@ -142,30 +163,32 @@ PaError initializeBlioRingBuffers(
result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
#endif
if( inChan ) {
- data = calloc( ringBufferSize, blio->inputSampleSize );
+ data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan );
if( !data )
{
result = paInsufficientMemory;
goto error;
}
- assert( 0 == PaUtil_InitializeRingBuffer(
+ err = PaUtil_InitializeRingBuffer(
&blio->inputRingBuffer,
- ringBufferSize*blio->inputSampleSize,
- data ) );
+ 1, ringBufferSize*blio->inputSampleSizePow2*inChan,
+ data );
+ assert( !err );
}
if( outChan ) {
- data = calloc( ringBufferSize, blio->outputSampleSize );
+ data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan );
if( !data )
{
result = paInsufficientMemory;
goto error;
}
- assert( 0 == PaUtil_InitializeRingBuffer(
+ err = PaUtil_InitializeRingBuffer(
&blio->outputRingBuffer,
- ringBufferSize*blio->outputSampleSize,
- data ) );
+ 1, ringBufferSize*blio->outputSampleSizePow2*outChan,
+ data );
+ assert( !err );
}
result = resetBlioRingBuffers( blio );
@@ -247,7 +270,8 @@ PaError resetBlioRingBuffers( PaMacBlio *blio )
bzero( blio->outputRingBuffer.buffer,
blio->outputRingBuffer.bufferSize );
/* Advance buffer */
- PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
+ PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan );
+ //PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
/* Update isOutputFull. */
#ifdef PA_MAC__BLIO_MUTEX
@@ -323,6 +347,8 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
long avail;
long toRead;
long toWrite;
+ long read;
+ long written;
/* set flags returned by OS: */
OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
@@ -332,14 +358,15 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
/* check for underflow */
- if( avail < frameCount * blio->inputSampleSize * blio->inChan )
+ if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan )
OSAtomicOr32( paInputOverflow, &blio->statusFlags );
- toRead = MIN( avail, frameCount * blio->inputSampleSize * blio->inChan );
+ toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan );
/* copy the data */
/*printf( "reading %d\n", toRead );*/
- assert( toRead == PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead ) );
+ read = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead );
+ assert( toRead == read );
#ifdef PA_MAC__BLIO_MUTEX
/* Priority inversion. See notes below. */
blioSetIsInputEmpty( blio, false );
@@ -352,17 +379,18 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
/* check for underflow */
- if( avail < frameCount * blio->outputSampleSize * blio->outChan )
+ if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan )
OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
- toWrite = MIN( avail, frameCount * blio->outputSampleSize * blio->outChan );
+ toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan );
- if( toWrite != frameCount * blio->outputSampleSize * blio->outChan )
+ if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan )
bzero( ((char *)output)+toWrite,
- frameCount * blio->outputSampleSize * blio->outChan - toWrite );
+ frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite );
/* copy the data */
/*printf( "writing %d\n", toWrite );*/
- assert( toWrite == PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite ) );
+ written = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite );
+ assert( toWrite == written );
#ifdef PA_MAC__BLIO_MUTEX
/* We have a priority inversion here. However, we will only have to
wait if this was true and is now false, which means we've got
@@ -413,11 +441,11 @@ PaError ReadStream( PaStream* stream,
#endif
}
} while( avail == 0 );
- toRead = MIN( avail, frames * blio->inputSampleSize * blio->inChan );
- toRead -= toRead % blio->inputSampleSize * blio->inChan ;
+ toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan );
+ toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ;
PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead );
cbuf += toRead;
- frames -= toRead / ( blio->inputSampleSize * blio->inChan );
+ frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan );
if( toRead == avail ) {
#ifdef PA_MAC_BLIO_MUTEX
@@ -443,7 +471,7 @@ PaError ReadStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
- OSAtomicAnd32( ~paInputOverflow, &blio->statusFlags );
+ OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags );
ret = paInputOverflowed;
}
@@ -491,11 +519,11 @@ PaError WriteStream( PaStream* stream,
}
} while( avail == 0 );
- toWrite = MIN( avail, frames * blio->outputSampleSize * blio->outChan );
- toWrite -= toWrite % blio->outputSampleSize * blio->outChan ;
+ toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan );
+ toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ;
PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite );
cbuf += toWrite;
- frames -= toWrite / ( blio->outputSampleSize * blio->outChan );
+ frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan );
#ifdef PA_MAC_BLIO_MUTEX
if( toWrite == avail ) {
@@ -520,7 +548,7 @@ PaError WriteStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
- OSAtomicAnd32( ~paOutputUnderflow, &blio->statusFlags );
+ OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
ret = paOutputUnderflowed;
}
@@ -549,7 +577,7 @@ signed long GetStreamReadAvailable( PaStream* stream )
VVDBUG(("GetStreamReadAvailable()\n"));
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer )
- / ( blio->outputSampleSize * blio->outChan );
+ / ( blio->inputSampleSizeActual * blio->inChan );
}
@@ -559,6 +587,6 @@ signed long GetStreamWriteAvailable( PaStream* stream )
VVDBUG(("GetStreamWriteAvailable()\n"));
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer )
- / ( blio->outputSampleSize * blio->outChan );
+ / ( blio->outputSampleSizeActual * blio->outChan );
}
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h
index 8ad79eaa..971223b3 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h
@@ -53,7 +53,7 @@
/**
@file
- @ingroup hostaip_src
+ @ingroup hostapi_src
*/
#ifndef PA_MAC_CORE_BLOCKING_H_
@@ -79,10 +79,13 @@
typedef struct {
PaUtilRingBuffer inputRingBuffer;
PaUtilRingBuffer outputRingBuffer;
+ size_t ringBufferFrames;
PaSampleFormat inputSampleFormat;
- size_t inputSampleSize;
+ size_t inputSampleSizeActual;
+ size_t inputSampleSizePow2;
PaSampleFormat outputSampleFormat;
- size_t outputSampleSize;
+ size_t outputSampleSizeActual;
+ size_t outputSampleSizePow2;
size_t framesPerBuffer;
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h
index 998b819c..1797cbaf 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h
@@ -61,10 +61,11 @@
#ifndef PA_MAC_CORE_INTERNAL_H__
#define PA_MAC_CORE_INTERNAL_H__
+#include <CoreAudio/CoreAudio.h>
+#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
-
#include "portaudio.h"
#include "pa_util.h"
#include "pa_hostapi.h"
@@ -139,6 +140,7 @@ typedef struct PaMacCoreStream
/* We need to preallocate an inputBuffer for reading data. */
AudioBufferList inputAudioBufferList;
AudioTimeStamp startTime;
+ /* FIXME: instead of volatile, these should be properly memory barriered */
volatile PaStreamCallbackFlags xrunFlags;
volatile bool isTimeSet;
volatile enum {
@@ -146,7 +148,8 @@ typedef struct PaMacCoreStream
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.
+ STOPPING = 2, /* The stream is in the process of closing
+ because the user has called StopStream.
This state is just used internally;
externally it is indistinguishable from
ACTIVE.*/
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c
index 37403251..5bc592e8 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c
@@ -57,6 +57,10 @@
*/
#include "pa_mac_core_utilities.h"
+#include "pa_mac_core_internal.h"
+#include <libkern/OSAtomic.h>
+#include <strings.h>
+#include <pthread.h>
PaError PaMacCore_SetUnixError( int err, int line )
{
@@ -199,10 +203,19 @@ PaError PaMacCore_SetError(OSStatus error, int line, int isError)
else
errorType = "Warning";
- if ((int)error < -99999 || (int)error > 99999)
- DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText));
- else
- DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText));
+ char str[20];
+ // see if it appears to be a 4-char-code
+ *(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
+ if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4]))
+ {
+ str[0] = str[5] = '\'';
+ str[6] = '\0';
+ } else {
+ // no, format it as an integer
+ sprintf(str, "%d", (int)error);
+ }
+
+ DBUG(("%s on line %d: err='%s', msg=%s\n", errorType, line, str, errorText));
PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
@@ -520,9 +533,9 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
- const bool isOutput,
- unsigned long requestedFramesPerBuffer,
- unsigned long *actualFramesPerBuffer )
+ const bool isOutput,
+ UInt32 requestedFramesPerBuffer,
+ UInt32 *actualFramesPerBuffer )
{
UInt32 afpb;
const bool isInput = !isOutput;
@@ -609,3 +622,117 @@ PaError setBestFramesPerBuffer( const AudioDeviceID device,
return paNoError;
}
+
+/**********************
+ *
+ * XRun stuff
+ *
+ **********************/
+
+struct PaMacXRunListNode_s {
+ PaMacCoreStream *stream;
+ struct PaMacXRunListNode_s *next;
+} ;
+
+typedef struct PaMacXRunListNode_s PaMacXRunListNode;
+
+/** Always empty, so that it can always be the one returned by
+ addToXRunListenerList. note that it's not a pointer. */
+static PaMacXRunListNode firstXRunListNode;
+static int xRunListSize;
+static pthread_mutex_t xrunMutex;
+
+OSStatus xrunCallback(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData)
+{
+ PaMacXRunListNode *node = (PaMacXRunListNode *) inClientData;
+
+ int ret = pthread_mutex_trylock( &xrunMutex ) ;
+
+ if( ret == 0 ) {
+
+ node = node->next ; //skip the first node
+
+ for( ; node; node=node->next ) {
+ PaMacCoreStream *stream = node->stream;
+
+ if( stream->state != ACTIVE )
+ continue; //if the stream isn't active, we don't care if the device is dropping
+
+ if( isInput ) {
+ if( stream->inputDevice == inDevice )
+ OSAtomicOr32( paInputOverflow, (uint32_t *)&(stream->xrunFlags) );
+ } else {
+ if( stream->outputDevice == inDevice )
+ OSAtomicOr32( paOutputUnderflow, (uint32_t *)&(stream->xrunFlags) );
+ }
+ }
+
+ pthread_mutex_unlock( &xrunMutex );
+ }
+
+ return 0;
+}
+
+int initializeXRunListenerList()
+{
+ xRunListSize = 0;
+ bzero( (void *) &firstXRunListNode, sizeof(firstXRunListNode) );
+ return pthread_mutex_init( &xrunMutex, NULL );
+}
+int destroyXRunListenerList()
+{
+ PaMacXRunListNode *node;
+ node = firstXRunListNode.next;
+ while( node ) {
+ PaMacXRunListNode *tmp = node;
+ node = node->next;
+ free( tmp );
+ }
+ xRunListSize = 0;
+ return pthread_mutex_destroy( &xrunMutex );
+}
+
+void *addToXRunListenerList( void *stream )
+{
+ pthread_mutex_lock( &xrunMutex );
+ PaMacXRunListNode *newNode;
+ // setup new node:
+ newNode = (PaMacXRunListNode *) malloc( sizeof( PaMacXRunListNode ) );
+ newNode->stream = (PaMacCoreStream *) stream;
+ newNode->next = firstXRunListNode.next;
+ // insert:
+ firstXRunListNode.next = newNode;
+ pthread_mutex_unlock( &xrunMutex );
+
+ return &firstXRunListNode;
+}
+
+int removeFromXRunListenerList( void *stream )
+{
+ pthread_mutex_lock( &xrunMutex );
+ PaMacXRunListNode *node, *prev;
+ prev = &firstXRunListNode;
+ node = firstXRunListNode.next;
+ while( node ) {
+ if( node->stream == stream ) {
+ //found it:
+ --xRunListSize;
+ prev->next = node->next;
+ free( node );
+ pthread_mutex_unlock( &xrunMutex );
+ return xRunListSize;
+ }
+ prev = prev->next;
+ node = node->next;
+ }
+
+ pthread_mutex_unlock( &xrunMutex );
+ // failure
+ return xRunListSize;
+}
+
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h
index 8a69c25a..899826d5 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h
@@ -199,7 +199,32 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
- const bool isOutput,
- unsigned long requestedFramesPerBuffer,
- unsigned long *actualFramesPerBuffer );
+ const bool isOutput,
+ UInt32 requestedFramesPerBuffer,
+ UInt32 *actualFramesPerBuffer );
+
+
+/*********************
+ *
+ * xrun handling
+ *
+ *********************/
+
+OSStatus xrunCallback(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData ) ;
+
+/** returns zero on success or a unix style error code. */
+int initializeXRunListenerList();
+/** returns zero on success or a unix style error code. */
+int destroyXRunListenerList();
+
+/**Returns the list, so that it can be passed to CorAudio.*/
+void *addToXRunListenerList( void *stream );
+/**Returns the number of Listeners in the list remaining.*/
+int removeFromXRunListenerList( void *stream );
+
#endif /* PA_MAC_CORE_UTILITIES_H__*/