diff options
author | Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> | 2011-09-07 00:15:34 (GMT) |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-09-12 08:30:20 (GMT) |
commit | 294c4fb8ab01728358836f478bcc1174ba7fb9d8 (patch) | |
tree | 2afde5810233ceb00dafa573061f224026238ada /sound/usb/urb.c | |
parent | 1ef0e0a05345b7411bdabbfca27f58bd33dcc7c8 (diff) | |
download | linux-fsl-qoriq-294c4fb8ab01728358836f478bcc1174ba7fb9d8.tar.xz |
ALSA: usb: refine delay information with USB frame counter
Existing code only updates the audio delay when URBs were
submitted/retired. This can introduce an uncertainty of 8ms
on the number of samples played out with the default settings,
and a lot more when URBs convey more packets to reduce the
interrupt rate and power consumption.
This patch relies on the USB frame counter to reduce the
uncertainty to less than 2ms worst-case. The delay information
essentially becomes independent of the URB size and number of
packets. This should help applications like PulseAudio which
require accurate audio timing. Clemens Ladisch reported
a decrease of mplayer's A-V difference from nrpacks down to at
most 1ms.
Thanks to Clemens for also pointing out that the implementation
of frame counters varies between different HCDs. Only the
8 lowest-bits are used to estimate the delay.
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
[clemens: changed debug code]
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/urb.c')
-rw-r--r-- | sound/usb/urb.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/sound/usb/urb.c b/sound/usb/urb.c index e184349..b4dcccc 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c @@ -718,7 +718,16 @@ static int prepare_playback_urb(struct snd_usb_substream *subs, subs->hwptr_done += bytes; if (subs->hwptr_done >= runtime->buffer_size * stride) subs->hwptr_done -= runtime->buffer_size * stride; + + /* update delay with exact number of samples queued */ + runtime->delay = subs->last_delay; runtime->delay += frames; + subs->last_delay = runtime->delay; + + /* realign last_frame_number */ + subs->last_frame_number = usb_get_current_frame_number(subs->dev); + subs->last_frame_number &= 0xFF; /* keep 8 LSBs */ + spin_unlock_irqrestore(&subs->lock, flags); urb->transfer_buffer_length = bytes; if (period_elapsed) @@ -737,12 +746,27 @@ static int retire_playback_urb(struct snd_usb_substream *subs, unsigned long flags; int stride = runtime->frame_bits >> 3; int processed = urb->transfer_buffer_length / stride; + int est_delay; spin_lock_irqsave(&subs->lock, flags); - if (processed > runtime->delay) - runtime->delay = 0; + + est_delay = snd_usb_pcm_delay(subs, runtime->rate); + /* update delay with exact number of samples played */ + if (processed > subs->last_delay) + subs->last_delay = 0; else - runtime->delay -= processed; + subs->last_delay -= processed; + runtime->delay = subs->last_delay; + + /* + * Report when delay estimate is off by more than 2ms. + * The error should be lower than 2ms since the estimate relies + * on two reads of a counter updated every ms. + */ + if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2) + snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n", + est_delay, subs->last_delay); + spin_unlock_irqrestore(&subs->lock, flags); return 0; } |