aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pd/portaudio/pa_mac_core/pa_mac_core.h69
-rw-r--r--pd/portaudio/pa_mac_core/pa_mac_core_utilities.c466
2 files changed, 535 insertions, 0 deletions
diff --git a/pd/portaudio/pa_mac_core/pa_mac_core.h b/pd/portaudio/pa_mac_core/pa_mac_core.h
new file mode 100644
index 00000000..5994294a
--- /dev/null
+++ b/pd/portaudio/pa_mac_core/pa_mac_core.h
@@ -0,0 +1,69 @@
+/*
+ * Mac spcific flags for PA.
+ * portaudio.h should be included before this file.
+ */
+
+/*
+ * A pointer to a paMacCoreStreamInfo may be passed as
+ * the hostApiSpecificStreamInfo in the PaStreamParameters struct
+ * when opening a stream. Use NULL, for the defaults. Note that for
+ * duplex streams, both infos should be the same or behaviour
+ * is undefined.
+ */
+typedef struct paMacCoreStreamInfo
+{
+ unsigned long size; /**size of whole structure including this header */
+ PaHostApiTypeId hostApiType;/**host API for which this data is intended */
+ unsigned long version; /**structure version */
+ unsigned long flags; /* flags to modify behaviour */
+} paMacCoreStreamInfo;
+
+/* Use this function to initialize a paMacCoreStreamInfo struct
+ using the requested flags. */
+void paSetupMacCoreStreamInfo( paMacCoreStreamInfo *data, unsigned long flags )
+{
+ bzero( data, sizeof( paMacCoreStreamInfo ) );
+ data->size = sizeof( paMacCoreStreamInfo );
+ data->hostApiType = paCoreAudio;
+ data->version = 0x01;
+ data->flags = flags;
+}
+
+/*
+ * The following flags alter the behaviour of PA on the mac platform.
+ * they can be ORed together. These should work both for opening and
+ * checking a device.
+ */
+/* Allows PortAudio to change things like the device's frame size,
+ * which allows for much lower latency, but might disrupt the device
+ * if other programs are using it. */
+const unsigned long paMacCore_ChangeDeviceParameters = 0x01;
+
+/* In combination with the above flag,
+ * causes the stream opening to fail, unless the exact sample rates
+ * are supported by the device. */
+const unsigned long paMacCore_FailIfConversionRequired = 0x02;
+
+/* These flags set the SR conversion quality, if required. The wierd ordering
+ * allows Maximum Quality to be the default.*/
+const unsigned long paMacCore_ConversionQualityMin = 0x0100;
+const unsigned long paMacCore_ConversionQualityMedium = 0x0200;
+const unsigned long paMacCore_ConversionQualityLow = 0x0300;
+const unsigned long paMacCore_ConversionQualityHigh = 0x0400;
+const unsigned long paMacCore_ConversionQualityMax = 0x0000;
+
+/*
+ * Here are some "preset" combinations of flags (above) to get to some
+ * common configurations. THIS IS OVERKILL, but if more flags are added
+ * it won't be.
+ */
+/*This is the default setting: do as much sample rate conversion as possible
+ * and as little mucking with the device as possible. */
+const unsigned long paMacCorePlayNice = 0x00;
+/*This setting is tuned for pro audio apps. It allows SR conversion on input
+ and output, but it tries to set the appropriate SR on the device.*/
+const unsigned long paMacCorePro = 0x01;
+/*This is a setting to minimize CPU usage and still play nice.*/
+const unsigned long paMacCoreMinimizeCPUButPlayNice = 0x0100;
+/*This is a setting to minimize CPU usage, even if that means interrupting the device. */
+const unsigned long paMacCoreMinimizeCPU = 0x0101;
diff --git a/pd/portaudio/pa_mac_core/pa_mac_core_utilities.c b/pd/portaudio/pa_mac_core/pa_mac_core_utilities.c
new file mode 100644
index 00000000..f2cbd29c
--- /dev/null
+++ b/pd/portaudio/pa_mac_core/pa_mac_core_utilities.c
@@ -0,0 +1,466 @@
+/*
+ *
+ * pa_mac_core_utilities.c
+ *
+ * utilitiy functions for pa_mac_core.c
+ *
+ * This functions are more like helper functions than part of the core,
+ * so I moved them to a separate file so the core code would be cleaner.
+ *
+ * by Bjorn Roche.
+ */
+
+/*
+ * Translates MacOS generated errors into PaErrors
+ */
+static 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_IllegalInstrument:
+ errorText = "Audio Unit: Illegal Instrument";
+ result = paInternalError; break;
+ case kAudioUnitErr_InstrumentTypeNotFound:
+ errorText = "Audio Unit: Instrument Type Not Found";
+ 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: Unitialized";
+ 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";
+
+ 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));
+
+ PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
+
+ return result;
+}
+
+
+
+
+/*
+ * Durring 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 has some fancy logic to make sure that changes
+ * are acknowledged before another is requested. That seems to help a lot.
+ */
+
+#include <pthread.h>
+typedef struct {
+ bool once; /* I didn't end up using this. bdr */
+ pthread_mutex_t mutex;
+} MutexAndBool ;
+
+static OSStatus propertyProc(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData )
+{
+ MutexAndBool *mab = (MutexAndBool *) inClientData;
+ mab->once = TRUE;
+ pthread_mutex_unlock( &(mab->mutex) );
+ 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.*/
+PaError AudioDeviceSetPropertyNowAndWaitForChange(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ UInt32 inPropertyDataSize,
+ const void *inPropertyData,
+ void *outPropertyData )
+{
+ OSStatus macErr;
+ int unixErr;
+ MutexAndBool mab;
+ UInt32 outPropertyDataSize = inPropertyDataSize;
+
+ /* First, see if it already has that value. If so, return. */
+ macErr = AudioDeviceGetProperty( inDevice, inChannel,
+ isInput, inPropertyID,
+ &outPropertyDataSize, outPropertyData );
+ if( macErr )
+ goto failMac2;
+ if( inPropertyDataSize!=outPropertyDataSize )
+ return paInternalError;
+ if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) )
+ return paNoError;
+
+ /* setup and lock mutex */
+ mab.once = FALSE;
+ unixErr = pthread_mutex_init( &mab.mutex, NULL );
+ if( unixErr )
+ goto failUnix2;
+ unixErr = pthread_mutex_lock( &mab.mutex );
+ if( unixErr )
+ goto failUnix;
+
+ /* add property listener */
+ macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc,
+ &mab );
+ if( macErr )
+ goto failMac;
+ /* set property */
+ macErr = AudioDeviceSetProperty( inDevice, NULL, inChannel,
+ isInput, inPropertyID,
+ inPropertyDataSize, inPropertyData );
+ if( macErr ) {
+ /* we couldn't set the property, so we'll just unlock the mutex
+ and move on. */
+ pthread_mutex_unlock( &mab.mutex );
+ }
+
+ /* wait for property to change */
+ unixErr = pthread_mutex_lock( &mab.mutex );
+ if( unixErr )
+ goto failUnix;
+
+ /* now read the property back out */
+ macErr = AudioDeviceGetProperty( inDevice, inChannel,
+ isInput, inPropertyID,
+ &outPropertyDataSize, outPropertyData );
+ if( macErr )
+ goto failMac;
+ /* cleanup */
+ AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc );
+ unixErr = pthread_mutex_unlock( &mab.mutex );
+ if( unixErr )
+ goto failUnix2;
+ unixErr = pthread_mutex_destroy( &mab.mutex );
+ if( unixErr )
+ goto failUnix2;
+
+ return paNoError;
+
+ failUnix:
+ pthread_mutex_destroy( &mab.mutex );
+ AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc );
+
+ failUnix2:
+ DBUG( ("Error #%d while setting a device property: %s\n", unixErr, strerror( unixErr ) ) );
+ return paUnanticipatedHostError;
+
+ failMac:
+ pthread_mutex_destroy( &mab.mutex );
+ AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
+ inPropertyID, propertyProc );
+ failMac2:
+ 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.
+ */
+static PaError setBestSampleRateForDevice( const AudioDeviceID device,
+ const bool isOutput,
+ const bool requireExact,
+ const Float64 desiredSrate )
+{
+ /*FIXME: changing the sample rate is disruptive to other programs using the
+ device, so it might be good to offer a custom flag to not change the
+ sample rate and just do conversion. (in my casual tests, there is
+ no disruption unless the sample rate really does need to change) */
+ 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",device,(float)desiredSrate));
+
+ /* -- try setting the sample rate -- */
+ err = AudioDeviceSetPropertyNowAndWaitForChange(
+ device, 0, isInput,
+ kAudioDevicePropertyNominalSampleRate,
+ propsize, &desiredSrate, &srate );
+ if( err )
+ return err;
+
+ /* -- 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 = AudioDeviceGetPropertyInfo( device, 0, isInput,
+ kAudioDevicePropertyAvailableNominalSampleRates,
+ &propsize, NULL );
+ if( err )
+ return ERR( err );
+ ranges = (AudioValueRange *)calloc( 1, propsize );
+ if( !ranges )
+ return paInsufficientMemory;
+ err = 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 );
+ err = AudioDeviceSetPropertyNowAndWaitForChange(
+ device, 0, isInput,
+ kAudioDevicePropertyNominalSampleRate,
+ propsize, &best, &srate );
+ if( err )
+ return err;
+
+ if( err )
+ return ERR( err );
+ /* -- if the set rate matches, we are done -- */
+ if( srate == best )
+ return paNoError;
+
+ /* -- otherwise, something wierd 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 simmilar too setBestSampleRate only failure here is
+ not usually catastrophic.
+*/
+static PaError setBestFramesPerBuffer( const AudioDeviceID device,
+ const bool isOutput,
+ unsigned long requestedFramesPerBuffer,
+ unsigned long *actualFramesPerBuffer )
+{
+ UInt32 afpb;
+ const bool isInput = !isOutput;
+ UInt32 propsize = sizeof(UInt32);
+ OSErr err;
+ Float64 min = -1; /*the min blocksize*/
+ Float64 best = -1; /*the best blocksize*/
+ int i=0;
+ AudioValueRange *ranges;
+
+ if( actualFramesPerBuffer == NULL )
+ actualFramesPerBuffer = &afpb;
+
+
+ /* -- try and set exact FPB -- */
+ err = AudioDeviceSetProperty( device, NULL, 0, isInput,
+ kAudioDevicePropertyBufferFrameSize,
+ propsize, &requestedFramesPerBuffer);
+ err = AudioDeviceGetProperty( device, 0, isInput,
+ kAudioDevicePropertyBufferFrameSize,
+ &propsize, actualFramesPerBuffer);
+ if( err )
+ return ERR( err );
+ if( *actualFramesPerBuffer == requestedFramesPerBuffer )
+ return paNoError; /* we are done */
+
+ /* -- fetch available block sizes -- */
+ err = AudioDeviceGetPropertyInfo( device, 0, isInput,
+ kAudioDevicePropertyBufferSizeRange,
+ &propsize, NULL );
+ if( err )
+ return ERR( err );
+ ranges = (AudioValueRange *)calloc( 1, propsize );
+ if( !ranges )
+ return paInsufficientMemory;
+ err = AudioDeviceGetProperty( device, 0, isInput,
+ kAudioDevicePropertyBufferSizeRange,
+ &propsize, ranges );
+ if( err )
+ {
+ free( ranges );
+ return ERR( err );
+ }
+ VDBUG(("Requested block size of %lu was not available.\n",
+ requestedFramesPerBuffer ));
+ VDBUG(("%lu Available block sizes are:\n",propsize/sizeof(AudioValueRange)));
+#ifdef MAC_CORE_VERBOSE_DEBUG
+ for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
+ VDBUG( ("\t%g-%g\n",
+ (float) ranges[i].mMinimum,
+ (float) ranges[i].mMaximum ) );
+#endif
+ VDBUG(("-----\n"));
+
+ /* --- now pick the best available framesPerBuffer -- */
+ for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
+ {
+ if( min == -1 || ranges[i].mMinimum < min ) min = ranges[i].mMinimum;
+ if( ranges[i].mMaximum < requestedFramesPerBuffer ) {
+ if( best < 0 )
+ best = ranges[i].mMaximum;
+ else if( ranges[i].mMaximum > best )
+ best = ranges[i].mMaximum;
+ }
+ }
+ if( best == -1 )
+ best = min;
+ VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) );
+ free( ranges );
+
+ /* --- set the buffer size (ignore errors) -- */
+ requestedFramesPerBuffer = (UInt32) best ;
+ propsize = sizeof( UInt32 );
+ err = AudioDeviceSetProperty( device, NULL, 0, isInput,
+ kAudioDevicePropertyBufferSize,
+ propsize, &requestedFramesPerBuffer );
+ /* --- read the property to check that it was set -- */
+ err = AudioDeviceGetProperty( device, 0, isInput,
+ kAudioDevicePropertyBufferSize,
+ &propsize, actualFramesPerBuffer );
+
+ if( err )
+ return ERR( err );
+
+ return paNoError;
+}