Commit | Line | Data |
---|---|---|
e9e02819 TI |
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(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(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(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(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(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(val); | |
326 | bytes = ump_sysex_message_length(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(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(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(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(midi1->caf.data); | |
398 | break; | |
399 | case UMP_MSG_STATUS_PITCH_BEND: | |
400 | midi2->pb.data = upscale_14_to_32bit((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 | (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; | |
f25f17dc | 431 | switch (midi2->note.status) { |
e9e02819 TI |
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(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(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(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(midi2->caf.data); | |
450 | break; | |
451 | case UMP_MSG_STATUS_PITCH_BEND: | |
452 | v = downscale_32_to_14bit(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 | (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((union snd_ump_midi1_msg *)ump_ev->ump, | |
485 | ev_cvt); | |
486 | break; | |
487 | case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE: | |
488 | len = cvt_ump_midi1_to_event((union snd_ump_midi1_msg *)ump_ev->ump, | |
489 | ev_cvt); | |
490 | break; | |
491 | case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE: | |
492 | len = cvt_ump_midi2_to_event((union snd_ump_midi2_msg *)ump_ev->ump, | |
493 | ev_cvt); | |
494 | break; | |
495 | case UMP_MSG_TYPE_DATA: | |
496 | len = cvt_ump_sysex7_to_event(ump_ev->ump, sysex_buf, 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 | &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 | (struct snd_seq_event *)&ev, | |
527 | atomic, hop); | |
528 | } | |
529 | ||
d2b70607 TI |
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(ev->ump[0]); | |
5437ac9b TI |
537 | if (ump_is_groupless_msg(ump_message_type(ev->ump[0]))) |
538 | return dest->group_filter & (1U << 0); | |
d2b70607 TI |
539 | /* check the bitmap for 1-based group number */ |
540 | return dest->group_filter & (1U << (group + 1)); | |
541 | } | |
542 | ||
e9e02819 TI |
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 | |
d2b70607 TI |
555 | if (ump_event_filtered(dest, ump_ev)) |
556 | return 0; // skip if group filter is set and matching | |
e9e02819 TI |
557 | type = ump_message_type(ump_ev->ump[0]); |
558 | ||
559 | if (snd_seq_client_is_ump(dest)) { | |
560 | if (snd_seq_client_is_midi2(dest) && | |
561 | type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE) | |
562 | return cvt_ump_midi1_to_midi2(dest, dest_port, | |
563 | event, atomic, hop); | |
564 | else if (!snd_seq_client_is_midi2(dest) && | |
565 | type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE) | |
566 | return cvt_ump_midi2_to_midi1(dest, dest_port, | |
567 | event, atomic, hop); | |
568 | /* non-EP port and different group is set? */ | |
569 | if (dest_port->ump_group && | |
5437ac9b | 570 | !ump_is_groupless_msg(type) && |
e9e02819 TI |
571 | ump_message_group(*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; | |
983b9180 | 717 | data->system.parm2 = event->data.control.value & 0x7f; |
e9e02819 TI |
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(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(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((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(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(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(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(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(lsb); | |
932 | return 2; | |
933 | } | |
934 | ||
935 | data->cc.data = upscale_7_to_32bit(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(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 | (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 | (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 | (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(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(&ev_cvt, 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 | (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(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(&ev_cvt, 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 | (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(val, 2); | |
1138 | #endif | |
1139 | val[0] |= ump_compose(UMP_MSG_TYPE_DATA, get_ump_group(dest_port), | |
1140 | status, 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(&ev_cvt, event); | |
1159 | for (;;) { | |
1160 | len = snd_seq_expand_var_event_at(event, 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, ev_cvt.ump, status, xbuf, len); | |
1184 | err = __snd_seq_deliver_single_event(dest, dest_port, | |
1185 | (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 | { | |
22eefaea TI |
1200 | if (dest->group_filter & (1U << dest_port->ump_group)) |
1201 | return 0; /* group filtered - skip the event */ | |
e9e02819 TI |
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(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 | } |