From 4bb5a9397366f6c773f221f31966ba7cfaffd8a3 Mon Sep 17 00:00:00 2001
From: Jari Vetoniemi <mailroxas@gmail.com>
Date: Sat, 3 Nov 2018 06:13:28 +0200
Subject: simulate mmap access

---
 src/pcm.c       | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/stubs.h     |  2 --
 src/util/util.h |  1 +
 3 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/src/pcm.c b/src/pcm.c
index 014f35a..0acd77d 100644
--- a/src/pcm.c
+++ b/src/pcm.c
@@ -6,6 +6,11 @@
 #include "util/dsp.h"
 #include "util/util.h"
 
+static const snd_pcm_access_t SUPPORTED_ACCESS[] = {
+   SND_PCM_ACCESS_MMAP_INTERLEAVED,
+   SND_PCM_ACCESS_RW_INTERLEAVED
+};
+
 static const struct format_info {
    const char *name;
    snd_pcm_format_t fmt;
@@ -116,6 +121,10 @@ struct _snd_pcm {
    struct _snd_pcm_hw_params hw;
    struct _snd_pcm_sw_params sw;
    struct timespec start_time;
+   struct {
+      snd_pcm_channel_area_t areas[NCHAN_MAX];
+      unsigned char *data;
+   } mmap;
    struct sio_hdl *hdl;
    const char *name;
    snd_pcm_uframes_t position, written, avail;
@@ -260,6 +269,7 @@ int
 snd_pcm_close(snd_pcm_t *pcm)
 {
    sio_close(pcm->hdl);
+   free(pcm->mmap.data);
    free(pcm);
    return 0;
 }
@@ -499,12 +509,48 @@ snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
       ret = snd_pcm_bytes_to_frames(pcm, io.read(buffer, snd_pcm_frames_to_bytes(pcm, size), &state));
    }
 
+   static size_t old_ret;
+   if (ret != old_ret) {
+      WARNX("%lu %lu %lu", pcm->avail, size, ret);
+      old_ret = ret;
+   }
+
    assert(pcm->avail >= ret);
    pcm->written += ret;
    pcm->avail -= ret;
    return ret;
 }
 
+int
+snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames)
+{
+   snd_pcm_uframes_t todo_frames = pcm->avail;
+   if (frames) todo_frames = MIN(todo_frames, *frames);
+
+   if (pcm->hw.stream == SND_PCM_STREAM_CAPTURE)
+      todo_frames = snd_pcm_readi(pcm, pcm->mmap.data, todo_frames);
+
+   if (offset) *offset = 0;
+   if (frames) *frames = todo_frames;
+   if (areas) {
+      const unsigned int chans = (pcm->hw.stream == SND_PCM_STREAM_PLAYBACK ? pcm->hw.par.pchan : pcm->hw.par.rchan);
+      for (unsigned int i = 0; i < chans; ++i)
+         pcm->mmap.areas[i].addr = pcm->mmap.data + snd_pcm_format_size(pcm->hw.format, todo_frames) * i;
+      *areas = pcm->mmap.areas;
+   }
+
+   return 0;
+}
+
+snd_pcm_sframes_t
+snd_pcm_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames)
+{
+   if (pcm->hw.stream == SND_PCM_STREAM_PLAYBACK)
+      return snd_pcm_writei(pcm, pcm->mmap.data, frames);
+
+   return frames;
+}
+
 static uint64_t
 get_time_ns(void)
 {
@@ -692,6 +738,22 @@ out:
    return ret;
 }
 
+static bool
+is_mmap_access(const snd_pcm_access_t access)
+{
+   return (access == SND_PCM_ACCESS_MMAP_INTERLEAVED || access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || access == SND_PCM_ACCESS_MMAP_COMPLEX);
+}
+
+static void
+ensure_mmap_buffer(snd_pcm_t *pcm)
+{
+   free(pcm->mmap.data);
+   pcm->mmap.data = NULL;
+
+   if (is_mmap_access(pcm->hw.access) && !(pcm->mmap.data = calloc(1, snd_pcm_frames_to_bytes(pcm, pcm->hw.par.bufsz))))
+      ERR1(EXIT_FAILURE, "realloc");
+}
+
 int
 snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 {
@@ -704,6 +766,7 @@ snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 
       WARNX("set: rate: %u, round: %u, appbufsz: %u chan: %u", params->par.rate, params->par.round, params->par.appbufsz, (params->stream == SND_PCM_STREAM_PLAYBACK ? params->par.pchan : params->par.rchan));
       pcm->hw = *params;
+      ensure_mmap_buffer(pcm);
    }
 
    return snd_pcm_prepare(pcm);
@@ -726,17 +789,20 @@ snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 int
 snd_pcm_hw_params_test_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)
 {
-   if (_access != SND_PCM_ACCESS_RW_INTERLEAVED) {
-      WARNX("access mode `0x%x` not supported yet", _access);
-      return -1;
+   for (size_t i = 0; i < ARRAY_SIZE(SUPPORTED_ACCESS); ++i) {
+      if (SUPPORTED_ACCESS[i] == _access)
+         return 0;
    }
 
-   return 0;
+   WARNX("access mode `0x%x` not supported yet", _access);
+   return -1;
 }
 
 int
 snd_pcm_hw_params_set_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)
 {
+   WARNX("0x%x", _access);
+
    if (snd_pcm_hw_params_test_access(pcm, params, _access) != 0)
       return -1;
 
diff --git a/src/stubs.h b/src/stubs.h
index 4eafb06..d7c4174 100644
--- a/src/stubs.h
+++ b/src/stubs.h
@@ -645,8 +645,6 @@ int snd_pcm_dump_setup(snd_pcm_t *pcm, snd_output_t *out) { WARNX1("stub"); retu
 int snd_pcm_hw_params_dump(snd_pcm_hw_params_t *params, snd_output_t *out) { WARNX1("stub"); return 0; }
 int snd_pcm_sw_params_dump(snd_pcm_sw_params_t *params, snd_output_t *out) { WARNX1("stub"); return 0; }
 int snd_pcm_status_dump(snd_pcm_status_t *status, snd_output_t *out) { WARNX1("stub"); return 0; }
-int snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames) { WARNX1("stub"); return 0; }
-snd_pcm_sframes_t snd_pcm_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames) { WARNX1("stub"); return 0; }
 snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) { WARNX1("stub"); return 0; }
 snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) { WARNX1("stub"); return 0; }
 snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) { WARNX1("stub"); return 0; }
diff --git a/src/util/util.h b/src/util/util.h
index fe3f50c..a6defc3 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -22,6 +22,7 @@ do_debug(void)
 #define WARNX(x, ...) do { if (do_debug()) warnx("asound: %s " x, __func__, ##__VA_ARGS__); } while (0)
 #define ERRX1(x, y) do { errx(x, "asound: %s %s", __func__, y); } while (0)
 #define ERRX(x, y, ...) do { errx(x, "asound: %s " y, __func__, ##__VA_ARGS__); } while (0)
+#define ERR1(x, y) do { err(x, "asound: %s %s", __func__, y); } while (0)
 #define ERR(x, y, ...) do { err(x, "asound: %s " y, __func__, ##__VA_ARGS__); } while (0)
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-- 
cgit v1.2.3-70-g09d2