From 7b03a41e3ddf9aebdddc643dad4e6cc35a87c8e4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 26 Jul 2024 15:03:30 +0200 Subject: [PATCH] spa: add some helpers to convert MIDI to and from UMP --- spa/include/spa/control/ump-utils.h | 210 ++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 spa/include/spa/control/ump-utils.h diff --git a/spa/include/spa/control/ump-utils.h b/spa/include/spa/control/ump-utils.h new file mode 100644 index 000000000..504659f77 --- /dev/null +++ b/spa/include/spa/control/ump-utils.h @@ -0,0 +1,210 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + + +#ifndef SPA_CONTROL_UMP_UTILS_H +#define SPA_CONTROL_UMP_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * \addtogroup spa_control + * \{ + */ + +static inline size_t spa_ump_message_size(uint8_t message_type) +{ + static const uint32_t ump_sizes[] = { + [0x0] = 1, /* Utility messages */ + [0x1] = 1, /* System messages */ + [0x2] = 1, /* MIDI 1.0 messages */ + [0x3] = 2, /* 7bit SysEx messages */ + [0x4] = 2, /* MIDI 2.0 messages */ + [0x5] = 4, /* 8bit data message */ + [0x6] = 1, + [0x7] = 1, + [0x8] = 2, + [0x9] = 2, + [0xa] = 2, + [0xb] = 3, + [0xc] = 3, + [0xd] = 4, /* Flexible data messages */ + [0xe] = 4, + [0xf] = 4, /* Stream messages */ + }; + return ump_sizes[message_type & 0xf]; +} + +static inline int spa_ump_to_midi(uint32_t *ump, size_t ump_size, + uint8_t *midi, size_t midi_maxsize) +{ + int size = 0; + + if (ump_size < 4) + return 0; + if (midi_maxsize < 8) + return -ENOSPC; + + switch (ump[0] >> 28) { + case 0x1: /* System Real Time and System Common Messages (except System Exclusive) */ + midi[size++] = (ump[0] >> 16) & 0xff; + if (midi[0] >= 0xf1 && midi[0] <= 0xf3) { + midi[size++] = (ump[0] >> 8) & 0x7f; + if (midi[0] == 0xf2) + midi[size++] = ump[0] & 0x7f; + } + break; + case 0x2: /* MIDI 1.0 Channel Voice Messages */ + midi[size++] = (ump[0] >> 16); + midi[size++] = (ump[0] >> 8); + if (midi[0] < 0xc0 || midi[0] > 0xdf) + midi[size++] = (ump[0]); + break; + case 0x3: /* Data Messages (including System Exclusive) */ + { + uint8_t status, i, bytes; + + if (ump_size < 8) + return 0; + + status = (ump[0] >> 20) & 0xf; + bytes = SPA_CLAMP((ump[0] >> 16) & 0xf, 0u, 6u); + + if (status == 0 || status == 1) + midi[size++] = 0xf0; + for (i = 0 ; i < bytes; i++) + /* ump[0] >> 8 | ump[0] | ump[1] >> 24 | ump[1] >>16 ... */ + midi[size++] = ump[(i+2)/4] >> ((5-i)%4 * 8); + if (status == 0 || status == 3) + midi[size++] = 0xf7; + break; + } + case 0x4: /* MIDI 2.0 Channel Voice Messages */ + if (ump_size < 8) + return 0; + midi[size++] = (ump[0] >> 16) | 0x80; + if (midi[0] < 0xc0 || midi[0] > 0xdf) + midi[size++] = (ump[0] >> 8) & 0x7f; + midi[size++] = (ump[1] >> 25); + break; + + case 0x0: /* Utility Messages */ + case 0x5: /* Data Messages */ + default: + return 0; + } + return size; +} + +static inline int spa_ump_from_midi(uint8_t **midi, size_t *midi_size, + uint32_t *ump, size_t ump_maxsize, uint8_t group, uint64_t *state) +{ + int size = 0; + uint32_t prefix = group << 24, to_consume = 0, bytes; + uint8_t status, *m = (*midi); + + if (*midi_size < 1) + return 0; + if (ump_maxsize < 16) + return -ENOSPC; + + status = m[0]; + + /* SysEx */ + if (*state == 0 && status == 0xf0) + *state = 1; /* sysex start */ + if (*state & 3) { + prefix |= 0x30000000; + if (*state == 1) { + m++; + to_consume++; + } + bytes = SPA_CLAMP(*midi_size - to_consume, 0u, 6u); + to_consume += bytes; + if (bytes > 0 && m[bytes-1] == 0xf7) { + bytes--; + if (*state == 2) + prefix |= 0x3 << 20; + *state = 0; + } else if (*state == 1) { + prefix |= 0x1 << 20; + *state = 2; /* sysex continue */ + } else { + prefix |= 0x2 << 20; + } + if (bytes > 0) { + ump[size++] = prefix | bytes << 16 | (m[0] & 0x7f) << 8; + ump[size++] = 0; + } + if (bytes > 1) + ump[0] |= (m[1] & 0x7f); + if (bytes > 2) + ump[1] |= (m[2] & 0x7f) << 24; + if (bytes > 3) + ump[1] |= (m[3] & 0x7f) << 16; + if (bytes > 4) + ump[1] |= (m[4] & 0x7f) << 8; + if (bytes > 5) + ump[1] |= (m[5] & 0x7f); + } else { + /* regular messages */ + switch (status) { + case 0x80 ... 0x8f: + case 0x90 ... 0x9f: + case 0xa0 ... 0xaf: + case 0xb0 ... 0xbf: + case 0xe0 ... 0xef: + to_consume = 3; + prefix |= 0x20000000; + break; + case 0xc0 ... 0xdf: + to_consume = 2; + prefix |= 0x20000000; + break; + case 0xf2: + to_consume = 3; + prefix = 0x10000000; + break; + case 0xf1: case 0xf3: + to_consume = 2; + prefix = 0x10000000; + break; + case 0xf4 ... 0xff: + to_consume = 1; + prefix = 0x10000000; + break; + default: + return -EIO; + } + if (*midi_size < to_consume) { + to_consume = *midi_size; + } else { + prefix |= status << 16; + if (to_consume > 1) + prefix |= (m[1] & 0x7f) << 8; + if (to_consume > 2) + prefix |= (m[2] & 0x7f); + ump[size++] = prefix; + } + } + (*midi_size) -= to_consume; + (*midi) += to_consume; + + return size * 4; +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_CONTROL_UMP_UTILS_H */