http://bugs.winehq.org/show_bug.cgi?id=29472
--- Comment #9 from Jörg Höhle hoehle@users.sourceforge.net 2012-01-03 12:18:06 CST ---
It's how dsound has worked since before [...]
Yet another instance of a double bug that makes it so hard to change something in the fragile equilibrium that Wine's audio was/is. In the past, position was locked to buffer avail so the bug was not noticeable.
See how mmdevapi's periodic filler operates with GetCurrentPadding only. DSound should do exactly the same. It wants to fill a buffer, not perform lip synch'ing. Look up the DONTS about avail_update and delay in Poettering's blog: http://0pointer.de/blog/projects/guide-to-sound-apis.html
In my vision, when DSound is called by mmdevapi's periodic event, it should - query GetCurrentPadding to see how much it can mix; - mix that much data and Release it. Perhaps it also wants to perform rate limiting such that apps can still add a secondary buffer that plays almost immediately, without DSound playing "I've already queued 2s of data, your request will have to wait until after that".
GetPosition does not qualify as a play position for DSound because it could be that late that it doesn't even fit into DSound's tiny buffer; we don't control it; for the sake of bug #28723 we introduced latency, mmdevapi's and ALSA's buffer all add up. Instead, I believe we should treat DSound + mmdevapi + ALSA as what it really is: a chain of filters, adding up their latency, each with their own buffering constraints. DSound's primary buffer contents should be fed to mmdevapi piece by piece. IOW, DSound's read pointer follows mmdevapi's write pointer. Perhaps we could add GetStreamLatency to come a little closer to the actual speaker position.
OTOH you may argue that dsound:GetCurrentPosition appears to be the only function that apps can use for synchronisation purposes (requiring GetPosition & snd_pcm_delay). Alas it is expressed in terms of buffer positions, which is not that surprising given that D means Direct, i.e. a HW buffer model. Hence Wine must find a consistent way to map speaker positions onto the buffer(s). Clearly, here mmdevapi has the better separation of concerns.
MS seems to realize this, see DSBCAPS_TRUEPLAYPOSITION http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sd...
The dilemma is: For the sake of bug #28723, we've increased latency in mmdevapi, still having GetPosition return a true speaker position for the purpose of lip synchronization, while DSound's buffer model and following lack of distinction between frontmost buffer position and true speaker position does not fit well with mmdevapi's streaming. DSound is much like ALSA's hw:0: there, snd_pcm_delay + avail_update == buffer_size, i.e. what leaves the buffer is immediately heard, according to ALSA, there's no other latency.
So Wine's DSound jobs are: a) Have GetCurrentPosition provide values suitable for lip-sync'ing -- see MSDN about DSBCAPS_GETCURRENTPOSITION2 b) Have buffers large enough that a) can work; c) Have buffers small enough that the illusion of direct HW access can be maintained; d) Have latency as small as possible (fast audio feedback); Great fun!
What I don't know is: does DSound's primary buffer "materialize" (via a data pointer) when using secondary buffers, or is it then inaccessible to apps? In the latter case, mmdevapi's buffer could serve as primary (mixing like described above), eliminating a bit of latency for the sake of Wine gamers that want short audio response times. When the app requests WRITEPRIMARY, this shortcut is not possible, and primary's contents must be copied into mmdevapi, because the two buffer behaviours are incompatible on paper (one is fixed circular, the other dynamic). It is conceivable that native avoids that by having a fixed mmdevapi buffer with only rare auxiliary ones in case of wrap-around (like winealsa, unlike winecoreaudio), the output of my tests seems to back this up, but it's undocumented. I wonder whether/when we'll hit the first bug like DSound bug #28748, comment #1, that some apps touches mmdevapi's buffer outside of the Get/ReleaseBuffer pair. That might crash winecoreaudio.