[alsa-devel] Misusing snd_pcm_avail_update()
clemens at ladisch.de
Tue Jan 20 09:29:37 CET 2009
Lennart Poettering wrote:
> Currently in the 'glitch-free' logic of PulseAudio I use
> snd_pcm_avail_update() to estimate how I need to program my system
> timers for the next wake-up for the next buffer fill-up. For that I
> assume that the current fill level of the hardware buffer is the
> hardware buffer size minus what s_p_a_u() returns. I then convert that
> fill level from sample units to time units, and fix it up by the
> deviation of the sound card time from the system time. Finally I
> substract some extra margin just to make sure.
> This I assumed would tell me how much time will pass until an underrun
> happens if I don't write anything.
> Mostly this logic works fine. But on some setups and cases it
> doesn't. ALSA will signal an underrun much much earlier than what I
> estimated like this.
> I am now wondering why? One possibility of course is that s_p_a_u() is
> not reliable, due to driver issues (there were problems in the HDA
> driver about this, right?).
Some hardware doesn't realiably tell the current position in the buffer.
> Also, s_p_a_u() might simply lag behind quite a bit,
In the case above, when a driver detects that the hardware position is
incorrect, it uses the last known value. Usually, this isn't off more
than a few samples.
There is hardware that does not allow reading the current position.
With such a device, the position you get is computed at every interrupt,
i.e., you get the last period boundary.
> or -- what I think is most likely -- because samples are
> popped in larger blocks form the hw playback buffer we reach the
> underrun much earlier than expected.
This happens, too. Many PCI devices read PCM data in blocks of 32 or
64 bytes. Many wavetable chips (Emu10k1, DS-1, CS46xx) read sample data
in blocks of 256 or 512 samples. USB transfers blocks of at least 1 ms
length, but often a multiple of that to reduce the number of USB
After choosing hardware parameters, you can call
snd_pcm_hw_params_is_block_transfer() to determine if the device
transfers samples in comparatively large blocks. (The wavetable and
USB drivers set this flag.) There is currently no function to determine
the block size.
In the worst case, the current position isn't guaranteed to be more
accurate than the last period boundary.
> I do acknowledge that the way i use s_p_a_u() is probably a misuse of
> the API.
The API was primarily designed for applications that are woken up at
period boundaries. Using s_p_a_u() to bypass the synchronization
implied by period interrupts _is_ possible, but it cannot give you more
precision than the hardware supports.
> Now, considering all this I'd like to ask for a new API function that
> tells me how much time I really have before the next underrun.
Well, you could make the "some extra margin" above larger than one period.
Or monitor the device over some time and see what the smallest increment
is you get in successive s_p_a_u() return values.
More information about the Alsa-devel