diff options
author | Jaroslav Kysela <perex@perex.cz> | 2012-05-13 07:39:45 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-05-15 02:34:38 -0400 |
commit | b012513c66cfb41f816532f93a934b5c0b38c1bf (patch) | |
tree | d305c6fef148f0e65e2f6c88e2aee36b43617108 /sound | |
parent | 92b862c7d685f5971a954e6ded51891d4adc412b (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.c | 62 |
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 | 449 | static 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 | |||
451 | static 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 | |||
468 | static 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 | ||
477 | static unsigned int loopback_pos_update(struct loopback_cable *cable) | 475 | static 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; |