[PATCH 5/6] dsound: rework ugly mixer logic

Maarten Lankhorst maarten.lankhorst at canonical.com
Wed Oct 24 07:16:58 CDT 2012

Op 24-10-12 12:52, Joerg-Cyril.Hoehle at t-systems.com schreef:
> Hi,
> Maarten Lankhorst wrote:
>> For example Skyrim with a 36 ms stream latency will just buffer in more data to
>> compensate instead. But it can't do it if you report that it's fine to feed data every 5 ms.
> Please elaborate.  Every now and then I wish you used more words to
> explain your observations.
> Where do the 5ms come from?  What's the relationship with the 36ms latency?

Well skyrim is pretty aggressive with its low latency. It seems to want to
buffer 20 ms only when default period is 10 ms, but when I did a bunch of
tests with tons of underruns my minimum and default period ended up at 36 ms,
skyrim still ran correctly, and looking at 'pacmd list-sink-inputs' it just
increased buffer length in that case.

> You patch DSound so I assume that Skyrim uses DSound, not mmdevapi
> directly.  Shouldn't DSound export capabilities that make sense given
> its abstraction?  IIRC, there's some former mailto wine-devel or bug
> report where I talked about the alternating period-sized buffer
> abstraction that is underlying DSound's API and how all positions
> being reported modulo buffer size are problematic if someone misses
> the period intervals.

Nah, originally when I started hacking on dsound I assumed skyrim was using
dsound, but it's only used for the intro. I'm running the Skyrim steam
version with windows set to win7.

I'm not sure yet how dsound should be fixed on bigger periods, I believe using
IAudioClock for reporting position *might* be the correct answer, but it's not
properly test yet on behavior, so I chose for the simple solution of directly
reporting next mix position as play pos, which is the same as current code.

> Of course, that abstraction cannot explain a 36ms latency with two
> alternating period-sized "direct hardware" buffers of 5ms.

Indeed, skyrim was creating the 20 ms buffer directly, and allocating a bigger
buffer didn't help. So instead when an application requests 2 default periods
I try to set latency to half requested period (5 ms instead of 10ms) and I'm more
aggressive about firing events when data can be written. Pulseaudio seems to
behave differently at lower latencies though, pointer updates are no longer
done at a fixed size, presumably because it puts pulseaudio in a real danger
of underruns so it mixes as much as it can each pass.

> Wine's DSound must maintain a set of mutually consistent variables
> { play position, write position, buffer size, period size }
> that in addition need to make sense w.r.t. audio output.
> I'm not convinced that it does.  There should be tests to verify that.

run the dsound tests with WINETEST_INTERACTIVE=1.

> In particular, wine's DSound must not let play position leave the virtual DSound
> buffer, even if mmdevapi reports that it's 2 seconds behind with PulseAudio.
> (IMHO, should that happen, then DSound must cap the reported position).

At the point dsound queues the data to mmdevapi it can no longer do anything
about it, so instead of trying something more complicated the best way to deal
with it is to accept that fact and keep queued latency as fixed as possible,
and ideally small.

I create a 80 ms buffer, please read msdn's documentation of iaudioclient::initialize,
the requirements for shared mode is that it will create a buffer that's at least that
large to prevent underruns, so if a 2 second buffer is created as a result, then
somehow the audio system believes we should buffer 2 seconds. Not much we can do about
that, and according to docs looks like we should keep the buffer as filled as possible
in that case. However I believe that this is unlikely happen, and more likely we end
up with only having to buffer a fixed 80 ms.

80 ms is generous btw, if you're feeling lucky you can set dsound latency much lower,
40 ms buffering would probably be the lowest safe value, but if you're feeling
adventerous initialize buffer length to 100 ns and get the driver to come up with
the minimal period and buffer length it supports. But things do seem to break
down when you go that low. Pulseaudio got angry at me for those underruns
and increased minimum latency to 8 ms after I did some testing at sub 1 ms buffers.


> (BTW, I'd find it easier for Wine if DSound's primary were equal to mmdevapi's
> buffer, except that with winecoreaudio, there's no such mmdevapi buffer...)

I've been doing some cleanups and there's really no need to do this. I handled the
primary buffer mixing as a special case of secondary buffer and it works just fine. The
idea behind my patches has been to simply remove primary buffer altogether, as far as
possible. device->buffer is equal to primary buffer size in writeprimary mode, before
being copied to audio client. It's used for mixing to float if in secondary mode when
iaudioclient uses a non-float format, and it's NULL for float format. In case of float
it gets mixed to the audio buffer directly, saving a memcpy and a conversion.


More information about the wine-devel mailing list