[alsa-devel] bug in alsa-lib/snd_pcm_plugin_delay for capture?

pl bossart bossart.nospam at gmail.com
Mon Nov 22 17:27:24 CET 2010

>>> Could you test patch bellow?
>>> http://git.alsa-project.org/?p=alsa-lib.git;a=commitdiff;h=ba9332e9192814a5431a3a2505d25d74a9232124
>> Nah, no luck. PulseAudio still reports an error and if I add
>> assert(*delayp >= *availp) the code bombs out.
>> Here is one way to solve the problem. I don't claim it's the right
>> one, but it's compatible with the way the availp value is computed,
>> and it makes the error message go away in PulseAudio.
> The double avail_update() calling seems too expensive to me. Could you test
> to use snd_pcm_mmap_capture_avail() instead snd_pcm_avail_update() calls (to
> assign both slave_avail and plugin_avail local variables)?

Nope, it still does not work if you use mmap_capture_avail. The root
cause of the problem is the while loop in snd_pcm_plugin_avail_update
(copied for reference below), it's called only in the capture case and
the value returned by the xfer variable causes the delay to be
eventually lower than the avail samples.
My fix is indeed expensive but it makes sure the same loop is called
twice, so that these two delay and avail values are consistent. I
don't know how to fix it in a different manner since I don't
understand this while loop in the first place. Maybe we need to
refactor this loop so that it's called in the delay() routine as well?

while (size > 0 && slave_size > 0) {
			snd_pcm_uframes_t frames = size;
			snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
			const snd_pcm_channel_area_t *slave_areas;
			snd_pcm_uframes_t slave_offset;
			snd_pcm_uframes_t slave_frames = ULONG_MAX;
			snd_pcm_sframes_t result;
			int err;

			err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
			if (err < 0)
				return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
			if (frames > cont)
				frames = cont;
			frames = (plugin->read)(pcm, areas, hw_offset, frames,
					      slave_areas, slave_offset, &slave_frames);
			snd_pcm_mmap_hw_forward(pcm, frames);
			result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
			if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
				snd_pcm_sframes_t res;
				res = plugin->undo_read(slave, areas, hw_offset, frames,
slave_frames - result);
				if (res < 0)
					return xfer > 0 ? (snd_pcm_sframes_t)xfer : res;
				frames -= res;
			if (result <= 0)
				return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
			if (frames == cont)
				hw_offset = 0;
				hw_offset += frames;
			size -= frames;
			slave_size -= slave_frames;
			xfer += frames;
		return (snd_pcm_sframes_t)xfer;

More information about the Alsa-devel mailing list