All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/2] alsa-lib patches for rawmidi framing
@ 2021-08-17 14:21 David Henningsson
  2021-08-17 14:21 ` [PATCH v3 1/2] Add rawmidi framing API David Henningsson
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: David Henningsson @ 2021-08-17 14:21 UTC (permalink / raw)
  To: tiwai, alsa-devel, perex; +Cc: David Henningsson

v3: Adjusted according to comments from tiwai

David Henningsson (2):
  Add rawmidi framing API
  Add test for rawmidi framing API

 include/rawmidi.h           | 38 +++++++++++++++++
 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, 212 insertions(+), 8 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3 1/2] Add rawmidi framing API
  2021-08-17 14:21 [PATCH v3 0/2] alsa-lib patches for rawmidi framing David Henningsson
@ 2021-08-17 14:21 ` David Henningsson
  2021-08-17 14:21 ` [PATCH v3 2/2] Add test for " David Henningsson
  2021-08-20  6:49 ` [PATCH v3 0/2] alsa-lib patches for rawmidi framing Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: David Henningsson @ 2021-08-17 14:21 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           | 38 +++++++++++++++++
 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, 153 insertions(+), 2 deletions(-)

diff --git a/include/rawmidi.h b/include/rawmidi.h
index d88c932c..83210181 100644
--- a/include/rawmidi.h
+++ b/include/rawmidi.h
@@ -79,6 +79,39 @@ typedef enum _snd_rawmidi_type {
 	SND_RAWMIDI_TYPE_VIRTUAL
 } snd_rawmidi_type_t;
 
+/** Type of clock used with rawmidi tstamp framing */
+typedef enum _snd_rawmidi_clock {
+	SND_RAWMIDI_CLOCK_NONE = 0,
+	SND_RAWMIDI_CLOCK_REALTIME = 1,
+	SND_RAWMIDI_CLOCK_MONOTONIC = 2,
+	SND_RAWMIDI_CLOCK_MONOTONIC_RAW = 3,
+} snd_rawmidi_clock_t;
+
+/** Enable or disable rawmidi framing */
+typedef enum _snd_rawmidi_framing {
+	SND_RAWMIDI_FRAMING_NONE = 0,
+	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. */
+typedef 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.
+	 */
+	__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 +159,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, snd_rawmidi_framing_t val);
+snd_rawmidi_framing_t 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, snd_rawmidi_clock_t val);
+snd_rawmidi_clock_t 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..ae4b2600 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 type of rawmidi 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, snd_rawmidi_framing_t 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)
+ */
+snd_rawmidi_framing_t 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, snd_rawmidi_clock_t 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)
+ */
+snd_rawmidi_clock_t 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 v3 2/2] Add test for rawmidi framing API
  2021-08-17 14:21 [PATCH v3 0/2] alsa-lib patches for rawmidi framing David Henningsson
  2021-08-17 14:21 ` [PATCH v3 1/2] Add rawmidi framing API David Henningsson
@ 2021-08-17 14:21 ` David Henningsson
  2021-08-20  6:49 ` [PATCH v3 0/2] alsa-lib patches for rawmidi framing Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: David Henningsson @ 2021-08-17 14:21 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(&params);
+				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 v3 0/2] alsa-lib patches for rawmidi framing
  2021-08-17 14:21 [PATCH v3 0/2] alsa-lib patches for rawmidi framing David Henningsson
  2021-08-17 14:21 ` [PATCH v3 1/2] Add rawmidi framing API David Henningsson
  2021-08-17 14:21 ` [PATCH v3 2/2] Add test for " David Henningsson
@ 2021-08-20  6:49 ` Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2021-08-20  6:49 UTC (permalink / raw)
  To: David Henningsson; +Cc: alsa-devel

On Tue, 17 Aug 2021 16:21:11 +0200,
David Henningsson wrote:
> 
> v3: Adjusted according to comments from tiwai
> 
> David Henningsson (2):
>   Add rawmidi framing API
>   Add test for rawmidi framing API

Applied both patches now.  Thanks.


Takashi

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-08-20  6:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-17 14:21 [PATCH v3 0/2] alsa-lib patches for rawmidi framing David Henningsson
2021-08-17 14:21 ` [PATCH v3 1/2] Add rawmidi framing API David Henningsson
2021-08-17 14:21 ` [PATCH v3 2/2] Add test for " David Henningsson
2021-08-20  6:49 ` [PATCH v3 0/2] alsa-lib patches for rawmidi framing Takashi Iwai

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.