[PATCH] winepulse.drv: Add PulseAudio driver

Chris Robinson chris.kcat at gmail.com
Mon Jun 18 10:49:55 CDT 2012


On Monday, June 18, 2012 9:31:04 AM Andrew Eikum wrote:
> We chatted a little on IRC this weekend, but thanks again for the
> advice.

No problem. :)

> Mmdevapi sends it data to the lower systems in period-sized chunks,
> and the reported buffer fill level decreases in period-sized chunks.
> But the audio clock position is more accurate than that, returning
> some value between periods, and taking into account latency.

So the pulse driver would likely need a period-sized intermediary buffer, to 
store the unwritten samples that can't fill a full period (then keep it stored 
until enough writes are made to fill it and it's written to the stream).

The PA_STREAM_INTERPOLATE_TIMING and PA_STREAM_AUTO_TIMING_UPDATE flags should 
provide timing with a good bit of granularity, along with regularly being 
resync'd to the server's clock.

> Unfortunately, PulseAudio (and, apparently, every Linux audio API)
> refuses to make any guarantees at all with regard to things like
> buffer sizes, period sizes, callback regularity, and latencies.

Right, which is why you can't rely on what PulseAudio sets for the 
buffer/tlength size, and you have to manage the expected size yourself, but 
that's easy. Luckilly PulseAudio supports buffer sizes up to about 2 seconds, 
I believe, which is good enough for mmdevapi. Unlike ALSA and others, Pulse 
has no problems handling buffers as large as what mmdevapi supports.

PulseAudio doesn't really do periods. It actually disables audio hardware 
interrupts when it can and uses high-resolution timers to work out where the 
audio pointers are at any given time. The period values can pretty much just 
be emulated and neither pulseaudio or the app should care that much.

Latency issues are what my approach tries to improve. Although ALSA's dmix 
still does perform better for playback, PulseAudio is at least now comparible 
and actually usable when it comes to responsive/interactive audio.

For the callback regularity issues, keep in mind that you'll never get 100% 
guarantees with protected mode code on a multi-tasking OS, so as long as apps 
work as expected, it's "good enough". However, you can improve over Pulse's 
callbacks by using a background thread that keeps an eye on the amount of data 
pulseaudio has chewed through, and trigger any callbacks or events yourself as 
needed.

> It'd be handy if there was some page explaining how PulseAudio's data
> model works. Stuff like where the data is stored, when and how it's
> transfered, when callbacks are triggered, what the members of
> pa_buffer_attr actually mean (their documentation is useless), how the
> stream flags affect stream operation and the callbacks, when the
> buffer attributes might change and how applications should deal with
> those changes. This stuff is all unclear to me, and I think it's a big
> source of my frustration with the API.

The sample data for playback streams is stored in the server, AFAIK. It's 
transfered when you call pa_stream_write, and it's either done synchronously 
or asynchronously depending on who "owns" the buffer it's given. With 
mmdevapi's design, there's no real problem to let pulse maintain ownership and 
do the writes asynchronously.

Callbacks are triggered when the client receives the appropriate signals from 
the server. When exactly the server sends the signal depends on what the 
signal is and the various settings and attributes.

PulseAudio's documentation isn't all that bad, considering. If you want to 
talk about bad API docs, look at ALSA.

> Yeah, I experimented with ADJUST_LATENCY, as it seems to be the trick
> to getting lower latencies. Then we have to maintain our own buffer,
> which is why I switched to using the write callback, to have Pulse
> tell us when it's ready for more data from our internal buffer.

You shouldn't need a shadow buffer with ADJUST_LATENCY. Just write it to pulse 
as the app writes it to mmdevapi. Why delay? It can handle the buffer sizes.

This also avoids the timing problems with the write callback. The most you'll 
need is a temporary storage buffer for when an app writes an incomplete 
period.



More information about the wine-devel mailing list