aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>2011-09-06 20:15:34 -0400
committerTakashi Iwai <tiwai@suse.de>2011-09-12 04:30:20 -0400
commit294c4fb8ab01728358836f478bcc1174ba7fb9d8 (patch)
tree2afde5810233ceb00dafa573061f224026238ada /sound/usb
parent1ef0e0a05345b7411bdabbfca27f58bd33dcc7c8 (diff)
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')
-rw-r--r--sound/usb/card.h2
-rw-r--r--sound/usb/pcm.c28
-rw-r--r--sound/usb/pcm.h3
-rw-r--r--sound/usb/urb.c30
4 files changed, 60 insertions, 3 deletions
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ae4251d5abf7..a39edcc32a93 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -94,6 +94,8 @@ struct snd_usb_substream {
94 spinlock_t lock; 94 spinlock_t lock;
95 95
96 struct snd_urb_ops ops; /* callbacks (must be filled at init) */ 96 struct snd_urb_ops ops; /* callbacks (must be filled at init) */
97 int last_frame_number; /* stored frame number */
98 int last_delay; /* stored delay */
97}; 99};
98 100
99struct snd_usb_stream { 101struct snd_usb_stream {
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index b8dcbf407bbb..0b699ca1957e 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -34,6 +34,30 @@
34#include "clock.h" 34#include "clock.h"
35#include "power.h" 35#include "power.h"
36 36
37/* return the estimated delay based on USB frame counters */
38snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
39 unsigned int rate)
40{
41 int current_frame_number;
42 int frame_diff;
43 int est_delay;
44
45 current_frame_number = usb_get_current_frame_number(subs->dev);
46 /*
47 * HCD implementations use different widths, use lower 8 bits.
48 * The delay will be managed up to 256ms, which is more than
49 * enough
50 */
51 frame_diff = (current_frame_number - subs->last_frame_number) & 0xff;
52
53 /* Approximation based on number of samples per USB frame (ms),
54 some truncation for 44.1 but the estimate is good enough */
55 est_delay = subs->last_delay - (frame_diff * rate / 1000);
56 if (est_delay < 0)
57 est_delay = 0;
58 return est_delay;
59}
60
37/* 61/*
38 * return the current pcm pointer. just based on the hwptr_done value. 62 * return the current pcm pointer. just based on the hwptr_done value.
39 */ 63 */
@@ -45,6 +69,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
45 subs = (struct snd_usb_substream *)substream->runtime->private_data; 69 subs = (struct snd_usb_substream *)substream->runtime->private_data;
46 spin_lock(&subs->lock); 70 spin_lock(&subs->lock);
47 hwptr_done = subs->hwptr_done; 71 hwptr_done = subs->hwptr_done;
72 substream->runtime->delay = snd_usb_pcm_delay(subs,
73 substream->runtime->rate);
48 spin_unlock(&subs->lock); 74 spin_unlock(&subs->lock);
49 return hwptr_done / (substream->runtime->frame_bits >> 3); 75 return hwptr_done / (substream->runtime->frame_bits >> 3);
50} 76}
@@ -417,6 +443,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
417 subs->hwptr_done = 0; 443 subs->hwptr_done = 0;
418 subs->transfer_done = 0; 444 subs->transfer_done = 0;
419 subs->phase = 0; 445 subs->phase = 0;
446 subs->last_delay = 0;
447 subs->last_frame_number = 0;
420 runtime->delay = 0; 448 runtime->delay = 0;
421 449
422 return snd_usb_substream_prepare(subs, runtime); 450 return snd_usb_substream_prepare(subs, runtime);
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index ed3e283f618d..df7a003682ad 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -1,6 +1,9 @@
1#ifndef __USBAUDIO_PCM_H 1#ifndef __USBAUDIO_PCM_H
2#define __USBAUDIO_PCM_H 2#define __USBAUDIO_PCM_H
3 3
4snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
5 unsigned int rate);
6
4void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); 7void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
5 8
6int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, 9int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
index e184349aee83..b4dcccc237dc 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,
718 subs->hwptr_done += bytes; 718 subs->hwptr_done += bytes;
719 if (subs->hwptr_done >= runtime->buffer_size * stride) 719 if (subs->hwptr_done >= runtime->buffer_size * stride)
720 subs->hwptr_done -= runtime->buffer_size * stride; 720 subs->hwptr_done -= runtime->buffer_size * stride;
721
722 /* update delay with exact number of samples queued */
723 runtime->delay = subs->last_delay;
721 runtime->delay += frames; 724 runtime->delay += frames;
725 subs->last_delay = runtime->delay;
726
727 /* realign last_frame_number */
728 subs->last_frame_number = usb_get_current_frame_number(subs->dev);
729 subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
730
722 spin_unlock_irqrestore(&subs->lock, flags); 731 spin_unlock_irqrestore(&subs->lock, flags);
723 urb->transfer_buffer_length = bytes; 732 urb->transfer_buffer_length = bytes;
724 if (period_elapsed) 733 if (period_elapsed)
@@ -737,12 +746,27 @@ static int retire_playback_urb(struct snd_usb_substream *subs,
737 unsigned long flags; 746 unsigned long flags;
738 int stride = runtime->frame_bits >> 3; 747 int stride = runtime->frame_bits >> 3;
739 int processed = urb->transfer_buffer_length / stride; 748 int processed = urb->transfer_buffer_length / stride;
749 int est_delay;
740 750
741 spin_lock_irqsave(&subs->lock, flags); 751 spin_lock_irqsave(&subs->lock, flags);
742 if (processed > runtime->delay) 752
743 runtime->delay = 0; 753 est_delay = snd_usb_pcm_delay(subs, runtime->rate);
754 /* update delay with exact number of samples played */
755 if (processed > subs->last_delay)
756 subs->last_delay = 0;
744 else 757 else
745 runtime->delay -= processed; 758 subs->last_delay -= processed;
759 runtime->delay = subs->last_delay;
760
761 /*
762 * Report when delay estimate is off by more than 2ms.
763 * The error should be lower than 2ms since the estimate relies
764 * on two reads of a counter updated every ms.
765 */
766 if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
767 snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n",
768 est_delay, subs->last_delay);
769
746 spin_unlock_irqrestore(&subs->lock, flags); 770 spin_unlock_irqrestore(&subs->lock, flags);
747 return 0; 771 return 0;
748} 772}