Update of /cvsroot/pure-data/pd/portaudio/src/hostapi/coreaudio In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19525/portaudio/src/hostapi/coreaudio
Added Files: notes.txt pa_mac_core.c pa_mac_core_blocking.c pa_mac_core_blocking.h pa_mac_core_internal.h pa_mac_core_old.c pa_mac_core_utilities.c pa_mac_core_utilities.h Log Message: CVS upload mistakes
--- NEW FILE: pa_mac_core_utilities.h --- /* * 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 */
#ifndef PA_MAC_CORE_UTILITIES_H__ #define PA_MAC_CORE_UTILITIES_H__
#include <pthread.h> #include "portaudio.h" #include "pa_util.h" #include <AudioUnit/AudioUnit.h> #include <AudioToolbox/AudioToolbox.h>
#ifndef MIN #define MIN(a, b) (((a)<(b))?(a):(b)) #endif
#ifndef MAX #define MAX(a, b) (((a)<(b))?(b):(a)) #endif
#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 UNIX_ERR(err) PaMacCore_SetUnixError( err, __LINE__ )
PaError PaMacCore_SetUnixError( int err, int line );
/* * Translates MacOS generated errors into PaErrors */ PaError PaMacCore_SetError(OSStatus error, int line, int isError);
/* * 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. * */ long computeRingBufferSize( const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, long inputFramesPerBuffer, long outputFramesPerBuffer, double sampleRate );
/* * 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. */
typedef struct { bool once; /* I didn't end up using this. bdr */ pthread_mutex_t mutex; } MutexAndBool ;
OSStatus propertyProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void* inClientData );
/* 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 );
/* * 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 ); /* 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. */ PaError setBestFramesPerBuffer( const AudioDeviceID device, const bool isOutput, unsigned long requestedFramesPerBuffer, unsigned long *actualFramesPerBuffer ); #endif /* PA_MAC_CORE_UTILITIES_H__*/
--- NEW FILE: pa_mac_core_internal.h --- /* * Internal interfaces for PortAudio 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 pa_mac_core @ingroup hostapi_src @author Bjorn Roche @brief AUHAL implementation of PortAudio */
#ifndef PA_MAC_CORE_INTERNAL_H__ #define PA_MAC_CORE_INTERNAL_H__
#include <AudioUnit/AudioUnit.h> #include <AudioToolbox/AudioToolbox.h>
#include "portaudio.h" #include "pa_util.h" #include "pa_hostapi.h" #include "pa_stream.h" #include "pa_allocation.h" #include "pa_cpuload.h" #include "pa_process.h" #include "pa_ringbuffer.h"
#include "pa_mac_core_blocking.h"
/* function prototypes */
#ifdef __cplusplus extern "C" { #endif /* __cplusplus */
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus } #endif /* __cplusplus */
#define RING_BUFFER_ADVANCE_DENOMINATOR (4)
PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); signed long GetStreamReadAvailable( PaStream* stream ); signed long GetStreamWriteAvailable( PaStream* stream ); /* PaMacAUHAL - host api datastructure specific to this implementation */ typedef struct { PaUtilHostApiRepresentation inheritedHostApiRep; PaUtilStreamInterface callbackStreamInterface; PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations;
/* implementation specific data goes here */ long devCount; AudioDeviceID *devIds; /*array of all audio devices*/ AudioDeviceID defaultIn; AudioDeviceID defaultOut; } PaMacAUHAL;
/* stream data structure specifically for this implementation */ typedef struct PaMacCoreStream { PaUtilStreamRepresentation streamRepresentation; PaUtilCpuLoadMeasurer cpuLoadMeasurer; PaUtilBufferProcessor bufferProcessor;
/* implementation specific data goes here */ bool bufferProcessorIsInitialized; AudioUnit inputUnit; AudioUnit outputUnit; AudioDeviceID inputDevice; AudioDeviceID outputDevice; size_t userInChan; size_t userOutChan; size_t inputFramesPerBuffer; size_t outputFramesPerBuffer; PaMacBlio blio; /* We use this ring buffer when input and out devs are different. */ PaUtilRingBuffer 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 PaStreamCallbackFlags xrunFlags; volatile bool isTimeSet; 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; //these may be different from the stream sample rate due to SR conversion: double outDeviceSampleRate; double inDeviceSampleRate; } PaMacCoreStream;
#endif /* PA_MAC_CORE_INTERNAL_H__ */
--- NEW FILE: pa_mac_core_utilities.c --- /* * 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"
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/audi... */ 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; }
/* * 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 latencyTimesChannelCount ; long framesPerBufferTimesChannelCount ;
VVDBUG(( "computeRingBufferSize()\n" ));
assert( inputParameters || outputParameters );
if( outputParameters && inputParameters ) { latencyTimesChannelCount = MAX( inputParameters->suggestedLatency * inputParameters->channelCount, outputParameters->suggestedLatency * outputParameters->channelCount ); framesPerBufferTimesChannelCount = MAX( inputFramesPerBuffer * inputParameters->channelCount, outputFramesPerBuffer * outputParameters->channelCount ); } else if( outputParameters ) { latencyTimesChannelCount = outputParameters->suggestedLatency * outputParameters->channelCount; framesPerBufferTimesChannelCount = outputFramesPerBuffer * outputParameters->channelCount; } else /* we have inputParameters */ { latencyTimesChannelCount = inputParameters->suggestedLatency * inputParameters->channelCount; framesPerBufferTimesChannelCount = inputFramesPerBuffer * inputParameters->channelCount; }
ringSize = (long) ( latencyTimesChannelCount * sampleRate * 2 + .5); VDBUG( ( "suggested latency * channelCount: %d\n", (int) (latencyTimesChannelCount*sampleRate) ) ); if( ringSize < framesPerBufferTimesChannelCount * 3 ) ringSize = framesPerBufferTimesChannelCount * 3 ; VDBUG(("framesPerBuffer*channelCount:%d\n",(int)framesPerBufferTimesChannelCount)); 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; }
/* * 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. */
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. */ 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. */ 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; }
--- NEW FILE: notes.txt --- 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 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).
Additionally, the user may define a "channel mapping" by calling paSetupMacCoreChannelMap() on their stream info structure before opening 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 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. Also, there are currently issues with some devices whehn plugging/unplugging devices.
- 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: should work fine.
- multichannel: It has been tested successfully on multichannel hardware from MOTU: traveler and 896HD. Also Presonus firepod and others. It is believed to work with all Core Audio devices, including virtual devices such as soundflower.
- 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 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.
Creating a channel map:
How to create the map array - Text taken From AUHAL.rtfd : [3] Channel Maps Clients can tell the AUHAL units which channels of the device they are interested in. For example, the client may be processing stereo data, but outputting to a six-channel device. This is done by using the kAudioOutputUnitProperty_ChannelMap property. To use this property:
For Output: Create an array of SInt32 that is the size of the number of channels of the device (Get the Format of the AUHAL's output Element == 0) Initialize each of the array's values to -1 (-1 indicates that that channel is NOT to be presented in the conversion.)
Next, for each channel of your app's output, set: channelMapArray[deviceOutputChannel] = desiredAppOutputChannel.
For example: we have a 6 channel output device and our application has a stereo source it wants to provide to the device. Suppose we want that stereo source to go to the 3rd and 4th channels of the device. The channel map would look like this: { -1, -1, 0, 1, -1, -1 }
Where the formats are: Input Element == 0: 2 channels (- client format - settable) Output Element == 0: 6 channels (- device format - NOT settable)
So channel 2 (zero-based) of the device will take the first channel of output and channel 3 will take the second channel of output. (This translates to the 3rd and 4th plugs of the 6 output plugs of the device of course!)
For Input: Create an array of SInt32 that is the size of the number of channels of the format you require for input. Get (or Set in this case as needed) the AUHAL's output Element == 1.
Next, for each channel of input you require, set: channelMapArray[desiredAppInputChannel] = deviceOutputChannel;
For example: we have a 6 channel input device from which we wish to receive stereo input from the 3rd and 4th channels. The channel map looks like this: { 2, 3 }
Where the formats are: Input Element == 0: 2 channels (- device format - NOT settable) Output Element == 0: 6 channels (- client format - settable)
----------------------------------------
Notes on Original implementation:
by Phil Burk and Darren Gibbs
Last updated March 20, 2002
WHAT WORKS
Output with very low latency, <10 msec. Half duplex input or output. Full duplex on the same CoreAudio device. The paFLoat32, paInt16, paInt8, paUInt8 sample formats. Pa_GetCPULoad() Pa_StreamTime()
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 Built-In audio is typically one CoreAudio device.
Mono doesn't work.
DEVICE MAPPING
CoreAudio devices can support both input and output. But the sample rates supported may be different. So we have map one or two PortAudio device to each CoreAudio device depending on whether it supports 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.
--- NEW FILE: pa_mac_core_blocking.h --- /* * Internal blocking interfaces for PortAudio 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 hostaip_src */
#ifndef PA_MAC_CORE_BLOCKING_H_ #define PA_MAC_CORE_BLOCKING_H_
#include "pa_ringbuffer.h" #include "portaudio.h" #include "pa_mac_core_utilities.h"
/* * Number of miliseconds to busy wait whil waiting for data in blocking calls. */ #define PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL (5) /* * Define exactly one of these blocking methods * PA_MAC_BLIO_MUTEX is not actively maintained. */ #define PA_MAC_BLIO_BUSY_WAIT /* #define PA_MAC_BLIO_MUTEX */
typedef struct { PaUtilRingBuffer inputRingBuffer; PaUtilRingBuffer outputRingBuffer; PaSampleFormat inputSampleFormat; size_t inputSampleSize; PaSampleFormat outputSampleFormat; size_t outputSampleSize;
size_t framesPerBuffer;
int inChan; int outChan;
//PaStreamCallbackFlags statusFlags; uint32_t statusFlags; PaError errors;
/* Here we handle blocking, using condition variables. */ #ifdef PA_MAC_BLIO_MUTEX volatile bool isInputEmpty; pthread_mutex_t inputMutex; pthread_cond_t inputCond;
volatile bool isOutputFull; pthread_mutex_t outputMutex; pthread_cond_t outputCond; #endif } PaMacBlio;
/* * These functions operate on condition and related variables. */
PaError initializeBlioRingBuffers( PaMacBlio *blio, PaSampleFormat inputSampleFormat, PaSampleFormat outputSampleFormat, size_t framesPerBuffer, long ringBufferSize, int inChan, int outChan ); PaError destroyBlioRingBuffers( PaMacBlio *blio ); PaError resetBlioRingBuffers( PaMacBlio *blio );
int BlioCallback( const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData );
void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio );
#endif /*PA_MAC_CORE_BLOCKING_H_*/
--- NEW FILE: pa_mac_core_old.c --- /* * $Id: pa_mac_core_old.c,v 1.1 2007/08/18 23:49:33 millerpuckette 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 * * Authors: Ross Bencina and Phil Burk * Copyright (c) 1999-2000 Ross Bencina and 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. */
#include <CoreAudio/CoreAudio.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"
// ===== constants =====
// ===== structs ===== #pragma mark structs
// PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation typedef struct PaMacCore_HAR { PaUtilHostApiRepresentation inheritedHostApiRep; PaUtilStreamInterface callbackStreamInterface; PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations; AudioDeviceID *macCoreDeviceIds; } PaMacCoreHostApiRepresentation;
typedef struct PaMacCore_DI { PaDeviceInfo inheritedDeviceInfo; } PaMacCoreDeviceInfo;
// PaMacCoreStream - a stream data structure specifically for this implementation typedef struct PaMacCore_S { PaUtilStreamRepresentation streamRepresentation; PaUtilCpuLoadMeasurer cpuLoadMeasurer; PaUtilBufferProcessor bufferProcessor;
int primeStreamUsingCallback;
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 } 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;
// ===== CoreAudio-PortAudio bridge functions ===== #pragma mark CoreAudio-PortAudio bridge functions
// 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; }
/* This function is unused 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) { if( macCoreHostApi->allocations ) { PaUtil_FreeAllAllocations( macCoreHostApi->allocations ); PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations ); }
PaUtil_FreeMemory( macCoreHostApi ); }
static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput) { UInt32 propSize; PaError err = paNoError; UInt32 i; int numChannels = 0; AudioBufferList *buflist;
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; } } } PaUtil_FreeMemory(buflist);
return err; }
static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex ) { PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo; deviceInfo->structVersion = 2; deviceInfo->hostApi = hostApiIndex;
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; propSize = sizeof(Float64); err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); if (!err) { deviceInfo->defaultSampleRate = sampleRate; }
// Get channel info err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1); err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0);
return err; }
static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, 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 ) { return paInsufficientMemory; }
// allocate all device info structs in a contiguous block deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory( macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices ); if( !deviceInfoArray ) { return paInsufficientMemory; }
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; } if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) { hostApi->info.defaultOutputDevice = i; } InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex); hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo); } }
return result; }
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; } destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator); coreAudioBuffer += sizeof(Float32); portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing; } return noErr; }
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; } return noErr; }
static OSStatus AudioIOProc( 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 *outputBuffer = &outOutputData->mBuffers[0]; unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
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); }
PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
if (result == paComplete || result == paAbort) { Pa_StopStream(clientData->stream); }
PaUtil_FreeMemory( timeInfo ); return noErr; }
// This is not for input-only streams, this is for streams where the input device is different from the output device 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));
CopyInputData(clientData, inInputData, frameCount); PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); if( result == paComplete || result == paAbort ) Pa_StopStream(clientData->stream); PaUtil_FreeMemory( timeInfo ); return noErr; }
// 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) { 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));
//clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
CopyOutputData(outOutputData, clientData, frameCount);
PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); return noErr; }
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; }
static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput) { 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 } else if (actualFramesPerBuffer > framesPerBuffer) { result = paBufferTooSmall; } else if (actualFramesPerBuffer < framesPerBuffer) { result = paBufferTooBig; }
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
#ifdef __cplusplus extern "C" { #endif // __cplusplus
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus } #endif // __cplusplus
static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) { PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
CleanUp(macCoreHostApi); }
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; } } 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; } }
return result; }
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 ) { 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); }
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); }
if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) { AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice;
AudioDeviceAddIOProc(device, AudioIOProc, clientData); } else { // using different devices for input and output AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData); AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData); }
return err; }
// Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted. static PaError CloseStream( PaStream* s ) { PaError err = paNoError; PaMacCoreStream *stream = (PaMacCoreStream*)s;
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
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)); }
return err; }
static PaError StartStream( PaStream *s ) { PaError err = paNoError; PaMacCoreStream *stream = (PaMacCoreStream*)s;
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)); } } else { err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc)); }
stream->isActive = 1; stream->isStopped = 0; return err; }
static PaError AbortStream( 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)); } } else { err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc)); }
stream->isActive = 0; stream->isStopped = 1; return err; }
static PaError StopStream( PaStream *s ) { // TODO: this should be nicer than abort return AbortStream(s); }
static PaError IsStreamStopped( PaStream *s ) { PaMacCoreStream *stream = (PaMacCoreStream*)s;
return stream->isStopped; }
static PaError IsStreamActive( PaStream *s ) { PaMacCoreStream *stream = (PaMacCoreStream*)s;
return stream->isActive; }
static PaTime GetStreamTime( 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; }
static double GetStreamCpuLoad( PaStream* s ) { PaMacCoreStream *stream = (PaMacCoreStream*)s;
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.
static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames ) { return paInternalError; }
static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames ) { return paInternalError; }
static signed long GetStreamReadAvailable( PaStream* s ) { return paInternalError; }
static signed long GetStreamWriteAvailable( PaStream* s ) { return paInternalError; }
// 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; }
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";
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; }
--- NEW FILE: pa_mac_core_blocking.c --- /* * Implementation of the PortAudio API for Apple AUHAL * * 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 hostaip_src
This file contains the implementation required for blocking I/O. It is separated from pa_mac_core.c simply to ease development. */
#include "pa_mac_core_blocking.h" #include "pa_mac_core_internal.h" #include <assert.h> #ifdef MOSX_USE_NON_ATOMIC_FLAG_BITS # define OSAtomicOr32( a, b ) ( (*(b)) |= (a) ) # define OSAtomicAnd32( a, b ) ( (*(b)) &= (a) ) #else # include <libkern/OSAtomic.h> #endif
/* * This fnuction determines the size of a particular sample format. * if the format is not recognized, this returns zero. */ static size_t computeSampleSizeFromFormat( PaSampleFormat format ) { switch( format ) { case paFloat32: return 4; case paInt32: return 4; case paInt24: return 3; case paInt16: return 2; case paInt8: case paUInt8: return 1; default: return 0; } }
/* * Functions for initializing, resetting, and destroying BLIO structures. * */
/* This should be called with the relevant info when initializing a stream for callback. */ PaError initializeBlioRingBuffers( PaMacBlio *blio, PaSampleFormat inputSampleFormat, PaSampleFormat outputSampleFormat, size_t framesPerBuffer, long ringBufferSize, int inChan, int outChan ) { void *data; int result;
/* zeroify things */ bzero( blio, sizeof( PaMacBlio ) ); /* this is redundant, but the buffers are used to check if the bufffers have been initialized, so we do it explicitly. */ blio->inputRingBuffer.buffer = NULL; blio->outputRingBuffer.buffer = NULL;
/* initialize simple data */ blio->inputSampleFormat = inputSampleFormat; blio->inputSampleSize = computeSampleSizeFromFormat(inputSampleFormat); blio->outputSampleFormat = outputSampleFormat; blio->outputSampleSize = computeSampleSizeFromFormat(outputSampleFormat); blio->framesPerBuffer = framesPerBuffer; blio->inChan = inChan; blio->outChan = outChan; blio->statusFlags = 0; blio->errors = paNoError; #ifdef PA_MAC_BLIO_MUTEX blio->isInputEmpty = false; blio->isOutputFull = false; #endif
/* setup ring buffers */ #ifdef PA_MAC_BLIO_MUTEX result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 ); if( result ) goto error; result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) ); if( result ) goto error; result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) ); if( result ) goto error; result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) ); #endif if( inChan ) { data = calloc( ringBufferSize, blio->inputSampleSize ); if( !data ) { result = paInsufficientMemory; goto error; }
assert( 0 == PaUtil_InitializeRingBuffer( &blio->inputRingBuffer, ringBufferSize*blio->inputSampleSize, data ) ); } if( outChan ) { data = calloc( ringBufferSize, blio->outputSampleSize ); if( !data ) { result = paInsufficientMemory; goto error; }
assert( 0 == PaUtil_InitializeRingBuffer( &blio->outputRingBuffer, ringBufferSize*blio->outputSampleSize, data ) ); }
result = resetBlioRingBuffers( blio ); if( result ) goto error;
return 0;
error: destroyBlioRingBuffers( blio ); return result; }
#ifdef PA_MAC_BLIO_MUTEX PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty ) { PaError result = paNoError; if( isEmpty == blio->isInputEmpty ) goto done;
/* we need to update the value. Here's what we do: * - Lock the mutex, so noone else can write. * - update the value. * - unlock. * - broadcast to all listeners. */ result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) ); if( result ) goto done; blio->isInputEmpty = isEmpty; result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) ); if( result ) goto done; result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) ); if( result ) goto done;
done: return result; } PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull ) { PaError result = paNoError; if( isFull == blio->isOutputFull ) goto done;
/* we need to update the value. Here's what we do: * - Lock the mutex, so noone else can write. * - update the value. * - unlock. * - broadcast to all listeners. */ result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) ); if( result ) goto done; blio->isOutputFull = isFull; result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) ); if( result ) goto done; result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) ); if( result ) goto done;
done: return result; } #endif
/* This should be called after stopping or aborting the stream, so that on next start, the buffers will be ready. */ PaError resetBlioRingBuffers( PaMacBlio *blio ) { #ifdef PA_MAC__BLIO_MUTEX int result; #endif blio->statusFlags = 0; if( blio->outputRingBuffer.buffer ) { PaUtil_FlushRingBuffer( &blio->outputRingBuffer ); bzero( blio->outputRingBuffer.buffer, blio->outputRingBuffer.bufferSize ); /* Advance buffer */ PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
/* Update isOutputFull. */ #ifdef PA_MAC__BLIO_MUTEX result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize ); if( result ) goto error; #endif /* printf( "------%d\n" , blio->framesPerBuffer ); printf( "------%d\n" , blio->outChan ); printf( "------%d\n" , blio->outputSampleSize ); printf( "------%d\n" , blio->framesPerBuffer*blio->outChan*blio->outputSampleSize ); */ } if( blio->inputRingBuffer.buffer ) { PaUtil_FlushRingBuffer( &blio->inputRingBuffer ); bzero( blio->inputRingBuffer.buffer, blio->inputRingBuffer.bufferSize ); /* Update isInputEmpty. */ #ifdef PA_MAC__BLIO_MUTEX result = blioSetIsInputEmpty( blio, true ); if( result ) goto error; #endif } return paNoError; #ifdef PA_MAC__BLIO_MUTEX error: return result; #endif }
/*This should be called when you are done with the blio. It can safely be called multiple times if there are no exceptions. */ PaError destroyBlioRingBuffers( PaMacBlio *blio ) { PaError result = paNoError; if( blio->inputRingBuffer.buffer ) { free( blio->inputRingBuffer.buffer ); #ifdef PA_MAC__BLIO_MUTEX result = UNIX_ERR( pthread_mutex_destroy( & blio->inputMutex ) ); if( result ) return result; result = UNIX_ERR( pthread_cond_destroy( & blio->inputCond ) ); if( result ) return result; #endif } blio->inputRingBuffer.buffer = NULL; if( blio->outputRingBuffer.buffer ) { free( blio->outputRingBuffer.buffer ); #ifdef PA_MAC__BLIO_MUTEX result = UNIX_ERR( pthread_mutex_destroy( & blio->outputMutex ) ); if( result ) return result; result = UNIX_ERR( pthread_cond_destroy( & blio->outputCond ) ); if( result ) return result; #endif } blio->outputRingBuffer.buffer = NULL;
return result; }
/* * this is the BlioCallback function. It expects to recieve a PaMacBlio Object * pointer as userData. * */ int BlioCallback( const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) { PaMacBlio *blio = (PaMacBlio*)userData; long avail; long toRead; long toWrite;
/* set flags returned by OS: */ OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
/* --- Handle Input Buffer --- */ if( blio->inChan ) { avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
/* check for underflow */ if( avail < frameCount * blio->inputSampleSize * blio->inChan ) OSAtomicOr32( paInputOverflow, &blio->statusFlags );
toRead = MIN( avail, frameCount * blio->inputSampleSize * blio->inChan );
/* copy the data */ /*printf( "reading %d\n", toRead );*/ assert( toRead == PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead ) ); #ifdef PA_MAC__BLIO_MUTEX /* Priority inversion. See notes below. */ blioSetIsInputEmpty( blio, false ); #endif }
/* --- Handle Output Buffer --- */ if( blio->outChan ) { avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
/* check for underflow */ if( avail < frameCount * blio->outputSampleSize * blio->outChan ) OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
toWrite = MIN( avail, frameCount * blio->outputSampleSize * blio->outChan );
if( toWrite != frameCount * blio->outputSampleSize * blio->outChan ) bzero( ((char *)output)+toWrite, frameCount * blio->outputSampleSize * blio->outChan - toWrite ); /* copy the data */ /*printf( "writing %d\n", toWrite );*/ assert( toWrite == PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite ) ); #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 some room in the buffer. Hopefully problems will be minimized. */ blioSetIsOutputFull( blio, false ); #endif }
return paContinue; }
PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ) { PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; char *cbuf = (char *) buffer; PaError ret = paNoError; VVDBUG(("ReadStream()\n"));
while( frames > 0 ) { long avail; long toRead; do { avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ); /* printf( "Read Buffer is %%%g full: %ld of %ld.\n", 100 * (float)avail / (float) blio->inputRingBuffer.bufferSize, avail, blio->inputRingBuffer.bufferSize ); */ if( avail == 0 ) { #ifdef PA_MAC_BLIO_MUTEX /**block when empty*/ ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) ); if( ret ) return ret; while( blio->isInputEmpty ) { ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) ); if( ret ) return ret; } ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) ); if( ret ) return ret; #else Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); #endif } } while( avail == 0 ); toRead = MIN( avail, frames * blio->inputSampleSize * blio->inChan ); toRead -= toRead % blio->inputSampleSize * blio->inChan ; PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead ); cbuf += toRead; frames -= toRead / ( blio->inputSampleSize * blio->inChan );
if( toRead == avail ) { #ifdef PA_MAC_BLIO_MUTEX /* we just emptied the buffer, so we need to mark it as empty. */ ret = blioSetIsInputEmpty( blio, true ); if( ret ) return ret; /* of course, in the meantime, the callback may have put some sats in, so so check for that, too, to avoid a race condition. */ if( PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) ) { blioSetIsInputEmpty( blio, false ); if( ret ) return ret; } #endif } }
/* Report either paNoError or paInputOverflowed. */ /* may also want to report other errors, but this is non-standard. */ ret = blio->statusFlags & paInputOverflow;
/* report underflow only once: */ if( ret ) { OSAtomicAnd32( ~paInputOverflow, &blio->statusFlags ); ret = paInputOverflowed; }
return ret; }
PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ) { PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; char *cbuf = (char *) buffer; PaError ret = paNoError; VVDBUG(("WriteStream()\n"));
while( frames > 0 ) { long avail = 0; long toWrite;
do { avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); /* printf( "Write Buffer is %%%g full: %ld of %ld.\n", 100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize, avail, blio->outputRingBuffer.bufferSize ); */ if( avail == 0 ) { #ifdef PA_MAC_BLIO_MUTEX /*block while full*/ ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) ); if( ret ) return ret; while( blio->isOutputFull ) { ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) ); if( ret ) return ret; } ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) ); if( ret ) return ret; #else Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); #endif } } while( avail == 0 );
toWrite = MIN( avail, frames * blio->outputSampleSize * blio->outChan ); toWrite -= toWrite % blio->outputSampleSize * blio->outChan ; PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite ); cbuf += toWrite; frames -= toWrite / ( blio->outputSampleSize * blio->outChan );
#ifdef PA_MAC_BLIO_MUTEX if( toWrite == avail ) { /* we just filled up the buffer, so we need to mark it as filled. */ ret = blioSetIsOutputFull( blio, true ); if( ret ) return ret; /* of course, in the meantime, we may have emptied the buffer, so so check for that, too, to avoid a race condition. */ if( PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) ) { blioSetIsOutputFull( blio, false ); if( ret ) return ret; } } #endif }
/* Report either paNoError or paOutputUnderflowed. */ /* may also want to report other errors, but this is non-standard. */ ret = blio->statusFlags & paOutputUnderflow;
/* report underflow only once: */ if( ret ) { OSAtomicAnd32( ~paOutputUnderflow, &blio->statusFlags ); ret = paOutputUnderflowed; }
return ret; }
/* * */ void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio ) { if( blio->outputRingBuffer.buffer ) { long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); while( avail != blio->outputRingBuffer.bufferSize ) { if( avail == 0 ) Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); } } }
signed long GetStreamReadAvailable( PaStream* stream ) { PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; VVDBUG(("GetStreamReadAvailable()\n"));
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) / ( blio->outputSampleSize * blio->outChan ); }
signed long GetStreamWriteAvailable( PaStream* stream ) { PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; VVDBUG(("GetStreamWriteAvailable()\n"));
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) / ( blio->outputSampleSize * blio->outChan ); }
--- NEW FILE: pa_mac_core.c --- /* * Implementation of the PortAudio API for Apple AUHAL * * 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. * * [...2199 lines suppressed...]
return stream->state == STOPPED ? 1 : 0; }
static PaError IsStreamActive( PaStream *s ) { PaMacCoreStream *stream = (PaMacCoreStream*)s; 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 ); }