http://bugs.winehq.org/show_bug.cgi?id=28723
--- Comment #67 from Alexey Loukianov mooroon2@mail.ru 2011-12-02 18:03:54 CST --- You're welcome. Meanwhile I've been just reported about a real-world bug caused by "plug" alsa-lib plugin which affects - surprise! - RAGE under Wine with prefix set to Win7.
Here is my comment in AppDB about it: http://appdb.winehq.org/objectManager.php?sClass=version&iId=24512#Comme...
What happens is rate plugin not draining samples from the ALSA buffer waiting for entire period to be pumped into it so the GetCurrentPadding() reported by winealsa to XA2 remains equal to 896 (i.e. entire "mmdev duration buffer") and thus XA2 is unable to pump-out more data (buffer is constantly full from it's point of view) and so it results in no sound produced at all :-). Pretty funny case IMO.
Concerning your interpretation - hmm, it might be as valid as mine is. How the things actually are can only be determined by reverse-engineering MS code, but it is in any case irrelevant for us - we just have to mimic the observed devpos change behavior in winealsa and nothing more :-).
As for remark about GP vs. adding silence - correct me if I'm wrong but isn't the only reason to add the silence is to workaround the "pcm is not starting unless at least one period of data had been pumped out"? I mean, as a part of XRun recovery process we add up some silence if required to be sure pcm really starts, but we are not obliged to reflect these artificially-inserted frames in the devpos reported to an app. As we had XRun - the stutter had already happed so adding some silence wouldn't be fatal corruption to and already corrupted sound output. Taking into account silent frames it the devpos reported to an app is pretty simple.
At stream Init(): This->inserted_silent_frames = 0; This->devpos = 0;
At stream Start() or on Xrun recovery, after inserting N silent frames in order to force pcm start: This->devpos = This->written_frames - This->held_frames; This->inserted_silent_frames = N;
On Stop(): This->devpos = This->written_frames - This->held_frames;
At GetPosition():
if(This->started) { if(delay_frames > 0) This->devpos = max(This->written_frames - This->held_frames - delay_frames - This->inserted_silent_frames, This->devpos); else if(pad_frames > 0) This->devpos = max(This->written_frames - This->held_frames - pad_frames - This->inserted_silent_frames, This->devpos); else This->devpos = max(This->written_frames - This->held_frames - This->inserted_silent_frames, This->devpos); } *pos = This->devpos;
IOW silent frames which we use to force pcm start - they are pretty equivalent to the artificially added latency and thus should contribute to it. The logic above it something I had just now quickly scratched and taking into account that I'm pretty sleepy ATM chances are it's partly or completely wrong. It's a deep night here so I'm going to take a rest for now and would try to come back and think more about it tomorrow.