diff mbox series

[alsa-lib,6/8] ump: Add helpers for handling SysEx data

Message ID 20230520070021.1301-7-tiwai@suse.de
State Superseded
Headers show
Series Add MIDI 2.0 support | expand

Commit Message

Takashi Iwai May 20, 2023, 7 a.m. UTC
Yet a few more helpers for handling SysEx data with UMP packets.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/ump_msg.h | 27 +++++++++++++++
 src/rawmidi/ump.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 113 insertions(+)
diff mbox series

Patch

diff --git a/include/ump_msg.h b/include/ump_msg.h
index 1b7f70667054..4ccce546f8d5 100644
--- a/include/ump_msg.h
+++ b/include/ump_msg.h
@@ -500,6 +500,14 @@  enum {
 	SND_UMP_MSG_RESET		= 0xff,
 };
 
+/** MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */
+enum {
+	SND_UMP_SYSEX_STATUS_SINGLE	= 0,
+	SND_UMP_SYSEX_STATUS_START	= 1,
+	SND_UMP_SYSEX_STATUS_CONTINUE	= 2,
+	SND_UMP_SYSEX_STATUS_END	= 3,
+};
+
 /**
  * \brief get UMP status (4bit) from 32bit UMP message header
  */
@@ -564,6 +572,25 @@  static inline uint8_t snd_ump_msg_group(const uint32_t *ump)
 	return snd_ump_msg_hdr_group(*ump);
 }
 
+/**
+ * \brief get UMP sysex message status
+ */
+static inline uint8_t snd_ump_sysex_msg_status(const uint32_t *ump)
+{
+	return (*ump >> 20) & 0xf;
+}
+
+/**
+ * \brief get UMP sysex message length
+ */
+static inline uint8_t snd_ump_sysex_msg_length(const uint32_t *ump)
+{
+	return (*ump >> 16) & 0xf;
+}
+
+int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen,
+			     size_t *filled);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c
index 5da79459f618..2482884f2661 100644
--- a/src/rawmidi/ump.c
+++ b/src/rawmidi/ump.c
@@ -614,3 +614,89 @@  int snd_ump_block_info(snd_ump_t *ump, snd_ump_block_info_t *info)
 {
 	return _snd_rawmidi_ump_block_info(ump->rawmidi, info);
 }
+
+/*
+ * UMP sysex helpers
+ */
+static int expand_sysex_data(const uint32_t *data, uint8_t *buf,
+			     size_t maxlen, unsigned char bytes, int offset)
+{
+	int size = 0;
+
+	for (; bytes; bytes--, size++) {
+		if (!maxlen)
+			break;
+		buf[size] = (*data >> offset) & 0x7f;
+		if (!offset) {
+			offset = 24;
+			data++;
+		} else {
+			offset -= 8;
+		}
+	}
+
+	return size;
+}
+
+static int expand_sysex7(const uint32_t *ump, uint8_t *buf, size_t maxlen,
+			 size_t *filled)
+{
+	unsigned char status;
+	unsigned char bytes;
+
+	*filled = 0;
+	if (!maxlen)
+		return 0;
+
+	status = snd_ump_sysex_msg_status(ump);
+	bytes = snd_ump_sysex_msg_length(ump);
+	if (bytes > 6)
+		return 0; // invalid - skip
+
+	*filled = expand_sysex_data(ump, buf, maxlen, bytes, 8);
+	return (status == SND_UMP_SYSEX_STATUS_SINGLE ||
+		status == SND_UMP_SYSEX_STATUS_END);
+}
+
+static int expand_sysex8(const uint32_t *ump, uint8_t *buf, size_t maxlen,
+			  size_t *filled)
+{
+	unsigned char status;
+	unsigned char bytes;
+
+	*filled = 0;
+	if (!maxlen)
+		return 0;
+
+	status = snd_ump_sysex_msg_status(ump);
+	if (status > SND_UMP_SYSEX_STATUS_END)
+		return 0; // unsupported, skip
+	bytes = snd_ump_sysex_msg_length(ump);
+	if (!bytes || bytes > 14)
+		return 0; // skip
+
+	*filled = expand_sysex_data(ump, buf, maxlen, bytes - 1, 0);
+	return (status == SND_UMP_SYSEX_STATUS_SINGLE ||
+		status == SND_UMP_SYSEX_STATUS_END);
+}
+
+/**
+ * \brief fill sysex byte from a UMP packet
+ * \param ump UMP packet pointer
+ * \param buf buffer point to fill sysex bytes
+ * \param maxlen max buffer size in bytes
+ * \param filled the size of filled sysex bytes on the buffer
+ * \return 1 if the sysex finished, otherwise 0
+ */
+int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen,
+			     size_t *filled)
+{
+	switch (snd_ump_msg_type(ump)) {
+	case SND_UMP_MSG_TYPE_DATA:
+		return expand_sysex7(ump, buf, maxlen, filled);
+	case SND_UMP_MSG_TYPE_EXTENDED_DATA:
+		return expand_sysex8(ump, buf, maxlen, filled);
+	default:
+		return -EINVAL;
+	}
+}