aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2012-05-13 07:39:45 -0400
committerTakashi Iwai <tiwai@suse.de>2012-05-15 02:34:38 -0400
commitb012513c66cfb41f816532f93a934b5c0b38c1bf (patch)
treed305c6fef148f0e65e2f6c88e2aee36b43617108 /sound
parent92b862c7d685f5971a954e6ded51891d4adc412b (diff)
ALSA: snd-aloop - improve the sample copy accurracy
Maintain both streams (playback, capture) synchronized. Previous code didn't take in account the small byte count drifts caused by the irq position rounding. Signed-off-by: Jaroslav Kysela <perex@perex.cz> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/drivers/aloop.c62
1 files changed, 35 insertions, 27 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index ad079b63b8ba..8b5c36f4d303 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -117,6 +117,7 @@ struct loopback_pcm {
117 /* timer stuff */ 117 /* timer stuff */
118 unsigned int irq_pos; /* fractional IRQ position */ 118 unsigned int irq_pos; /* fractional IRQ position */
119 unsigned int period_size_frac; 119 unsigned int period_size_frac;
120 unsigned int last_drift;
120 unsigned long last_jiffies; 121 unsigned long last_jiffies;
121 struct timer_list timer; 122 struct timer_list timer;
122}; 123};
@@ -264,6 +265,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
264 return err; 265 return err;
265 dpcm->last_jiffies = jiffies; 266 dpcm->last_jiffies = jiffies;
266 dpcm->pcm_rate_shift = 0; 267 dpcm->pcm_rate_shift = 0;
268 dpcm->last_drift = 0;
267 spin_lock(&cable->lock); 269 spin_lock(&cable->lock);
268 cable->running |= stream; 270 cable->running |= stream;
269 cable->pause &= ~stream; 271 cable->pause &= ~stream;
@@ -444,34 +446,30 @@ static void copy_play_buf(struct loopback_pcm *play,
444 } 446 }
445} 447}
446 448
447#define BYTEPOS_UPDATE_POSONLY 0 449static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
448#define BYTEPOS_UPDATE_CLEAR 1 450 unsigned int jiffies_delta)
449#define BYTEPOS_UPDATE_COPY 2
450
451static void loopback_bytepos_update(struct loopback_pcm *dpcm,
452 unsigned int delta,
453 unsigned int cmd)
454{ 451{
455 unsigned int count;
456 unsigned long last_pos; 452 unsigned long last_pos;
453 unsigned int delta;
457 454
458 last_pos = byte_pos(dpcm, dpcm->irq_pos); 455 last_pos = byte_pos(dpcm, dpcm->irq_pos);
459 dpcm->irq_pos += delta * dpcm->pcm_bps; 456 dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps;
460 count = byte_pos(dpcm, dpcm->irq_pos) - last_pos; 457 delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
461 if (!count) 458 if (delta >= dpcm->last_drift)
462 return; 459 delta -= dpcm->last_drift;
463 if (cmd == BYTEPOS_UPDATE_CLEAR) 460 dpcm->last_drift = 0;
464 clear_capture_buf(dpcm, count);
465 else if (cmd == BYTEPOS_UPDATE_COPY)
466 copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
467 dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
468 count);
469 dpcm->buf_pos += count;
470 dpcm->buf_pos %= dpcm->pcm_buffer_size;
471 if (dpcm->irq_pos >= dpcm->period_size_frac) { 461 if (dpcm->irq_pos >= dpcm->period_size_frac) {
472 dpcm->irq_pos %= dpcm->period_size_frac; 462 dpcm->irq_pos %= dpcm->period_size_frac;
473 dpcm->period_update_pending = 1; 463 dpcm->period_update_pending = 1;
474 } 464 }
465 return delta;
466}
467
468static inline void bytepos_finish(struct loopback_pcm *dpcm,
469 unsigned int delta)
470{
471 dpcm->buf_pos += delta;
472 dpcm->buf_pos %= dpcm->pcm_buffer_size;
475} 473}
476 474
477static unsigned int loopback_pos_update(struct loopback_cable *cable) 475static unsigned int loopback_pos_update(struct loopback_cable *cable)
@@ -481,7 +479,7 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
481 struct loopback_pcm *dpcm_capt = 479 struct loopback_pcm *dpcm_capt =
482 cable->streams[SNDRV_PCM_STREAM_CAPTURE]; 480 cable->streams[SNDRV_PCM_STREAM_CAPTURE];
483 unsigned long delta_play = 0, delta_capt = 0; 481 unsigned long delta_play = 0, delta_capt = 0;
484 unsigned int running; 482 unsigned int running, count1, count2;
485 unsigned long flags; 483 unsigned long flags;
486 484
487 spin_lock_irqsave(&cable->lock, flags); 485 spin_lock_irqsave(&cable->lock, flags);
@@ -500,12 +498,13 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
500 goto unlock; 498 goto unlock;
501 499
502 if (delta_play > delta_capt) { 500 if (delta_play > delta_capt) {
503 loopback_bytepos_update(dpcm_play, delta_play - delta_capt, 501 count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
504 BYTEPOS_UPDATE_POSONLY); 502 bytepos_finish(dpcm_play, count1);
505 delta_play = delta_capt; 503 delta_play = delta_capt;
506 } else if (delta_play < delta_capt) { 504 } else if (delta_play < delta_capt) {
507 loopback_bytepos_update(dpcm_capt, delta_capt - delta_play, 505 count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play);
508 BYTEPOS_UPDATE_CLEAR); 506 clear_capture_buf(dpcm_capt, count1);
507 bytepos_finish(dpcm_capt, count1);
509 delta_capt = delta_play; 508 delta_capt = delta_play;
510 } 509 }
511 510
@@ -513,8 +512,17 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
513 goto unlock; 512 goto unlock;
514 513
515 /* note delta_capt == delta_play at this moment */ 514 /* note delta_capt == delta_play at this moment */
516 loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); 515 count1 = bytepos_delta(dpcm_play, delta_play);
517 loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); 516 count2 = bytepos_delta(dpcm_capt, delta_capt);
517 if (count1 < count2) {
518 dpcm_capt->last_drift = count2 - count1;
519 count1 = count2;
520 } else if (count1 > count2) {
521 dpcm_play->last_drift = count1 - count2;
522 }
523 copy_play_buf(dpcm_play, dpcm_capt, count1);
524 bytepos_finish(dpcm_play, count1);
525 bytepos_finish(dpcm_capt, count1);
518 unlock: 526 unlock:
519 spin_unlock_irqrestore(&cable->lock, flags); 527 spin_unlock_irqrestore(&cable->lock, flags);
520 return running; 528 return running;