http://bugs.winehq.org/show_bug.cgi?id=28723
--- Comment #66 from Jörg Höhle hoehle@users.sourceforge.net 2011-12-02 16:20:55 CST --- Alexey, thank you very much for this analysis. Now that we know almost all and everything about mmdevapi, let's write a driver that works with hw:0, plugw:0, dmix, plug:dmix and pulse, with ALSA from 2004 to present. Hopefully X-Mas vacation will be long enough :-)
Looks like as soon as mixer pumps out last period into hw it immediately updates the DP
My interpretation is different. The actual underrun condition is not when GetCurrentPadding (P) is 0 at event time. It's when it's been 0 for a whole period and the app supplied no frames during that period. T: 2028.7ms S: 88704f P: 0f DP: 88208 dDP: 496 T: 2030.5ms S: 88704f P: 0f DP: 88704 dDP: 0 The last period was pumped out at 2020.6ms, but it's at 2030.5 that mmdevapi appears to put the stream into XRUN mode and bump GetPosition to S - P, as if Stop had been called. OTOH, 496 is to close to 448 to be sure mmdevapi isn't waiting for the last frame.
Meanwhile, I had an idea how Wine could implement GetPosition despite adding silence: it can keep an array of ~32 entries about how much silence frames where added during the last 32 periods, assuming GetPosition lags by no more than 32x10=320ms. Perhaps it's even easier: every time it adds silence, it'll bump GetPosition at the next event (like in this XRUN case) and make sure to never report a position smaller than before. Analysing GetPosition logs in the "write 5ms every 10ms" test case would reveal what native does.