From b47db2718119737753a0f3f71236f024b4d56fea Mon Sep 17 00:00:00 2001 From: philburk Date: Thu, 21 Mar 2002 00:39:42 +0000 Subject: [PATCH] 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. --- pa_mac_core/notes.txt | 11 +- pa_mac_core/pa_mac_core.c | 334 ++++++++++++++++++++++++-------------- 2 files changed, 218 insertions(+), 127 deletions(-) diff --git a/pa_mac_core/notes.txt b/pa_mac_core/notes.txt index 7e8835b..c79b90e 100644 --- a/pa_mac_core/notes.txt +++ b/pa_mac_core/notes.txt @@ -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 diff --git a/pa_mac_core/pa_mac_core.c b/pa_mac_core/pa_mac_core.c index 43fab18..f384382 100644 --- a/pa_mac_core/pa_mac_core.c +++ b/pa_mac_core/pa_mac_core.c @@ -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 #include #include #include + #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( ¤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 ) @@ -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; inumSampleRates == 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; } -- 2.43.0