* [PATCH v2 0/2] alsa-lib patches for rawmidi tstamp framing
@ 2021-08-17 12:51 David Henningsson
2021-08-17 12:51 ` [PATCH v2 1/2] Add rawmidi framing API David Henningsson
2021-08-17 12:51 ` [PATCH v2 2/2] Add test for " David Henningsson
0 siblings, 2 replies; 4+ messages in thread
From: David Henningsson @ 2021-08-17 12:51 UTC (permalink / raw)
To: tiwai, alsa-devel, perex; +Cc: David Henningsson
v2: Improved commit messages
David Henningsson (2):
Add rawmidi framing API
Add test for rawmidi framing API
include/rawmidi.h | 26 ++++++++++++
include/sound/uapi/asound.h | 30 +++++++++++++-
src/rawmidi/rawmidi.c | 83 +++++++++++++++++++++++++++++++++++++
src/rawmidi/rawmidi_hw.c | 2 +
src/rawmidi/rawmidi_local.h | 2 +
test/rawmidi.c | 65 ++++++++++++++++++++++++++---
6 files changed, 200 insertions(+), 8 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 1/2] Add rawmidi framing API
2021-08-17 12:51 [PATCH v2 0/2] alsa-lib patches for rawmidi tstamp framing David Henningsson
@ 2021-08-17 12:51 ` David Henningsson
2021-08-17 13:46 ` Takashi Iwai
2021-08-17 12:51 ` [PATCH v2 2/2] Add test for " David Henningsson
1 sibling, 1 reply; 4+ messages in thread
From: David Henningsson @ 2021-08-17 12:51 UTC (permalink / raw)
To: tiwai, alsa-devel, perex; +Cc: David Henningsson
Optionally, incoming rawmidi bytes can be put inside a frame of type snd_rawmidi_framing_tstamp_t.
The main current benefit is that can enable in-kernel timestamping of incoming bytes, and that
timestamp is likely to be more precise than what userspace can offer.
Tstamp type framing requires a kernel >= 5.14 and a buffer size that is a multiple of
sizeof(snd_rawmidi_framing_tstamp_t). It is only available on input streams.
Signed-off-by: David Henningsson <coding@diwic.se>
---
include/rawmidi.h | 26 ++++++++++++
include/sound/uapi/asound.h | 30 +++++++++++++-
src/rawmidi/rawmidi.c | 83 +++++++++++++++++++++++++++++++++++++
src/rawmidi/rawmidi_hw.c | 2 +
src/rawmidi/rawmidi_local.h | 2 +
5 files changed, 141 insertions(+), 2 deletions(-)
diff --git a/include/rawmidi.h b/include/rawmidi.h
index d88c932c..2f27cb78 100644
--- a/include/rawmidi.h
+++ b/include/rawmidi.h
@@ -79,6 +79,27 @@ typedef enum _snd_rawmidi_type {
SND_RAWMIDI_TYPE_VIRTUAL
} snd_rawmidi_type_t;
+#define SND_RAWMIDI_CLOCK_NONE (0<<3)
+#define SND_RAWMIDI_CLOCK_REALTIME (1<<3)
+#define SND_RAWMIDI_CLOCK_MONOTONIC (2<<3)
+#define SND_RAWMIDI_CLOCK_MONOTONIC_RAW (3<<3)
+
+#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16
+
+/** Incoming RawMidi bytes is put inside this container if tstamp type framing is enabled. */
+typedef struct _snd_rawmidi_framing_tstamp {
+ /**
+ * For now, frame_type is always 0. Midi 2.0 is expected to add new
+ * types here. Applications are expected to skip unknown frame types.
+ */
+ __u8 frame_type;
+ __u8 length; /* number of valid bytes in data field */
+ __u8 reserved[2];
+ __u32 tv_nsec; /* nanoseconds */
+ __u64 tv_sec; /* seconds */
+ __u8 data[SND_RAWMIDI_FRAMING_DATA_LENGTH];
+} 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,
@@ -126,6 +147,11 @@ int snd_rawmidi_params_set_avail_min(snd_rawmidi_t *rmidi, snd_rawmidi_params_t
size_t snd_rawmidi_params_get_avail_min(const snd_rawmidi_params_t *params);
int snd_rawmidi_params_set_no_active_sensing(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params, int val);
int snd_rawmidi_params_get_no_active_sensing(const snd_rawmidi_params_t *params);
+int snd_rawmidi_params_set_framing_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, unsigned int val);
+int snd_rawmidi_params_get_framing_type(const snd_rawmidi_params_t *params);
+int snd_rawmidi_params_set_clock_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, unsigned int val);
+int snd_rawmidi_params_get_clock_type(const snd_rawmidi_params_t *params);
+
int snd_rawmidi_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params);
int snd_rawmidi_params_current(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params);
size_t snd_rawmidi_status_sizeof(void);
diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h
index ec610c27..9fe3943f 100644
--- a/include/sound/uapi/asound.h
+++ b/include/sound/uapi/asound.h
@@ -702,7 +702,7 @@ enum {
* Raw MIDI section - /dev/snd/midi??
*/
-#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1)
+#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2)
enum {
SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
@@ -728,12 +728,38 @@ struct snd_rawmidi_info {
unsigned char reserved[64]; /* reserved for future use */
};
+#define SNDRV_RAWMIDI_MODE_FRAMING_MASK (7<<0)
+#define SNDRV_RAWMIDI_MODE_FRAMING_SHIFT 0
+#define SNDRV_RAWMIDI_MODE_FRAMING_NONE (0<<0)
+#define SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP (1<<0)
+#define SNDRV_RAWMIDI_MODE_CLOCK_MASK (7<<3)
+#define SNDRV_RAWMIDI_MODE_CLOCK_SHIFT 3
+#define SNDRV_RAWMIDI_MODE_CLOCK_NONE (0<<3)
+#define SNDRV_RAWMIDI_MODE_CLOCK_REALTIME (1<<3)
+#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC (2<<3)
+#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW (3<<3)
+
+#define SNDRV_RAWMIDI_FRAMING_DATA_LENGTH 16
+
+struct snd_rawmidi_framing_tstamp {
+ /* For now, frame_type is always 0. Midi 2.0 is expected to add new
+ * types here. Applications are expected to skip unknown frame types.
+ */
+ __u8 frame_type;
+ __u8 length; /* number of valid bytes in data field */
+ __u8 reserved[2];
+ __u32 tv_nsec; /* nanoseconds */
+ __u64 tv_sec; /* seconds */
+ __u8 data[SNDRV_RAWMIDI_FRAMING_DATA_LENGTH];
+} __packed;
+
struct snd_rawmidi_params {
int stream;
size_t buffer_size; /* queue size in bytes */
size_t avail_min; /* minimum avail bytes for wakeup */
unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
- unsigned char reserved[16]; /* reserved for future use */
+ unsigned int mode; /* For input data only, frame incoming data */
+ unsigned char reserved[12]; /* reserved for future use */
};
struct snd_rawmidi_status {
diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c
index 55f44821..8ca7a8ac 100644
--- a/src/rawmidi/rawmidi.c
+++ b/src/rawmidi/rawmidi.c
@@ -118,6 +118,15 @@ hw:soundwave,1,2
hw:DEV=1,CARD=soundwave,SUBDEV=2
\endcode
+\section rawmidi_framing Framing of rawmidi data
+
+Optionally, incoming rawmidi bytes can be put inside a frame of type snd_rawmidi_framing_tstamp_t.
+The main current benefit is that can enable in-kernel timestamping of incoming bytes, and that
+timestamp is likely to be more precise than what userspace can offer.
+
+Tstamp type framing requires a kernel >= 5.14 and a buffer size that is a multiple of
+sizeof(snd_rawmidi_framing_tstamp_t). It is only available on input streams.
+
\section rawmidi_examples Examples
The full featured examples with cross-links:
@@ -154,6 +163,7 @@ static int snd_rawmidi_params_default(snd_rawmidi_t *rawmidi, snd_rawmidi_params
params->buffer_size = page_size();
params->avail_min = 1;
params->no_active_sensing = 1;
+ params->mode = 0;
return 0;
}
@@ -811,6 +821,77 @@ int snd_rawmidi_params_get_no_active_sensing(const snd_rawmidi_params_t *params)
return params->no_active_sensing;
}
+/**
+ * \brief enable or disable rawmidi framing
+ * \param rawmidi RawMidi handle
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \param val (0 = no framing, 1 = tstamp type framing)
+ * \return 0 on success, otherwise a negative error code.
+ *
+ * Notable error codes:
+ * -EINVAL - "val" is invalid
+ * -ENOTSUP - Kernel is too old to support framing.
+ *
+ */
+int snd_rawmidi_params_set_framing_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, unsigned int val)
+{
+ assert(rawmidi && params);
+ if (val > SNDRV_RAWMIDI_MODE_FRAMING_MASK >> SNDRV_RAWMIDI_MODE_FRAMING_SHIFT)
+ return -EINVAL;
+ if (val != SNDRV_RAWMIDI_MODE_FRAMING_NONE &&
+ (rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) || rawmidi->stream != SND_RAWMIDI_STREAM_INPUT))
+ return -ENOTSUP;
+ params->mode = (params->mode & ~SNDRV_RAWMIDI_MODE_FRAMING_MASK) + (val << SNDRV_RAWMIDI_MODE_FRAMING_SHIFT);
+ return 0;
+}
+
+/**
+ * \brief get current framing type
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \return the current type (0 = no framing, 1 = tstamp type framing)
+ */
+int snd_rawmidi_params_get_framing_type(const snd_rawmidi_params_t *params)
+{
+ assert(params);
+ return (params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK) >> SNDRV_RAWMIDI_MODE_FRAMING_SHIFT;
+}
+
+/**
+ * \brief sets clock type for tstamp type framing
+ * \param rawmidi RawMidi handle
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \param val one of the SND_RAWMIDI_CLOCK_* constants
+ * \return 0 on success, otherwise a negative error code.
+ *
+ * Notable error codes:
+ * -EINVAL - "val" is invalid
+ * -ENOTSUP - Kernel is too old to support framing.
+ *
+ */
+int snd_rawmidi_params_set_clock_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, unsigned int val)
+{
+ assert(rawmidi && params);
+ if (val > SNDRV_RAWMIDI_MODE_CLOCK_MASK >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT)
+ return -EINVAL;
+ if (val != SNDRV_RAWMIDI_MODE_CLOCK_NONE &&
+ (rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) || rawmidi->stream != SND_RAWMIDI_STREAM_INPUT))
+ return -ENOTSUP;
+ params->mode = (params->mode & ~SNDRV_RAWMIDI_MODE_CLOCK_MASK) + (val << SNDRV_RAWMIDI_MODE_CLOCK_SHIFT);
+ return 0;
+}
+
+/**
+ * \brief get current clock type (for tstamp type framing)
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \return the current clock type (one of the SND_RAWMIDI_CLOCK_* constants)
+ */
+int snd_rawmidi_params_get_clock_type(const snd_rawmidi_params_t *params)
+{
+ assert(params);
+ return (params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK) >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT;
+}
+
+
/**
* \brief set parameters about rawmidi stream
* \param rawmidi RawMidi handle
@@ -828,6 +909,7 @@ int snd_rawmidi_params(snd_rawmidi_t *rawmidi, snd_rawmidi_params_t * params)
rawmidi->buffer_size = params->buffer_size;
rawmidi->avail_min = params->avail_min;
rawmidi->no_active_sensing = params->no_active_sensing;
+ rawmidi->params_mode = rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) ? 0 : params->mode;
return 0;
}
@@ -844,6 +926,7 @@ int snd_rawmidi_params_current(snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *par
params->buffer_size = rawmidi->buffer_size;
params->avail_min = rawmidi->avail_min;
params->no_active_sensing = rawmidi->no_active_sensing;
+ params->mode = rawmidi->params_mode;
return 0;
}
diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c
index 99927d75..6bfbc1f3 100644
--- a/src/rawmidi/rawmidi_hw.c
+++ b/src/rawmidi/rawmidi_hw.c
@@ -285,6 +285,7 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
rmidi->poll_fd = fd;
rmidi->ops = &snd_rawmidi_hw_ops;
rmidi->private_data = hw;
+ rmidi->version = ver;
hw->open++;
*inputp = rmidi;
}
@@ -300,6 +301,7 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
rmidi->poll_fd = fd;
rmidi->ops = &snd_rawmidi_hw_ops;
rmidi->private_data = hw;
+ rmidi->version = ver;
hw->open++;
*outputp = rmidi;
}
diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h
index 721e1ec9..c4068d7c 100644
--- a/src/rawmidi/rawmidi_local.h
+++ b/src/rawmidi/rawmidi_local.h
@@ -42,12 +42,14 @@ struct _snd_rawmidi {
snd_rawmidi_type_t type;
snd_rawmidi_stream_t stream;
int mode;
+ int version;
int poll_fd;
const snd_rawmidi_ops_t *ops;
void *private_data;
size_t buffer_size;
size_t avail_min;
unsigned int no_active_sensing: 1;
+ int params_mode;
};
int snd_rawmidi_hw_open(snd_rawmidi_t **input, snd_rawmidi_t **output,
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/2] Add test for rawmidi framing API
2021-08-17 12:51 [PATCH v2 0/2] alsa-lib patches for rawmidi tstamp framing David Henningsson
2021-08-17 12:51 ` [PATCH v2 1/2] Add rawmidi framing API David Henningsson
@ 2021-08-17 12:51 ` David Henningsson
1 sibling, 0 replies; 4+ messages in thread
From: David Henningsson @ 2021-08-17 12:51 UTC (permalink / raw)
To: tiwai, alsa-devel, perex; +Cc: David Henningsson
Adds a "clock type" parameter to the "rawmidi" test program, that when
used and combined with -i (and -v to be useful), activates tstamp type
framing with the specified clock type.
Signed-off-by: David Henningsson <coding@diwic.se>
---
test/rawmidi.c | 65 +++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 59 insertions(+), 6 deletions(-)
diff --git a/test/rawmidi.c b/test/rawmidi.c
index 67f585b0..1090d44b 100644
--- a/test/rawmidi.c
+++ b/test/rawmidi.c
@@ -13,6 +13,7 @@ static void usage(void)
fprintf(stderr, " -o device-id : test ALSA output device\n");
fprintf(stderr, " -I node : test input node\n");
fprintf(stderr, " -O node : test output node\n");
+ fprintf(stderr, " -c clock : kernel clock type (0=none, 1=realtime, 2=monotonic, 3=monotonic raw)\n");
fprintf(stderr, " -t: test midi thru\n");
fprintf(stderr, " example:\n");
fprintf(stderr, " rawmidi -i hw:0,0 -O /dev/midi1\n");
@@ -37,7 +38,8 @@ int main(int argc,char** argv)
char *device_out = NULL;
char *node_in = NULL;
char *node_out = NULL;
-
+ int clock_type = -1;
+
int fd_in = -1,fd_out = -1;
snd_rawmidi_t *handle_in = 0,*handle_out = 0;
@@ -58,6 +60,10 @@ int main(int argc,char** argv)
case 't':
thru = 1;
break;
+ case 'c':
+ if (i + 1 < argc)
+ clock_type = atoi(argv[++i]);
+ break;
case 'i':
if (i + 1 < argc)
device_in = argv[++i];
@@ -133,20 +139,67 @@ int main(int argc,char** argv)
}
}
-
-
if (!thru) {
if (handle_in || fd_in!=-1) {
+ if (clock_type != -1) {
+ snd_rawmidi_params_t *params;
+ snd_rawmidi_params_malloc(¶ms);
+ if (!handle_in) {
+ fprintf(stderr, "-c only usable with -i");
+ clock_type = -1;
+ }
+ if (clock_type != -1) {
+ fprintf(stderr, "Enable kernel clock type %d\n", clock_type);
+ snd_rawmidi_params_current(handle_in, params);
+ err = snd_rawmidi_params_set_framing_type(handle_in, params, 1);
+ if (err) {
+ fprintf(stderr,"snd_rawmidi_params_set_framing_type failed: %d\n", err);
+ clock_type = -1;
+ }
+ }
+ if (clock_type != -1) {
+ err = snd_rawmidi_params_set_clock_type(handle_in, params, clock_type);
+ if (err) {
+ fprintf(stderr, "snd_rawmidi_params_set_clock_type failed: %d\n", err);
+ clock_type = -1;
+ }
+ }
+ if (clock_type != -1) {
+ err = snd_rawmidi_params(handle_in, params);
+ if (err) {
+ fprintf(stderr, "snd_rawmidi_params failed: %d\n", err);
+ clock_type = -1;
+ }
+ }
+ snd_rawmidi_params_free(params);
+ }
+
fprintf(stderr,"Read midi in\n");
fprintf(stderr,"Press ctrl-c to stop\n");
}
if (handle_in) {
unsigned char ch;
+ snd_rawmidi_framing_tstamp_t frame;
while (!stop) {
- snd_rawmidi_read(handle_in,&ch,1);
- if (verbose) {
- fprintf(stderr,"read %02x\n",ch);
+ 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]);
+ fprintf(stderr, "\n");
+ }
+ }
+ else {
+ snd_rawmidi_read(handle_in,&ch,1);
+ if (verbose)
+ fprintf(stderr,"read %02x\n",ch);
}
}
}
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 1/2] Add rawmidi framing API
2021-08-17 12:51 ` [PATCH v2 1/2] Add rawmidi framing API David Henningsson
@ 2021-08-17 13:46 ` Takashi Iwai
0 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2021-08-17 13:46 UTC (permalink / raw)
To: David Henningsson; +Cc: alsa-devel
On Tue, 17 Aug 2021 14:51:31 +0200,
David Henningsson wrote:
>
> --- a/include/rawmidi.h
> +++ b/include/rawmidi.h
> @@ -79,6 +79,27 @@ typedef enum _snd_rawmidi_type {
> SND_RAWMIDI_TYPE_VIRTUAL
> } snd_rawmidi_type_t;
>
> +#define SND_RAWMIDI_CLOCK_NONE (0<<3)
> +#define SND_RAWMIDI_CLOCK_REALTIME (1<<3)
> +#define SND_RAWMIDI_CLOCK_MONOTONIC (2<<3)
> +#define SND_RAWMIDI_CLOCK_MONOTONIC_RAW (3<<3)
IMO, better to be like below:
enum {
SND_RAWMIDI_CLOCK_NONE = 0,
SND_RAWMIDI_CLOCK_REALTIME = 1,
SND_RAWMIDI_CLOCK_MONOTONIC = 2,
SND_RAWMIDI_CLOCK_MONOTONIC_RAW = 3,
};
The shift should be done in the function.
The enum above can be also typedef'ed for the later reference.
> +typedef struct _snd_rawmidi_framing_tstamp {
> + /**
> + * For now, frame_type is always 0. Midi 2.0 is expected to add new
> + * types here. Applications are expected to skip unknown frame types.
> + */
> + __u8 frame_type;
The 0 frame type should be also defined explicitly, such as
SNDRV_RAWMIDI_FRAME_TYPE_XXX.
> +int snd_rawmidi_params_set_framing_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, unsigned int val);
You can define the enum type to be more specific, too.
> +int snd_rawmidi_params_set_clock_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, unsigned int val);
Ditto.
thanks,
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2021-08-17 13:47 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-17 12:51 [PATCH v2 0/2] alsa-lib patches for rawmidi tstamp framing David Henningsson
2021-08-17 12:51 ` [PATCH v2 1/2] Add rawmidi framing API David Henningsson
2021-08-17 13:46 ` Takashi Iwai
2021-08-17 12:51 ` [PATCH v2 2/2] Add test for " David Henningsson
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.