The sad state of audio GetPosition

Maarten Lankhorst m.b.lankhorst at gmail.com
Mon Aug 22 05:10:55 CDT 2011


On 08/19/2011 05:10 PM, Joerg-Cyril.Hoehle at 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.



More information about the wine-devel mailing list