PipeWire  1.6.4
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
13 extern "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
28 SPA_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 
51 SPA_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);
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];
116  to_consume = 0;
117  *state = 2;
118  }
119  else if (*state == 2) {
120  midi[size++] = status;
121  midi[size++] = (u[1] >> 24);
122  *state = 0;
123  }
124  break;
125  default:
126  midi[size++] = status;
127  midi[size++] = (u[0] >> 8) & 0x7f;
129  case 0xd0:
130  midi[size++] = (u[1] >> 25);
131  break;
132  }
133  break;
134  }
135  case 0x0: /* Utility Messages */
136  case 0x5: /* Data Messages */
137  default:
138  break;
139  }
140 done:
141  (*ump_size) -= to_consume;
142  (*ump) = SPA_PTROFF(*ump, to_consume, uint32_t);
143  return size;
144 }
145 
146 SPA_API_CONTROL_UMP_UTILS int spa_ump_from_midi(uint8_t **midi, size_t *midi_size,
147  uint32_t *ump, size_t ump_maxsize, uint8_t group, uint64_t *state)
148 {
149  int size = 0;
150  uint32_t i, prefix = group << 24, to_consume = 0, bytes;
151  uint8_t status, *m = (*midi), end;
152 
153  if (*midi_size < 1)
154  return 0;
155  if (ump_maxsize < 16)
156  return -ENOSPC;
157 
158  status = m[0];
159 
160  /* SysEx */
161  if (*state == 0) {
162  if (status == 0xf0)
163  *state = 1; /* sysex start */
164  else if (status == 0xf7)
165  *state = 2; /* sysex continue */
166  }
167  if (*state & 3) {
168  prefix |= 0x30000000;
169  if (status & 0x80) {
170  m++;
171  to_consume++;
172  }
173  bytes = SPA_CLAMP(*midi_size - to_consume, 0u, 7u);
174  if (bytes > 0) {
175  end = m[bytes-1];
176  if (end & 0x80) {
177  bytes--; /* skip terminator */
178  to_consume++;
179  }
180  else
181  end = 0xf0; /* pretend there is a continue terminator */
182 
183  bytes = SPA_CLAMP(bytes, 0u, 6u);
184  to_consume += bytes;
185 
186  if (end == 0xf7) {
187  if (*state == 2) {
188  /* continue and done */
189  prefix |= 0x3 << 20;
190  *state = 0;
191  }
192  } else if (*state == 1) {
193  /* first packet but not finished */
194  prefix |= 0x1 << 20;
195  *state = 2; /* sysex continue */
196  } else {
197  /* continue and not finished */
198  prefix |= 0x2 << 20;
199  }
200  ump[size++] = prefix | bytes << 16;
201  ump[size++] = 0;
202  for (i = 0 ; i < bytes; i++)
203  /* ump[0] |= (m[0] & 0x7f) << 8
204  * ump[0] |= (m[1] & 0x7f)
205  * ump[1] |= (m[2] & 0x7f) << 24
206  * ... */
207  ump[(i+2)/4] |= (m[i] & 0x7f) << ((5-i)%4 * 8);
208  }
209  } else {
210  /* regular messages */
211  switch (status) {
212  case 0x80 ... 0x8f:
213  case 0x90 ... 0x9f:
214  case 0xa0 ... 0xaf:
215  case 0xb0 ... 0xbf:
216  case 0xe0 ... 0xef:
217  to_consume = 3;
218  prefix |= 0x20000000;
219  break;
220  case 0xc0 ... 0xdf:
221  to_consume = 2;
222  prefix |= 0x20000000;
223  break;
224  case 0xf2:
225  to_consume = 3;
226  prefix |= 0x10000000;
227  break;
228  case 0xf1: case 0xf3:
229  to_consume = 2;
230  prefix |= 0x10000000;
231  break;
232  case 0xf4 ... 0xff:
233  to_consume = 1;
234  prefix |= 0x10000000;
235  break;
236  default:
237  return -EIO;
238  }
239  if (*midi_size < to_consume) {
240  to_consume = *midi_size;
241  } else {
242  prefix |= status << 16;
243  if (to_consume > 1)
244  prefix |= (m[1] & 0x7f) << 8;
245  if (to_consume > 2)
246  prefix |= (m[2] & 0x7f);
247  ump[size++] = prefix;
248  }
249  }
250  (*midi_size) -= to_consume;
251  (*midi) += to_consume;
252 
253  return size * 4;
254 }
255 
260 #ifdef __cplusplus
261 } /* extern "C" */
262 #endif
263 
264 #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:153
#define SPA_CLAMP(v, low, high)
Definition: defs.h:177
#define SPA_FALLTHROUGH
SPA_FALLTHROUGH is an annotation to suppress compiler warnings about switch cases that fall through w...
Definition: defs.h:84
#define SPA_PTROFF(ptr_, offset_, type_)
Return the address (buffer + offset) as pointer of type.
Definition: defs.h:222
#define SPA_API_CONTROL_UMP_UTILS
Definition: ump-utils.h:27