From 369df322577034ea872978d6470edbadf30d81a9 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 9 Apr 2019 10:12:34 +0200 Subject: [PATCH] pcm: multi plugin: detach the hw_ptr and appl_ptr from master_slave Unfortunately, the master_slave buffer pointers are not always in sync with the presented avail value and the higher layers (like write_areas) got confused. Create own hw_ptr and appl_ptr. This commit also tries to fix the hwsync and delay implementation (iterate through all slaves). The multi plugin was designed only for hardware which runs really in sync. Anyway, users are trying to use this plugin for other purposes. Signed-off-by: Jaroslav Kysela --- src/pcm/pcm_multi.c | 63 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 0afd31bf..065ae3f2 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -54,6 +54,7 @@ typedef struct { } snd_pcm_multi_channel_t; typedef struct { + snd_pcm_uframes_t appl_ptr, hw_ptr; unsigned int slaves_count; unsigned int master_slave; snd_pcm_multi_slave_t *slaves; @@ -401,18 +402,65 @@ static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm) return snd_pcm_state(slave); } +static void snd_pcm_multi_hwptr_update(snd_pcm_t *pcm) +{ + snd_pcm_multi_t *multi = pcm->private_data; + snd_pcm_uframes_t hw_ptr = 0, slave_hw_ptr, avail, last_avail; + unsigned int i; + /* the logic is really simple, choose the lowest hw_ptr from slaves */ + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + last_avail = 0; + for (i = 0; i < multi->slaves_count; ++i) { + slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr; + avail = __snd_pcm_playback_avail(pcm, multi->hw_ptr, slave_hw_ptr); + if (avail > last_avail) { + hw_ptr = slave_hw_ptr; + last_avail = avail; + } + } + } else { + last_avail = LONG_MAX; + for (i = 0; i < multi->slaves_count; ++i) { + slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr; + avail = __snd_pcm_capture_avail(pcm, multi->hw_ptr, slave_hw_ptr); + if (avail < last_avail) { + hw_ptr = slave_hw_ptr; + last_avail = avail; + } + } + } + multi->hw_ptr = hw_ptr; +} + static int snd_pcm_multi_hwsync(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private_data; - snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; - return snd_pcm_hwsync(slave); + unsigned int i; + int err; + for (i = 0; i < multi->slaves_count; ++i) { + err = snd_pcm_hwsync(multi->slaves[i].pcm); + if (err < 0) + return err; + } + snd_pcm_multi_hwptr_update(pcm); + return 0; } static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) { snd_pcm_multi_t *multi = pcm->private_data; - snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; - return snd_pcm_delay(slave, delayp); + snd_pcm_sframes_t d, dr = 0; + unsigned int i; + int err; + for (i = 0; i < multi->slaves_count; ++i) { + err = snd_pcm_delay(multi->slaves[i].pcm, &d); + if (err < 0) + return err; + if (dr < d) + dr = d; + } + *delayp = dr; + return 0; } static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm) @@ -428,6 +476,7 @@ static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm) if (ret > avail) ret = avail; } + snd_pcm_multi_hwptr_update(pcm); return ret; } @@ -731,6 +780,8 @@ static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm, if ((snd_pcm_uframes_t)result != size) return -EIO; } + multi->appl_ptr += size; + multi->appl_ptr %= pcm->boundary; return size; } @@ -1081,8 +1132,8 @@ int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd; pcm->poll_events = multi->slaves[master_slave].pcm->poll_events; pcm->tstamp_type = multi->slaves[master_slave].pcm->tstamp_type; - snd_pcm_link_hw_ptr(pcm, multi->slaves[master_slave].pcm); - snd_pcm_link_appl_ptr(pcm, multi->slaves[master_slave].pcm); + snd_pcm_set_hw_ptr(pcm, &multi->hw_ptr, -1, 0); + snd_pcm_set_appl_ptr(pcm, &multi->appl_ptr, -1, 0); *pcmp = pcm; return 0; } -- 2.47.1