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)
#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)
/**************************************************************
{
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;
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 **********/
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( ¤tTime, 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( ¤tTime, &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 )
// 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 )
// 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 )
return defaultDeviceID;
}
-/****************************************** END CPU UTILIZATION *******/
-
+/******************************************************************/
static PaError Pa_QueryDevices( void )
{
OSStatus err = noErr;
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();
}
/*************************************************************************/
-
/* 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)
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
{
OSErr err;
int index;
+ UInt32 outSize;
AudioStreamBasicDescription formatDesc;
Boolean result;
AudioDeviceID devID;
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;
break;
}
-
// Get the device name
- deviceInfo->name = deviceNameFromID( devID, isInput );
+ deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput );
return 1;
error:
PaError PaHost_Init( void )
{
-#if PA_SIMULATE_UNDERFLOW
- PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
-#endif
return Pa_MaybeQueryDevices();
}
AudioBufferList* outOutputData )
{
PaError result = 0;
- char *inBufPtr;
- char *outBufPtr;
+ char *inputNativeBufferfPtr;
+ char *outputNativeBufferfPtr;
int gotInput = 0;
int gotOutput = 0;
int i;
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
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;
}
{
/* 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 );
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 )
}
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)
{
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);
goto error;
}
+ // setup conversion procedure
+ result = PaConvert_SetupInput( past, paFloat32 );
+
return result;
error:
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 )
result = paHostError;
goto error;
}
-
+
+ // setup conversion procedure
+ result = PaConvert_SetupOutput( past, paFloat32 );
+
return result;
error:
{
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) )
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:
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;
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.
return minUserBuffers;
}
+#endif
/*************************************************************************
** Cleanup device info.
/*************************************************************************/
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;
}