Proposed Enhancements to PortAudio API

015 - Improve Callback Timestamp Info

Enhancement Proposals Index, PortAudio Home Page

Updated: October 18, 2002

Status

This proposal is sufficiently well defined to be implemented. It has been partially implemented in the v19-devel branch.

Background

The timing information provided by the outTime callback parameter and Pa_StreamTime() function is not sufficient to perform some syncronisation tasks. One example is that of syncronising PortAudio generated audio with another timebase, such as syncronisation pulses in a timestamped MIDI event stream. Furthermore, the behavior of the outTime callback parameters is not defined in the presence of buffer underflows and overflows and it is difficult to conceive of a single behavioral definition for outTime which could fulfill all possible requirements.

Proposal

Redefine Pa_GetStreamTime() (formerly Pa_StreamTime()) to have completely different behavior. The new behavior is to return the current time in seconds according to the clock used to generate buffer timestamps for the associated stream. The clock runs continuously while the stream is open, i.e. between calls to Pa_OpenStream() and Pa_CloseStream(). The clock's origin (the time at which it was zero) is unspecified.

typedef double PaTime; /* time expressed in seconds */

/*
Return the current time according to the clock used 
to generate buffer timestamps for stream.
*/
PaTime Pa_GetStreamTime( PaStream *stream );

Remove the current outTime parameter from the audio callback, and replace it with a pointer to a PaStreamCallbackTimeInfo structure which contains the time at which the first sample of the input buffer was received at the ADC input (inputBufferAdcTime), the time at which the first sample of the output buffer will exit the DAC (outputBufferDacTime), and the time at which the callback was initiated (currentTime). These times are all expressed in seconds based on the stream-specific clock returned by Pa_GetStreamTime().

typedef struct PaStreamCallbackTimeInfo{
    PaTime inputBufferAdcTime;
    PaTime currentTime;
    PaTime outputBufferDacTime,
} PaStreamCallbackTimeInfo;

typedef int PortAudioCallback(
      void *inputBuffer, void *outputBuffer,
      unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
      unsigned long flags, void *userData);

Discussion

Various alternate names were considered for the function named Pa_GetStreamTime() in this proposal - primarily because GetStreamTime() collides with the old StreamTime() function which had a completely different behavior. However a better name could not be found.

The possibility of retaining the original form of Pa_StreamTime() for clients which simply need to display the elapsed stream playback time was discussed. This proposal was rejected as the functionality currently provided by Pa_StreamTime() can be implemented using the new timestamp parameters. Such a solution can potentially be more robust than implementing the functionality in PortAudio because it is difficult to define a single useful behavior of the old Pa_StreamTime() function in the presence of buffer overruns and underruns.

Originally it was planned to have a global PortAudio timebase returned by a function named Pa_GetTime(). However, different host APIs on the same platform may dictate that their timebases are relative to different time sources (timeGetTime() vs. QueryPerformanceCounter() on windows for example.) Trying to syncronise multiple independent time bases is a problematic task (due to potential clock drift), so it was concluded that a safer approach would be to allow each stream to have its own time base.

Various options for specifying the stream clock's origin and lifetime (times when it is running) have been considered, including only allowing the clock to run between StreamStart() and StreamStop(). The current solution provides clients who whish to use the clock with access to the clock even while the stream is not running - this may be useful for applications that timestamp events using the stream's clock, or display time-based information even when the stream is stopped.

Impact Analysis

This proposal will require clients to rewrite code that depends on the Pa_StreamTime() parameter.