]> git.alsa-project.org Git - alsa-lib.git/commitdiff
pcm: multi plugin: detach the hw_ptr and appl_ptr from master_slave
authorJaroslav Kysela <perex@perex.cz>
Tue, 9 Apr 2019 08:12:34 +0000 (10:12 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 9 Apr 2019 08:27:36 +0000 (10:27 +0200)
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 <perex@perex.cz>
src/pcm/pcm_multi.c

index 0afd31bf5980dcf128fbbe8e3cfa4543b78c6444..065ae3f2978d2a068e039e694da59525d7790284 100644 (file)
@@ -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;
 }