]> git.alsa-project.org Git - alsa-lib.git/commitdiff
rawmidi: define more abstract API for the timestamp reads
authorJaroslav Kysela <perex@perex.cz>
Tue, 24 Aug 2021 09:36:30 +0000 (11:36 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 28 Oct 2021 06:40:20 +0000 (08:40 +0200)
The frame structure is a bit internal thing for the kernel
data transfer implementation. Introduce snd_rawmidi_tread()
function which is straight for the application usage and hides
the framing data transfers (kernel space API).

The current code implements the read cache and does the merging
of the frame reads with the similar timestamps (opposite
to the kernel data split for big chunks).

If the application wants to use super-duper-lighting-fast reads,
the snd_rawmidi_read() may be used, but the structure must be
defined on it's own, because this mechanism is not preferred
and unsupported.

BugLink: https://github.com/alsa-project/alsa-lib/issues/172
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
include/rawmidi.h
src/rawmidi/rawmidi.c
src/rawmidi/rawmidi_hw.c
src/rawmidi/rawmidi_local.h
test/rawmidi.c

index 5866d133b46b0279c7ac3683234300594e6d83a4..5f44d3d9c2165ceea53e4d7875cb2e2417dd5aea 100644 (file)
@@ -93,26 +93,6 @@ typedef enum _snd_rawmidi_framing {
        SND_RAWMIDI_FRAMING_TSTAMP = 1,
 } snd_rawmidi_framing_t;
 
-#define SND_RAWMIDI_FRAME_TYPE_DEFAULT 0
-
-#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16
-
-/** Incoming RawMidi bytes is put inside this container if tstamp type framing is enabled. */
-struct _snd_rawmidi_framing_tstamp {
-       /**
-        * For now, frame_type is always SND_RAWMIDI_FRAME_TYPE_DEFAULT.
-        * Midi 2.0 is expected to add new types here.
-        * Applications are expected to skip unknown frame types.
-        */
-       uint8_t frame_type;
-       uint8_t length; /* number of valid bytes in data field */
-       uint8_t reserved[2];
-       uint32_t tv_nsec;               /* nanoseconds */
-       uint64_t tv_sec;                /* seconds */
-       uint8_t data[SND_RAWMIDI_FRAMING_DATA_LENGTH];
-} __attribute__((packed));
-typedef struct _snd_rawmidi_framing_tstamp snd_rawmidi_framing_tstamp_t;
-
 int snd_rawmidi_open(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi,
                     const char *name, int mode);
 int snd_rawmidi_open_lconf(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi,
@@ -184,6 +164,7 @@ int snd_rawmidi_drain(snd_rawmidi_t *rmidi);
 int snd_rawmidi_drop(snd_rawmidi_t *rmidi);
 ssize_t snd_rawmidi_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size);
 ssize_t snd_rawmidi_read(snd_rawmidi_t *rmidi, void *buffer, size_t size);
+ssize_t snd_rawmidi_tread(snd_rawmidi_t *rmidi, struct timespec *tstamp, void *buffer, size_t size);
 const char *snd_rawmidi_name(snd_rawmidi_t *rmidi);
 snd_rawmidi_type_t snd_rawmidi_type(snd_rawmidi_t *rmidi);
 snd_rawmidi_stream_t snd_rawmidi_stream(snd_rawmidi_t *rawmidi);
index 43619a1403cae2b3f1602c21d5b01b312777c7a8..bbb025e37812cc6db03b37f914608dca388c4c38 100644 (file)
@@ -1071,11 +1071,30 @@ ssize_t snd_rawmidi_write(snd_rawmidi_t *rawmidi, const void *buffer, size_t siz
  * \param rawmidi RawMidi handle
  * \param buffer buffer to store the input MIDI bytes
  * \param size input buffer size in bytes
+ * \retval count of MIDI bytes otherwise a negative error code
  */
 ssize_t snd_rawmidi_read(snd_rawmidi_t *rawmidi, void *buffer, size_t size)
 {
        assert(rawmidi);
        assert(rawmidi->stream == SND_RAWMIDI_STREAM_INPUT);
+       if ((rawmidi->params_mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK) == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
+               size &= ~(sizeof(struct snd_rawmidi_framing_tstamp) - 1);
        assert(buffer || size == 0);
        return (rawmidi->ops->read)(rawmidi, buffer, size);
 }
+
+/**
+ * \brief read MIDI bytes from MIDI stream with timestamp
+ * \param rawmidi RawMidi handle
+ * \param[out] tstamp timestamp for the returned MIDI bytes
+ * \param buffer buffer to store the input MIDI bytes
+ * \param size input buffer size in bytes
+ * \retval count of MIDI bytes otherwise a negative error code
+ */
+ssize_t snd_rawmidi_tread(snd_rawmidi_t *rawmidi, struct timespec *tstamp, void *buffer, size_t size)
+{
+       assert(rawmidi);
+       assert(rawmidi->stream == SND_RAWMIDI_STREAM_INPUT);
+       assert(buffer || size == 0);
+       return (rawmidi->ops->tread)(rawmidi, tstamp, buffer, size);
+}
index 6bfbc1f3681378e3b7ad233a4c12f162c2238f3a..c6afec10435084bb1d9ed48311be77a883ba81bd 100644 (file)
@@ -42,9 +42,21 @@ typedef struct {
        int open;
        int fd;
        int card, device, subdevice;
+       unsigned char *buf;
+       size_t buf_size;        /* total buffer size in bytes */
+       size_t buf_fill;        /* filled buffer size in bytes */
+       size_t buf_pos;         /* offset to frame in the read buffer (bytes) */
+       size_t buf_fpos;        /* offset to the frame data array (bytes 0-16) */
 } snd_rawmidi_hw_t;
 #endif
 
+static void buf_reset(snd_rawmidi_hw_t *hw)
+{
+       hw->buf_fill = 0;
+       hw->buf_pos = 0;
+       hw->buf_fpos = 0;
+}
+
 static int snd_rawmidi_hw_close(snd_rawmidi_t *rmidi)
 {
        snd_rawmidi_hw_t *hw = rmidi->private_data;
@@ -57,6 +69,7 @@ static int snd_rawmidi_hw_close(snd_rawmidi_t *rmidi)
                err = -errno;
                SYSERR("close failed\n");
        }
+       free(hw->buf);
        free(hw);
        return err;
 }
@@ -100,6 +113,7 @@ static int snd_rawmidi_hw_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * pa
                SYSERR("SNDRV_RAWMIDI_IOCTL_PARAMS failed");
                return -errno;
        }
+       buf_reset(hw);
        return 0;
 }
 
@@ -122,6 +136,7 @@ static int snd_rawmidi_hw_drop(snd_rawmidi_t *rmidi)
                SYSERR("SNDRV_RAWMIDI_IOCTL_DROP failed");
                return -errno;
        }
+       buf_reset(hw);
        return 0;
 }
 
@@ -156,6 +171,96 @@ static ssize_t snd_rawmidi_hw_read(snd_rawmidi_t *rmidi, void *buffer, size_t si
        return result;
 }
 
+static ssize_t read_from_ts_buf(snd_rawmidi_hw_t *hw, struct timespec *tstamp,
+                               void *buffer, size_t size)
+{
+       struct snd_rawmidi_framing_tstamp *f;
+       size_t flen;
+       ssize_t result = 0;
+
+       f = (struct snd_rawmidi_framing_tstamp *)(hw->buf + hw->buf_pos);
+       while (hw->buf_fill >= sizeof(*f)) {
+               if (f->frame_type == 0) {
+                       tstamp->tv_sec = f->tv_sec;
+                       tstamp->tv_nsec = f->tv_nsec;
+                       break;
+               }
+               hw->buf_pos += sizeof(*f);
+               hw->buf_fill -= sizeof(*f);
+               f++;
+       }
+       while (size > 0 && hw->buf_fill >= sizeof(*f)) {
+               /* skip other frames */
+               if (f->frame_type != 0)
+                       goto __next;
+               if (f->length == 0 || f->length > SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
+                       return -EINVAL;
+               if (tstamp->tv_sec != (time_t)f->tv_sec ||
+                   tstamp->tv_nsec != f->tv_nsec)
+                       break;
+               flen = f->length - hw->buf_fpos;
+               if (size < flen) {
+                       /* partial copy */
+                       memcpy(buffer, f->data + hw->buf_fpos, size);
+                       hw->buf_fpos += size;
+                       result += size;
+                       break;
+               }
+               memcpy(buffer, f->data + hw->buf_fpos, flen);
+               hw->buf_fpos = 0;
+               buffer += flen;
+               size -= flen;
+               result += flen;
+            __next:
+               hw->buf_pos += sizeof(*f);
+               hw->buf_fill -= sizeof(*f);
+               f++;
+       }
+       return result;
+}
+
+static ssize_t snd_rawmidi_hw_tread(snd_rawmidi_t *rmidi, struct timespec *tstamp,
+                                   void *buffer, size_t size)
+{
+       snd_rawmidi_hw_t *hw = rmidi->private_data;
+       ssize_t result = 0, ret;
+       size_t alloc_size;
+
+       /* no timestamp */
+       tstamp->tv_sec = tstamp->tv_nsec = 0;
+
+       /* copy buffered frames */
+       if (hw->buf_fill > 0) {
+               result = read_from_ts_buf(hw, tstamp, buffer, size);
+               if (result < 0 || size == (size_t)result ||
+                   hw->buf_fill >= sizeof(struct snd_rawmidi_framing_tstamp))
+                       return result;
+               buffer += result;
+               size -= result;
+       }
+
+       alloc_size = page_align(size * 2);      /* keep room for the frame meta data */
+       if (alloc_size > hw->buf_size) {
+               void *buf = realloc(hw->buf, alloc_size);
+               if (buf == NULL)
+                       return result > 0 ? result : -ENOMEM;
+               hw->buf = buf;
+               hw->buf_size = alloc_size;
+       }
+
+       buf_reset(hw);
+       ret = read(hw->fd, hw->buf, hw->buf_size);
+       if (ret < 0)
+               return result > 0 ? result : -errno;
+       if (ret < (ssize_t)sizeof(struct snd_rawmidi_framing_tstamp))
+               return result;
+       hw->buf_fill = ret;
+       ret = read_from_ts_buf(hw, tstamp, buffer, size);
+       if (ret < 0 && result > 0)
+               return result;
+       return ret + result;
+}
+
 static const snd_rawmidi_ops_t snd_rawmidi_hw_ops = {
        .close = snd_rawmidi_hw_close,
        .nonblock = snd_rawmidi_hw_nonblock,
@@ -166,6 +271,7 @@ static const snd_rawmidi_ops_t snd_rawmidi_hw_ops = {
        .drain = snd_rawmidi_hw_drain,
        .write = snd_rawmidi_hw_write,
        .read = snd_rawmidi_hw_read,
+       .tread = snd_rawmidi_hw_tread
 };
 
 
index c4068d7c8fb213302c6451fdda8f9d5910d337fc..4a88e7e4a5baa87b536c839bbb145b3fa6ee580f 100644 (file)
@@ -34,6 +34,7 @@ typedef struct {
        int (*drain)(snd_rawmidi_t *rawmidi);
        ssize_t (*write)(snd_rawmidi_t *rawmidi, const void *buffer, size_t size);
        ssize_t (*read)(snd_rawmidi_t *rawmidi, void *buffer, size_t size);
+       ssize_t (*tread)(snd_rawmidi_t *rawmidi, struct timespec *tstamp, void *buffer, size_t size);
 } snd_rawmidi_ops_t;
 
 struct _snd_rawmidi {
index 1090d44bcfe3fb3669cdae0e6e16f84370f45703..357abbe4f95b3e716846dd85dbcb435bf5e7669c 100644 (file)
@@ -179,27 +179,27 @@ int main(int argc,char** argv)
                }
 
                if (handle_in) {
-                       unsigned char ch;
-                       snd_rawmidi_framing_tstamp_t frame;
+                       unsigned char buf[1024];
+                       ssize_t ret;
                        while (!stop) {
                                if (clock_type != -1) {
-                                       snd_rawmidi_read(handle_in, &frame, sizeof(frame));
-                                       if (verbose) {
-                                               int i;
-                                               if (frame.frame_type) {
-                                                       fprintf(stderr, "read unknown frame %d", frame.frame_type);
-                                                       continue;
-                                               }
-                                               fprintf(stderr, "read [%lld:%09d]", frame.tv_sec, frame.tv_nsec);
-                                               for (i = 0; i < frame.length; i++)
-                                                       fprintf(stderr, " %02x", frame.data[i]);
+                                       struct timespec tstamp;
+                                       ret = snd_rawmidi_tread(handle_in, &tstamp, buf, sizeof(buf));
+                                       if (ret < 0)
+                                               fprintf(stderr, "read timestamp error: %d - %s\n", (int)ret, snd_strerror(ret));
+                                       if (ret > 0 && verbose) {
+                                               fprintf(stderr, "read [%lld:%09lld]", (long long)tstamp.tv_sec, (long long)tstamp.tv_nsec);
+                                               for (i = 0; i < ret; i++)
+                                                       fprintf(stderr, " %02x", buf[i]);
                                                fprintf(stderr, "\n");
                                        }
-                               }
-                               else {
-                                       snd_rawmidi_read(handle_in,&ch,1);
-                                       if (verbose)
-                                               fprintf(stderr,"read %02x\n",ch);
+                               } else {
+                                       ret = snd_rawmidi_read(handle_in, buf, sizeof(buf));
+                                       if (ret < 0)
+                                               fprintf(stderr, "read error: %d - %s\n", (int)ret, snd_strerror(ret));
+                                       if (ret > 0 && verbose)
+                                               for (i = 0; i < ret; i++)
+                                                       fprintf(stderr,"read %02x\n",buf[i]);
                                }
                        }
                }