PipeWire 1.3.0
Loading...
Searching...
No Matches
ump-utils.h
Go to the documentation of this file.
1/* Simple Plugin API */
2/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */
3/* SPDX-License-Identifier: MIT */
4
5
6#ifndef SPA_CONTROL_UMP_UTILS_H
7#define SPA_CONTROL_UMP_UTILS_H
8
9#ifdef __cplusplus
10extern "C" {
11#endif
12
13#include <errno.h>
14#include <spa/utils/defs.h>
15
21static inline size_t spa_ump_message_size(uint8_t message_type)
22{
23 static const uint32_t ump_sizes[] = {
24 [0x0] = 1, /* Utility messages */
25 [0x1] = 1, /* System messages */
26 [0x2] = 1, /* MIDI 1.0 messages */
27 [0x3] = 2, /* 7bit SysEx messages */
28 [0x4] = 2, /* MIDI 2.0 messages */
29 [0x5] = 4, /* 8bit data message */
30 [0x6] = 1,
31 [0x7] = 1,
32 [0x8] = 2,
33 [0x9] = 2,
34 [0xa] = 2,
35 [0xb] = 3,
36 [0xc] = 3,
37 [0xd] = 4, /* Flexible data messages */
38 [0xe] = 4,
39 [0xf] = 4, /* Stream messages */
40 };
41 return ump_sizes[message_type & 0xf];
42}
43
44static inline int spa_ump_to_midi(uint32_t *ump, size_t ump_size,
45 uint8_t *midi, size_t midi_maxsize)
46{
47 int size = 0;
48
49 if (ump_size < 4)
50 return 0;
51 if (midi_maxsize < 8)
52 return -ENOSPC;
53
54 switch (ump[0] >> 28) {
55 case 0x1: /* System Real Time and System Common Messages (except System Exclusive) */
56 midi[size++] = (ump[0] >> 16) & 0xff;
57 if (midi[0] >= 0xf1 && midi[0] <= 0xf3) {
58 midi[size++] = (ump[0] >> 8) & 0x7f;
59 if (midi[0] == 0xf2)
60 midi[size++] = ump[0] & 0x7f;
61 }
62 break;
63 case 0x2: /* MIDI 1.0 Channel Voice Messages */
64 midi[size++] = (ump[0] >> 16);
65 midi[size++] = (ump[0] >> 8);
66 if (midi[0] < 0xc0 || midi[0] > 0xdf)
67 midi[size++] = (ump[0]);
68 break;
69 case 0x3: /* Data Messages (including System Exclusive) */
70 {
71 uint8_t status, i, bytes;
72
73 if (ump_size < 8)
74 return 0;
75
76 status = (ump[0] >> 20) & 0xf;
77 bytes = SPA_CLAMP((ump[0] >> 16) & 0xf, 0u, 6u);
78
79 if (status == 0 || status == 1)
80 midi[size++] = 0xf0;
81 for (i = 0 ; i < bytes; i++)
82 /* ump[0] >> 8 | ump[0] | ump[1] >> 24 | ump[1] >>16 ... */
83 midi[size++] = ump[(i+2)/4] >> ((5-i)%4 * 8);
84 if (status == 0 || status == 3)
85 midi[size++] = 0xf7;
86 break;
87 }
88 case 0x4: /* MIDI 2.0 Channel Voice Messages */
89 if (ump_size < 8)
90 return 0;
91 midi[size++] = (ump[0] >> 16) | 0x80;
92 if (midi[0] < 0xc0 || midi[0] > 0xdf)
93 midi[size++] = (ump[0] >> 8) & 0x7f;
94 midi[size++] = (ump[1] >> 25);
95 break;
96
97 case 0x0: /* Utility Messages */
98 case 0x5: /* Data Messages */
99 default:
100 return 0;
101 }
102 return size;
103}
104
105static inline int spa_ump_from_midi(uint8_t **midi, size_t *midi_size,
106 uint32_t *ump, size_t ump_maxsize, uint8_t group, uint64_t *state)
107{
108 int size = 0;
109 uint32_t i, prefix = group << 24, to_consume = 0, bytes;
110 uint8_t status, *m = (*midi), end;
111
112 if (*midi_size < 1)
113 return 0;
114 if (ump_maxsize < 16)
115 return -ENOSPC;
116
117 status = m[0];
118
119 /* SysEx */
120 if (*state == 0) {
121 if (status == 0xf0)
122 *state = 1; /* sysex start */
123 else if (status == 0xf7)
124 *state = 2; /* sysex continue */
125 }
126 if (*state & 3) {
127 prefix |= 0x30000000;
128 if (status & 0x80) {
129 m++;
130 to_consume++;
131 }
132 bytes = SPA_CLAMP(*midi_size - to_consume, 0u, 7u);
133 if (bytes > 0) {
134 end = m[bytes-1];
135 if (end & 0x80) {
136 bytes--; /* skip terminator */
137 to_consume++;
138 }
139 else
140 end = 0xf0; /* pretend there is a continue terminator */
141
142 bytes = SPA_CLAMP(bytes, 0u, 6u);
143 to_consume += bytes;
144
145 if (end == 0xf7) {
146 if (*state == 2) {
147 /* continue and done */
148 prefix |= 0x3 << 20;
149 *state = 0;
150 }
151 } else if (*state == 1) {
152 /* first packet but not finished */
153 prefix |= 0x1 << 20;
154 *state = 2; /* sysex continue */
155 } else {
156 /* continue and not finished */
157 prefix |= 0x2 << 20;
158 }
159 ump[size++] = prefix | bytes << 16;
160 ump[size++] = 0;
161 for (i = 0 ; i < bytes; i++)
162 /* ump[0] |= (m[0] & 0x7f) << 8
163 * ump[0] |= (m[1] & 0x7f)
164 * ump[1] |= (m[2] & 0x7f) << 24
165 * ... */
166 ump[(i+2)/4] |= (m[i] & 0x7f) << ((5-i)%4 * 8);
167 }
168 } else {
169 /* regular messages */
170 switch (status) {
171 case 0x80 ... 0x8f:
172 case 0x90 ... 0x9f:
173 case 0xa0 ... 0xaf:
174 case 0xb0 ... 0xbf:
175 case 0xe0 ... 0xef:
176 to_consume = 3;
177 prefix |= 0x20000000;
178 break;
179 case 0xc0 ... 0xdf:
180 to_consume = 2;
181 prefix |= 0x20000000;
182 break;
183 case 0xf2:
184 to_consume = 3;
185 prefix = 0x10000000;
186 break;
187 case 0xf1: case 0xf3:
188 to_consume = 2;
189 prefix = 0x10000000;
190 break;
191 case 0xf4 ... 0xff:
192 to_consume = 1;
193 prefix = 0x10000000;
194 break;
195 default:
196 return -EIO;
197 }
198 if (*midi_size < to_consume) {
199 to_consume = *midi_size;
200 } else {
201 prefix |= status << 16;
202 if (to_consume > 1)
203 prefix |= (m[1] & 0x7f) << 8;
204 if (to_consume > 2)
205 prefix |= (m[2] & 0x7f);
206 ump[size++] = prefix;
207 }
208 }
209 (*midi_size) -= to_consume;
210 (*midi) += to_consume;
211
212 return size * 4;
213}
214
219#ifdef __cplusplus
220} /* extern "C" */
221#endif
222
223#endif /* SPA_CONTROL_UMP_UTILS_H */
spa/utils/defs.h
static int spa_ump_to_midi(uint32_t *ump, size_t ump_size, uint8_t *midi, size_t midi_maxsize)
Definition ump-utils.h:49
static size_t spa_ump_message_size(uint8_t message_type)
Definition ump-utils.h:26
static 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)
Definition ump-utils.h:110
#define SPA_CLAMP(v, low, high)
Definition defs.h:177