From 9c0e19a3be2288db79e2502e5fa450c3e20a668d Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Fri, 9 May 2003 16:04:00 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r610, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=611 --- pd/portaudio/pa_mac_core/notes.txt | 14 +- pd/portaudio/pa_mac_core/pa_mac_core.c | 1514 ++++++++++++-------------------- 2 files changed, 547 insertions(+), 981 deletions(-) (limited to 'pd/portaudio/pa_mac_core') diff --git a/pd/portaudio/pa_mac_core/notes.txt b/pd/portaudio/pa_mac_core/notes.txt index 3b557d9a..c79b90e6 100644 --- a/pd/portaudio/pa_mac_core/notes.txt +++ b/pd/portaudio/pa_mac_core/notes.txt @@ -2,25 +2,25 @@ Notes on Core Audio Implementation of PortAudio by Phil Burk and Darren Gibbs -Document last updated October 18, 2002 +Document last updated March 20, 2002 WHAT WORKS Output with very low latency, <10 msec. Half duplex input or output. -Full duplex +Full duplex on the same CoreAudio device. The paFLoat32, paInt16, paInt8, paUInt8 sample formats. Pa_GetCPULoad() Pa_StreamTime() KNOWN BUGS OR LIMITATIONS -The iMic supports multiple sample rates. -But there is a bug when changing sample rates: - Run patest_record.c at rate A - it works. - Then run patest_record.c at rate B - it FAIL! - Then run patest_record.c again at rate B - it works! +We do not yet support simultaneous input and output on different +devices. Note that some CoreAudio devices like the Roland UH30 look +like one device but are actually two different CoreAudio devices. The +BuiltIn audio is typically one CoreAudio device. +Mono doesn't work. DEVICE MAPPING diff --git a/pd/portaudio/pa_mac_core/pa_mac_core.c b/pd/portaudio/pa_mac_core/pa_mac_core.c index de781d09..9a4b1488 100644 --- a/pd/portaudio/pa_mac_core/pa_mac_core.c +++ b/pd/portaudio/pa_mac_core/pa_mac_core.c @@ -1,5 +1,5 @@ /* - * $Id: pa_mac_core.c,v 1.8.4.3 2002/10/18 01:29:06 philburk Exp $ + * $Id: pa_mac_core.c,v 1.8 2002/04/12 18:07:20 philburk Exp $ * pa_mac_core.c * Implementation of PortAudio for Mac OS X Core Audio * @@ -7,18 +7,7 @@ * Latest Version at: http://www.portaudio.com * * Authors: Ross Bencina and Phil Burk - * Copyright (c) 1999-2002 Ross Bencina and Phil Burk - * - * Theory of Operation - * - * This code uses the HAL (Hardware Access Layer) of the Apple CoreAudio library. - * This is the layer closes to the hardware. - * The HAL layer only supports the native HW supported sample rates. - * So if the chip only supports 44100 Hz, then the HAL only supports 44100. - * To provide other rates we use the handy Apple AUConverter which provides - * sample rate conversion, mono-to-stereo conversion, and buffer size adaptation. - * - * License + * 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 @@ -54,28 +43,18 @@ audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices). 2.22.2002 - Stephane Letz - Explicit cast needed for compilation with Code Warrior 7 3.19.2002 - Phil Burk - Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file. - Return error if opened in mono mode cuz not supported. [Supported 10.12.2002] + Return error if opened in mono mode cuz not supported. Add support for Pa_GetCPULoad(); Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!) Check for invalid sample rates and return an error. - Check for getenv("PA_MIN_LATENCY_MSEC") to set latency externally. + Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally. Better error checking for invalid channel counts and invalid devices. 3.29.2002 - Phil Burk - Fixed Pa_GetCPULoad() for small buffers. 3.31.2002 - Phil Burk - Use getrusage() instead of gettimeofday() for CPU Load calculation. - 10.12.2002 - Phil Burk - Use AudioConverter to allow wide range of sample rates, and mono. - Use FIFO (from pablio/rinbuffer.h) so that we can pull data through converter. - Added PaOSX_FixVolumeScalar() to make iMic audible. - 10.17.2002 - Phil Burk - Support full duplex between two different devices. - Name internal functions PaOSX_* - Dumped useless PA_MIN_LATENCY_MSEC environment variable. - Use kAudioDevicePropertyStreamFormatMatch to determine max channels. TODO: -O- debug problem when changing sample rates on iMic -O- add support for paInt32 format -O- Why does iMic have grunge for the first second or two then clears up? -O- request notification when formats change or device unplugged -O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz? +O- how do mono output? +O- FIFO between input and output callbacks if different devices, like in pa_mac.c */ #include @@ -83,14 +62,10 @@ O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz? #include #include #include -#include -#include -#include #include "portaudio.h" #include "pa_host.h" #include "pa_trace.h" -#include "ringbuffer.h" /************************************************* Constants ********/ @@ -98,45 +73,37 @@ O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz? #define PA_TRACE_RUN (0) #define PA_TRACE_START_STOP (1) -#define PA_MIN_LATENCY_MSEC (1) +#define PA_MIN_LATENCY_MSEC (8) #define MIN_TIMEOUT_MSEC (1000) #define PRINT(x) { printf x; fflush(stdout); } -#define PRINT_ERR( msg, err ) PRINT(( msg ": error = 0x%0lX = '%s'\n", (err), ErrorToString(err)) ) -#define DBUG(x) /* PRINT(x) */ -#define DBUGBACK(x) /* if( sMaxBackgroundErrorMessages-- > 0 ) PRINT(x) */ -#define DBUGX(x) +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) /**/ +#define DBUGX(x) /* PRINT(x) /**/ // define value of isInput passed to CoreAudio routines #define IS_INPUT (true) #define IS_OUTPUT (false) -typedef struct PaHostInOut -{ - AudioDeviceID audioDeviceID; // CoreAudio specific ID - int bytesPerUserNativeBuffer; /* User buffer size in native host format. Depends on numChannels. */ - AudioConverterRef converter; - void *converterBuffer; -} PaHostInOut; - /************************************************************** * Structure for internal host specific stream data. * This is allocated on a per stream basis. */ typedef struct PaHostSoundControl { - PaHostInOut input; - PaHostInOut output; - AudioDeviceID primaryDeviceID; - Boolean usingSecondDevice; - int framesPerHostBuffer; - /* For sample rate, format conversion, or when using two devices. */ - RingBuffer ringBuffer; - char *ringBufferData; + AudioDeviceID pahsc_AudioDeviceID; // Must be the same for input and output for now. + /* Input -------------- */ + int pahsc_BytesPerUserNativeInputBuffer; /* native buffer size in bytes per user chunk */ + /* Output -------------- */ + int pahsc_BytesPerUserNativeOutputBuffer; /* native buffer size in bytes per user chunk */ + /* Init Time -------------- */ + int pahsc_FramesPerHostBuffer; + int pahsc_UserBuffersPerHostBuffer; /* For measuring CPU utilization. */ - struct rusage entryRusage; - double inverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ -} PaHostSoundControl; + struct rusage pahsc_EntryRusage; + double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ +} +PaHostSoundControl; /************************************************************** * Structure for internal extended device info. @@ -158,18 +125,14 @@ static int sNumOutputDevices = 0; static PaHostDeviceInfo *sDeviceInfos = NULL; static int sDefaultInputDeviceID = paNoDevice; static int sDefaultOutputDeviceID = paNoDevice; -static int sSavedHostError = 0; +static int sPaHostError = 0; + static int sNumCoreDevices = 0; static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs -static const double supportedSampleRateRange[] = { 8000.0, 96000.0 }; static const char sMapperSuffixInput[] = " - Input"; static const char sMapperSuffixOutput[] = " - Output"; -/* Debug support. */ -//static int sMaxBackgroundErrorMessages = 100; -//static int sCoverageCounter = 1; // used to check code coverage during validation - /* We index the input devices first, then the output devices. */ #define LOWEST_INPUT_DEVID (0) #define HIGHEST_INPUT_DEVID (sNumInputDevices - 1) @@ -180,64 +143,15 @@ static const char sMapperSuffixOutput[] = " - Output"; /************************************************* Prototypes **********/ -static PaError PaOSX_QueryDevices( void ); -static int PaOSX_ScanDevices( Boolean isInput ); -static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); -static PaDeviceID PaOSX_QueryDefaultInputDevice( void ); -static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ); -static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ); - -/**********************************************************************/ -/* OS X errors are 4 character ID that can be printed. - * Note that uses a static pad so result must be printed immediately. - */ -static OSStatus statusText[2] = { 0, 0 }; -static const char *ErrorToString( OSStatus err ) -{ - const char *str; - - switch (err) - { - case kAudioHardwareUnspecifiedError: - str = "kAudioHardwareUnspecifiedError"; - break; - case kAudioHardwareNotRunningError: - str = "kAudioHardwareNotRunningError"; - break; - case kAudioHardwareUnknownPropertyError: - str = "kAudioHardwareUnknownPropertyError"; - break; - case kAudioDeviceUnsupportedFormatError: - str = "kAudioDeviceUnsupportedFormatError"; - break; - case kAudioHardwareBadPropertySizeError: - str = "kAudioHardwareBadPropertySizeError"; - break; - case kAudioHardwareIllegalOperationError: - str = "kAudioHardwareIllegalOperationError"; - break; - default: - str = "Unknown CoreAudio Error!"; - statusText[0] = err; - str = (const char *)statusText; - break; - } +static PaError Pa_QueryDevices( void ); +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); - return str; -} +static int PaHost_ScanDevices( Boolean isInput ); +static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); -/**********************************************************************/ -static unsigned long RoundUpToNextPowerOf2( unsigned long n ) -{ - long numBits = 0; - if( ((n-1) & n) == 0) return n; /* Already Power of two. */ - while( n > 0 ) - { - n= n>>1; - numBits++; - } - return (1<past_DeviceData; if( pahsc == NULL ) return; /* Query user CPU timer for usage analysis and to prevent overuse of CPU. */ - getrusage( RUSAGE_SELF, &pahsc->entryRusage ); + getrusage( RUSAGE_SELF, &pahsc->pahsc_EntryRusage ); } static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB ) @@ -274,10 +188,10 @@ static void Pa_EndUsageCalculation( internalPortAudioStream *past ) if( getrusage( RUSAGE_SELF, ¤tRusage ) == 0 ) { - usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->entryRusage.ru_utime ); + usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->pahsc_EntryRusage.ru_utime ); /* Use inverse because it is faster than the divide. */ - newUsage = usecsElapsed * pahsc->inverseMicrosPerHostBuffer; + newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer; past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + (LOWPASS_COEFFICIENT_1 * newUsage); @@ -286,104 +200,92 @@ static void Pa_EndUsageCalculation( internalPortAudioStream *past ) /****************************************** END CPU UTILIZATION *******/ /************************************************************************/ -static PaDeviceID PaOSX_QueryDefaultInputDevice( void ) +static PaDeviceID Pa_QueryDefaultInputDevice( void ) { - OSStatus err = noErr; - UInt32 count; - int i; + OSStatus err = noErr; + UInt32 count; + int i; AudioDeviceID tempDeviceID = kAudioDeviceUnknown; - PaDeviceID defaultDeviceID = paNoDevice; + PaDeviceID defaultDeviceID = paNoDevice; // get the default output device for the HAL // it is required to pass the size of the data to be returned count = sizeof(tempDeviceID); err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID); - if (err != noErr) goto error; + if (err != noErr) goto Bail; // scan input devices to see which one matches this device defaultDeviceID = paNoDevice; for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ ) { - DBUG(("PaOSX_QueryDefaultInputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); + DBUG(("Pa_QueryDefaultInputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) { defaultDeviceID = i; break; } } -error: +Bail: return defaultDeviceID; } /************************************************************************/ -static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ) +static PaDeviceID Pa_QueryDefaultOutputDevice( void ) { - OSStatus err = noErr; - UInt32 count; - int i; + OSStatus err = noErr; + UInt32 count; + int i; AudioDeviceID tempDeviceID = kAudioDeviceUnknown; - PaDeviceID defaultDeviceID = paNoDevice; + PaDeviceID defaultDeviceID = paNoDevice; // get the default output device for the HAL // it is required to pass the size of the data to be returned count = sizeof(tempDeviceID); err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID); - if (err != noErr) goto error; + if (err != noErr) goto Bail; // scan output devices to see which one matches this device defaultDeviceID = paNoDevice; for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ ) { - DBUG(("PaOSX_QueryDefaultOutputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); + DBUG(("Pa_QueryDefaultOutputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) { defaultDeviceID = i; break; } } -error: +Bail: return defaultDeviceID; } /******************************************************************/ -static PaError PaOSX_QueryDevices( void ) +static PaError Pa_QueryDevices( void ) { OSStatus err = noErr; UInt32 outSize; Boolean outWritable; - int numBytes; + int numBytes; // find out how many Core Audio devices there are, if any - outSize = sizeof(outWritable); err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable); if (err != noErr) - { - PRINT_ERR("Couldn't get info about list of audio devices", err); - sSavedHostError = err; - return paHostError; - } - + ERR_RPT(("Couldn't get info about list of audio devices\n")); + // calculate the number of device available sNumCoreDevices = outSize / sizeof(AudioDeviceID); // Bail if there aren't any devices if (sNumCoreDevices < 1) - { - PRINT(("No Devices Available")); - return paHostError; - } - + ERR_RPT(("No Devices Available\n")); + // make space for the devices we are about to get sCoreDeviceIDs = (AudioDeviceID *)malloc(outSize); // get an array of AudioDeviceIDs err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sCoreDeviceIDs); if (err != noErr) - { - PRINT_ERR("Couldn't get list of audio device IDs", err); - sSavedHostError = err; - return paHostError; - } + ERR_RPT(("Couldn't get list of audio device IDs\n")); // Allocate structures to hold device info pointers. // There will be a maximum of two Pa devices per Core Audio device, input and/or output. @@ -393,22 +295,35 @@ static PaError PaOSX_QueryDevices( void ) // Scan all the Core Audio devices to see which support input and allocate a // PaHostDeviceInfo structure for each one. - PaOSX_ScanDevices( IS_INPUT ); + PaHost_ScanDevices( IS_INPUT ); sNumInputDevices = sNumPaDevices; // Now scan all the output devices. - PaOSX_ScanDevices( IS_OUTPUT ); + PaHost_ScanDevices( IS_OUTPUT ); sNumOutputDevices = sNumPaDevices - sNumInputDevices; // Figure out which of the devices that we scanned is the default device. - sDefaultInputDeviceID = PaOSX_QueryDefaultInputDevice(); - sDefaultOutputDeviceID = PaOSX_QueryDefaultOutputDevice(); + sDefaultInputDeviceID = Pa_QueryDefaultInputDevice(); + sDefaultOutputDeviceID = Pa_QueryDefaultOutputDevice(); return paNoError; } +/************************************************************************************/ +long Pa_GetHostError() +{ + return sPaHostError; +} + +/*************************************************************************/ +int Pa_CountDevices() +{ + if( sNumPaDevices <= 0 ) Pa_Initialize(); + return sNumPaDevices; +} + /*************************************************************************/ /* Allocate a string containing the device name. */ -static char *PaOSX_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) +static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) { OSStatus err = noErr; UInt32 outSize; @@ -424,18 +339,87 @@ static char *PaOSX_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) { err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, deviceName); if (err != noErr) - PRINT_ERR("Couldn't get audio device name", err); + ERR_RPT(("Couldn't get audio device name.\n")); } } return deviceName; } +/*************************************************************************/ +// An AudioStreamBasicDescription is passed in to query whether or not +// the format is supported. A kAudioDeviceUnsupportedFormatError will +// be returned if the format is not supported and kAudioHardwareNoError +// will be returned if it is supported. AudioStreamBasicDescription +// fields set to 0 will be ignored in the query, but otherwise values +// must match exactly. + +Boolean deviceDoesSupportFormat(AudioDeviceID deviceID, AudioStreamBasicDescription *desc, Boolean isInput ) +{ + OSStatus err = noErr; + UInt32 outSize; + + outSize = sizeof(*desc); + err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &outSize, desc); + + if (err == kAudioHardwareNoError) + return true; + else + return false; +} + +/*************************************************************************/ +// return an error string +char* coreAudioErrorString (int errCode ) +{ + char *str; + + switch (errCode) + { + case kAudioHardwareUnspecifiedError: + str = "kAudioHardwareUnspecifiedError"; + break; + case kAudioHardwareNotRunningError: + str = "kAudioHardwareNotRunningError"; + break; + case kAudioHardwareUnknownPropertyError: + str = "kAudioHardwareUnknownPropertyError"; + break; + case kAudioDeviceUnsupportedFormatError: + str = "kAudioDeviceUnsupportedFormatError"; + break; + case kAudioHardwareBadPropertySizeError: + str = "kAudioHardwareBadPropertySizeError"; + break; + case kAudioHardwareIllegalOperationError: + str = "kAudioHardwareIllegalOperationError"; + break; + default: + str = "Unknown CoreAudio Error!"; + break; + } + + return str; +} + +/************************************************************************* +** PaDeviceInfo structures have already been created +** so just return the pointer. +** +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + if( id < 0 || id >= sNumPaDevices ) + return NULL; + + return &sDeviceInfos[id].paInfo; +} + /************************************************************************* ** Scan all of the Core Audio devices to see which support input or output. ** Changes sNumDevices, and fills in sDeviceInfos. */ -static int PaOSX_ScanDevices( Boolean isInput ) +static int PaHost_ScanDevices( Boolean isInput ) { int coreDeviceIndex; int result; @@ -446,8 +430,8 @@ static int PaOSX_ScanDevices( Boolean isInput ) { // try to fill in next PaHostDeviceInfo hostDeviceInfo = &sDeviceInfos[sNumPaDevices]; - result = PaOSX_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput ); - DBUGX(("PaOSX_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, coreDeviceIndex )); + result = PaHost_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput ); + DBUG(("PaHost_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, hostDeviceInfo->audioDeviceID )); if( result > 0 ) { sNumPaDevices += 1; // bump global counter if we got one @@ -458,7 +442,6 @@ static int PaOSX_ScanDevices( Boolean isInput ) return numAdded; } - /************************************************************************* ** Try to fill in the device info for this device. ** Return 1 if a good device that PA can use. @@ -466,55 +449,59 @@ static int PaOSX_ScanDevices( Boolean isInput ) ** or return negative error. ** */ -static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) +static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) { - OSStatus err; - UInt32 outSize; + OSErr err; + int index; + UInt32 outSize; AudioStreamBasicDescription formatDesc; + Boolean result; AudioDeviceID devID; - PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; + double *sampleRates = NULL; /* non-const ptr */ + + PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; + double possibleSampleRates[] = {8000.0, 11025.0, 22050.0, 44100.0, 48000.0, 88200.0, 96000.0}; + int maxNumSampleRates = sizeof( possibleSampleRates ) / sizeof( double ); deviceInfo->structVersion = 1; deviceInfo->maxInputChannels = 0; deviceInfo->maxOutputChannels = 0; - - deviceInfo->sampleRates = supportedSampleRateRange; // because we use sample rate converter to get continuous rates deviceInfo->numSampleRates = -1; devID = sCoreDeviceIDs[ coreDeviceIndex ]; hostDeviceInfo->audioDeviceID = devID; - DBUG(("PaOSX_QueryDeviceInfo: coreDeviceIndex = %d, devID = %d, isInput = %d\n", - coreDeviceIndex, devID, isInput )); - // Get data format info from the device. - outSize = sizeof(formatDesc); - err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); - // This just may not be an appropriate device for input or output so leave quietly. - if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; - // Right now the Core Audio headers only define one formatID: LinearPCM - // Apparently LinearPCM must be Float32 for now. - if( (formatDesc.mFormatID == kAudioFormatLinearPCM) && - (formatDesc.mFormatFlags & kLinearPCMFormatFlagIsFloat) ) - { - deviceInfo->nativeSampleFormats = paFloat32; - } - else + // Figure out supported sample rates + // Make room in case device supports all rates. + sampleRates = (double*)PaHost_AllocateFastMemory( maxNumSampleRates * sizeof(double) ); + if( sampleRates == NULL ) return paInsufficientMemory; + + deviceInfo->sampleRates = sampleRates; + deviceInfo->numSampleRates = 0; + + // Loop through the possible sampling rates and check each to see if the device supports it. + for (index = 0; index < maxNumSampleRates; index ++) { - return paSampleFormatNotSupported; + memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); + formatDesc.mSampleRate = possibleSampleRates[index]; + result = deviceDoesSupportFormat( devID, &formatDesc, isInput ); + + if (result == true) + { + deviceInfo->numSampleRates += 1; + *sampleRates = possibleSampleRates[index]; + sampleRates++; + } } + // If no sample rates supported, then not a very good device. + if( deviceInfo->numSampleRates == 0 ) goto error; - // Determine maximum number of channels supported. - memset( &formatDesc, 0, sizeof(formatDesc)); - formatDesc.mChannelsPerFrame = 256; // FIXME - what about device with > 256 channels + // Get data format info from the device. outSize = sizeof(formatDesc); - err = AudioDeviceGetProperty( devID, 0, - isInput, kAudioDevicePropertyStreamFormatMatch, &outSize, &formatDesc); - if( err != noErr ) - { - PRINT_ERR("PaOSX_QueryDeviceInfo: Could not get device format match", err); - sSavedHostError = err; - return paHostError; - } + err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); + + // If no channels supported, then not a very good device. + if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; if( isInput ) { @@ -525,268 +512,205 @@ static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDevi deviceInfo->maxOutputChannels = formatDesc.mChannelsPerFrame; } + // FIXME - where to put current sample rate?: formatDesc.mSampleRate + + // Right now the Core Audio headers only define one formatID: LinearPCM + // Apparently LinearPCM must be Float32 for now. + switch (formatDesc.mFormatID) + { + case kAudioFormatLinearPCM: + deviceInfo->nativeSampleFormats = paFloat32; + + // FIXME - details about the format are in these flags. + // formatDesc.mFormatFlags + + // here are the possibilities + // kLinearPCMFormatFlagIsFloat // set for floating point, clear for integer + // kLinearPCMFormatFlagIsBigEndian // set for big endian, clear for little + // kLinearPCMFormatFlagIsSignedInteger // set for signed integer, clear for unsigned integer, + // only valid if kLinearPCMFormatFlagIsFloat is clear + // kLinearPCMFormatFlagIsPacked // set if the sample bits are packed as closely together as possible, + // clear if they are high or low aligned within the channel + // kLinearPCMFormatFlagIsAlignedHigh // set if the sample bits are placed + break; + + default: + deviceInfo->nativeSampleFormats = paFloat32; // FIXME + break; + } + // Get the device name - deviceInfo->name = PaOSX_DeviceNameFromID( devID, isInput ); + deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput ); return 1; error: + if( sampleRates != NULL ) free( sampleRates ); return 0; } -/**********************************************************************/ -static PaError PaOSX_MaybeQueryDevices( void ) +/************************************************************************* +** Returns recommended device ID. +** On the PC, the recommended device can be specified by the user by +** setting an environment variable. For example, to use device #1. +** +** set PA_RECOMMENDED_OUTPUT_DEVICE=1 +** +** The user should first determine the available device ID by using +** the supplied application "pa_devs". +*/ +#define PA_ENV_BUF_SIZE (32) +#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE") +#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE") + +static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName ) +{ +#if 0 + UInt32 hresult; + char envbuf[PA_ENV_BUF_SIZE]; + PaDeviceID recommendedID = paNoDevice; + + /* Let user determine default device by setting environment variable. */ + hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + recommendedID = atoi( envbuf ); + } + + return recommendedID; +#endif + return paNoDevice; +} + +static PaError Pa_MaybeQueryDevices( void ) { if( sNumPaDevices == 0 ) { - return PaOSX_QueryDevices(); + return Pa_QueryDevices(); } return 0; } -static char zeroPad[256] = { 0 }; - /********************************************************************** -** This is the proc that supplies the data to the AudioConverterFillBuffer call. -** We can pass back arbitrarily sized blocks so if the FIFO region is split -** just pass back the first half. +** Check for environment variable, else query devices and use result. */ -static OSStatus PaOSX_InputConverterCallbackProc (AudioConverterRef inAudioConverter, - UInt32* outDataSize, - void** outData, - void* inUserData) +PaDeviceID Pa_GetDefaultInputDeviceID( void ) { - internalPortAudioStream *past = (internalPortAudioStream *) inUserData; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - void *dataPtr1; - long size1; - void *dataPtr2; - long size2; - - /* Pass contiguous region from FIFO directly to converter. */ - RingBuffer_GetReadRegions( &pahsc->ringBuffer, *outDataSize, - &dataPtr1, &size1, &dataPtr2, &size2 ); - - if( size1 > 0 ) - { - *outData = dataPtr1; - *outDataSize = size1; - RingBuffer_AdvanceReadIndex( &pahsc->ringBuffer, size1 ); - DBUGX(("PaOSX_InputConverterCallbackProc: read %ld bytes from FIFO.\n", size1 )); - } - else + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME ); + if( result < 0 ) { - DBUGBACK(("PaOSX_InputConverterCallbackProc: got no data!\n")); - *outData = zeroPad; // Give it zero data to keep it happy. - *outDataSize = sizeof(zeroPad); + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultInputDeviceID; } - return noErr; + return result; } -/***************************************************************************** -** Get audio input, if any, from passed in buffer, or from converter or from FIFO -** and run PA callback. -*/ -static OSStatus PaOSX_LoadAndProcess( internalPortAudioStream *past, - void *inputBuffer, void *outputBuffer ) +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) { - OSStatus err = noErr; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - - if( past->past_StopSoon ) + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME ); + if( result < 0 ) { - if( outputBuffer ) - { - /* Clear remainder of audio buffer if we are waiting for stop. */ - AddTraceMessage("PaOSX_HandleInputOutput: zero rest of wave buffer ", i ); - memset( outputBuffer, 0, pahsc->output.bytesPerUserNativeBuffer ); - } - } - else - { - /* Do we need data from the converted input? */ - if( pahsc->input.converter != NULL ) - { - UInt32 size = pahsc->input.bytesPerUserNativeBuffer; - err = AudioConverterFillBuffer( - pahsc->input.converter, - PaOSX_InputConverterCallbackProc, - past, - &size, - pahsc->input.converterBuffer); - if( err != noErr ) return err; - inputBuffer = pahsc->input.converterBuffer; - } - /* Or should just get the data directly from the FIFO? */ - else if( pahsc->ringBufferData != NULL ) - { - if( RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= pahsc->input.bytesPerUserNativeBuffer) - { - RingBuffer_Read( &pahsc->ringBuffer, pahsc->input.converterBuffer, pahsc->input.bytesPerUserNativeBuffer ); - inputBuffer = pahsc->input.converterBuffer; - } - } - - /* Fill part of audio converter buffer by converting input to user format, - * calling user callback, then converting output to native format. */ - if( PaConvert_Process( past, inputBuffer, outputBuffer )) - { - past->past_StopSoon = 1; - } + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultOutputDeviceID; } - return err; + return result; } -/***************************************************************************** -** This is the proc that supplies the data to the AudioConverterFillBuffer call +/********************************************************************** +** Initialize Host dependant part of API. */ -static OSStatus PaOSX_OutputConverterCallbackProc (AudioConverterRef inAudioConverter, - UInt32* outDataSize, - void** outData, - void* inUserData) + +PaError PaHost_Init( void ) { - internalPortAudioStream *past = (internalPortAudioStream *) inUserData; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - - *outData = pahsc->output.converterBuffer; - *outDataSize = pahsc->output.bytesPerUserNativeBuffer; - - return PaOSX_LoadAndProcess ( past, pahsc->input.converterBuffer, pahsc->output.converterBuffer ); + return Pa_MaybeQueryDevices(); } /********************************************************************** ** Fill any available output buffers and use any available ** input buffers by calling user callback. -** Will set past->past_StopSoon if user callback indicates that it is finished. */ -static OSStatus PaOSX_HandleInputOutput( internalPortAudioStream *past, - const AudioBufferList* inInputData, - AudioBufferList* outOutputData ) +static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferList* inInputData, + AudioBufferList* outOutputData ) { - OSStatus err = noErr; - char *inputNativeBufferfPtr = NULL; - char *outputNativeBufferfPtr = NULL; - int i; + PaError result = 0; + char *inputNativeBufferfPtr = NULL; + char *outputNativeBufferfPtr = NULL; + int i; + int buffersProcessed = 0; + int done = 0; PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + + past->past_NumCallbacks += 1; + +#if PA_TRACE_RUN + AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks ); +#endif + + Pa_StartUsageCalculation( past ); /* If we are using output, then we need an empty output buffer. */ - if( outOutputData->mNumberBuffers > 0 ) + if( past->past_NumOutputChannels > 0 ) { outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData; } - if( inInputData->mNumberBuffers > 0 ) + /* If we are using input, then we need a full input buffer. */ + if( past->past_NumInputChannels > 0 ) { inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData; - - /* If there is a FIFO for input then write to it. */ - if( (pahsc->ringBufferData != NULL) && !pahsc->usingSecondDevice ) - { - long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); - long numBytes = inInputData->mBuffers[0].mDataByteSize; - if( numBytes <= writeRoom ) - { - RingBuffer_Write( &pahsc->ringBuffer, inputNativeBufferfPtr, numBytes ); - DBUGBACK(("PaOSX_HandleInputOutput: wrote %ld bytes to FIFO.\n", inInputData->mBuffers[0].mDataByteSize)); - } // FIXME else ??? - } - } - - if( pahsc->output.converter != NULL ) - { - /* Using output and input converter. */ - UInt32 size = outOutputData->mBuffers[0].mDataByteSize; - err = AudioConverterFillBuffer( - pahsc->output.converter, - PaOSX_OutputConverterCallbackProc, - past, - &size, - outputNativeBufferfPtr); - if( err != noErr ) - { - PRINT_ERR("PaOSX_HandleInputOutput: AudioConverterFillBuffer failed", err); - goto error; - } - } - else if( (pahsc->input.converter != NULL) && !pahsc->usingSecondDevice) - { - /* Using just an input converter. */ - /* Generate user buffers as long as we have a half full input FIFO. */ - long gotHalf = pahsc->ringBuffer.bufferSize / 2; - while( (RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= gotHalf) && - (past->past_StopSoon == 0) ) - { - err = PaOSX_LoadAndProcess ( past, NULL, outputNativeBufferfPtr ); - if( err != noErr ) goto error; - if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->output.bytesPerUserNativeBuffer; - } - } - else - { - /* No AUConverters used. */ - /* Each host buffer contains multiple user buffers so do them all now. */ - for( i=0; ipast_NumUserBuffers; i++ ) - { - err = PaOSX_LoadAndProcess ( past, inputNativeBufferfPtr, outputNativeBufferfPtr ); - if( err != noErr ) goto error; - if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->input.bytesPerUserNativeBuffer; - if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->output.bytesPerUserNativeBuffer; - } } - -error: - return err; -} -/****************************************************************** - * This callback is used when two separate devices are used for input and output. - * This often happens when using USB devices which present as two devices: input and output. - * It just writes its data to a FIFO so that it can be read by the main callback - * proc PaOSX_CoreAudioIOCallback(). - */ -static OSStatus PaOSX_CoreAudioInputCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, - void* contextPtr) -{ - internalPortAudioStream *past = (internalPortAudioStream *) contextPtr; - PaHostSoundControl *pahsc; - pahsc = (PaHostSoundControl *) past->past_DeviceData; - - /* If there is a FIFO for input then write to it. */ - if( pahsc->ringBufferData != NULL ) + buffersProcessed += 1; + + /* Each host buffer contains multiple user buffers so do them all now. */ + for( i=0; ipahsc_UserBuffersPerHostBuffer; i++ ) { - long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); - long numBytes = inInputData->mBuffers[0].mDataByteSize; - if( numBytes <= writeRoom ) + if( done ) { - RingBuffer_Write( &pahsc->ringBuffer, inInputData->mBuffers[0].mData, inInputData->mBuffers[0].mDataByteSize ); + if( outputNativeBufferfPtr ) + { + /* Clear remainder of wave buffer if we are waiting for stop. */ + AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i ); + memset( outputNativeBufferfPtr, 0, pahsc->pahsc_BytesPerUserNativeOutputBuffer ); + } } else { - DBUGBACK(("PaOSX_CoreAudioInputCallback: FIFO too full to write!\n")); - } + /* Convert 32 bit native data to user data and call user routine. */ + result = PaConvert_Process( past, inputNativeBufferfPtr, outputNativeBufferfPtr ); + if( result != 0) done = 1; + } + if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeInputBuffer; + if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeOutputBuffer; } - - return noErr; + + Pa_EndUsageCalculation( past ); + +#if PA_TRACE_RUN + AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed ); +#endif + + return (result != 0) ? result : done; } -/****************************************************************** - * This is the primary callback for CoreAudio. - * It can handle input and/or output for a single device. - * It takes input from CoreAudio, converts it and passes it to the - * PortAudio callback. Then takes the PA results and passes it back to CoreAudio. - */ -static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, +OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* contextPtr) { - OSStatus err = noErr; + + PaError result = 0; internalPortAudioStream *past; PaHostSoundControl *pahsc; past = (internalPortAudioStream *) contextPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; +// printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers); + /* Has someone asked us to abort by calling Pa_AbortStream()? */ if( past->past_StopNow ) { @@ -797,7 +721,7 @@ static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioT */ else if( past->past_StopSoon ) { - // FIXME - Pretend all done. Should wait for audio to play out but CoreAudio latency very low. + // FIXME - pretend all done past->past_IsActive = 0; /* Will cause thread to return. */ } else @@ -808,220 +732,63 @@ static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioT past->past_FrameCount = inOutputTime->mSampleTime; } - /* Measure CPU load. */ - Pa_StartUsageCalculation( past ); - past->past_NumCallbacks += 1; - /* Process full input buffer and fill up empty output buffers. */ - err = PaOSX_HandleInputOutput( past, inInputData, outOutputData ); - - Pa_EndUsageCalculation( past ); - } + if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0) + { + /* User callback has asked us to stop. */ +#if PA_TRACE_START_STOP + AddTraceMessage( "Pa_OutputThreadProc: TimeSlice() returned ", result ); +#endif + past->past_StopSoon = 1; /* Request that audio play out then stop. */ + result = paNoError; + } + } - // FIXME PaOSX_UpdateStreamTime( pahsc ); - if( err != 0 ) DBUG(("PaOSX_CoreAudioIOCallback: returns %ld.\n", err )); + // FIXME PaHost_UpdateStreamTime( pahsc ); - return err; + return result; } +#if 0 +static int PaHost_CalcTimeOut( internalPortAudioStream *past ) +{ + /* Calculate timeOut longer than longest time it could take to play all buffers. */ + int timeOut = (UInt32) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate); + if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC; + return timeOut; +} +#endif + + /*******************************************************************/ /* Attempt to set device sample rate. */ -static PaError PaOSX_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate ) +static PaError PaHost_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate ) { AudioStreamBasicDescription formatDesc; - PaError result = paNoError; OSStatus err; - UInt32 dataSize; - - // try to set to desired rate memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mSampleRate = sampleRate; err = AudioDeviceSetProperty( devID, 0, 0, isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - if (err != noErr) - { - result = paInvalidSampleRate; - - /* Could not set to desired rate so query for closest match. */ - DBUG(("PaOSX_SetSampleRate: couldn't set to %f. Try to find match.\n", sampleRate )); - dataSize = sizeof(formatDesc); - AudioDeviceGetProperty( devID, 0, - isInput, kAudioDevicePropertyStreamFormatMatch, &dataSize, &formatDesc); - formatDesc.mSampleRate = sampleRate; - err = AudioDeviceGetProperty( devID, 0, - isInput, kAudioDevicePropertyStreamFormatMatch, &dataSize, &formatDesc); - if (err == noErr) - { - /* Set to that matching rate. */ - sampleRate = formatDesc.mSampleRate; - DBUG(("PaOSX_SetSampleRate: match succeeded, set to %f instead\n", sampleRate )); - memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); - formatDesc.mSampleRate = sampleRate; - AudioDeviceSetProperty( devID, 0, 0, - isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - } - } - return result; -} - -/******************************************************************* - * Check volume level of device. If below threshold, then set to newLevel. - * Using volume instead of decibels because decibel range varies by device. - */ -static void PaOSX_FixVolumeScalars( AudioDeviceID devID, Boolean isInput, - int numChannels, double threshold, double newLevel ) -{ - OSStatus err = noErr; - UInt32 dataSize; - int iChannel; - -/* The master channel is 0. Left and right are channels 1 and 2. */ -/* Fix volume. */ - for( iChannel = 0; iChannel<=numChannels; iChannel++ ) - { - Float32 fdata32; - dataSize = sizeof( fdata32 ); - err = AudioDeviceGetProperty( devID, iChannel, isInput, - kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); - if( err == noErr ) - { - DBUG(("kAudioDevicePropertyVolumeScalar for channel %d = %f\n", iChannel, fdata32)); - if( fdata32 <= (Float32) threshold ) - { - dataSize = sizeof( fdata32 ); - fdata32 = (Float32) newLevel; - err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, - kAudioDevicePropertyVolumeScalar, dataSize, &fdata32 ); - if( err != noErr ) - { - PRINT(("Warning: audio volume is very low and could not be turned up.\n")); - } - else - { - PRINT(("Volume for audio channel %d was <= %4.2f so set to %4.2f by PortAudio!\n", - iChannel, threshold, newLevel )); - } - } - } - } -/* Unmute if muted. */ - for( iChannel = 0; iChannel<=numChannels; iChannel++ ) - { - UInt32 uidata32; - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, iChannel, isInput, - kAudioDevicePropertyMute, &dataSize, &uidata32 ); - if( err == noErr ) - { - DBUG(("uidata32 for channel %d = %ld\n", iChannel, uidata32)); - if( uidata32 == 1 ) // muted? - { - dataSize = sizeof( uidata32 ); - uidata32 = 0; // unmute - err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, - kAudioDevicePropertyMute, dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT(("Warning: audio is muted and could not be unmuted!\n")); - } - else - { - PRINT(("Audio channel %d was unmuted by PortAudio!\n", iChannel )); - } - } - } - } + if (err != kAudioHardwareNoError) return paInvalidSampleRate; + else return paNoError; } -#if 0 -static void PaOSX_DumpDeviceInfo( AudioDeviceID devID, Boolean isInput ) -{ - OSStatus err = noErr; - UInt32 dataSize; - UInt32 uidata32; - Float32 fdata32; - AudioValueRange audioRange; - - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyLatency, &dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading kAudioDevicePropertyLatency", err); - return; - } - PRINT(("kAudioDevicePropertyLatency = %d\n", (int)uidata32 )); - - dataSize = sizeof( fdata32 ); - err = AudioDeviceGetProperty( devID, 1, isInput, - kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading kAudioDevicePropertyVolumeScalar", err); - return; - } - PRINT(("kAudioDevicePropertyVolumeScalar = %f\n", fdata32 )); - - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferSize, &dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size", err); - return; - } - PRINT(("kAudioDevicePropertyBufferSize = %d bytes\n", (int)uidata32 )); - - dataSize = sizeof( audioRange ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferSizeRange, &dataSize, &audioRange ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size range", err); - return; - } - PRINT(("kAudioDevicePropertyBufferSizeRange = %g to %g bytes\n", audioRange.mMinimum, audioRange.mMaximum )); - - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferFrameSize, &dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size", err); - return; - } - PRINT(("kAudioDevicePropertyBufferFrameSize = %d frames\n", (int)uidata32 )); - - dataSize = sizeof( audioRange ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferFrameSizeRange, &dataSize, &audioRange ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size range", err); - return; - } - PRINT(("kAudioDevicePropertyBufferFrameSizeRange = %g to %g frames\n", audioRange.mMinimum, audioRange.mMaximum )); - - return; -} -#endif - /*******************************************************************/ -static PaError PaOSX_OpenInputDevice( internalPortAudioStream *past ) +PaError PaHost_OpenInputStream( internalPortAudioStream *past ) { PaHostSoundControl *pahsc; const PaHostDeviceInfo *hostDeviceInfo; PaError result = paNoError; + UInt32 bytesPerHostBuffer; UInt32 dataSize; OSStatus err = noErr; - int needConverter = 0; - double deviceRate = past->past_SampleRate; - - DBUG(("PaOSX_OpenInputDevice: -------------\n")); + int bytesPerInputFrame; pahsc = (PaHostSoundControl *) past->past_DeviceData; + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID)); if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) || (past->past_InputDeviceID > HIGHEST_INPUT_DEVID) ) { @@ -1029,134 +796,68 @@ static PaError PaOSX_OpenInputDevice( internalPortAudioStream *past ) } hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID]; - PaOSX_FixVolumeScalars( pahsc->input.audioDeviceID, IS_INPUT, - hostDeviceInfo->paInfo.maxInputChannels, 0.1, 0.9 ); - /* Try to set sample rate. */ - result = PaOSX_SetSampleRate( pahsc->input.audioDeviceID, IS_INPUT, past->past_SampleRate ); - if( result != paNoError ) - { - DBUG(("PaOSX_OpenInputDevice: Need converter for sample rate = %f\n", past->past_SampleRate )); - needConverter = 1; - result = paNoError; - } - else - { - DBUG(("PaOSX_OpenInputDevice: successfully set sample rate to %f\n", past->past_SampleRate )); - } + result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_INPUT, past->past_SampleRate ); + if( result != paNoError ) return result; - /* Try to set number of channels. */ - if( past->past_NumInputChannels > hostDeviceInfo->paInfo.maxInputChannels ) - { - return paInvalidChannelCount; /* Too many channels! */ - } - else if( past->past_NumInputChannels < hostDeviceInfo->paInfo.maxInputChannels ) + if( past->past_NumInputChannels != hostDeviceInfo->paInfo.maxInputChannels ) { - +#if 1 + return paInvalidChannelCount; // FIXME - how support mono? +#else +FIXME - should this be set on a stream basis? Is it possible to change? + /* Attempt to set number of channels. */ AudioStreamBasicDescription formatDesc; OSStatus err; memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mChannelsPerFrame = past->past_NumInputChannels; - err = AudioDeviceSetProperty( pahsc->input.audioDeviceID, 0, 0, + + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - if (err != noErr) + if (err != kAudioHardwareNoError) { - needConverter = 1; + result = paInvalidChannelCount; + goto error; } +#endif } - /* Try to set the I/O bufferSize of the device. */ - dataSize = sizeof(pahsc->framesPerHostBuffer); - err = AudioDeviceSetProperty( pahsc->input.audioDeviceID, 0, 0, IS_INPUT, - kAudioDevicePropertyBufferFrameSize, dataSize, - &pahsc->framesPerHostBuffer); + // calculate native buffer sizes in bytes + bytesPerInputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; + pahsc->pahsc_BytesPerUserNativeInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame; + bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame; + + // Change the bufferSize of the device! Is this per device or just for our stream? + dataSize = sizeof(UInt32); + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT, + kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); if( err != noErr ) { - DBUG(("PaOSX_OpenInputDevice: Need converter for buffer size = %d\n", pahsc->framesPerHostBuffer)); - needConverter = 1; + ERR_RPT(("Could not force buffer size!")); + result = paHostError; + goto error; } - - // setup PA conversion procedure + + // setup conversion procedure result = PaConvert_SetupInput( past, paFloat32 ); - - if( needConverter ) - { - AudioStreamBasicDescription sourceStreamFormat, destStreamFormat; - - /* Get source device format */ - dataSize = sizeof(sourceStreamFormat); - err = AudioDeviceGetProperty(pahsc->input.audioDeviceID, 0, IS_INPUT, - kAudioDevicePropertyStreamFormat, &dataSize, &sourceStreamFormat); - if( err != noErr ) - { - PRINT_ERR("PaOSX_OpenInputDevice: Could not get input device format", err); - sSavedHostError = err; - return paHostError; - } - deviceRate = sourceStreamFormat.mSampleRate; - DBUG(("PaOSX_OpenInputDevice: current device sample rate = %f\n", deviceRate )); - - /* Set target user format. */ - destStreamFormat = sourceStreamFormat; - destStreamFormat.mSampleRate = past->past_SampleRate; // sample rate of the user synthesis code - destStreamFormat.mChannelsPerFrame = past->past_NumInputChannels; // the number of channels in each frame - - err = AudioConverterNew ( - &sourceStreamFormat, - &destStreamFormat, - &pahsc->input.converter); - if( err != noErr ) - { - PRINT_ERR("Could not create input format converter", err); - sSavedHostError = err; - return paHostError; - } - } - - /* Allocate FIFO between Device callback and Converter callback so that device can push data - * and converter can pull data. - */ - if( needConverter || pahsc->usingSecondDevice ) - { - double sampleRateRatio; - long minSize, numBytes; - - /* Allocate an input buffer because we need it between the user callback and the converter. */ - pahsc->input.converterBuffer = PaHost_AllocateFastMemory( pahsc->input.bytesPerUserNativeBuffer ); - if( pahsc->input.converterBuffer == NULL ) - { - return paInsufficientMemory; - } - sampleRateRatio = deviceRate / past->past_SampleRate; - minSize = pahsc->input.bytesPerUserNativeBuffer * 4 * sampleRateRatio; - numBytes = RoundUpToNextPowerOf2( minSize ); - DBUG(("PaOSX_OpenInputDevice: FIFO numBytes = %ld\n", numBytes)); - pahsc->ringBufferData = PaHost_AllocateFastMemory( numBytes ); - if( pahsc->ringBufferData == NULL ) - { - return paInsufficientMemory; - } - RingBuffer_Init( &pahsc->ringBuffer, numBytes, pahsc->ringBufferData ); - // make it look full at beginning - RingBuffer_AdvanceWriteIndex( &pahsc->ringBuffer, numBytes ); - } - + return result; + +error: return result; } /*******************************************************************/ -static PaError PaOSX_OpenOutputDevice( internalPortAudioStream *past ) +PaError PaHost_OpenOutputStream( internalPortAudioStream *past ) { PaHostSoundControl *pahsc; const PaHostDeviceInfo *hostDeviceInfo; PaError result = paNoError; + UInt32 bytesPerHostBuffer; UInt32 dataSize; OSStatus err = noErr; - int needConverter = 0; + int bytesPerOutputFrame; - DBUG(("PaOSX_OpenOutputDevice: -------------\n")); - pahsc = (PaHostSoundControl *) past->past_DeviceData; DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID)); @@ -1165,141 +866,101 @@ static PaError PaOSX_OpenOutputDevice( internalPortAudioStream *past ) { return paInvalidDeviceId; } - hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID]; - - //PaOSX_DumpDeviceInfo( pahsc->output.audioDeviceID, IS_OUTPUT ); - PaOSX_FixVolumeScalars( pahsc->output.audioDeviceID, IS_OUTPUT, - hostDeviceInfo->paInfo.maxOutputChannels, 0.1, 0.9 ); - /* Try to set sample rate. */ - result = PaOSX_SetSampleRate( pahsc->output.audioDeviceID, IS_OUTPUT, past->past_SampleRate ); - if( result != paNoError ) - { - DBUG(("PaOSX_OpenOutputDevice: Need converter for sample rate = %f\n", past->past_SampleRate )); - needConverter = 1; - result = paNoError; - } - else - { - DBUG(("PaOSX_OpenOutputDevice: successfully set sample rate to %f\n", past->past_SampleRate )); - } + result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_OUTPUT, past->past_SampleRate ); + if( result != paNoError ) return result; - if( past->past_NumOutputChannels > hostDeviceInfo->paInfo.maxOutputChannels ) - { - return paInvalidChannelCount; /* Too many channels! */ - } - else + if( past->past_NumOutputChannels != hostDeviceInfo->paInfo.maxOutputChannels ) { +#if 1 + return paInvalidChannelCount; // FIXME - how support mono? +#else /* Attempt to set number of channels. */ AudioStreamBasicDescription formatDesc; OSStatus err; memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mChannelsPerFrame = past->past_NumOutputChannels; - err = AudioDeviceSetProperty( pahsc->output.audioDeviceID, 0, 0, + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + if (err != kAudioHardwareNoError) { - DBUG(("PaOSX_OpenOutputDevice: Need converter for num channels.\n")); - needConverter = 1; + result = paInvalidChannelCount; + goto error; } +#endif } - /* Change the I/O bufferSize of the device. */ - dataSize = sizeof(pahsc->framesPerHostBuffer); - err = AudioDeviceSetProperty( pahsc->output.audioDeviceID, 0, 0, IS_OUTPUT, - kAudioDevicePropertyBufferFrameSize, dataSize, - &pahsc->framesPerHostBuffer); + // calculate buffer sizes in bytes + bytesPerOutputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; + pahsc->pahsc_BytesPerUserNativeOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame; + bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame; + + // Change the bufferSize of the device! Is this per device or just for our stream? + dataSize = sizeof(bytesPerHostBuffer); + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT, + kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); if( err != noErr ) { - DBUG(("PaOSX_OpenOutputDevice: Need converter for buffer size = %d\n", pahsc->framesPerHostBuffer)); - needConverter = 1; + ERR_RPT(("Could not force buffer size!")); + result = paHostError; + goto error; } - + // setup conversion procedure result = PaConvert_SetupOutput( past, paFloat32 ); - if( needConverter ) - { - AudioStreamBasicDescription sourceStreamFormat, destStreamFormat; - DBUG(("PaOSX_OpenOutputDevice: using AUConverter!\n")); - /* Get target device format */ - dataSize = sizeof(destStreamFormat); - err = AudioDeviceGetProperty(pahsc->output.audioDeviceID, 0, IS_OUTPUT, - kAudioDevicePropertyStreamFormat, &dataSize, &destStreamFormat); - if( err != noErr ) - { - PRINT_ERR("PaOSX_OpenOutputDevice: Could not get output device format", err); - sSavedHostError = err; - return paHostError; - } + return result; - /* Set source user format. */ - sourceStreamFormat = destStreamFormat; - sourceStreamFormat.mSampleRate = past->past_SampleRate; // sample rate of the user synthesis code - sourceStreamFormat.mChannelsPerFrame = past->past_NumOutputChannels; // the number of channels in each frame - - /* Allocate an output buffer because we need it between the user callback and the converter. */ - pahsc->output.converterBuffer = PaHost_AllocateFastMemory( pahsc->output.bytesPerUserNativeBuffer ); - err = AudioConverterNew ( - &sourceStreamFormat, - &destStreamFormat, - &pahsc->output.converter); - if( err != noErr ) - { - PRINT_ERR("Could not create output format converter", err); - sSavedHostError = err; - return paHostError; - } - } - +error: return result; } +/*******************************************************************/ +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return pahsc->pahsc_FramesPerHostBuffer; +} + + /******************************************************************* * Determine how many User Buffers we can put into our CoreAudio stream buffer. * Uses: * past->past_FramesPerUserBuffer, etc. * Sets: * past->past_NumUserBuffers -* pahsc->framesPerHostBuffer -* pahsc->input.bytesPerUserNativeBuffer -* pahsc->output.bytesPerUserNativeBuffer +* pahsc->pahsc_UserBuffersPerHostBuffer +* pahsc->pahsc_FramesPerHostBuffer */ -static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ) +static void PaHost_CalcHostBufferSize( internalPortAudioStream *past ) { PaHostSoundControl *pahsc = ( PaHostSoundControl *)past->past_DeviceData; + unsigned int minNumUserBuffers; - // Determine number of user buffers based strictly on minimum reasonable buffer size. - // We ignore the Pa_OpenStream numBuffer parameter because CoreAudio has a big - // mix buffer and handles latency automatically. - past->past_NumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + // Determine number of user buffers based on minimum latency. + minNumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + // Compare to user requested number in user wants more than the minimum. + past->past_NumUserBuffers = ( minNumUserBuffers > past->past_NumUserBuffers ) ? + minNumUserBuffers : past->past_NumUserBuffers; + DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); + // For CoreAudio, we only have one Host buffer, so... + pahsc->pahsc_UserBuffersPerHostBuffer = past->past_NumUserBuffers; // Calculate size of CoreAudio buffer. - pahsc->framesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; + pahsc->pahsc_FramesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; - // calculate buffer sizes in bytes - pahsc->input.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * - Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; - pahsc->output.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * - Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; - - DBUG(("PaOSX_CalcNumHostBuffers: past_NumUserBuffers = %ld\n", past->past_NumUserBuffers )); - DBUG(("PaOSX_CalcNumHostBuffers: framesPerHostBuffer = %d\n", pahsc->framesPerHostBuffer )); - DBUG(("PaOSX_CalcNumHostBuffers: input.bytesPerUserNativeBuffer = %d\n", pahsc->input.bytesPerUserNativeBuffer )); - DBUG(("PaOSX_CalcNumHostBuffers: output.bytesPerUserNativeBuffer = %d\n", pahsc->output.bytesPerUserNativeBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer )); } -/*****************************************************************************/ -/************** Internal Host API ********************************************/ -/*****************************************************************************/ +/*******************************************************************/ PaError PaHost_OpenStream( internalPortAudioStream *past ) { PaError result = paNoError; PaHostSoundControl *pahsc; - Boolean useInput; - Boolean useOutput; /* Allocate and initialize host data. */ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl)); @@ -1310,53 +971,43 @@ PaError PaHost_OpenStream( internalPortAudioStream *past ) } memset( pahsc, 0, sizeof(PaHostSoundControl) ); past->past_DeviceData = (void *) pahsc; - pahsc->primaryDeviceID = kAudioDeviceUnknown; - pahsc->input.audioDeviceID = kAudioDeviceUnknown; - pahsc->output.audioDeviceID = kAudioDeviceUnknown; - - PaOSX_CalcHostBufferSize( past ); - - /* Setup constants for CPU load measurement. */ - pahsc->inverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->framesPerHostBuffer); - useOutput = (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0); - useInput = (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0); - - // Set device IDs - if( useOutput ) + // If we are using both input and out, then they must be on the same CoreAudio device, + // until we implement a FIFO between two devices. + if( (past->past_OutputDeviceID != paNoDevice) && (past->past_InputDeviceID != paNoDevice) ) { - pahsc->output.audioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; - pahsc->primaryDeviceID = pahsc->output.audioDeviceID; - if( useInput ) + AudioDeviceID inputID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + AudioDeviceID outputID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + if( inputID != outputID ) { - pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; - if( pahsc->input.audioDeviceID != pahsc->primaryDeviceID ) - { - pahsc->usingSecondDevice = TRUE; // Use two separate devices! - } + ERR_RPT(("PortAudio: input and output must use same CoreAudio device!\n")); + return paInvalidDeviceId; } } - else + + PaHost_CalcHostBufferSize( past ); + { - /* Just input, not output. */ - pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; - pahsc->primaryDeviceID = pahsc->input.audioDeviceID; + int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate); + PRINT(("PortAudio on OS X - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency )); } - DBUG(("outputDeviceID = %ld\n", pahsc->output.audioDeviceID )); - DBUG(("inputDeviceID = %ld\n", pahsc->input.audioDeviceID )); - DBUG(("primaryDeviceID = %ld\n", pahsc->primaryDeviceID )); + /* Setup constants for CPU load measurement. */ + pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer); + /* ------------------ OUTPUT */ - if( useOutput ) + if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) ) { - result = PaOSX_OpenOutputDevice( past ); + pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + result = PaHost_OpenOutputStream( past ); if( result < 0 ) goto error; } /* ------------------ INPUT */ - if( useInput ) + if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) ) { - result = PaOSX_OpenInputDevice( past ); + pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + result = PaHost_OpenInputStream( past ); if( result < 0 ) goto error; } @@ -1390,49 +1041,21 @@ PaError PaHost_StartEngine( internalPortAudioStream *past ) past->past_StopNow = 0; past->past_IsActive = 1; -/* If full duplex and using two separate devices then start input device. */ - if( pahsc->usingSecondDevice ) - { - // Associate an IO proc with the device and pass a pointer to the audio data context - err = AudioDeviceAddIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback, past); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc secondary failed", err ); - goto error; - } - - // start playing sound through the device - err = AudioDeviceStart(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceStart secondary failed", err ); - PRINT(("The program may succeed if you run it again!\n")); - goto error; - } - } - // Associate an IO proc with the device and pass a pointer to the audio data context - err = AudioDeviceAddIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback, past); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc primary failed", err ); - goto error; - } + err = AudioDeviceAddIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc, past); + if (err != noErr) goto error; // start playing sound through the device - err = AudioDeviceStart(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceStart primary failed", err ); - PRINT(("The program may succeed if you run it again!\n")); - goto error; - } - + err = AudioDeviceStart(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto error; return result; +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result ); +#endif + error: - sSavedHostError = err; - return paHostError; + return paHostError; // FIXME - save host error } /*************************************************************************/ @@ -1454,29 +1077,16 @@ PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) #endif // FIXME - we should ask proc to stop instead of stopping abruptly - err = AudioDeviceStop(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); - if (err != noErr) - { - goto error; - } + err = AudioDeviceStop(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto Bail; - err = AudioDeviceRemoveIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); - if (err != noErr) goto error; - -/* If full duplex and using two separate devices then start input device. */ - if( pahsc->usingSecondDevice ) - { - err = AudioDeviceStop(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); - if (err != noErr) goto error; - err = AudioDeviceRemoveIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); - if (err != noErr) goto error; - } + err = AudioDeviceRemoveIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto Bail; return paNoError; -error: - sSavedHostError = err; - return paHostError; +Bail: + return paHostError; // FIXME - save err } /*************************************************************************/ @@ -1499,47 +1109,70 @@ PaError PaHost_CloseStream( internalPortAudioStream *past ) if( past == NULL ) return paBadStreamPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; if( pahsc == NULL ) return paNoError; - - //PaOSX_DumpDeviceInfo( sDeviceInfos[past->past_OutputDeviceID].audioDeviceID, IS_OUTPUT ); #if PA_TRACE_START_STOP AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); #endif - if( pahsc->output.converterBuffer != NULL ) - { - PaHost_FreeFastMemory( pahsc->output.converterBuffer, pahsc->output.bytesPerUserNativeBuffer ); - } - if( pahsc->input.converterBuffer != NULL ) - { - PaHost_FreeFastMemory( pahsc->input.converterBuffer, pahsc->input.bytesPerUserNativeBuffer ); - } - if( pahsc->ringBufferData != NULL ) - { - PaHost_FreeFastMemory( pahsc->ringBufferData, pahsc->ringBuffer.bufferSize ); - } - if( pahsc->output.converter != NULL ) - { - verify_noerr(AudioConverterDispose (pahsc->output.converter)); - } - if( pahsc->input.converter != NULL ) - { - verify_noerr(AudioConverterDispose (pahsc->input.converter)); - } - free( pahsc ); past->past_DeviceData = NULL; return paNoError; } -/********************************************************************** -** Initialize Host dependant part of API. +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the cshrc file. */ -PaError PaHost_Init( void ) +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") + +#if 1 +int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) { - return PaOSX_MaybeQueryDevices(); + int minBuffers; + int denominator; + int minLatencyMsec = PA_MIN_LATENCY_MSEC; + char *minLatencyText = getenv(PA_LATENCY_ENV_NAME); + if( minLatencyText != NULL ) + { + PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText )); + minLatencyMsec = atoi( minLatencyText ); + if( minLatencyMsec < 1 ) minLatencyMsec = 1; + else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000; + } + + denominator = 1000.0 * framesPerBuffer; + minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); + if( minBuffers < 1 ) minBuffers = 1; + return minBuffers; } +#else +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. +*/ +int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +{ + int minUserBuffers; + int minFramesPerHostBuffer; + + // Calculate minimum and maximum sizes based on timing and sample rate. + minFramesPerHostBuffer = (int) (PA_MIN_LATENCY_MSEC * sampleRate / 1000.0); + // round up to nearest multiple of 8 + minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7; + DBUG(("Pa_GetMinNumBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer )); + + minUserBuffers = (minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer; + if( minUserBuffers < 1 ) minUserBuffers = 1; + + return minUserBuffers; +} +#endif /************************************************************************* ** Cleanup device info. @@ -1552,10 +1185,8 @@ PaError PaHost_Term( void ) { for( i=0; ipast_IsActive; } -/*******************************************************************/ -PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - return pahsc->framesPerHostBuffer; -} - -/*****************************************************************************/ -/************** External User API ********************************************/ -/*****************************************************************************/ - -/********************************************************************** -** Query devices and use result. -*/ -PaDeviceID Pa_GetDefaultInputDeviceID( void ) -{ - PaError result = PaOSX_MaybeQueryDevices(); - if( result < 0 ) return result; - return sDefaultInputDeviceID; -} - -PaDeviceID Pa_GetDefaultOutputDeviceID( void ) -{ - PaError result = PaOSX_MaybeQueryDevices(); - if( result < 0 ) return result; - return sDefaultOutputDeviceID; -} - - -/************************************************************************* -** Determine minimum number of buffers required for this host based -** on minimum latency. Because CoreAudio manages latency, this just sets -** a reasonable small buffer size. -*/ -int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) -{ - int minBuffers; - double denominator; - int minLatencyMsec = PA_MIN_LATENCY_MSEC; - denominator = 1000.0 * framesPerBuffer; - minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); - if( minBuffers < 1 ) minBuffers = 1; - return minBuffers; -} - -/*************************************************************************/ -void Pa_Sleep( long msec ) -{ - usleep( msec * 1000 ); -} - /*************************************************************************/ PaTimestamp Pa_StreamTime( PortAudioStream *stream ) { @@ -1659,37 +1252,10 @@ PaTimestamp Pa_StreamTime( PortAudioStream *stream ) if( past == NULL ) return paBadStreamPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; - AudioDeviceGetCurrentTime(pahsc->primaryDeviceID, &timeStamp); + AudioDeviceGetCurrentTime(pahsc->pahsc_AudioDeviceID, &timeStamp); streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ? timeStamp.mSampleTime : past->past_FrameCount; return streamTime; } - -/************************************************************************************/ -long Pa_GetHostError() -{ - return sSavedHostError; -} - -/*************************************************************************/ -int Pa_CountDevices() -{ - if( sNumPaDevices <= 0 ) Pa_Initialize(); - return sNumPaDevices; -} - -/************************************************************************* -** PaDeviceInfo structures have already been created -** so just return the pointer. -** -*/ -const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) -{ - if( id < 0 || id >= sNumPaDevices ) - return NULL; - - return &sDeviceInfos[id].paInfo; -} - -- cgit v1.2.1