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,
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);
* \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);
+}
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;
err = -errno;
SYSERR("close failed\n");
}
+ free(hw->buf);
free(hw);
return err;
}
SYSERR("SNDRV_RAWMIDI_IOCTL_PARAMS failed");
return -errno;
}
+ buf_reset(hw);
return 0;
}
SYSERR("SNDRV_RAWMIDI_IOCTL_DROP failed");
return -errno;
}
+ buf_reset(hw);
return 0;
}
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,
.drain = snd_rawmidi_hw_drain,
.write = snd_rawmidi_hw_write,
.read = snd_rawmidi_hw_read,
+ .tread = snd_rawmidi_hw_tread
};
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 {
}
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]);
}
}
}