aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2010-07-30 19:58:12 +0200
committerJaroslav Kysela <perex@perex.cz>2010-07-30 19:58:12 +0200
commit6d6173d248e5d54ae553e1f44e6147a2e5d93e8d (patch)
tree916c78b5f2a4912de3d70eca6d87af34a2df975a
parent433580cdc6bd71908743e47c429b5b5ad90d12bf (diff)
downloadalsa-driver-build-unstable-6d6173d248e5d54ae553e1f44e6147a2e5d93e8d.tar.gz
snd-aloop: Fix the garbage copy at the end of playback (draining)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--drivers/aloop-kernel.c72
1 files changed, 47 insertions, 25 deletions
diff --git a/drivers/aloop-kernel.c b/drivers/aloop-kernel.c
index 9fc3c3f54..6f3724bb8 100644
--- a/drivers/aloop-kernel.c
+++ b/drivers/aloop-kernel.c
@@ -227,31 +227,6 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static void copy_play_buf(struct loopback_pcm *play,
- struct loopback_pcm *capt,
- unsigned int bytes)
-{
- char *src = play->substream->runtime->dma_area;
- char *dst = capt->substream->runtime->dma_area;
- unsigned int src_off = play->buf_pos;
- unsigned int dst_off = capt->buf_pos;
-
- for (;;) {
- unsigned int size = bytes;
- if (src_off + size > play->pcm_buffer_size)
- size = play->pcm_buffer_size - src_off;
- if (dst_off + size > capt->pcm_buffer_size)
- size = capt->pcm_buffer_size - dst_off;
- memcpy(dst + dst_off, src + src_off, size);
- capt->silent_size = 0;
- bytes -= size;
- if (!bytes)
- break;
- src_off = (src_off + size) % play->pcm_buffer_size;
- dst_off = (dst_off + size) % capt->pcm_buffer_size;
- }
-}
-
static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
{
struct snd_pcm_runtime *runtime = dpcm->substream->runtime;
@@ -278,6 +253,53 @@ static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
}
}
+static void copy_play_buf(struct loopback_pcm *play,
+ struct loopback_pcm *capt,
+ unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = play->substream->runtime;
+ char *src = play->substream->runtime->dma_area;
+ char *dst = capt->substream->runtime->dma_area;
+ unsigned int src_off = play->buf_pos;
+ unsigned int dst_off = capt->buf_pos;
+ unsigned int clear_bytes = 0;
+
+ /* check if playback is draining, trim the capture copy size
+ * when our pointer is at the end of playback ring buffer */
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+ snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) {
+ snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
+ appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
+ appl_ptr1 -= appl_ptr1 % runtime->buffer_size;
+ appl_ptr1 += play->buf_pos / play->pcm_salign;
+ if (appl_ptr < appl_ptr1)
+ appl_ptr1 -= runtime->buffer_size;
+ diff = (appl_ptr - appl_ptr1) * play->pcm_salign;
+ if (diff < bytes) {
+ clear_bytes = bytes - diff;
+ bytes = diff;
+ }
+ }
+
+ for (;;) {
+ unsigned int size = bytes;
+ if (src_off + size > play->pcm_buffer_size)
+ size = play->pcm_buffer_size - src_off;
+ if (dst_off + size > capt->pcm_buffer_size)
+ size = capt->pcm_buffer_size - dst_off;
+ memcpy(dst + dst_off, src + src_off, size);
+ capt->silent_size = 0;
+ bytes -= size;
+ if (!bytes)
+ break;
+ src_off = (src_off + size) % play->pcm_buffer_size;
+ dst_off = (dst_off + size) % capt->pcm_buffer_size;
+ }
+
+ if (clear_bytes > 0)
+ clear_capture_buf(capt, clear_bytes);
+}
+
#define BYTEPOS_UPDATE_POSONLY 0
#define BYTEPOS_UPDATE_CLEAR 1
#define BYTEPOS_UPDATE_COPY 2