Aug. 22, 2011
10:10 a.m.
On 08/19/2011 05:10 PM, Joerg-Cyril.Hoehle(a)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:
> 1. Compute upper bound
> 2. 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.