PipeWire  1.4.6
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
10 extern "C" {
11 #endif
12 
13 #include <errno.h>
14 #include <spa/utils/defs.h>
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(uint32_t *ump, size_t ump_size,
52  uint8_t *midi, size_t midi_maxsize)
53 {
54  int size = 0;
55 
56  if (ump_size < 4)
57  return 0;
58  if (midi_maxsize < 8)
59  return -ENOSPC;
60 
61  switch (ump[0] >> 28) {
62  case 0x1: /* System Real Time and System Common Messages (except System Exclusive) */
63  midi[size++] = (ump[0] >> 16) & 0xff;
64  if (midi[0] >= 0xf1 && midi[0] <= 0xf3) {
65  midi[size++] = (ump[0] >> 8) & 0x7f;
66  if (midi[0] == 0xf2)
67  midi[size++] = ump[0] & 0x7f;
68  }
69  break;
70  case 0x2: /* MIDI 1.0 Channel Voice Messages */
71  midi[size++] = (ump[0] >> 16);
72  midi[size++] = (ump[0] >> 8);
73  if (midi[0] < 0xc0 || midi[0] > 0xdf)
74  midi[size++] = (ump[0]);
75  break;
76  case 0x3: /* Data Messages (including System Exclusive) */
77  {
78  uint8_t status, i, bytes;
79 
80  if (ump_size < 8)
81  return 0;
82 
83  status = (ump[0] >> 20) & 0xf;
84  bytes = SPA_CLAMP((ump[0] >> 16) & 0xf, 0u, 6u);
85 
86  if (status == 0 || status == 1)
87  midi[size++] = 0xf0;
88  for (i = 0 ; i < bytes; i++)
89  /* ump[0] >> 8 | ump[0] | ump[1] >> 24 | ump[1] >>16 ... */
90  midi[size++] = ump[(i+2)/4] >> ((5-i)%4 * 8);
91  if (status == 0 || status == 3)
92  midi[size++] = 0xf7;
93  break;
94  }
95  case 0x4: /* MIDI 2.0 Channel Voice Messages */
96  if (ump_size < 8)
97  return 0;
98  midi[size++] = (ump[0] >> 16) | 0x80;
99  switch (midi[0] & 0xf0) {
100  case 0xc0:
101  midi[size++] = (ump[1] >> 24);
102  break;
103  default:
104  midi[size++] = (ump[0] >> 8) & 0x7f;
106  case 0xd0:
107  midi[size++] = (ump[1] >> 25);
108  break;
109  }
110  break;
111 
112  case 0x0: /* Utility Messages */
113  case 0x5: /* Data Messages */
114  default:
115  return 0;
116  }
117  return size;
118 }
119 
120 SPA_API_CONTROL_UMP_UTILS int spa_ump_from_midi(uint8_t **midi, size_t *midi_size,
121  uint32_t *ump, size_t ump_maxsize, uint8_t group, uint64_t *state)
122 {
123  int size = 0;
124  uint32_t i, prefix = group << 24, to_consume = 0, bytes;
125  uint8_t status, *m = (*midi), end;
126 
127  if (*midi_size < 1)
128  return 0;
129  if (ump_maxsize < 16)
130  return -ENOSPC;
131 
132  status = m[0];
133 
134  /* SysEx */
135  if (*state == 0) {
136  if (status == 0xf0)
137  *state = 1; /* sysex start */
138  else if (status == 0xf7)
139  *state = 2; /* sysex continue */
140  }
141  if (*state & 3) {
142  prefix |= 0x30000000;
143  if (status & 0x80) {
144  m++;
145  to_consume++;
146  }
147  bytes = SPA_CLAMP(*midi_size - to_consume, 0u, 7u);
148  if (bytes > 0) {
149  end = m[bytes-1];
150  if (end & 0x80) {
151  bytes--; /* skip terminator */
152  to_consume++;
153  }
154  else
155  end = 0xf0; /* pretend there is a continue terminator */
156 
157  bytes = SPA_CLAMP(bytes, 0u, 6u);
158  to_consume += bytes;
159 
160  if (end == 0xf7) {
161  if (*state == 2) {
162  /* continue and done */
163  prefix |= 0x3 << 20;
164  *state = 0;
165  }
166  } else if (*state == 1) {
167  /* first packet but not finished */
168  prefix |= 0x1 << 20;
169  *state = 2; /* sysex continue */
170  } else {
171  /* continue and not finished */
172  prefix |= 0x2 << 20;
173  }
174  ump[size++] = prefix | bytes << 16;
175  ump[size++] = 0;
176  for (i = 0 ; i < bytes; i++)
177  /* ump[0] |= (m[0] & 0x7f) << 8
178  * ump[0] |= (m[1] & 0x7f)
179  * ump[1] |= (m[2] & 0x7f) << 24
180  * ... */
181  ump[(i+2)/4] |= (m[i] & 0x7f) << ((5-i)%4 * 8);
182  }
183  } else {
184  /* regular messages */
185  switch (status) {
186  case 0x80 ... 0x8f:
187  case 0x90 ... 0x9f:
188  case 0xa0 ... 0xaf:
189  case 0xb0 ... 0xbf:
190  case 0xe0 ... 0xef:
191  to_consume = 3;
192  prefix |= 0x20000000;
193  break;
194  case 0xc0 ... 0xdf:
195  to_consume = 2;
196  prefix |= 0x20000000;
197  break;
198  case 0xf2:
199  to_consume = 3;
200  prefix = 0x10000000;
201  break;
202  case 0xf1: case 0xf3:
203  to_consume = 2;
204  prefix = 0x10000000;
205  break;
206  case 0xf4 ... 0xff:
207  to_consume = 1;
208  prefix = 0x10000000;
209  break;
210  default:
211  return -EIO;
212  }
213  if (*midi_size < to_consume) {
214  to_consume = *midi_size;
215  } else {
216  prefix |= status << 16;
217  if (to_consume > 1)
218  prefix |= (m[1] & 0x7f) << 8;
219  if (to_consume > 2)
220  prefix |= (m[2] & 0x7f);
221  ump[size++] = prefix;
222  }
223  }
224  (*midi_size) -= to_consume;
225  (*midi) += to_consume;
226 
227  return size * 4;
228 }
229 
234 #ifdef __cplusplus
235 } /* extern "C" */
236 #endif
237 
238 #endif /* SPA_CONTROL_UMP_UTILS_H */
spa/utils/defs.h
SPA_API_CONTROL_UMP_UTILS int spa_ump_to_midi(uint32_t *ump, size_t ump_size, uint8_t *midi, size_t midi_maxsize)
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:127
#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_API_CONTROL_UMP_UTILS
Definition: ump-utils.h:27