diff options
Diffstat (limited to '3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c')
-rw-r--r-- | 3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c | 814 |
1 files changed, 814 insertions, 0 deletions
diff --git a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c new file mode 100644 index 0000000..0d3b183 --- /dev/null +++ b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c @@ -0,0 +1,814 @@ +/* + * Helper and utility functions for pa_mac_core.c (Apple AUHAL implementation) + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * 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. + * + * The following people also deserve acknowledgements: + * + * Olivier Tristan for feedback and testing + * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O + * interface. + * + * + * 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 + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** + @file + @ingroup hostapi_src +*/ + +#include "pa_mac_core_utilities.h" +#include "pa_mac_core_internal.h" +#include <libkern/OSAtomic.h> +#include <strings.h> +#include <pthread.h> +#include <sys/time.h> + +OSStatus PaMacCore_AudioHardwareGetProperty( + AudioHardwarePropertyID inPropertyID, + UInt32* ioPropertyDataSize, + void* outPropertyData ) +{ + AudioObjectPropertyAddress address = { inPropertyID, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + return AudioObjectGetPropertyData(kAudioObjectSystemObject, &address, 0, NULL, ioPropertyDataSize, outPropertyData); +} + +OSStatus PaMacCore_AudioHardwareGetPropertySize( + AudioHardwarePropertyID inPropertyID, + UInt32* outSize ) +{ + AudioObjectPropertyAddress address = { inPropertyID, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &address, 0, NULL, outSize); +} + +OSStatus PaMacCore_AudioDeviceGetProperty( + AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + UInt32* ioPropertyDataSize, + void* outPropertyData ) +{ + AudioObjectPropertyScope scope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + AudioObjectPropertyAddress address = { inPropertyID, scope, inChannel }; + return AudioObjectGetPropertyData(inDevice, &address, 0, NULL, ioPropertyDataSize, outPropertyData); +} + +OSStatus PaMacCore_AudioDeviceSetProperty( + AudioDeviceID inDevice, + const AudioTimeStamp* inWhen, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void* inPropertyData ) +{ + AudioObjectPropertyScope scope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + AudioObjectPropertyAddress address = { inPropertyID, scope, inChannel }; + return AudioObjectSetPropertyData(inDevice, &address, 0, NULL, inPropertyDataSize, inPropertyData); +} + +OSStatus PaMacCore_AudioDeviceGetPropertySize( + AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + UInt32* outSize ) +{ + AudioObjectPropertyScope scope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + AudioObjectPropertyAddress address = { inPropertyID, scope, inChannel }; + return AudioObjectGetPropertyDataSize(inDevice, &address, 0, NULL, outSize); +} + +OSStatus PaMacCore_AudioDeviceAddPropertyListener( + AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + AudioObjectPropertyListenerProc inProc, + void* inClientData ) +{ + AudioObjectPropertyScope scope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + AudioObjectPropertyAddress address = { inPropertyID, scope, inChannel }; + return AudioObjectAddPropertyListener(inDevice, &address, inProc, inClientData); +} + +OSStatus PaMacCore_AudioDeviceRemovePropertyListener( + AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + AudioObjectPropertyListenerProc inProc, + void* inClientData ) +{ + AudioObjectPropertyScope scope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + AudioObjectPropertyAddress address = { inPropertyID, scope, inChannel }; + return AudioObjectRemovePropertyListener(inDevice, &address, inProc, inClientData); +} + +OSStatus PaMacCore_AudioStreamGetProperty( + AudioStreamID inStream, + UInt32 inChannel, + AudioDevicePropertyID inPropertyID, + UInt32* ioPropertyDataSize, + void* outPropertyData ) +{ + AudioObjectPropertyAddress address = { inPropertyID, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + return AudioObjectGetPropertyData(inStream, &address, 0, NULL, ioPropertyDataSize, outPropertyData); +} + +PaError PaMacCore_SetUnixError( int err, int line ) +{ + PaError ret; + const char *errorText; + + if( err == 0 ) + { + return paNoError; + } + + ret = paNoError; + errorText = strerror( err ); + + /** Map Unix error to PaError. Pretty much the only one that maps + is ENOMEM. */ + if( err == ENOMEM ) + ret = paInsufficientMemory; + else + ret = paInternalError; + + DBUG(("%d on line %d: msg='%s'\n", err, line, errorText)); + PaUtil_SetLastHostErrorInfo( paCoreAudio, err, errorText ); + + return ret; +} + +/* + * Translates MacOS generated errors into PaErrors + */ +PaError PaMacCore_SetError(OSStatus error, int line, int isError) +{ + /*FIXME: still need to handle possible ComponentResult values.*/ + /* unfortunately, they don't seem to be documented anywhere.*/ + PaError result; + const char *errorType; + const char *errorText; + + switch (error) { + case kAudioHardwareNoError: + return paNoError; + case kAudioHardwareNotRunningError: + errorText = "Audio Hardware Not Running"; + result = paInternalError; + break; + case kAudioHardwareUnspecifiedError: + errorText = "Unspecified Audio Hardware Error"; + result = paInternalError; + break; + case kAudioHardwareUnknownPropertyError: + errorText = "Audio Hardware: Unknown Property"; + result = paInternalError; + break; + case kAudioHardwareBadPropertySizeError: + errorText = "Audio Hardware: Bad Property Size"; + result = paInternalError; + break; + case kAudioHardwareIllegalOperationError: + errorText = "Audio Hardware: Illegal Operation"; + result = paInternalError; + break; + case kAudioHardwareBadDeviceError: + errorText = "Audio Hardware: Bad Device"; + result = paInvalidDevice; + break; + case kAudioHardwareBadStreamError: + errorText = "Audio Hardware: BadStream"; + result = paBadStreamPtr; + break; + case kAudioHardwareUnsupportedOperationError: + errorText = "Audio Hardware: Unsupported Operation"; + result = paInternalError; + break; + case kAudioDeviceUnsupportedFormatError: + errorText = "Audio Device: Unsupported Format"; + result = paSampleFormatNotSupported; + break; + case kAudioDevicePermissionsError: + errorText = "Audio Device: Permissions Error"; + result = paDeviceUnavailable; + break; + /* Audio Unit Errors: http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/audio_units/chapter_5_section_3.html */ + case kAudioUnitErr_InvalidProperty: + errorText = "Audio Unit: Invalid Property"; + result = paInternalError; + break; + case kAudioUnitErr_InvalidParameter: + errorText = "Audio Unit: Invalid Parameter"; + result = paInternalError; + break; + case kAudioUnitErr_NoConnection: + errorText = "Audio Unit: No Connection"; + result = paInternalError; + break; + case kAudioUnitErr_FailedInitialization: + errorText = "Audio Unit: Initialization Failed"; + result = paInternalError; + break; + case kAudioUnitErr_TooManyFramesToProcess: + errorText = "Audio Unit: Too Many Frames"; + result = paInternalError; + break; + case kAudioUnitErr_InvalidFile: + errorText = "Audio Unit: Invalid File"; + result = paInternalError; + break; + case kAudioUnitErr_UnknownFileType: + errorText = "Audio Unit: Unknown File Type"; + result = paInternalError; + break; + case kAudioUnitErr_FileNotSpecified: + errorText = "Audio Unit: File Not Specified"; + result = paInternalError; + break; + case kAudioUnitErr_FormatNotSupported: + errorText = "Audio Unit: Format Not Supported"; + result = paInternalError; + break; + case kAudioUnitErr_Uninitialized: + errorText = "Audio Unit: Uninitialized"; + result = paInternalError; + break; + case kAudioUnitErr_InvalidScope: + errorText = "Audio Unit: Invalid Scope"; + result = paInternalError; + break; + case kAudioUnitErr_PropertyNotWritable: + errorText = "Audio Unit: PropertyNotWritable"; + result = paInternalError; + break; + case kAudioUnitErr_InvalidPropertyValue: + errorText = "Audio Unit: Invalid Property Value"; + result = paInternalError; + break; + case kAudioUnitErr_PropertyNotInUse: + errorText = "Audio Unit: Property Not In Use"; + result = paInternalError; + break; + case kAudioUnitErr_Initialized: + errorText = "Audio Unit: Initialized"; + result = paInternalError; + break; + case kAudioUnitErr_InvalidOfflineRender: + errorText = "Audio Unit: Invalid Offline Render"; + result = paInternalError; + break; + case kAudioUnitErr_Unauthorized: + errorText = "Audio Unit: Unauthorized"; + result = paInternalError; + break; + case kAudioUnitErr_CannotDoInCurrentContext: + errorText = "Audio Unit: cannot do in current context"; + result = paInternalError; + break; + default: + errorText = "Unknown Error"; + result = paInternalError; + } + + if (isError) + errorType = "Error"; + else + errorType = "Warning"; + + 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 ); + + return result; +} + +/* + * This function computes an appropriate ring buffer size given + * a requested latency (in seconds), sample rate and framesPerBuffer. + * + * The returned ringBufferSize is computed using the following + * constraints: + * - it must be at least 4. + * - it must be at least 3x framesPerBuffer. + * - it must be at least 2x the suggestedLatency. + * - it must be a power of 2. + * This function attempts to compute the minimum such size. + * + * FEEDBACK: too liberal/conservative/another way? + */ +long computeRingBufferSize( const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + long inputFramesPerBuffer, + long outputFramesPerBuffer, + double sampleRate ) +{ + long ringSize; + int index; + int i; + double latency ; + long framesPerBuffer ; + + VVDBUG(( "computeRingBufferSize()\n" )); + + assert( inputParameters || outputParameters ); + + if( outputParameters && inputParameters ) + { + latency = MAX( inputParameters->suggestedLatency, outputParameters->suggestedLatency ); + framesPerBuffer = MAX( inputFramesPerBuffer, outputFramesPerBuffer ); + } + else if( outputParameters ) + { + latency = outputParameters->suggestedLatency; + framesPerBuffer = outputFramesPerBuffer ; + } + else /* we have inputParameters */ + { + latency = inputParameters->suggestedLatency; + framesPerBuffer = inputFramesPerBuffer ; + } + + ringSize = (long) ( latency * sampleRate * 2 + .5); + VDBUG( ( "suggested latency : %d\n", (int) (latency*sampleRate) ) ); + if( ringSize < framesPerBuffer * 3 ) + ringSize = framesPerBuffer * 3 ; + VDBUG(("framesPerBuffer:%d\n",(int)framesPerBuffer)); + VDBUG(("Ringbuffer size (1): %d\n", (int)ringSize )); + + /* make sure it's at least 4 */ + ringSize = MAX( ringSize, 4 ); + + /* 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 ); + + VDBUG(( "Final Ringbuffer size (2): %d\n", (int)ringSize )); + return ringSize; +} + + +/* + * During testing of core audio, I found that serious crashes could occur + * if properties such as sample rate were changed multiple times in rapid + * succession. The function below could be used to with a condition variable. + * to prevent propertychanges from happening until the last property + * change is acknowledged. Instead, I implemented a busy-wait, which is simpler + * to implement b/c in second round of testing (nov '09) property changes occurred + * quickly and so there was no real way to test the condition variable implementation. + * therefore, this function is not used, but it is aluded to in commented code below, + * since it represents a theoretically better implementation. + */ + +OSStatus propertyProc( + AudioObjectID inObjectID, + UInt32 inNumberAddresses, + const AudioObjectPropertyAddress* inAddresses, + void* inClientData ) +{ + // this is where we would set the condition variable + return noErr; +} + +/* sets the value of the given property and waits for the change to + be acknowledged, and returns the final value, which is not guaranteed + by this function to be the same as the desired value. Obviously, this + function can only be used for data whose input and output are the + same size and format, and their size and format are known in advance. + whether or not the call succeeds, if the data is successfully read, + it is returned in outPropertyData. If it is not read successfully, + outPropertyData is zeroed, which may or may not be useful in + determining if the property was read. */ +PaError AudioDeviceSetPropertyNowAndWaitForChange( + AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void *inPropertyData, + void *outPropertyData ) +{ + OSStatus macErr; + UInt32 outPropertyDataSize = inPropertyDataSize; + + /* First, see if it already has that value. If so, return. */ + macErr = PaMacCore_AudioDeviceGetProperty( inDevice, inChannel, + isInput, inPropertyID, + &outPropertyDataSize, outPropertyData ); + if( macErr ) { + memset( outPropertyData, 0, inPropertyDataSize ); + goto failMac; + } + if( inPropertyDataSize!=outPropertyDataSize ) + return paInternalError; + if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) ) + return paNoError; + + /* Ideally, we'd use a condition variable to determine changes. + we could set that up here. */ + + /* If we were using a cond variable, we'd do something useful here, + but for now, this is just to make 10.6 happy. */ + macErr = PaMacCore_AudioDeviceAddPropertyListener( inDevice, inChannel, isInput, + inPropertyID, propertyProc, + NULL ); + if( macErr ) + /* we couldn't add a listener. */ + goto failMac; + + /* set property */ + macErr = PaMacCore_AudioDeviceSetProperty( inDevice, NULL, inChannel, + isInput, inPropertyID, + inPropertyDataSize, inPropertyData ); + if( macErr ) + goto failMac; + + /* busy-wait up to 30 seconds for the property to change */ + /* busy-wait is justified here only because the correct alternative (condition variable) + was hard to test, since most of the waiting ended up being for setting rather than + getting in OS X 10.5. This was not the case in earlier OS versions. */ + struct timeval tv1, tv2; + gettimeofday( &tv1, NULL ); + memcpy( &tv2, &tv1, sizeof( struct timeval ) ); + while( tv2.tv_sec - tv1.tv_sec < 30 ) { + /* now read the property back out */ + macErr = PaMacCore_AudioDeviceGetProperty( inDevice, inChannel, + isInput, inPropertyID, + &outPropertyDataSize, outPropertyData ); + if( macErr ) { + memset( outPropertyData, 0, inPropertyDataSize ); + goto failMac; + } + /* and compare... */ + if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) ) { + PaMacCore_AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, inPropertyID, propertyProc, NULL); + return paNoError; + } + /* No match yet, so let's sleep and try again. */ + Pa_Sleep( 100 ); + gettimeofday( &tv2, NULL ); + } + DBUG( ("Timeout waiting for device setting.\n" ) ); + + PaMacCore_AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, inPropertyID, propertyProc, NULL ); + return paNoError; + +failMac: + PaMacCore_AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, inPropertyID, propertyProc, NULL ); + return ERR( macErr ); +} + +/* + * Sets the sample rate the HAL device. + * if requireExact: set the sample rate or fail. + * + * otherwise : set the exact sample rate. + * If that fails, check for available sample rates, and choose one + * higher than the requested rate. If there isn't a higher one, + * just use the highest available. + */ +PaError setBestSampleRateForDevice( const AudioDeviceID device, + const bool isOutput, + const bool requireExact, + const Float64 desiredSrate ) +{ + const bool isInput = isOutput ? 0 : 1; + Float64 srate; + UInt32 propsize = sizeof( Float64 ); + OSErr err; + AudioValueRange *ranges; + int i=0; + Float64 max = -1; /*the maximum rate available*/ + Float64 best = -1; /*the lowest sample rate still greater than desired rate*/ + VDBUG(("Setting sample rate for device %ld to %g.\n",(long)device,(float)desiredSrate)); + + /* -- try setting the sample rate -- */ + srate = 0; + err = AudioDeviceSetPropertyNowAndWaitForChange( + device, 0, isInput, + kAudioDevicePropertyNominalSampleRate, + propsize, &desiredSrate, &srate ); + + /* -- if the rate agrees, and was changed, we are done -- */ + if( srate != 0 && srate == desiredSrate ) + return paNoError; + /* -- if the rate agrees, and we got no errors, we are done -- */ + if( !err && srate == desiredSrate ) + return paNoError; + /* -- we've failed if the rates disagree and we are setting input -- */ + if( requireExact ) + return paInvalidSampleRate; + + /* -- generate a list of available sample rates -- */ + err = PaMacCore_AudioDeviceGetPropertySize( device, 0, isInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propsize ); + if( err ) + return ERR( err ); + ranges = (AudioValueRange *)calloc( 1, propsize ); + if( !ranges ) + return paInsufficientMemory; + err = PaMacCore_AudioDeviceGetProperty( device, 0, isInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propsize, ranges ); + if( err ) + { + free( ranges ); + return ERR( err ); + } + VDBUG(("Requested sample rate of %g was not available.\n", (float)desiredSrate)); + VDBUG(("%lu Available Sample Rates are:\n",propsize/sizeof(AudioValueRange))); +#ifdef MAC_CORE_VERBOSE_DEBUG + for( i=0; i<propsize/sizeof(AudioValueRange); ++i ) + VDBUG( ("\t%g-%g\n", + (float) ranges[i].mMinimum, + (float) ranges[i].mMaximum ) ); +#endif + VDBUG(("-----\n")); + + /* -- now pick the best available sample rate -- */ + for( i=0; i<propsize/sizeof(AudioValueRange); ++i ) + { + if( ranges[i].mMaximum > max ) max = ranges[i].mMaximum; + if( ranges[i].mMinimum > desiredSrate ) { + if( best < 0 ) + best = ranges[i].mMinimum; + else if( ranges[i].mMinimum < best ) + best = ranges[i].mMinimum; + } + } + if( best < 0 ) + best = max; + VDBUG( ("Maximum Rate %g. best is %g.\n", max, best ) ); + free( ranges ); + + /* -- set the sample rate -- */ + propsize = sizeof( best ); + srate = 0; + err = AudioDeviceSetPropertyNowAndWaitForChange( + device, 0, isInput, + kAudioDevicePropertyNominalSampleRate, + propsize, &best, &srate ); + + /* -- if the set rate matches, we are done -- */ + if( srate != 0 && srate == best ) + return paNoError; + + if( err ) + return ERR( err ); + + /* -- otherwise, something weird happened: we didn't set the rate, and we got no errors. Just bail. */ + return paInternalError; +} + + +/* + Attempts to set the requestedFramesPerBuffer. If it can't set the exact + value, it settles for something smaller if available. If nothing smaller + is available, it uses the smallest available size. + actualFramesPerBuffer will be set to the actual value on successful return. + OK to pass NULL to actualFramesPerBuffer. + The logic is very similar too setBestSampleRate only failure here is + not usually catastrophic. +*/ +PaError setBestFramesPerBuffer( const AudioDeviceID device, + const bool isOutput, + UInt32 requestedFramesPerBuffer, + UInt32 *actualFramesPerBuffer ) +{ + UInt32 afpb; + const bool isInput = !isOutput; + UInt32 propsize = sizeof(UInt32); + OSErr err; + AudioValueRange range; + + if( actualFramesPerBuffer == NULL ) + { + actualFramesPerBuffer = &afpb; + } + + /* -- try and set exact FPB -- */ + err = PaMacCore_AudioDeviceSetProperty( device, NULL, 0, isInput, + kAudioDevicePropertyBufferFrameSize, + propsize, &requestedFramesPerBuffer); + err = PaMacCore_AudioDeviceGetProperty( device, 0, isInput, + kAudioDevicePropertyBufferFrameSize, + &propsize, actualFramesPerBuffer); + if( err ) + { + return ERR( err ); + } + // Did we get the size we asked for? + if( *actualFramesPerBuffer == requestedFramesPerBuffer ) + { + return paNoError; /* we are done */ + } + + // Clip requested value against legal range for the device. + propsize = sizeof(AudioValueRange); + err = PaMacCore_AudioDeviceGetProperty( device, 0, isInput, + kAudioDevicePropertyBufferFrameSizeRange, + &propsize, &range ); + if( err ) + { + return ERR( err ); + } + if( requestedFramesPerBuffer < range.mMinimum ) + { + requestedFramesPerBuffer = range.mMinimum; + } + else if( requestedFramesPerBuffer > range.mMaximum ) + { + requestedFramesPerBuffer = range.mMaximum; + } + + /* --- set the buffer size (ignore errors) -- */ + propsize = sizeof( UInt32 ); + err = PaMacCore_AudioDeviceSetProperty( device, NULL, 0, isInput, + kAudioDevicePropertyBufferFrameSize, + propsize, &requestedFramesPerBuffer ); + /* --- read the property to check that it was set -- */ + err = PaMacCore_AudioDeviceGetProperty( device, 0, isInput, + kAudioDevicePropertyBufferFrameSize, + &propsize, actualFramesPerBuffer ); + + if( err ) + return ERR( err ); + + 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( + AudioObjectID inDevice, + UInt32 inNumberAddresses, + const AudioObjectPropertyAddress* inAddresses, + void * inClientData) +{ + PaMacXRunListNode *node = (PaMacXRunListNode *) inClientData; + bool isInput = inAddresses->mScope == kAudioDevicePropertyScopeInput; + + 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, &stream->xrunFlags ); + } else { + if( stream->outputDevice == inDevice ) + OSAtomicOr32( paOutputUnderflow, &stream->xrunFlags ); + } + } + + pthread_mutex_unlock( &xrunMutex ); + } + + return 0; +} + +int initializeXRunListenerList( void ) +{ + xRunListSize = 0; + bzero( (void *) &firstXRunListNode, sizeof(firstXRunListNode) ); + return pthread_mutex_init( &xrunMutex, NULL ); +} +int destroyXRunListenerList( void ) +{ + 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; +} |