Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file.

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_LATEWNCY_MSEC") to set latency externally.
Better error checking for invalid channel counts and invalid devices.
This commit is contained in:
philburk 2002-03-21 00:39:42 +00:00
commit b47db27181
2 changed files with 218 additions and 127 deletions

View file

@ -2,14 +2,16 @@ Notes on Core Audio Implementation of PortAudio
by Phil Burk and Darren Gibbs
Document last updated Feb 7, 2002
Document last updated March 20, 2002
WHAT WORKS
Output with very low latency, <10 msec.
Half duplex input or output.
Full duplex on the same CoreAudio device.
The paFLoat32 sample format.
The paFLoat32, paInt16, paInt8, paUInt8 sample formats.
Pa_GetCPULoad()
Pa_StreamTime()
KNOWN BUGS OR LIMITATIONS
@ -18,10 +20,7 @@ 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.
User formats other than the native paFloat32 are not yet supported.
If you request a non-supported sample rate then you just get a
supported rate with no error messages.
Mono doesn't work.
DEVICE MAPPING

View file

@ -39,29 +39,32 @@
12.7.2001 - Gord Peters - Tweaks to compile on PA V17 and OS X 10.1
2.7.2002 - Darren and Phil - fixed isInput so GetProperty works better,
fixed device queries for numChannels and sampleRates,
one CoreAudio device now maps to separate input and output PaDevices,
audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices).
one CoreAudio device now maps to separate input and output PaDevices,
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.
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_LATEWNCY_MSEC") to set latency externally.
Better error checking for invalid channel counts and invalid devices.
TODO:
O- conversion for other user formats besides paFloat32
O- how do mono output?
O- FIFO between input and output callbacks if different devices, like in pa_mac.c
O- debug mono output
O- error check for unsupported sample rates
O- BUG: if ask for 22050 when only 44100 supported then get 44100 and no error message
*/
*/
#include <CoreServices/CoreServices.h>
#include <CoreAudio/CoreAudio.h>
#include <sys/time.h>
#include <unistd.h>
#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"
/************************************************* Constants ********/
/* Switches for debugging. */
#define PA_SIMULATE_UNDERFLOW (0) /* Set to one to force an underflow of the output buffer. */
/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
#define PA_TRACE_RUN (0)
@ -75,8 +78,8 @@ O- BUG: if ask for 22050 when only 44100 supported then get 44100 and no error m
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */
// define value of isInput passed to CodeAudio routines
#define IS_INPUT (true)
// define value of isInput passed to CoreAudio routines
#define IS_INPUT (true)
#define IS_OUTPUT (false)
/**************************************************************
@ -87,24 +90,15 @@ typedef struct PaHostSoundControl
{
AudioDeviceID pahsc_AudioDeviceID; // Must be the same for input and output for now.
/* Input -------------- */
int pahsc_BytesPerHostInputBuffer;
int pahsc_BytesPerUserInputBuffer; /* native buffer size in bytes */
int pahsc_BytesPerUserNativeInputBuffer; /* native buffer size in bytes per user chunk */
/* Output -------------- */
int pahsc_BytesPerHostOutputBuffer;
int pahsc_BytesPerUserOutputBuffer; /* native buffer size in bytes */
/* Run Time -------------- */
PaTimestamp pahsc_FramesPlayed;
long pahsc_LastPosition; /* used to track frames played. */
/* For measuring CPU utilization. */
// LARGE_INTEGER pahsc_EntryCount;
// LARGE_INTEGER pahsc_LastExitCount;
int pahsc_BytesPerUserNativeOutputBuffer; /* native buffer size in bytes per user chunk */
/* Init Time -------------- */
int pahsc_FramesPerHostBuffer;
int pahsc_UserBuffersPerHostBuffer;
// CRITICAL_SECTION pahsc_StreamLock; /* Mutext to prevent threads from colliding. */
int pahsc_StreamLockInited;
int pahsc_UserBuffersPerHostBuffer;
/* For measuring CPU utilization. */
struct timeval pahsc_EntryTime;
double pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */
}
PaHostSoundControl;
@ -136,10 +130,13 @@ static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs
static const char sMapperSuffixInput[] = " - Input";
static const char sMapperSuffixOutput[] = " - Output";
/************************************************* Macros ********/
/* We index the input devices first, then the output devices. */
#define LOWEST_INPUT_DEVID (0)
#define HIGHEST_INPUT_DEVID (sNumInputDevices - 1)
#define LOWEST_OUTPUT_DEVID (sNumInputDevices)
#define HIGHEST_OUTPUT_DEVID (sNumPaDevices - 1)
/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
/************************************************* Macros ********/
/************************************************* Prototypes **********/
@ -159,46 +156,44 @@ static void Pa_StartUsageCalculation( internalPortAudioStream *past )
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
/* Query system timer for usage analysis and to prevent overuse of CPU. */
// QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
gettimeofday( &pahsc->pahsc_EntryTime, NULL );
}
static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
{
long secs = timeA->tv_sec - timeB->tv_sec;
long usecs = secs * 1000000;
usecs += (timeA->tv_usec - timeB->tv_usec);
return usecs;
}
/******************************************************************************
** Measure fractional CPU load based on real-time it took to calculate
** buffers worth of output.
*/
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
{
#if 0
// LARGE_INTEGER CurrentCount = { 0, 0 };
// LONGLONG InsideCount;
// LONGLONG TotalCount;
/*
** Measure CPU utilization during this callback. Note that this calculation
** assumes that we had the processor the whole time.
*/
#define LOWPASS_COEFFICIENT_0 (0.9)
struct timeval currentTime;
long usecsElapsed;
double newUsage;
#define LOWPASS_COEFFICIENT_0 (0.95)
#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
if( QueryPerformanceCounter( &CurrentCount ) )
if( gettimeofday( &currentTime, NULL ) == 0 )
{
if( past->past_IfLastExitValid )
{
InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
TotalCount = CurrentCount.QuadPart - pahsc->pahsc_LastExitCount.QuadPart;
/* Low pass filter the result because sometimes we get called several times in a row.
* That can cause the TotalCount to be very low which can cause the usage to appear
* unnaturally high. So we must filter numerator and denominator separately!!!
*/
past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
(LOWPASS_COEFFICIENT_1 * InsideCount));
past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
(LOWPASS_COEFFICIENT_1 * TotalCount));
past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
}
pahsc->pahsc_LastExitCount = CurrentCount;
past->past_IfLastExitValid = 1;
usecsElapsed = SubtractTime_AminusB( &currentTime, &pahsc->pahsc_EntryTime );
/* Use inverse because it is faster than the divide. */
newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
(LOWPASS_COEFFICIENT_1 * newUsage);
}
#endif
}
/****************************************** END CPU UTILIZATION *******/
/************************************************************************/
static PaDeviceID Pa_QueryDefaultInputDevice( void )
@ -211,12 +206,13 @@ static PaDeviceID Pa_QueryDefaultInputDevice( void )
// get the default output device for the HAL
// it is required to pass the size of the data to be returned
count = sizeof(AudioDeviceID);
count = sizeof(tempDeviceID);
err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID);
if (err != noErr) goto Bail;
// scan input devices to see which one matches this device
defaultDeviceID = paNoDevice;
for( i=0; i<sNumInputDevices; i++ )
for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ )
{
DBUG(("Pa_QueryDefaultInputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID ));
if( sDeviceInfos[i].audioDeviceID == tempDeviceID )
@ -240,12 +236,13 @@ static PaDeviceID Pa_QueryDefaultOutputDevice( void )
// get the default output device for the HAL
// it is required to pass the size of the data to be returned
count = sizeof(AudioDeviceID);
count = sizeof(tempDeviceID);
err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID);
if (err != noErr) goto Bail;
// scan input devices to see which one matches this device
// scan output devices to see which one matches this device
defaultDeviceID = paNoDevice;
for( i=sNumInputDevices; i<sNumPaDevices; i++ )
for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ )
{
DBUG(("Pa_QueryDefaultOutputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID ));
if( sDeviceInfos[i].audioDeviceID == tempDeviceID )
@ -258,8 +255,7 @@ Bail:
return defaultDeviceID;
}
/****************************************** END CPU UTILIZATION *******/
/******************************************************************/
static PaError Pa_QueryDevices( void )
{
OSStatus err = noErr;
@ -301,6 +297,7 @@ static PaError Pa_QueryDevices( void )
PaHost_ScanDevices( IS_OUTPUT );
sNumOutputDevices = sNumPaDevices - sNumInputDevices;
// Figure out which of the devices that we scanned is the default device.
sDefaultInputDeviceID = Pa_QueryDefaultInputDevice();
sDefaultOutputDeviceID = Pa_QueryDefaultOutputDevice();
@ -321,14 +318,14 @@ int Pa_CountDevices()
}
/*************************************************************************/
/* Allocate a string containing the device name. */
char *deviceNameFromID(AudioDeviceID deviceID, Boolean isInput )
static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput )
{
OSStatus err = noErr;
UInt32 outSize;
Boolean outWritable;
char *deviceName = nil;
// query size of name
err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, &outWritable);
if (err == noErr)
@ -345,14 +342,6 @@ char *deviceNameFromID(AudioDeviceID deviceID, Boolean isInput )
return deviceName;
}
/*************************************************************************/
OSStatus deviceDataFormatFromID(AudioDeviceID deviceID , AudioStreamBasicDescription *desc, Boolean isInput )
{
UInt32 outSize = sizeof(*desc);
return AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, desc);
}
/*************************************************************************/
// An AudioStreamBasicDescription is passed in to query whether or not
// the format is supported. A kAudioDeviceUnsupportedFormatError will
@ -460,6 +449,7 @@ static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDev
{
OSErr err;
int index;
UInt32 outSize;
AudioStreamBasicDescription formatDesc;
Boolean result;
AudioDeviceID devID;
@ -503,7 +493,9 @@ static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDev
if( deviceInfo->numSampleRates == 0 ) goto error;
// Get data format info from the device.
err = deviceDataFormatFromID(devID, &formatDesc, isInput );
outSize = sizeof(formatDesc);
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;
@ -543,9 +535,8 @@ static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDev
break;
}
// Get the device name
deviceInfo->name = deviceNameFromID( devID, isInput );
deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput );
return 1;
error:
@ -630,9 +621,6 @@ PaDeviceID Pa_GetDefaultOutputDeviceID( void )
PaError PaHost_Init( void )
{
#if PA_SIMULATE_UNDERFLOW
PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
#endif
return Pa_MaybeQueryDevices();
}
@ -644,8 +632,8 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
AudioBufferList* outOutputData )
{
PaError result = 0;
char *inBufPtr;
char *outBufPtr;
char *inputNativeBufferfPtr;
char *outputNativeBufferfPtr;
int gotInput = 0;
int gotOutput = 0;
int i;
@ -656,13 +644,6 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
past->past_NumCallbacks += 1;
#if PA_SIMULATE_UNDERFLOW
if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
{
Sleep(UNDER_SLEEP_FOR);
}
#endif
#if PA_TRACE_RUN
AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks );
#endif
@ -673,16 +654,16 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
gotOutput = 0;
if( past->past_NumOutputChannels > 0 )
{
outBufPtr = (char*)outOutputData->mBuffers[0].mData,
outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData,
gotOutput = 1;
}
/* If we are using input, then we need a full input buffer. */
gotInput = 0;
inBufPtr = NULL;
inputNativeBufferfPtr = NULL;
if( past->past_NumInputChannels > 0 )
{
inBufPtr = (char*)inInputData->mBuffers[0].mData,
inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData,
gotInput = 1;
}
@ -697,17 +678,17 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
{
/* Clear remainder of wave buffer if we are waiting for stop. */
AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
memset( outBufPtr, 0, pahsc->pahsc_BytesPerUserOutputBuffer );
memset( outputNativeBufferfPtr, 0, pahsc->pahsc_BytesPerUserNativeOutputBuffer );
}
}
else
{
/* Convert 16 bit native data to user data and call user routine. */
result = Pa_CallConvertFloat32( past, (float *) inBufPtr, (float *) outBufPtr );
/* Convert 32 bit native data to user data and call user routine. */
result = PaConvert_Process( past, inputNativeBufferfPtr, outputNativeBufferfPtr );
if( result != 0) done = 1;
}
if( gotInput ) inBufPtr += pahsc->pahsc_BytesPerUserInputBuffer;
if( gotOutput) outBufPtr += pahsc->pahsc_BytesPerUserOutputBuffer;
if( gotInput ) inputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeInputBuffer;
if( gotOutput) outputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeOutputBuffer;
}
Pa_EndUsageCalculation( past );
@ -731,7 +712,7 @@ OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow,
past = (internalPortAudioStream *) contextPtr;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
// Just Curious: printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers);
// 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 )
@ -748,6 +729,12 @@ OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow,
}
else
{
/* use time stamp from CoreAudio if valid */
if( inOutputTime->mFlags & kAudioTimeStampSampleTimeValid)
{
past->past_FrameCount = inOutputTime->mSampleTime;
}
/* Process full input buffer and fill up empty output buffers. */
if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0)
{
@ -786,21 +773,43 @@ PaError PaHost_OpenInputStream( internalPortAudioStream *past )
int bytesPerInputFrame;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
if( (past->past_InputDeviceID < 0) || (past->past_InputDeviceID >= sNumPaDevices) )
if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) ||
(past->past_InputDeviceID > HIGHEST_INPUT_DEVID) )
{
return paInvalidDeviceId;
}
hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID];
// calculate buffer sizes in bytes
bytesPerInputFrame = Pa_GetSampleSize(past->past_InputSampleFormat) * past->past_NumInputChannels;
pahsc->pahsc_BytesPerUserInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame;
pahsc->pahsc_BytesPerHostInputBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame;
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->pahsc_AudioDeviceID, 0, 0,
IS_INPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
if (err != kAudioHardwareNoError)
{
result = paInvalidChannelCount;
goto error;
}
#endif
}
// 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?
bytesPerHostBuffer = pahsc->pahsc_BytesPerHostInputBuffer;
dataSize = sizeof(UInt32);
err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT,
kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
@ -811,6 +820,9 @@ PaError PaHost_OpenInputStream( internalPortAudioStream *past )
goto error;
}
// setup conversion procedure
result = PaConvert_SetupInput( past, paFloat32 );
return result;
error:
@ -827,24 +839,45 @@ PaError PaHost_OpenOutputStream( internalPortAudioStream *past )
UInt32 dataSize;
OSStatus err = noErr;
int bytesPerOutputFrame;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
if( (past->past_OutputDeviceID < 0) || (past->past_OutputDeviceID >= sNumPaDevices) )
if( (past->past_OutputDeviceID < LOWEST_OUTPUT_DEVID) ||
(past->past_OutputDeviceID > HIGHEST_OUTPUT_DEVID) )
{
return paInvalidDeviceId;
}
hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID];
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->pahsc_AudioDeviceID, 0, 0,
IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
if (err != kAudioHardwareNoError)
{
result = paInvalidChannelCount;
goto error;
}
#endif
}
// calculate buffer sizes in bytes
bytesPerOutputFrame = Pa_GetSampleSize(past->past_OutputSampleFormat) * past->past_NumOutputChannels;
pahsc->pahsc_BytesPerUserOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame;
pahsc->pahsc_BytesPerHostOutputBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame;
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?
bytesPerHostBuffer = pahsc->pahsc_BytesPerHostOutputBuffer;
dataSize = sizeof(UInt32);
dataSize = sizeof(bytesPerHostBuffer);
err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT,
kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
if( err != noErr )
@ -853,7 +886,10 @@ PaError PaHost_OpenOutputStream( internalPortAudioStream *past )
result = paHostError;
goto error;
}
// setup conversion procedure
result = PaConvert_SetupOutput( past, paFloat32 );
return result;
error:
@ -931,8 +967,11 @@ PaError PaHost_OpenStream( internalPortAudioStream *past )
{
int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate);
PRINT(("PortAudio on OSX - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
PRINT(("PortAudio on OS X - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
}
/* Setup constants for CPU load measurement. */
pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer);
/* ------------------ OUTPUT */
if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) )
@ -950,6 +989,22 @@ PaError PaHost_OpenStream( internalPortAudioStream *past )
pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID;
}
/* Attempt to set device sample rate. */
{
AudioStreamBasicDescription formatDesc;
OSStatus err;
memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
formatDesc.mSampleRate = past->past_SampleRate;
err = AudioDeviceSetProperty(pahsc->pahsc_AudioDeviceID, 0, 0,
IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
if (err != kAudioHardwareNoError)
{
result = paInvalidSampleRate;
goto error;
}
}
return result;
error:
@ -979,9 +1034,7 @@ PaError PaHost_StartEngine( internalPortAudioStream *past )
past->past_StopSoon = 0;
past->past_StopNow = 0;
past->past_IsActive = 1;
pahsc->pahsc_FramesPlayed = 0.0;
pahsc->pahsc_LastPosition = 0;
// Associate an IO proc with the device and pass a pointer to the audio data context
err = AudioDeviceAddIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc, past);
if (err != noErr) goto error;
@ -1061,6 +1114,38 @@ PaError PaHost_CloseStream( internalPortAudioStream *past )
return paNoError;
}
/*************************************************************************
** 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.
*/
#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.
@ -1081,6 +1166,7 @@ int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
return minUserBuffers;
}
#endif
/*************************************************************************
** Cleanup device info.
@ -1151,11 +1237,17 @@ PaError PaHost_StreamActive( internalPortAudioStream *past )
/*************************************************************************/
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
{
AudioTimeStamp timeStamp;
PaTimestamp streamTime;
PaHostSoundControl *pahsc;
internalPortAudioStream *past = (internalPortAudioStream *) stream;
if( past == NULL ) return paBadStreamPtr;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
AudioDeviceGetCurrentTime(pahsc->pahsc_AudioDeviceID, &timeStamp);
streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ?
timeStamp.mSampleTime : past->past_FrameCount;
// FIXME PaHost_UpdateStreamTime( pahsc );
return pahsc->pahsc_FramesPlayed;
return streamTime;
}