PipeWire 1.7.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#include <errno.h>
10#include <spa/utils/defs.h>
11
12#ifdef __cplusplus
13extern "C" {
14#endif
15
16#ifndef SPA_API_CONTROL_UMP_UTILS
17 #ifdef SPA_API_IMPL
18 #define SPA_API_CONTROL_UMP_UTILS SPA_API_IMPL
19 #else
20 #define SPA_API_CONTROL_UMP_UTILS static inline
21 #endif
22#endif
28SPA_API_CONTROL_UMP_UTILS size_t spa_ump_message_size(uint8_t message_type)
29{
30 static const uint32_t ump_sizes[] = {
31 [0x0] = 1, /* Utility messages */
32 [0x1] = 1, /* System messages */
33 [0x2] = 1, /* MIDI 1.0 messages */
34 [0x3] = 2, /* 7bit SysEx messages */
35 [0x4] = 2, /* MIDI 2.0 messages */
36 [0x5] = 4, /* 8bit data message */
37 [0x6] = 1,
38 [0x7] = 1,
39 [0x8] = 2,
40 [0x9] = 2,
41 [0xa] = 2,
42 [0xb] = 3,
43 [0xc] = 3,
44 [0xd] = 4, /* Flexible data messages */
45 [0xe] = 4,
46 [0xf] = 4, /* Stream messages */
47 };
48 return ump_sizes[message_type & 0xf];
49}
50
51SPA_API_CONTROL_UMP_UTILS int spa_ump_to_midi(const uint32_t **ump, size_t *ump_size,
52 uint8_t *midi, size_t midi_maxsize, uint64_t *state)
53{
54 int size = 0;
55 uint32_t to_consume = 0;
56 const uint32_t *u = *ump;
57
58 if (*ump_size < 4 ||
59 (to_consume = (spa_ump_message_size(u[0]>>28) * 4)) > *ump_size) {
60 to_consume = *ump_size;
61 goto done;
62 }
63 if (midi_maxsize < 8)
64 return -ENOSPC;
65
66 switch (u[0] >> 28) {
67 case 0x1: /* System Real Time and System Common Messages (except System Exclusive) */
68 midi[size++] = (u[0] >> 16) & 0xff;
69 if (midi[0] >= 0xf1 && midi[0] <= 0xf3) {
70 midi[size++] = (u[0] >> 8) & 0x7f;
71 if (midi[0] == 0xf2)
72 midi[size++] = u[0] & 0x7f;
73 }
74 break;
75 case 0x2: /* MIDI 1.0 Channel Voice Messages */
76 midi[size++] = (u[0] >> 16);
77 midi[size++] = (u[0] >> 8);
78 if (midi[0] < 0xc0 || midi[0] > 0xdf)
79 midi[size++] = (u[0]);
80 break;
81 case 0x3: /* Data Messages (including System Exclusive) */
82 {
83 uint8_t status, i, bytes;
84
85 status = (u[0] >> 20) & 0xf;
86 bytes = SPA_CLAMP((u[0] >> 16) & 0xf, 0u, 6u);
87
88 if (status == 0 || status == 1)
89 midi[size++] = 0xf0;
90 for (i = 0 ; i < bytes; i++)
91 /* u[0] >> 8 | u[0] | u[1] >> 24 | u[1] >>16 ... */
92 midi[size++] = u[(i+2)/4] >> ((5-i)%4 * 8);
93 if (status == 0 || status == 3)
94 midi[size++] = 0xf7;
95 break;
96 }
97 case 0x4: /* MIDI 2.0 Channel Voice Messages */
98 {
99 uint8_t status = (u[0] >> 16) | 0x80;
100 switch (status & 0xf0) {
101 case 0xc0:
102 /* program/bank change */
103 if (!(u[0] & 1))
104 *state = 2;
105 if (*state == 0) {
106 midi[size++] = (status & 0xf) | 0xb0;
107 midi[size++] = 0;
108 midi[size++] = (u[1] >> 8) & 0x7f;
109 to_consume = 0;
110 *state = 1;
111 }
112 else if (*state == 1) {
113 midi[size++] = (status & 0xf) | 0xb0;
114 midi[size++] = 32;
115 midi[size++] = u[1] & 0x7f;
116 to_consume = 0;
117 *state = 2;
118 }
119 else if (*state == 2) {
120 midi[size++] = status;
121 midi[size++] = (u[1] >> 24) & 0x7f;
122 *state = 0;
123 }
124 break;
125 case 0xd0:
126 midi[size++] = status;
127 midi[size++] = (u[1] >> 25);
128 break;
129 case 0xe0:
130 midi[size++] = status;
131 midi[size++] = (u[1] >> 18) & 0x7f;
132 midi[size++] = (u[1] >> 25);
133 break;
134 default:
135 midi[size++] = status;
136 midi[size++] = (u[0] >> 8) & 0x7f;
137 midi[size++] = (u[1] >> 25);
138 break;
139 }
140 break;
141 }
142 case 0x0: /* Utility Messages */
143 case 0x5: /* Data Messages */
144 default:
145 break;
146 }
147done:
148 (*ump_size) -= to_consume;
149 (*ump) = SPA_PTROFF(*ump, to_consume, uint32_t);
150 return size;
151}
152
153SPA_API_CONTROL_UMP_UTILS int spa_ump_from_midi(uint8_t **midi, size_t *midi_size,
154 uint32_t *ump, size_t ump_maxsize, uint8_t group, uint64_t *state)
155{
156 int size = 0;
157 uint32_t i, prefix = group << 24, to_consume = 0, bytes;
158 uint8_t status, *m = (*midi), end, prev_state;
159
160 if (*midi_size < 1)
161 return 0;
162 if (ump_maxsize < 16)
163 return -ENOSPC;
164
165 status = m[0];
166 prev_state = *state & 0x3;
167
168 /* SysEx */
169 if (*state == 0) {
170 if (status == 0xf0)
171 *state = 1; /* sysex start */
172 else if (status == 0xf7)
173 *state = 2; /* sysex continue */
174 }
175 if (*state & 3) {
176 prefix |= 0x30000000;
177 if (status & 0x80) {
178 m++;
179 to_consume++;
180 }
181 bytes = SPA_CLAMP(*midi_size - to_consume, 0u, 7u);
182 if (bytes > 0) {
183 end = m[bytes-1];
184 if (end & 0x80) {
185 bytes--; /* skip terminator */
186 to_consume++;
187 }
188 else
189 end = 0xf0; /* pretend there is a continue terminator */
190
191 bytes = SPA_CLAMP(bytes, 0u, 6u);
192 to_consume += bytes;
193
194 if (end == 0xf7) {
195 if (*state == 1) {
196 /* started and done in one packet */
197 prefix |= 0x0 << 20;
198 *state = 0;
199 } else {
200 /* continue and done */
201 prefix |= 0x3 << 20;
202 *state = 0;
203 }
204 } else if (*state == 1) {
205 /* first packet but not finished */
206 prefix |= 0x1 << 20;
207 *state = 2; /* sysex continue */
208 } else {
209 /* continue and not finished */
210 prefix |= 0x2 << 20;
211 }
212 ump[size++] = prefix | bytes << 16;
213 ump[size++] = 0;
214 for (i = 0 ; i < bytes; i++)
215 /* ump[0] |= (m[0] & 0x7f) << 8
216 * ump[0] |= (m[1] & 0x7f)
217 * ump[1] |= (m[2] & 0x7f) << 24
218 * ... */
219 ump[(i+2)/4] |= (m[i] & 0x7f) << ((5-i)%4 * 8);
220 } else if (status == 0xf7) {
221 *state = 0;
222 if (prev_state == 1) {
223 prefix |= 0x0 << 20;
224 ump[size++] = prefix;
225 ump[size++] = 0;
226 } else if (prev_state == 2) {
227 prefix |= 0x3 << 20;
228 ump[size++] = prefix;
229 ump[size++] = 0;
230 }
231 }
232 } else {
233 /* regular messages */
234 switch (status) {
235 case 0x80 ... 0x8f:
236 case 0x90 ... 0x9f:
237 case 0xa0 ... 0xaf:
238 case 0xb0 ... 0xbf:
239 case 0xe0 ... 0xef:
240 to_consume = 3;
241 prefix |= 0x20000000;
242 break;
243 case 0xc0 ... 0xdf:
244 to_consume = 2;
245 prefix |= 0x20000000;
246 break;
247 case 0xf2:
248 to_consume = 3;
249 prefix |= 0x10000000;
250 break;
251 case 0xf1: case 0xf3:
252 to_consume = 2;
253 prefix |= 0x10000000;
254 break;
255 case 0xf4 ... 0xff:
256 to_consume = 1;
257 prefix |= 0x10000000;
258 break;
259 default:
260 return -EIO;
261 }
262 if (*midi_size < to_consume) {
263 to_consume = *midi_size;
264 } else {
265 prefix |= status << 16;
266 if (to_consume > 1)
267 prefix |= (m[1] & 0x7f) << 8;
268 if (to_consume > 2)
269 prefix |= (m[2] & 0x7f);
270 ump[size++] = prefix;
271 }
272 }
273 (*midi_size) -= to_consume;
274 (*midi) += to_consume;
275
276 return size * 4;
277}
278
282
283#ifdef __cplusplus
284} /* extern "C" */
285#endif
286
287#endif /* SPA_CONTROL_UMP_UTILS_H */
spa/utils/defs.h
SPA_API_CONTROL_UMP_UTILS int spa_ump_to_midi(const uint32_t **ump, size_t *ump_size, uint8_t *midi, size_t midi_maxsize, uint64_t *state)
Definition ump-utils.h:58
SPA_API_CONTROL_UMP_UTILS size_t spa_ump_message_size(uint8_t message_type)
Definition ump-utils.h:35
SPA_API_CONTROL_UMP_UTILS 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:160
#define SPA_CLAMP(v, low, high)
Definition defs.h:178
#define SPA_PTROFF(ptr_, offset_, type_)
Return the address (buffer + offset) as pointer of type.
Definition defs.h:223
#define SPA_API_CONTROL_UMP_UTILS
Definition ump-utils.h:27