On 08/19/2011 05:10 PM, Joerg-Cyril.Hoehle@t-systems.com wrote:
Maarten Lankhorst wrote: [nice to hear from you]
IMHO AudioClient_Stop must not map to snd_pcm_drop. It is more like snd_pcm_pause. Or perhaps simply lead ALSA into an underrun.
afaict pause, with reset mapped to drop,
Indeed. But I believe I need a fallback because ALSA says that pause "works only on the hardware which supports" it.
I can't remember why pause didn't work, but if it works go for it.
I was solely thinking aloud that pause is TRT, not tried out yet.
However, I received test results from a "Windows 7 Ultimate" machine. It exhibits a similar bug -- in exclusive mode only: render.c:948: Test failed: Position 18191 too far after 100ms Shared mode works as my tests expect it (<= 48000/10 frames).
- snd_pcm_status_alloca(&status);
HeapAlloc(GetProcessHeap(), HEAP_ZERO_FLAG, snd_pcm_status_sizeof()) or something like that if available please..
Really? I don't want to go through the overhead of memory allocation when all I need is a stupid small amount of stack allocated memory.
- if(!This->initted){
return AUDCLNT_E_NOT_INITIALIZED;
Unneeded part.
Can't you obtain a handle to that COM object prior to calling Initialize which sets This->fmt?
No.
Follow that flow..
I beg your pardon?
IAudioClock is not available until Initialize has succeeded, so the check above is pointless.
- if(0){
- avail_frames = snd_pcm_status_get_avail(status);
- delay_frames = snd_pcm_status_get_delay(status);
if 0 is bad...
I tried out pcm_status because somebody in alsa-devel mentioned that it allows to grab avail + delay in one (sync'ed?). However, I found delay to be always 0 inside status!?!
Also, I found out that I need to call avail_update and delay in a particular order, otherwise I get stale values from an old call prior to the last sleep...
- if(avail_frames <= This->bufsize_alsa + MAX_LATE_SECONDS * This->fmt->nSamplesPerSec
&& delay_frames > 0)
Isn't delay_frames < 0 the definition of underrun?
Indeed. There are potentially N distinct underruns:
- the front end -- what snd_pcm_avail_update knows about;
- intermittent buffers (USB);
- the speaker -- what snd_pcm_delay knows about.
There could be a short front-end buffer underrun that goes unnoticed by the speaker if the TCP or USB in between buffers enough data *and* is able to speed up.
no point in adding MAX_LATE_SECONDS
That is some form of guard against broken values. E.g. people reporting in alsa-devel that PA sometimes complains about avail ~ MAXINT and such weird values.
Getting an avail update again? Why?
The theory is: position = written_frames(into ALSA) - delay and translates to: This->written_frames - This->held_frames - delay
However sometimes I can't trust delay. I still need to figure out when.
- IIRC after an underrun, snd_pcm_delay yields error X.
- or was it before starting?
- ...
The upper bound on position is always: This->written_frames(ReleaseBuffer) - This->held_frames - ALSA_padding (what ALSA's front end has not yet processed, in absence of underrun).
Perhaps that would be robust:
- Compute upper bound
- position = clamp(0, delay > 0 ? written-delay : written, upper_bound);
2b. except when not yet started ... 2c. except while stopped ...
I was even considering: 3. if position < This->previous_position stick to previous...
Yet perhaps it's better to allow intermittent garbage values than to stick to garbage!
OTOH, the delay values I see in the logs are subject to such variation (with PA) that I'm considering going with a clock instead, or perhaps:
- query delay once per tick (e.g. 10ms) => last_pos
- when asked, compute position from last_pos + time since tick * rate
The last_pos slot may be needed anyway once stopped in pause mode.
Don't worry about underruns and checking the way you do is fragile, I prefer 0 tracking, and just check the return value of snd_pcm_writei. Experience taught me this is the most stable way of doing underrun handling in alsa. Remove all the checks you're doing please, they will just break things more.