1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ALSA sequencer event conversion between UMP and legacy clients |
4 | */ |
5 | |
6 | #include <linux/init.h> |
7 | #include <linux/errno.h> |
8 | #include <linux/string.h> |
9 | #include <sound/core.h> |
10 | #include <sound/ump.h> |
11 | #include <sound/ump_msg.h> |
12 | #include "seq_ump_convert.h" |
13 | |
14 | /* |
15 | * Upgrade / downgrade value bits |
16 | */ |
17 | static u8 downscale_32_to_7bit(u32 src) |
18 | { |
19 | return src >> 25; |
20 | } |
21 | |
22 | static u16 downscale_32_to_14bit(u32 src) |
23 | { |
24 | return src >> 18; |
25 | } |
26 | |
27 | static u8 downscale_16_to_7bit(u16 src) |
28 | { |
29 | return src >> 9; |
30 | } |
31 | |
32 | static u16 upscale_7_to_16bit(u8 src) |
33 | { |
34 | u16 val, repeat; |
35 | |
36 | val = (u16)src << 9; |
37 | if (src <= 0x40) |
38 | return val; |
39 | repeat = src & 0x3f; |
40 | return val | (repeat << 3) | (repeat >> 3); |
41 | } |
42 | |
43 | static u32 upscale_7_to_32bit(u8 src) |
44 | { |
45 | u32 val, repeat; |
46 | |
47 | val = src << 25; |
48 | if (src <= 0x40) |
49 | return val; |
50 | repeat = src & 0x3f; |
51 | return val | (repeat << 19) | (repeat << 13) | |
52 | (repeat << 7) | (repeat << 1) | (repeat >> 5); |
53 | } |
54 | |
55 | static u32 upscale_14_to_32bit(u16 src) |
56 | { |
57 | u32 val, repeat; |
58 | |
59 | val = src << 18; |
60 | if (src <= 0x2000) |
61 | return val; |
62 | repeat = src & 0x1fff; |
63 | return val | (repeat << 5) | (repeat >> 8); |
64 | } |
65 | |
66 | static unsigned char get_ump_group(struct snd_seq_client_port *port) |
67 | { |
68 | return port->ump_group ? (port->ump_group - 1) : 0; |
69 | } |
70 | |
71 | /* create a UMP header */ |
72 | #define make_raw_ump(port, type) \ |
73 | ump_compose(type, get_ump_group(port), 0, 0) |
74 | |
75 | /* |
76 | * UMP -> MIDI1 sequencer event |
77 | */ |
78 | |
79 | /* MIDI 1.0 CVM */ |
80 | |
81 | /* encode note event */ |
82 | static void ump_midi1_to_note_ev(const union snd_ump_midi1_msg *val, |
83 | struct snd_seq_event *ev) |
84 | { |
85 | ev->data.note.channel = val->note.channel; |
86 | ev->data.note.note = val->note.note; |
87 | ev->data.note.velocity = val->note.velocity; |
88 | } |
89 | |
90 | /* encode one parameter controls */ |
91 | static void ump_midi1_to_ctrl_ev(const union snd_ump_midi1_msg *val, |
92 | struct snd_seq_event *ev) |
93 | { |
94 | ev->data.control.channel = val->caf.channel; |
95 | ev->data.control.value = val->caf.data; |
96 | } |
97 | |
98 | /* encode pitch wheel change */ |
99 | static void ump_midi1_to_pitchbend_ev(const union snd_ump_midi1_msg *val, |
100 | struct snd_seq_event *ev) |
101 | { |
102 | ev->data.control.channel = val->pb.channel; |
103 | ev->data.control.value = (val->pb.data_msb << 7) | val->pb.data_lsb; |
104 | ev->data.control.value -= 8192; |
105 | } |
106 | |
107 | /* encode midi control change */ |
108 | static void ump_midi1_to_cc_ev(const union snd_ump_midi1_msg *val, |
109 | struct snd_seq_event *ev) |
110 | { |
111 | ev->data.control.channel = val->cc.channel; |
112 | ev->data.control.param = val->cc.index; |
113 | ev->data.control.value = val->cc.data; |
114 | } |
115 | |
116 | /* Encoding MIDI 1.0 UMP packet */ |
117 | struct seq_ump_midi1_to_ev { |
118 | int seq_type; |
119 | void (*encode)(const union snd_ump_midi1_msg *val, struct snd_seq_event *ev); |
120 | }; |
121 | |
122 | /* Encoders for MIDI1 status 0x80-0xe0 */ |
123 | static struct seq_ump_midi1_to_ev midi1_msg_encoders[] = { |
124 | {SNDRV_SEQ_EVENT_NOTEOFF, ump_midi1_to_note_ev}, /* 0x80 */ |
125 | {SNDRV_SEQ_EVENT_NOTEON, ump_midi1_to_note_ev}, /* 0x90 */ |
126 | {SNDRV_SEQ_EVENT_KEYPRESS, ump_midi1_to_note_ev}, /* 0xa0 */ |
127 | {SNDRV_SEQ_EVENT_CONTROLLER, ump_midi1_to_cc_ev}, /* 0xb0 */ |
128 | {SNDRV_SEQ_EVENT_PGMCHANGE, ump_midi1_to_ctrl_ev}, /* 0xc0 */ |
129 | {SNDRV_SEQ_EVENT_CHANPRESS, ump_midi1_to_ctrl_ev}, /* 0xd0 */ |
130 | {SNDRV_SEQ_EVENT_PITCHBEND, ump_midi1_to_pitchbend_ev}, /* 0xe0 */ |
131 | }; |
132 | |
133 | static int cvt_ump_midi1_to_event(const union snd_ump_midi1_msg *val, |
134 | struct snd_seq_event *ev) |
135 | { |
136 | unsigned char status = val->note.status; |
137 | |
138 | if (status < 0x8 || status > 0xe) |
139 | return 0; /* invalid - skip */ |
140 | status -= 8; |
141 | ev->type = midi1_msg_encoders[status].seq_type; |
142 | ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; |
143 | midi1_msg_encoders[status].encode(val, ev); |
144 | return 1; |
145 | } |
146 | |
147 | /* MIDI System message */ |
148 | |
149 | /* encode one parameter value*/ |
150 | static void ump_system_to_one_param_ev(const union snd_ump_midi1_msg *val, |
151 | struct snd_seq_event *ev) |
152 | { |
153 | ev->data.control.value = val->system.parm1; |
154 | } |
155 | |
156 | /* encode song position */ |
157 | static void ump_system_to_songpos_ev(const union snd_ump_midi1_msg *val, |
158 | struct snd_seq_event *ev) |
159 | { |
160 | ev->data.control.value = (val->system.parm1 << 7) | val->system.parm2; |
161 | } |
162 | |
163 | /* Encoders for 0xf0 - 0xff */ |
164 | static struct seq_ump_midi1_to_ev system_msg_encoders[] = { |
165 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf0 */ |
166 | {SNDRV_SEQ_EVENT_QFRAME, ump_system_to_one_param_ev}, /* 0xf1 */ |
167 | {SNDRV_SEQ_EVENT_SONGPOS, ump_system_to_songpos_ev}, /* 0xf2 */ |
168 | {SNDRV_SEQ_EVENT_SONGSEL, ump_system_to_one_param_ev}, /* 0xf3 */ |
169 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf4 */ |
170 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf5 */ |
171 | {SNDRV_SEQ_EVENT_TUNE_REQUEST, NULL}, /* 0xf6 */ |
172 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf7 */ |
173 | {SNDRV_SEQ_EVENT_CLOCK, NULL}, /* 0xf8 */ |
174 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf9 */ |
175 | {SNDRV_SEQ_EVENT_START, NULL}, /* 0xfa */ |
176 | {SNDRV_SEQ_EVENT_CONTINUE, NULL}, /* 0xfb */ |
177 | {SNDRV_SEQ_EVENT_STOP, NULL}, /* 0xfc */ |
178 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xfd */ |
179 | {SNDRV_SEQ_EVENT_SENSING, NULL}, /* 0xfe */ |
180 | {SNDRV_SEQ_EVENT_RESET, NULL}, /* 0xff */ |
181 | }; |
182 | |
183 | static int cvt_ump_system_to_event(const union snd_ump_midi1_msg *val, |
184 | struct snd_seq_event *ev) |
185 | { |
186 | unsigned char status = val->system.status; |
187 | |
188 | if ((status & 0xf0) != UMP_MIDI1_MSG_REALTIME) |
189 | return 0; /* invalid status - skip */ |
190 | status &= 0x0f; |
191 | ev->type = system_msg_encoders[status].seq_type; |
192 | ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; |
193 | if (ev->type == SNDRV_SEQ_EVENT_NONE) |
194 | return 0; |
195 | if (system_msg_encoders[status].encode) |
196 | system_msg_encoders[status].encode(val, ev); |
197 | return 1; |
198 | } |
199 | |
200 | /* MIDI 2.0 CVM */ |
201 | |
202 | /* encode note event */ |
203 | static int ump_midi2_to_note_ev(const union snd_ump_midi2_msg *val, |
204 | struct snd_seq_event *ev) |
205 | { |
206 | ev->data.note.channel = val->note.channel; |
207 | ev->data.note.note = val->note.note; |
208 | ev->data.note.velocity = downscale_16_to_7bit(src: val->note.velocity); |
209 | /* correct note-on velocity 0 to 1; |
210 | * it's no longer equivalent as not-off for MIDI 2.0 |
211 | */ |
212 | if (ev->type == SNDRV_SEQ_EVENT_NOTEON && |
213 | !ev->data.note.velocity) |
214 | ev->data.note.velocity = 1; |
215 | return 1; |
216 | } |
217 | |
218 | /* encode pitch wheel change */ |
219 | static int ump_midi2_to_pitchbend_ev(const union snd_ump_midi2_msg *val, |
220 | struct snd_seq_event *ev) |
221 | { |
222 | ev->data.control.channel = val->pb.channel; |
223 | ev->data.control.value = downscale_32_to_14bit(src: val->pb.data); |
224 | ev->data.control.value -= 8192; |
225 | return 1; |
226 | } |
227 | |
228 | /* encode midi control change */ |
229 | static int ump_midi2_to_cc_ev(const union snd_ump_midi2_msg *val, |
230 | struct snd_seq_event *ev) |
231 | { |
232 | ev->data.control.channel = val->cc.channel; |
233 | ev->data.control.param = val->cc.index; |
234 | ev->data.control.value = downscale_32_to_7bit(src: val->cc.data); |
235 | return 1; |
236 | } |
237 | |
238 | /* encode midi program change */ |
239 | static int ump_midi2_to_pgm_ev(const union snd_ump_midi2_msg *val, |
240 | struct snd_seq_event *ev) |
241 | { |
242 | int size = 1; |
243 | |
244 | ev->data.control.channel = val->pg.channel; |
245 | if (val->pg.bank_valid) { |
246 | ev->type = SNDRV_SEQ_EVENT_CONTROL14; |
247 | ev->data.control.param = UMP_CC_BANK_SELECT; |
248 | ev->data.control.value = (val->pg.bank_msb << 7) | val->pg.bank_lsb; |
249 | ev[1] = ev[0]; |
250 | ev++; |
251 | ev->type = SNDRV_SEQ_EVENT_PGMCHANGE; |
252 | size = 2; |
253 | } |
254 | ev->data.control.value = val->pg.program; |
255 | return size; |
256 | } |
257 | |
258 | /* encode one parameter controls */ |
259 | static int ump_midi2_to_ctrl_ev(const union snd_ump_midi2_msg *val, |
260 | struct snd_seq_event *ev) |
261 | { |
262 | ev->data.control.channel = val->caf.channel; |
263 | ev->data.control.value = downscale_32_to_7bit(src: val->caf.data); |
264 | return 1; |
265 | } |
266 | |
267 | /* encode RPN/NRPN */ |
268 | static int ump_midi2_to_rpn_ev(const union snd_ump_midi2_msg *val, |
269 | struct snd_seq_event *ev) |
270 | { |
271 | ev->data.control.channel = val->rpn.channel; |
272 | ev->data.control.param = (val->rpn.bank << 7) | val->rpn.index; |
273 | ev->data.control.value = downscale_32_to_14bit(src: val->rpn.data); |
274 | return 1; |
275 | } |
276 | |
277 | /* Encoding MIDI 2.0 UMP Packet */ |
278 | struct seq_ump_midi2_to_ev { |
279 | int seq_type; |
280 | int (*encode)(const union snd_ump_midi2_msg *val, struct snd_seq_event *ev); |
281 | }; |
282 | |
283 | /* Encoders for MIDI2 status 0x00-0xf0 */ |
284 | static struct seq_ump_midi2_to_ev midi2_msg_encoders[] = { |
285 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x00 */ |
286 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x10 */ |
287 | {SNDRV_SEQ_EVENT_REGPARAM, ump_midi2_to_rpn_ev}, /* 0x20 */ |
288 | {SNDRV_SEQ_EVENT_NONREGPARAM, ump_midi2_to_rpn_ev}, /* 0x30 */ |
289 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x40 */ |
290 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x50 */ |
291 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x60 */ |
292 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x70 */ |
293 | {SNDRV_SEQ_EVENT_NOTEOFF, ump_midi2_to_note_ev}, /* 0x80 */ |
294 | {SNDRV_SEQ_EVENT_NOTEON, ump_midi2_to_note_ev}, /* 0x90 */ |
295 | {SNDRV_SEQ_EVENT_KEYPRESS, ump_midi2_to_note_ev}, /* 0xa0 */ |
296 | {SNDRV_SEQ_EVENT_CONTROLLER, ump_midi2_to_cc_ev}, /* 0xb0 */ |
297 | {SNDRV_SEQ_EVENT_PGMCHANGE, ump_midi2_to_pgm_ev}, /* 0xc0 */ |
298 | {SNDRV_SEQ_EVENT_CHANPRESS, ump_midi2_to_ctrl_ev}, /* 0xd0 */ |
299 | {SNDRV_SEQ_EVENT_PITCHBEND, ump_midi2_to_pitchbend_ev}, /* 0xe0 */ |
300 | {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf0 */ |
301 | }; |
302 | |
303 | static int cvt_ump_midi2_to_event(const union snd_ump_midi2_msg *val, |
304 | struct snd_seq_event *ev) |
305 | { |
306 | unsigned char status = val->note.status; |
307 | |
308 | ev->type = midi2_msg_encoders[status].seq_type; |
309 | if (ev->type == SNDRV_SEQ_EVENT_NONE) |
310 | return 0; /* skip */ |
311 | ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; |
312 | return midi2_msg_encoders[status].encode(val, ev); |
313 | } |
314 | |
315 | /* parse and compose for a sysex var-length event */ |
316 | static int cvt_ump_sysex7_to_event(const u32 *data, unsigned char *buf, |
317 | struct snd_seq_event *ev) |
318 | { |
319 | unsigned char status; |
320 | unsigned char bytes; |
321 | u32 val; |
322 | int size = 0; |
323 | |
324 | val = data[0]; |
325 | status = ump_sysex_message_status(data: val); |
326 | bytes = ump_sysex_message_length(data: val); |
327 | if (bytes > 6) |
328 | return 0; // skip |
329 | |
330 | if (status == UMP_SYSEX_STATUS_SINGLE || |
331 | status == UMP_SYSEX_STATUS_START) { |
332 | buf[0] = UMP_MIDI1_MSG_SYSEX_START; |
333 | size = 1; |
334 | } |
335 | |
336 | if (bytes > 0) |
337 | buf[size++] = (val >> 8) & 0x7f; |
338 | if (bytes > 1) |
339 | buf[size++] = val & 0x7f; |
340 | val = data[1]; |
341 | if (bytes > 2) |
342 | buf[size++] = (val >> 24) & 0x7f; |
343 | if (bytes > 3) |
344 | buf[size++] = (val >> 16) & 0x7f; |
345 | if (bytes > 4) |
346 | buf[size++] = (val >> 8) & 0x7f; |
347 | if (bytes > 5) |
348 | buf[size++] = val & 0x7f; |
349 | |
350 | if (status == UMP_SYSEX_STATUS_SINGLE || |
351 | status == UMP_SYSEX_STATUS_END) |
352 | buf[size++] = UMP_MIDI1_MSG_SYSEX_END; |
353 | |
354 | ev->type = SNDRV_SEQ_EVENT_SYSEX; |
355 | ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; |
356 | ev->data.ext.len = size; |
357 | ev->data.ext.ptr = buf; |
358 | return 1; |
359 | } |
360 | |
361 | /* convert UMP packet from MIDI 1.0 to MIDI 2.0 and deliver it */ |
362 | static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest, |
363 | struct snd_seq_client_port *dest_port, |
364 | struct snd_seq_event *__event, |
365 | int atomic, int hop) |
366 | { |
367 | struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event; |
368 | struct snd_seq_ump_event ev_cvt; |
369 | const union snd_ump_midi1_msg *midi1 = (const union snd_ump_midi1_msg *)event->ump; |
370 | union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)ev_cvt.ump; |
371 | |
372 | ev_cvt = *event; |
373 | memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump)); |
374 | |
375 | midi2->note.type = UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE; |
376 | midi2->note.group = midi1->note.group; |
377 | midi2->note.status = midi1->note.status; |
378 | midi2->note.channel = midi1->note.channel; |
379 | switch (midi1->note.status) { |
380 | case UMP_MSG_STATUS_NOTE_ON: |
381 | case UMP_MSG_STATUS_NOTE_OFF: |
382 | midi2->note.note = midi1->note.note; |
383 | midi2->note.velocity = upscale_7_to_16bit(src: midi1->note.velocity); |
384 | break; |
385 | case UMP_MSG_STATUS_POLY_PRESSURE: |
386 | midi2->paf.note = midi1->paf.note; |
387 | midi2->paf.data = upscale_7_to_32bit(src: midi1->paf.data); |
388 | break; |
389 | case UMP_MSG_STATUS_CC: |
390 | midi2->cc.index = midi1->cc.index; |
391 | midi2->cc.data = upscale_7_to_32bit(src: midi1->cc.data); |
392 | break; |
393 | case UMP_MSG_STATUS_PROGRAM: |
394 | midi2->pg.program = midi1->pg.program; |
395 | break; |
396 | case UMP_MSG_STATUS_CHANNEL_PRESSURE: |
397 | midi2->caf.data = upscale_7_to_32bit(src: midi1->caf.data); |
398 | break; |
399 | case UMP_MSG_STATUS_PITCH_BEND: |
400 | midi2->pb.data = upscale_14_to_32bit(src: (midi1->pb.data_msb << 7) | |
401 | midi1->pb.data_lsb); |
402 | break; |
403 | default: |
404 | return 0; |
405 | } |
406 | |
407 | return __snd_seq_deliver_single_event(dest, dest_port, |
408 | event: (struct snd_seq_event *)&ev_cvt, |
409 | atomic, hop); |
410 | } |
411 | |
412 | /* convert UMP packet from MIDI 2.0 to MIDI 1.0 and deliver it */ |
413 | static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest, |
414 | struct snd_seq_client_port *dest_port, |
415 | struct snd_seq_event *__event, |
416 | int atomic, int hop) |
417 | { |
418 | struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event; |
419 | struct snd_seq_ump_event ev_cvt; |
420 | union snd_ump_midi1_msg *midi1 = (union snd_ump_midi1_msg *)ev_cvt.ump; |
421 | const union snd_ump_midi2_msg *midi2 = (const union snd_ump_midi2_msg *)event->ump; |
422 | u16 v; |
423 | |
424 | ev_cvt = *event; |
425 | memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump)); |
426 | |
427 | midi1->note.type = UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE; |
428 | midi1->note.group = midi2->note.group; |
429 | midi1->note.status = midi2->note.status; |
430 | midi1->note.channel = midi2->note.channel; |
431 | switch (midi2->note.status) { |
432 | case UMP_MSG_STATUS_NOTE_ON: |
433 | case UMP_MSG_STATUS_NOTE_OFF: |
434 | midi1->note.note = midi2->note.note; |
435 | midi1->note.velocity = downscale_16_to_7bit(src: midi2->note.velocity); |
436 | break; |
437 | case UMP_MSG_STATUS_POLY_PRESSURE: |
438 | midi1->paf.note = midi2->paf.note; |
439 | midi1->paf.data = downscale_32_to_7bit(src: midi2->paf.data); |
440 | break; |
441 | case UMP_MSG_STATUS_CC: |
442 | midi1->cc.index = midi2->cc.index; |
443 | midi1->cc.data = downscale_32_to_7bit(src: midi2->cc.data); |
444 | break; |
445 | case UMP_MSG_STATUS_PROGRAM: |
446 | midi1->pg.program = midi2->pg.program; |
447 | break; |
448 | case UMP_MSG_STATUS_CHANNEL_PRESSURE: |
449 | midi1->caf.data = downscale_32_to_7bit(src: midi2->caf.data); |
450 | break; |
451 | case UMP_MSG_STATUS_PITCH_BEND: |
452 | v = downscale_32_to_14bit(src: midi2->pb.data); |
453 | midi1->pb.data_msb = v >> 7; |
454 | midi1->pb.data_lsb = v & 0x7f; |
455 | break; |
456 | default: |
457 | return 0; |
458 | } |
459 | |
460 | return __snd_seq_deliver_single_event(dest, dest_port, |
461 | event: (struct snd_seq_event *)&ev_cvt, |
462 | atomic, hop); |
463 | } |
464 | |
465 | /* convert UMP to a legacy ALSA seq event and deliver it */ |
466 | static int cvt_ump_to_any(struct snd_seq_client *dest, |
467 | struct snd_seq_client_port *dest_port, |
468 | struct snd_seq_event *event, |
469 | unsigned char type, |
470 | int atomic, int hop) |
471 | { |
472 | struct snd_seq_event ev_cvt[2]; /* up to two events */ |
473 | struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event; |
474 | /* use the second event as a temp buffer for saving stack usage */ |
475 | unsigned char *sysex_buf = (unsigned char *)(ev_cvt + 1); |
476 | unsigned char flags = event->flags & ~SNDRV_SEQ_EVENT_UMP; |
477 | int i, len, err; |
478 | |
479 | ev_cvt[0] = ev_cvt[1] = *event; |
480 | ev_cvt[0].flags = flags; |
481 | ev_cvt[1].flags = flags; |
482 | switch (type) { |
483 | case UMP_MSG_TYPE_SYSTEM: |
484 | len = cvt_ump_system_to_event(val: (union snd_ump_midi1_msg *)ump_ev->ump, |
485 | ev: ev_cvt); |
486 | break; |
487 | case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE: |
488 | len = cvt_ump_midi1_to_event(val: (union snd_ump_midi1_msg *)ump_ev->ump, |
489 | ev: ev_cvt); |
490 | break; |
491 | case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE: |
492 | len = cvt_ump_midi2_to_event(val: (union snd_ump_midi2_msg *)ump_ev->ump, |
493 | ev: ev_cvt); |
494 | break; |
495 | case UMP_MSG_TYPE_DATA: |
496 | len = cvt_ump_sysex7_to_event(data: ump_ev->ump, buf: sysex_buf, ev: ev_cvt); |
497 | break; |
498 | default: |
499 | return 0; |
500 | } |
501 | |
502 | for (i = 0; i < len; i++) { |
503 | err = __snd_seq_deliver_single_event(dest, dest_port, |
504 | event: &ev_cvt[i], atomic, hop); |
505 | if (err < 0) |
506 | return err; |
507 | } |
508 | |
509 | return 0; |
510 | } |
511 | |
512 | /* Replace UMP group field with the destination and deliver */ |
513 | static int deliver_with_group_convert(struct snd_seq_client *dest, |
514 | struct snd_seq_client_port *dest_port, |
515 | struct snd_seq_ump_event *ump_ev, |
516 | int atomic, int hop) |
517 | { |
518 | struct snd_seq_ump_event ev = *ump_ev; |
519 | |
520 | /* rewrite the group to the destination port */ |
521 | ev.ump[0] &= ~(0xfU << 24); |
522 | /* fill with the new group; the dest_port->ump_group field is 1-based */ |
523 | ev.ump[0] |= ((dest_port->ump_group - 1) << 24); |
524 | |
525 | return __snd_seq_deliver_single_event(dest, dest_port, |
526 | event: (struct snd_seq_event *)&ev, |
527 | atomic, hop); |
528 | } |
529 | |
530 | /* apply the UMP event filter; return true to skip the event */ |
531 | static bool ump_event_filtered(struct snd_seq_client *dest, |
532 | const struct snd_seq_ump_event *ev) |
533 | { |
534 | unsigned char group; |
535 | |
536 | group = ump_message_group(data: ev->ump[0]); |
537 | if (ump_is_groupless_msg(ump_message_type(ev->ump[0]))) |
538 | return dest->group_filter & (1U << 0); |
539 | /* check the bitmap for 1-based group number */ |
540 | return dest->group_filter & (1U << (group + 1)); |
541 | } |
542 | |
543 | /* Convert from UMP packet and deliver */ |
544 | int snd_seq_deliver_from_ump(struct snd_seq_client *source, |
545 | struct snd_seq_client *dest, |
546 | struct snd_seq_client_port *dest_port, |
547 | struct snd_seq_event *event, |
548 | int atomic, int hop) |
549 | { |
550 | struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event; |
551 | unsigned char type; |
552 | |
553 | if (snd_seq_ev_is_variable(event)) |
554 | return 0; // skip, no variable event for UMP, so far |
555 | if (ump_event_filtered(dest, ev: ump_ev)) |
556 | return 0; // skip if group filter is set and matching |
557 | type = ump_message_type(data: ump_ev->ump[0]); |
558 | |
559 | if (snd_seq_client_is_ump(c: dest)) { |
560 | if (snd_seq_client_is_midi2(c: dest) && |
561 | type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE) |
562 | return cvt_ump_midi1_to_midi2(dest, dest_port, |
563 | event: event, atomic, hop); |
564 | else if (!snd_seq_client_is_midi2(c: dest) && |
565 | type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE) |
566 | return cvt_ump_midi2_to_midi1(dest, dest_port, |
567 | event: event, atomic, hop); |
568 | /* non-EP port and different group is set? */ |
569 | if (dest_port->ump_group && |
570 | !ump_is_groupless_msg(type) && |
571 | ump_message_group(data: *ump_ev->ump) + 1 != dest_port->ump_group) |
572 | return deliver_with_group_convert(dest, dest_port, |
573 | ump_ev, atomic, hop); |
574 | /* copy as-is */ |
575 | return __snd_seq_deliver_single_event(dest, dest_port, |
576 | event, atomic, hop); |
577 | } |
578 | |
579 | return cvt_ump_to_any(dest, dest_port, event, type, atomic, hop); |
580 | } |
581 | |
582 | /* |
583 | * MIDI1 sequencer event -> UMP conversion |
584 | */ |
585 | |
586 | /* Conversion to UMP MIDI 1.0 */ |
587 | |
588 | /* convert note on/off event to MIDI 1.0 UMP */ |
589 | static int note_ev_to_ump_midi1(const struct snd_seq_event *event, |
590 | struct snd_seq_client_port *dest_port, |
591 | union snd_ump_midi1_msg *data, |
592 | unsigned char status) |
593 | { |
594 | if (!event->data.note.velocity) |
595 | status = UMP_MSG_STATUS_NOTE_OFF; |
596 | data->note.status = status; |
597 | data->note.channel = event->data.note.channel & 0x0f; |
598 | data->note.velocity = event->data.note.velocity & 0x7f; |
599 | data->note.note = event->data.note.note & 0x7f; |
600 | return 1; |
601 | } |
602 | |
603 | /* convert CC event to MIDI 1.0 UMP */ |
604 | static int cc_ev_to_ump_midi1(const struct snd_seq_event *event, |
605 | struct snd_seq_client_port *dest_port, |
606 | union snd_ump_midi1_msg *data, |
607 | unsigned char status) |
608 | { |
609 | data->cc.status = status; |
610 | data->cc.channel = event->data.control.channel & 0x0f; |
611 | data->cc.index = event->data.control.param; |
612 | data->cc.data = event->data.control.value; |
613 | return 1; |
614 | } |
615 | |
616 | /* convert one-parameter control event to MIDI 1.0 UMP */ |
617 | static int ctrl_ev_to_ump_midi1(const struct snd_seq_event *event, |
618 | struct snd_seq_client_port *dest_port, |
619 | union snd_ump_midi1_msg *data, |
620 | unsigned char status) |
621 | { |
622 | data->caf.status = status; |
623 | data->caf.channel = event->data.control.channel & 0x0f; |
624 | data->caf.data = event->data.control.value & 0x7f; |
625 | return 1; |
626 | } |
627 | |
628 | /* convert pitchbend event to MIDI 1.0 UMP */ |
629 | static int pitchbend_ev_to_ump_midi1(const struct snd_seq_event *event, |
630 | struct snd_seq_client_port *dest_port, |
631 | union snd_ump_midi1_msg *data, |
632 | unsigned char status) |
633 | { |
634 | int val = event->data.control.value + 8192; |
635 | |
636 | val = clamp(val, 0, 0x3fff); |
637 | data->pb.status = status; |
638 | data->pb.channel = event->data.control.channel & 0x0f; |
639 | data->pb.data_msb = (val >> 7) & 0x7f; |
640 | data->pb.data_lsb = val & 0x7f; |
641 | return 1; |
642 | } |
643 | |
644 | /* convert 14bit control event to MIDI 1.0 UMP; split to two events */ |
645 | static int ctrl14_ev_to_ump_midi1(const struct snd_seq_event *event, |
646 | struct snd_seq_client_port *dest_port, |
647 | union snd_ump_midi1_msg *data, |
648 | unsigned char status) |
649 | { |
650 | data->cc.status = UMP_MSG_STATUS_CC; |
651 | data->cc.channel = event->data.control.channel & 0x0f; |
652 | data->cc.index = event->data.control.param & 0x7f; |
653 | if (event->data.control.param < 0x20) { |
654 | data->cc.data = (event->data.control.value >> 7) & 0x7f; |
655 | data[1] = data[0]; |
656 | data[1].cc.index = event->data.control.param | 0x20; |
657 | data[1].cc.data = event->data.control.value & 0x7f; |
658 | return 2; |
659 | } |
660 | |
661 | data->cc.data = event->data.control.value & 0x7f; |
662 | return 1; |
663 | } |
664 | |
665 | /* convert RPN/NRPN event to MIDI 1.0 UMP; split to four events */ |
666 | static int rpn_ev_to_ump_midi1(const struct snd_seq_event *event, |
667 | struct snd_seq_client_port *dest_port, |
668 | union snd_ump_midi1_msg *data, |
669 | unsigned char status) |
670 | { |
671 | bool is_rpn = (status == UMP_MSG_STATUS_RPN); |
672 | |
673 | data->cc.status = UMP_MSG_STATUS_CC; |
674 | data->cc.channel = event->data.control.channel & 0x0f; |
675 | data[1] = data[2] = data[3] = data[0]; |
676 | |
677 | data[0].cc.index = is_rpn ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB; |
678 | data[0].cc.data = (event->data.control.param >> 7) & 0x7f; |
679 | data[1].cc.index = is_rpn ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB; |
680 | data[1].cc.data = event->data.control.param & 0x7f; |
681 | data[2].cc.index = UMP_CC_DATA; |
682 | data[2].cc.data = (event->data.control.value >> 7) & 0x7f; |
683 | data[3].cc.index = UMP_CC_DATA_LSB; |
684 | data[3].cc.data = event->data.control.value & 0x7f; |
685 | return 4; |
686 | } |
687 | |
688 | /* convert system / RT message to UMP */ |
689 | static int system_ev_to_ump_midi1(const struct snd_seq_event *event, |
690 | struct snd_seq_client_port *dest_port, |
691 | union snd_ump_midi1_msg *data, |
692 | unsigned char status) |
693 | { |
694 | data->system.status = status; |
695 | return 1; |
696 | } |
697 | |
698 | /* convert system / RT message with 1 parameter to UMP */ |
699 | static int system_1p_ev_to_ump_midi1(const struct snd_seq_event *event, |
700 | struct snd_seq_client_port *dest_port, |
701 | union snd_ump_midi1_msg *data, |
702 | unsigned char status) |
703 | { |
704 | data->system.status = status; |
705 | data->system.parm1 = event->data.control.value & 0x7f; |
706 | return 1; |
707 | } |
708 | |
709 | /* convert system / RT message with two parameters to UMP */ |
710 | static int system_2p_ev_to_ump_midi1(const struct snd_seq_event *event, |
711 | struct snd_seq_client_port *dest_port, |
712 | union snd_ump_midi1_msg *data, |
713 | unsigned char status) |
714 | { |
715 | data->system.status = status; |
716 | data->system.parm1 = (event->data.control.value >> 7) & 0x7f; |
717 | data->system.parm2 = event->data.control.value & 0x7f; |
718 | return 1; |
719 | } |
720 | |
721 | /* Conversion to UMP MIDI 2.0 */ |
722 | |
723 | /* convert note on/off event to MIDI 2.0 UMP */ |
724 | static int note_ev_to_ump_midi2(const struct snd_seq_event *event, |
725 | struct snd_seq_client_port *dest_port, |
726 | union snd_ump_midi2_msg *data, |
727 | unsigned char status) |
728 | { |
729 | if (!event->data.note.velocity) |
730 | status = UMP_MSG_STATUS_NOTE_OFF; |
731 | data->note.status = status; |
732 | data->note.channel = event->data.note.channel & 0x0f; |
733 | data->note.note = event->data.note.note & 0x7f; |
734 | data->note.velocity = upscale_7_to_16bit(src: event->data.note.velocity & 0x7f); |
735 | return 1; |
736 | } |
737 | |
738 | /* convert PAF event to MIDI 2.0 UMP */ |
739 | static int paf_ev_to_ump_midi2(const struct snd_seq_event *event, |
740 | struct snd_seq_client_port *dest_port, |
741 | union snd_ump_midi2_msg *data, |
742 | unsigned char status) |
743 | { |
744 | data->paf.status = status; |
745 | data->paf.channel = event->data.note.channel & 0x0f; |
746 | data->paf.note = event->data.note.note & 0x7f; |
747 | data->paf.data = upscale_7_to_32bit(src: event->data.note.velocity & 0x7f); |
748 | return 1; |
749 | } |
750 | |
751 | /* set up the MIDI2 RPN/NRPN packet data from the parsed info */ |
752 | static void fill_rpn(struct snd_seq_ump_midi2_bank *cc, |
753 | union snd_ump_midi2_msg *data) |
754 | { |
755 | if (cc->rpn_set) { |
756 | data->rpn.status = UMP_MSG_STATUS_RPN; |
757 | data->rpn.bank = cc->cc_rpn_msb; |
758 | data->rpn.index = cc->cc_rpn_lsb; |
759 | cc->rpn_set = 0; |
760 | cc->cc_rpn_msb = cc->cc_rpn_lsb = 0; |
761 | } else { |
762 | data->rpn.status = UMP_MSG_STATUS_NRPN; |
763 | data->rpn.bank = cc->cc_nrpn_msb; |
764 | data->rpn.index = cc->cc_nrpn_lsb; |
765 | cc->nrpn_set = 0; |
766 | cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0; |
767 | } |
768 | data->rpn.data = upscale_14_to_32bit(src: (cc->cc_data_msb << 7) | |
769 | cc->cc_data_lsb); |
770 | cc->cc_data_msb = cc->cc_data_lsb = 0; |
771 | } |
772 | |
773 | /* convert CC event to MIDI 2.0 UMP */ |
774 | static int cc_ev_to_ump_midi2(const struct snd_seq_event *event, |
775 | struct snd_seq_client_port *dest_port, |
776 | union snd_ump_midi2_msg *data, |
777 | unsigned char status) |
778 | { |
779 | unsigned char channel = event->data.control.channel & 0x0f; |
780 | unsigned char index = event->data.control.param & 0x7f; |
781 | unsigned char val = event->data.control.value & 0x7f; |
782 | struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel]; |
783 | |
784 | /* process special CC's (bank/rpn/nrpn) */ |
785 | switch (index) { |
786 | case UMP_CC_RPN_MSB: |
787 | cc->rpn_set = 1; |
788 | cc->cc_rpn_msb = val; |
789 | return 0; // skip |
790 | case UMP_CC_RPN_LSB: |
791 | cc->rpn_set = 1; |
792 | cc->cc_rpn_lsb = val; |
793 | return 0; // skip |
794 | case UMP_CC_NRPN_MSB: |
795 | cc->nrpn_set = 1; |
796 | cc->cc_nrpn_msb = val; |
797 | return 0; // skip |
798 | case UMP_CC_NRPN_LSB: |
799 | cc->nrpn_set = 1; |
800 | cc->cc_nrpn_lsb = val; |
801 | return 0; // skip |
802 | case UMP_CC_DATA: |
803 | cc->cc_data_msb = val; |
804 | return 0; // skip |
805 | case UMP_CC_BANK_SELECT: |
806 | cc->bank_set = 1; |
807 | cc->cc_bank_msb = val; |
808 | return 0; // skip |
809 | case UMP_CC_BANK_SELECT_LSB: |
810 | cc->bank_set = 1; |
811 | cc->cc_bank_lsb = val; |
812 | return 0; // skip |
813 | case UMP_CC_DATA_LSB: |
814 | cc->cc_data_lsb = val; |
815 | if (!(cc->rpn_set || cc->nrpn_set)) |
816 | return 0; // skip |
817 | fill_rpn(cc, data); |
818 | return 1; |
819 | } |
820 | |
821 | data->cc.status = status; |
822 | data->cc.channel = channel; |
823 | data->cc.index = index; |
824 | data->cc.data = upscale_7_to_32bit(src: event->data.control.value & 0x7f); |
825 | return 1; |
826 | } |
827 | |
828 | /* convert one-parameter control event to MIDI 2.0 UMP */ |
829 | static int ctrl_ev_to_ump_midi2(const struct snd_seq_event *event, |
830 | struct snd_seq_client_port *dest_port, |
831 | union snd_ump_midi2_msg *data, |
832 | unsigned char status) |
833 | { |
834 | data->caf.status = status; |
835 | data->caf.channel = event->data.control.channel & 0x0f; |
836 | data->caf.data = upscale_7_to_32bit(src: event->data.control.value & 0x7f); |
837 | return 1; |
838 | } |
839 | |
840 | /* convert program change event to MIDI 2.0 UMP */ |
841 | static int pgm_ev_to_ump_midi2(const struct snd_seq_event *event, |
842 | struct snd_seq_client_port *dest_port, |
843 | union snd_ump_midi2_msg *data, |
844 | unsigned char status) |
845 | { |
846 | unsigned char channel = event->data.control.channel & 0x0f; |
847 | struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel]; |
848 | |
849 | data->pg.status = status; |
850 | data->pg.channel = channel; |
851 | data->pg.program = event->data.control.value & 0x7f; |
852 | if (cc->bank_set) { |
853 | data->pg.bank_valid = 1; |
854 | data->pg.bank_msb = cc->cc_bank_msb; |
855 | data->pg.bank_lsb = cc->cc_bank_lsb; |
856 | cc->bank_set = 0; |
857 | cc->cc_bank_msb = cc->cc_bank_lsb = 0; |
858 | } |
859 | return 1; |
860 | } |
861 | |
862 | /* convert pitchbend event to MIDI 2.0 UMP */ |
863 | static int pitchbend_ev_to_ump_midi2(const struct snd_seq_event *event, |
864 | struct snd_seq_client_port *dest_port, |
865 | union snd_ump_midi2_msg *data, |
866 | unsigned char status) |
867 | { |
868 | int val = event->data.control.value + 8192; |
869 | |
870 | val = clamp(val, 0, 0x3fff); |
871 | data->pb.status = status; |
872 | data->pb.channel = event->data.control.channel & 0x0f; |
873 | data->pb.data = upscale_14_to_32bit(src: val); |
874 | return 1; |
875 | } |
876 | |
877 | /* convert 14bit control event to MIDI 2.0 UMP; split to two events */ |
878 | static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event, |
879 | struct snd_seq_client_port *dest_port, |
880 | union snd_ump_midi2_msg *data, |
881 | unsigned char status) |
882 | { |
883 | unsigned char channel = event->data.control.channel & 0x0f; |
884 | unsigned char index = event->data.control.param & 0x7f; |
885 | struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel]; |
886 | unsigned char msb, lsb; |
887 | |
888 | msb = (event->data.control.value >> 7) & 0x7f; |
889 | lsb = event->data.control.value & 0x7f; |
890 | /* process special CC's (bank/rpn/nrpn) */ |
891 | switch (index) { |
892 | case UMP_CC_BANK_SELECT: |
893 | cc->cc_bank_msb = msb; |
894 | fallthrough; |
895 | case UMP_CC_BANK_SELECT_LSB: |
896 | cc->bank_set = 1; |
897 | cc->cc_bank_lsb = lsb; |
898 | return 0; // skip |
899 | case UMP_CC_RPN_MSB: |
900 | cc->cc_rpn_msb = msb; |
901 | fallthrough; |
902 | case UMP_CC_RPN_LSB: |
903 | cc->rpn_set = 1; |
904 | cc->cc_rpn_lsb = lsb; |
905 | return 0; // skip |
906 | case UMP_CC_NRPN_MSB: |
907 | cc->cc_nrpn_msb = msb; |
908 | fallthrough; |
909 | case UMP_CC_NRPN_LSB: |
910 | cc->nrpn_set = 1; |
911 | cc->cc_nrpn_lsb = lsb; |
912 | return 0; // skip |
913 | case UMP_CC_DATA: |
914 | cc->cc_data_msb = msb; |
915 | fallthrough; |
916 | case UMP_CC_DATA_LSB: |
917 | cc->cc_data_lsb = lsb; |
918 | if (!(cc->rpn_set || cc->nrpn_set)) |
919 | return 0; // skip |
920 | fill_rpn(cc, data); |
921 | return 1; |
922 | } |
923 | |
924 | data->cc.status = UMP_MSG_STATUS_CC; |
925 | data->cc.channel = channel; |
926 | data->cc.index = index; |
927 | if (event->data.control.param < 0x20) { |
928 | data->cc.data = upscale_7_to_32bit(src: msb); |
929 | data[1] = data[0]; |
930 | data[1].cc.index = event->data.control.param | 0x20; |
931 | data[1].cc.data = upscale_7_to_32bit(src: lsb); |
932 | return 2; |
933 | } |
934 | |
935 | data->cc.data = upscale_7_to_32bit(src: lsb); |
936 | return 1; |
937 | } |
938 | |
939 | /* convert RPN/NRPN event to MIDI 2.0 UMP */ |
940 | static int rpn_ev_to_ump_midi2(const struct snd_seq_event *event, |
941 | struct snd_seq_client_port *dest_port, |
942 | union snd_ump_midi2_msg *data, |
943 | unsigned char status) |
944 | { |
945 | data->rpn.status = status; |
946 | data->rpn.channel = event->data.control.channel; |
947 | data->rpn.bank = (event->data.control.param >> 7) & 0x7f; |
948 | data->rpn.index = event->data.control.param & 0x7f; |
949 | data->rpn.data = upscale_14_to_32bit(src: event->data.control.value & 0x3fff); |
950 | return 1; |
951 | } |
952 | |
953 | /* convert system / RT message to UMP */ |
954 | static int system_ev_to_ump_midi2(const struct snd_seq_event *event, |
955 | struct snd_seq_client_port *dest_port, |
956 | union snd_ump_midi2_msg *data, |
957 | unsigned char status) |
958 | { |
959 | return system_ev_to_ump_midi1(event, dest_port, |
960 | data: (union snd_ump_midi1_msg *)data, |
961 | status); |
962 | } |
963 | |
964 | /* convert system / RT message with 1 parameter to UMP */ |
965 | static int system_1p_ev_to_ump_midi2(const struct snd_seq_event *event, |
966 | struct snd_seq_client_port *dest_port, |
967 | union snd_ump_midi2_msg *data, |
968 | unsigned char status) |
969 | { |
970 | return system_1p_ev_to_ump_midi1(event, dest_port, |
971 | data: (union snd_ump_midi1_msg *)data, |
972 | status); |
973 | } |
974 | |
975 | /* convert system / RT message with two parameters to UMP */ |
976 | static int system_2p_ev_to_ump_midi2(const struct snd_seq_event *event, |
977 | struct snd_seq_client_port *dest_port, |
978 | union snd_ump_midi2_msg *data, |
979 | unsigned char status) |
980 | { |
981 | return system_1p_ev_to_ump_midi1(event, dest_port, |
982 | data: (union snd_ump_midi1_msg *)data, |
983 | status); |
984 | } |
985 | |
986 | struct seq_ev_to_ump { |
987 | int seq_type; |
988 | unsigned char status; |
989 | int (*midi1_encode)(const struct snd_seq_event *event, |
990 | struct snd_seq_client_port *dest_port, |
991 | union snd_ump_midi1_msg *data, |
992 | unsigned char status); |
993 | int (*midi2_encode)(const struct snd_seq_event *event, |
994 | struct snd_seq_client_port *dest_port, |
995 | union snd_ump_midi2_msg *data, |
996 | unsigned char status); |
997 | }; |
998 | |
999 | static const struct seq_ev_to_ump seq_ev_ump_encoders[] = { |
1000 | { SNDRV_SEQ_EVENT_NOTEON, UMP_MSG_STATUS_NOTE_ON, |
1001 | note_ev_to_ump_midi1, note_ev_to_ump_midi2 }, |
1002 | { SNDRV_SEQ_EVENT_NOTEOFF, UMP_MSG_STATUS_NOTE_OFF, |
1003 | note_ev_to_ump_midi1, note_ev_to_ump_midi2 }, |
1004 | { SNDRV_SEQ_EVENT_KEYPRESS, UMP_MSG_STATUS_POLY_PRESSURE, |
1005 | note_ev_to_ump_midi1, paf_ev_to_ump_midi2 }, |
1006 | { SNDRV_SEQ_EVENT_CONTROLLER, UMP_MSG_STATUS_CC, |
1007 | cc_ev_to_ump_midi1, cc_ev_to_ump_midi2 }, |
1008 | { SNDRV_SEQ_EVENT_PGMCHANGE, UMP_MSG_STATUS_PROGRAM, |
1009 | ctrl_ev_to_ump_midi1, pgm_ev_to_ump_midi2 }, |
1010 | { SNDRV_SEQ_EVENT_CHANPRESS, UMP_MSG_STATUS_CHANNEL_PRESSURE, |
1011 | ctrl_ev_to_ump_midi1, ctrl_ev_to_ump_midi2 }, |
1012 | { SNDRV_SEQ_EVENT_PITCHBEND, UMP_MSG_STATUS_PITCH_BEND, |
1013 | pitchbend_ev_to_ump_midi1, pitchbend_ev_to_ump_midi2 }, |
1014 | { SNDRV_SEQ_EVENT_CONTROL14, 0, |
1015 | ctrl14_ev_to_ump_midi1, ctrl14_ev_to_ump_midi2 }, |
1016 | { SNDRV_SEQ_EVENT_NONREGPARAM, UMP_MSG_STATUS_NRPN, |
1017 | rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 }, |
1018 | { SNDRV_SEQ_EVENT_REGPARAM, UMP_MSG_STATUS_RPN, |
1019 | rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 }, |
1020 | { SNDRV_SEQ_EVENT_QFRAME, UMP_SYSTEM_STATUS_MIDI_TIME_CODE, |
1021 | system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 }, |
1022 | { SNDRV_SEQ_EVENT_SONGPOS, UMP_SYSTEM_STATUS_SONG_POSITION, |
1023 | system_2p_ev_to_ump_midi1, system_2p_ev_to_ump_midi2 }, |
1024 | { SNDRV_SEQ_EVENT_SONGSEL, UMP_SYSTEM_STATUS_SONG_SELECT, |
1025 | system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 }, |
1026 | { SNDRV_SEQ_EVENT_TUNE_REQUEST, UMP_SYSTEM_STATUS_TUNE_REQUEST, |
1027 | system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, |
1028 | { SNDRV_SEQ_EVENT_CLOCK, UMP_SYSTEM_STATUS_TIMING_CLOCK, |
1029 | system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, |
1030 | { SNDRV_SEQ_EVENT_START, UMP_SYSTEM_STATUS_START, |
1031 | system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, |
1032 | { SNDRV_SEQ_EVENT_CONTINUE, UMP_SYSTEM_STATUS_CONTINUE, |
1033 | system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, |
1034 | { SNDRV_SEQ_EVENT_STOP, UMP_SYSTEM_STATUS_STOP, |
1035 | system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, |
1036 | { SNDRV_SEQ_EVENT_SENSING, UMP_SYSTEM_STATUS_ACTIVE_SENSING, |
1037 | system_ev_to_ump_midi1, system_ev_to_ump_midi2 }, |
1038 | }; |
1039 | |
1040 | static const struct seq_ev_to_ump *find_ump_encoder(int type) |
1041 | { |
1042 | int i; |
1043 | |
1044 | for (i = 0; i < ARRAY_SIZE(seq_ev_ump_encoders); i++) |
1045 | if (seq_ev_ump_encoders[i].seq_type == type) |
1046 | return &seq_ev_ump_encoders[i]; |
1047 | |
1048 | return NULL; |
1049 | } |
1050 | |
1051 | static void setup_ump_event(struct snd_seq_ump_event *dest, |
1052 | const struct snd_seq_event *src) |
1053 | { |
1054 | memcpy(dest, src, sizeof(*src)); |
1055 | dest->type = 0; |
1056 | dest->flags |= SNDRV_SEQ_EVENT_UMP; |
1057 | dest->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; |
1058 | memset(dest->ump, 0, sizeof(dest->ump)); |
1059 | } |
1060 | |
1061 | /* Convert ALSA seq event to UMP MIDI 1.0 and deliver it */ |
1062 | static int cvt_to_ump_midi1(struct snd_seq_client *dest, |
1063 | struct snd_seq_client_port *dest_port, |
1064 | struct snd_seq_event *event, |
1065 | int atomic, int hop) |
1066 | { |
1067 | const struct seq_ev_to_ump *encoder; |
1068 | struct snd_seq_ump_event ev_cvt; |
1069 | union snd_ump_midi1_msg data[4]; |
1070 | int i, n, err; |
1071 | |
1072 | encoder = find_ump_encoder(type: event->type); |
1073 | if (!encoder) |
1074 | return __snd_seq_deliver_single_event(dest, dest_port, |
1075 | event, atomic, hop); |
1076 | |
1077 | data->raw = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE); |
1078 | n = encoder->midi1_encode(event, dest_port, data, encoder->status); |
1079 | if (!n) |
1080 | return 0; |
1081 | |
1082 | setup_ump_event(dest: &ev_cvt, src: event); |
1083 | for (i = 0; i < n; i++) { |
1084 | ev_cvt.ump[0] = data[i].raw; |
1085 | err = __snd_seq_deliver_single_event(dest, dest_port, |
1086 | event: (struct snd_seq_event *)&ev_cvt, |
1087 | atomic, hop); |
1088 | if (err < 0) |
1089 | return err; |
1090 | } |
1091 | |
1092 | return 0; |
1093 | } |
1094 | |
1095 | /* Convert ALSA seq event to UMP MIDI 2.0 and deliver it */ |
1096 | static int cvt_to_ump_midi2(struct snd_seq_client *dest, |
1097 | struct snd_seq_client_port *dest_port, |
1098 | struct snd_seq_event *event, |
1099 | int atomic, int hop) |
1100 | { |
1101 | const struct seq_ev_to_ump *encoder; |
1102 | struct snd_seq_ump_event ev_cvt; |
1103 | union snd_ump_midi2_msg data[2]; |
1104 | int i, n, err; |
1105 | |
1106 | encoder = find_ump_encoder(type: event->type); |
1107 | if (!encoder) |
1108 | return __snd_seq_deliver_single_event(dest, dest_port, |
1109 | event, atomic, hop); |
1110 | |
1111 | data->raw[0] = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE); |
1112 | data->raw[1] = 0; |
1113 | n = encoder->midi2_encode(event, dest_port, data, encoder->status); |
1114 | if (!n) |
1115 | return 0; |
1116 | |
1117 | setup_ump_event(dest: &ev_cvt, src: event); |
1118 | for (i = 0; i < n; i++) { |
1119 | memcpy(ev_cvt.ump, &data[i], sizeof(data[i])); |
1120 | err = __snd_seq_deliver_single_event(dest, dest_port, |
1121 | event: (struct snd_seq_event *)&ev_cvt, |
1122 | atomic, hop); |
1123 | if (err < 0) |
1124 | return err; |
1125 | } |
1126 | |
1127 | return 0; |
1128 | } |
1129 | |
1130 | /* Fill up a sysex7 UMP from the byte stream */ |
1131 | static void fill_sysex7_ump(struct snd_seq_client_port *dest_port, |
1132 | u32 *val, u8 status, u8 *buf, int len) |
1133 | { |
1134 | memset(val, 0, 8); |
1135 | memcpy((u8 *)val + 2, buf, len); |
1136 | #ifdef __LITTLE_ENDIAN |
1137 | swab32_array(buf: val, words: 2); |
1138 | #endif |
1139 | val[0] |= ump_compose(type: UMP_MSG_TYPE_DATA, group: get_ump_group(port: dest_port), |
1140 | status, channel: len); |
1141 | } |
1142 | |
1143 | /* Convert sysex var event to UMP sysex7 packets and deliver them */ |
1144 | static int cvt_sysex_to_ump(struct snd_seq_client *dest, |
1145 | struct snd_seq_client_port *dest_port, |
1146 | struct snd_seq_event *event, |
1147 | int atomic, int hop) |
1148 | { |
1149 | struct snd_seq_ump_event ev_cvt; |
1150 | unsigned char status; |
1151 | u8 buf[6], *xbuf; |
1152 | int offset = 0; |
1153 | int len, err; |
1154 | |
1155 | if (!snd_seq_ev_is_variable(event)) |
1156 | return 0; |
1157 | |
1158 | setup_ump_event(dest: &ev_cvt, src: event); |
1159 | for (;;) { |
1160 | len = snd_seq_expand_var_event_at(event, count: sizeof(buf), buf, offset); |
1161 | if (len <= 0) |
1162 | break; |
1163 | if (WARN_ON(len > 6)) |
1164 | break; |
1165 | offset += len; |
1166 | xbuf = buf; |
1167 | if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) { |
1168 | status = UMP_SYSEX_STATUS_START; |
1169 | xbuf++; |
1170 | len--; |
1171 | if (len > 0 && xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) { |
1172 | status = UMP_SYSEX_STATUS_SINGLE; |
1173 | len--; |
1174 | } |
1175 | } else { |
1176 | if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) { |
1177 | status = UMP_SYSEX_STATUS_END; |
1178 | len--; |
1179 | } else { |
1180 | status = UMP_SYSEX_STATUS_CONTINUE; |
1181 | } |
1182 | } |
1183 | fill_sysex7_ump(dest_port, val: ev_cvt.ump, status, buf: xbuf, len); |
1184 | err = __snd_seq_deliver_single_event(dest, dest_port, |
1185 | event: (struct snd_seq_event *)&ev_cvt, |
1186 | atomic, hop); |
1187 | if (err < 0) |
1188 | return err; |
1189 | } |
1190 | return 0; |
1191 | } |
1192 | |
1193 | /* Convert to UMP packet and deliver */ |
1194 | int snd_seq_deliver_to_ump(struct snd_seq_client *source, |
1195 | struct snd_seq_client *dest, |
1196 | struct snd_seq_client_port *dest_port, |
1197 | struct snd_seq_event *event, |
1198 | int atomic, int hop) |
1199 | { |
1200 | if (dest->group_filter & (1U << dest_port->ump_group)) |
1201 | return 0; /* group filtered - skip the event */ |
1202 | if (event->type == SNDRV_SEQ_EVENT_SYSEX) |
1203 | return cvt_sysex_to_ump(dest, dest_port, event, atomic, hop); |
1204 | else if (snd_seq_client_is_midi2(c: dest)) |
1205 | return cvt_to_ump_midi2(dest, dest_port, event, atomic, hop); |
1206 | else |
1207 | return cvt_to_ump_midi1(dest, dest_port, event, atomic, hop); |
1208 | } |
1209 | |