The major mmdevapi and winmm audio bugs
ken at codeweavers.com
Sun Oct 9 04:02:36 CDT 2011
On Oct 7, 2011, at 9:24 AM, Joerg-Cyril.Hoehle at t-systems.com wrote:
> o #28039 IAudioClock_GetPosition must ignore underruns (MacOS)
> Here I've no idea how to solve this. My initial query remained unanswered:
> Ken? Andrew's yesterday patch (using GetCurrentPadding) is an interim
> work-around. GetPosition (speaker) is unrelated to padding which is
> solely about buffering.
It is expected that the queue's time keeps incrementing even when no buffers are enqueued. For example, see <https://developer.apple.com/library/ios/#qa/qa1718/_index.html>.
Here's one possible solution:
Enqueue the buffers with AudioQueueEnqueueBufferWithParameters() to get back the actual start time (in the queue's time frame) at which each buffer is scheduled to start. Then, compare AudioQueueGetCurrentTime() with the scheduled start time of the enqueued buffer(s) to see how far into which buffer the queue has played.
The code will have to track the position (in the desired sense) separately from the queue time. There will be a scalar value containing the sum of the lengths of all buffers known to have been completely played. There will also be a list of time ranges for those buffers which have been queued but are not known to have been completely played yet. For each buffer, the time range consists of its scheduled start time and its length.
At certain points -- ca_out_buffer_cb() and GetPosition() -- AudioQueueGetCurrentTime() will be called. The buffer list will be scanned. All the buffers whose end time (i.e. start time + length) is less than the queue time will be removed from the list, with their lengths added into the sum. That's all that's necessary for ca_out_buffer_cb() and is only done in that function to make sure the buffer list doesn't keep growing if GetPosition() is rarely or never called. For GetPosition(), the queue time will be compared to the first buffer that remains in the list, if any. If there are no buffers or the queue time is before the start time of the first buffer, then GetPosition() returns the sum. Otherwise, it will return the sum plus the queue time minus the buffer start time.
AudioClient_Reset() will clear the buffer list (and the sum?).
There's another approach that came to mind, but I don't think it's feasible: only run the queue when there are actually buffers to play. Don't start the queue until a buffer has been enqueued and stop it when there are no more ready. Basically, during the callback when the queue is requesting another buffer, if there's nothing to enqueue, call AudioQueueStop(..., FALSE). You may also need to use AudioQueueAddPropertyListener() to observe kAudioQueueProperty_IsRunning to learn when it has actually stopped, since the queue's time will reset to 0 at that point. During each period of the queue running, its time will reflect actual queued samples being played.
Timeline discontinuities occur when the sample rate changes, the output device is switched, the DSP has to be reset, or that sort of thing, I believe. They don't have anything to do with underruns of the kind you're considering.
More information about the wine-devel