http://bugs.winehq.org/show_bug.cgi?id=28723
--- Comment #62 from Alexey Loukianov mooroon2@mail.ru 2011-11-30 15:19:38 CST --- (In reply to comment #61)
As soon as the local ALSA buffer catches an underrun, ALSA stops and both avail_update and delay freeze. Typically avail_update frozen < buffer_size; frozen delay > 0
Test on my linux workstation shows that typically a call to snd_pcm_delay fails with EPIPE upon hitting xrun, and snd_pcm_available_update also returns -32 instead of meaningful value. This is observed with emu10k1 accessed directly through hw:0,0 ALSA pcm.
Things get broken in a way you describe only if I reconfigure ALSA to use rate plugin which seems to be bugged - it ignores snd_pcm_sw_params_set_start_threshold() value set to 1 and starts slave pcm only in case it had received at least one full period of sound data. I had been able to reliably reproduce bugged behavior with pretty simple modification to my mmdev-test testcase and can attach it along with reproduction instructions here on request.
What is happening is that if the period size is big enough and we were unfortunate to hit XRun when amount of frames left to be played is less than period size - winealsa.drv would recover xrun and pump all available frames to ALSA but the slave pcm and would not be started. Thus we would be left in a state when observed delay_frames and avail_frames appear "frozen" just like you had described. In fact observed values are really correct at that moment: "rate" plugin simply had buffered delay_frames in its internal buffer and waits for more frames to form full period before starting slave device. Thus the "device position" is really delay_frames behind written_frames, the only problem that it stays there while winealsa expects it to be moving after it had finished with recovering from XRun state.
Real remedy for this problem would be to fix "rate" plugin (and all other plugins that are bugged in a same way) to respect sw_params.start_threshold value, but I'm not sure ALSA folks would agree that it should be done. Most likely it would be needed to implement a workaround in Wine for this case, for example using following logic:
if(frames_held == 0) { if( (avail_frames_now == avail_frames_@prev_timer_cb_invocation) && (delay_frames_now == delay_frames_@prev_timer_cb_invocation) && ((alsa_buffer_frames - avail_frames_now) < alsa_period_frames) ) { // We're stuck with non-runing pcm which pretends to be running // There's no really correct way to handle this situation :-(. // Options are: // a) supply ALSA with // alsa_period_frames - (alsa_buffer_frames - avail_frames_now) // of silence so pcm would start and play the samples it holds. // b) reset pcm discarding all the samples it holds; } }
In general, processing data in large chunks boosts speed. Same for audio, e.g. why feed BGM ("background!") in 10ms chunks?
Sure it's better to feed in largest chunks possible as long as it wouldn't result in broken API emulation. That's why I find the approach you had proposed very promising.
----- cut ------ Andrew, while I've been experimenting with dmix period settings I've got a following warning in Wine logs:
0009:fixme:alsa:AudioClient_Initialize ALSA buffer time is smaller than our period time. Expect underruns. (512 < 441) 0009:trace:alsa:AudioClient_Initialize ALSA buffer size: 512 frames 0009:trace:alsa:AudioClient_Initialize ALSA period size: 256 frames 0009:trace:alsa:AudioClient_Initialize MMDevice period: 441 frames 0009:trace:alsa:AudioClient_Initialize MMDevice buffer size: 882 frames
Rationale behind this warning is obvious but the actual text is misleading (512 is by no means less than 441 :-) ). IMO it'd be more correct to change it into something like: "Buffer size provided by ALSA is too small, expect underruns".