diff --git a/include/rawmidi.h b/include/rawmidi.h index 5866d133..5f44d3d9 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -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); diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 43619a14..bbb025e3 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -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); +} diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c index 6bfbc1f3..c6afec10 100644 --- a/src/rawmidi/rawmidi_hw.c +++ b/src/rawmidi/rawmidi_hw.c @@ -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 }; diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h index c4068d7c..4a88e7e4 100644 --- a/src/rawmidi/rawmidi_local.h +++ b/src/rawmidi/rawmidi_local.h @@ -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 { diff --git a/test/rawmidi.c b/test/rawmidi.c index 1090d44b..357abbe4 100644 --- a/test/rawmidi.c +++ b/test/rawmidi.c @@ -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]); } } }