aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/pa_mac_core
diff options
context:
space:
mode:
authorGuenter Geiger <ggeiger@users.sourceforge.net>2003-05-09 16:04:00 +0000
committerGuenter Geiger <ggeiger@users.sourceforge.net>2003-05-09 16:04:00 +0000
commit9c0e19a3be2288db79e2502e5fa450c3e20a668d (patch)
treeca97ce615e037a533304fc4660dcf372ca3b9cd6 /pd/portaudio/pa_mac_core
parentef50dd62804d54af7da18d8bd8413c0dccd729b8 (diff)
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
Diffstat (limited to 'pd/portaudio/pa_mac_core')
-rw-r--r--pd/portaudio/pa_mac_core/notes.txt14
-rw-r--r--pd/portaudio/pa_mac_core/pa_mac_core.c1512
2 files changed, 546 insertions, 980 deletions
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 <CoreServices/CoreServices.h>
@@ -83,14 +62,10 @@ O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz?
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/DefaultAudioOutput.h>
-#include <AudioToolbox/AudioConverter.h>
#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<<numBits);
-}
+static PaDeviceID Pa_QueryDefaultInputDevice( void );
+static PaDeviceID Pa_QueryDefaultOutputDevice( void );
+static void PaHost_CalcHostBufferSize( internalPortAudioStream *past );
/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
static void Pa_StartUsageCalculation( internalPortAudioStream *past )
@@ -245,7 +159,7 @@ static void Pa_StartUsageCalculation( internalPortAudioStream *past )
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->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, &currentRusage ) == 0 )
{
- usecsElapsed = SubtractTime_AminusB( &currentRusage.ru_utime, &pahsc->entryRusage.ru_utime );
+ usecsElapsed = SubtractTime_AminusB( &currentRusage.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 )
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+ if( result < 0 )
{
- *outData = dataPtr1;
- *outDataSize = size1;
- RingBuffer_AdvanceReadIndex( &pahsc->ringBuffer, size1 );
- DBUGX(("PaOSX_InputConverterCallbackProc: read %ld bytes from FIFO.\n", size1 ));
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultInputDeviceID;
}
- else
- {
- DBUGBACK(("PaOSX_InputConverterCallbackProc: got no data!\n"));
- *outData = zeroPad; // Give it zero data to keep it happy.
- *outDataSize = sizeof(zeroPad);
- }
- 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 )
- {
- 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
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+ if( result < 0 )
{
- /* 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; i<past->past_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; i<pahsc->pahsc_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 result;
+}
- return err;
+#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 )
+{
+ 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 )
{
- return PaOSX_MaybeQueryDevices();
+ 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; i<sNumPaDevices; i++ )
{
- if( sDeviceInfos[i].paInfo.name != NULL )
- {
- free( (char*)sDeviceInfos[i].paInfo.name );
- }
+ if( sDeviceInfos[i].paInfo.name != NULL ) free( (char*)sDeviceInfos[i].paInfo.name );
+ if( sDeviceInfos[i].paInfo.sampleRates != NULL ) free( (void*)sDeviceInfos[i].paInfo.sampleRates );
}
free( sDeviceInfos );
sDeviceInfos = NULL;
@@ -1565,6 +1196,19 @@ PaError PaHost_Term( void )
return paNoError;
}
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+#if 0
+ struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select( 0, NULL, NULL, NULL, &timeout );
+#else
+ usleep( msec * 1000 );
+#endif
+}
+
/*************************************************************************
* Allocate memory that can be accessed in real-time.
* This may need to be held in physical memory so that it is not
@@ -1573,7 +1217,7 @@ PaError PaHost_Term( void )
*/
void *PaHost_AllocateFastMemory( long numBytes )
{
- void *addr = malloc( numBytes ); /* FIXME - do we need physical memory, not virtual memory? */
+ void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
if( addr != NULL ) memset( addr, 0, numBytes );
return addr;
}
@@ -1598,57 +1242,6 @@ PaError PaHost_StreamActive( internalPortAudioStream *past )
return (PaError) past->past_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;
-}
-