1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Helpers for UMP <-> MIDI 1.0 byte stream conversion
6 #include <linux/module.h>
7 #include <linux/export.h>
8 #include <sound/core.h>
9 #include <sound/asound.h>
10 #include <sound/ump.h>
11 #include <sound/ump_convert.h>
14 * Upgrade / downgrade value bits
16 static u8
downscale_32_to_7bit(u32 src
)
21 static u16
downscale_32_to_14bit(u32 src
)
26 static u8
downscale_16_to_7bit(u16 src
)
31 static u16
upscale_7_to_16bit(u8 src
)
39 return val
| (repeat
<< 3) | (repeat
>> 3);
42 static u32
upscale_7_to_32bit(u8 src
)
50 return val
| (repeat
<< 19) | (repeat
<< 13) |
51 (repeat
<< 7) | (repeat
<< 1) | (repeat
>> 5);
54 static u32
upscale_14_to_32bit(u16 src
)
61 repeat
= src
& 0x1fff;
62 return val
| (repeat
<< 5) | (repeat
>> 8);
66 * UMP -> MIDI 1 byte stream conversion
68 /* convert a UMP System message to MIDI 1.0 byte stream */
69 static int cvt_ump_system_to_legacy(u32 data
, unsigned char *buf
)
71 buf
[0] = ump_message_status_channel(data
);
72 switch (ump_message_status_code(data
)) {
73 case UMP_SYSTEM_STATUS_MIDI_TIME_CODE
:
74 case UMP_SYSTEM_STATUS_SONG_SELECT
:
75 buf
[1] = (data
>> 8) & 0x7f;
77 case UMP_SYSTEM_STATUS_SONG_POSITION
:
78 buf
[1] = (data
>> 8) & 0x7f;
86 /* convert a UMP MIDI 1.0 Channel Voice message to MIDI 1.0 byte stream */
87 static int cvt_ump_midi1_to_legacy(u32 data
, unsigned char *buf
)
89 buf
[0] = ump_message_status_channel(data
);
90 buf
[1] = (data
>> 8) & 0xff;
91 switch (ump_message_status_code(data
)) {
92 case UMP_MSG_STATUS_PROGRAM
:
93 case UMP_MSG_STATUS_CHANNEL_PRESSURE
:
101 /* convert a UMP MIDI 2.0 Channel Voice message to MIDI 1.0 byte stream */
102 static int cvt_ump_midi2_to_legacy(const union snd_ump_midi2_msg
*midi2
,
105 unsigned char status
= midi2
->note
.status
;
106 unsigned char channel
= midi2
->note
.channel
;
109 buf
[0] = (status
<< 4) | channel
;
111 case UMP_MSG_STATUS_NOTE_OFF
:
112 case UMP_MSG_STATUS_NOTE_ON
:
113 buf
[1] = midi2
->note
.note
;
114 buf
[2] = downscale_16_to_7bit(midi2
->note
.velocity
);
115 if (status
== UMP_MSG_STATUS_NOTE_ON
&& !buf
[2])
118 case UMP_MSG_STATUS_POLY_PRESSURE
:
119 buf
[1] = midi2
->paf
.note
;
120 buf
[2] = downscale_32_to_7bit(midi2
->paf
.data
);
122 case UMP_MSG_STATUS_CC
:
123 buf
[1] = midi2
->cc
.index
;
124 buf
[2] = downscale_32_to_7bit(midi2
->cc
.data
);
126 case UMP_MSG_STATUS_CHANNEL_PRESSURE
:
127 buf
[1] = downscale_32_to_7bit(midi2
->caf
.data
);
129 case UMP_MSG_STATUS_PROGRAM
:
130 if (midi2
->pg
.bank_valid
) {
131 buf
[0] = channel
| (UMP_MSG_STATUS_CC
<< 4);
132 buf
[1] = UMP_CC_BANK_SELECT
;
133 buf
[2] = midi2
->pg
.bank_msb
;
134 buf
[3] = channel
| (UMP_MSG_STATUS_CC
<< 4);
135 buf
[4] = UMP_CC_BANK_SELECT_LSB
;
136 buf
[5] = midi2
->pg
.bank_lsb
;
137 buf
[6] = channel
| (UMP_MSG_STATUS_PROGRAM
<< 4);
138 buf
[7] = midi2
->pg
.program
;
141 buf
[1] = midi2
->pg
.program
;
143 case UMP_MSG_STATUS_PITCH_BEND
:
144 v
= downscale_32_to_14bit(midi2
->pb
.data
);
148 case UMP_MSG_STATUS_RPN
:
149 case UMP_MSG_STATUS_NRPN
:
150 buf
[0] = channel
| (UMP_MSG_STATUS_CC
<< 4);
151 buf
[1] = status
== UMP_MSG_STATUS_RPN
? UMP_CC_RPN_MSB
: UMP_CC_NRPN_MSB
;
152 buf
[2] = midi2
->rpn
.bank
;
154 buf
[4] = status
== UMP_MSG_STATUS_RPN
? UMP_CC_RPN_LSB
: UMP_CC_NRPN_LSB
;
155 buf
[5] = midi2
->rpn
.index
;
157 buf
[7] = UMP_CC_DATA
;
158 v
= downscale_32_to_14bit(midi2
->rpn
.data
);
161 buf
[10] = UMP_CC_DATA_LSB
;
169 /* convert a UMP 7-bit SysEx message to MIDI 1.0 byte stream */
170 static int cvt_ump_sysex7_to_legacy(const u32
*data
, unsigned char *buf
)
172 unsigned char status
;
176 status
= ump_sysex_message_status(*data
);
177 if (status
> UMP_SYSEX_STATUS_END
)
178 return 0; // unsupported, skip
179 bytes
= ump_sysex_message_length(*data
);
184 if (status
== UMP_SYSEX_STATUS_SINGLE
||
185 status
== UMP_SYSEX_STATUS_START
) {
186 buf
[0] = UMP_MIDI1_MSG_SYSEX_START
;
191 for (; bytes
; bytes
--, size
++) {
192 buf
[size
] = (*data
>> offset
) & 0x7f;
201 if (status
== UMP_SYSEX_STATUS_SINGLE
||
202 status
== UMP_SYSEX_STATUS_END
)
203 buf
[size
++] = UMP_MIDI1_MSG_SYSEX_END
;
209 * snd_ump_convert_from_ump - convert from UMP to legacy MIDI
211 * @buf: buffer to store legacy MIDI data
212 * @group_ret: pointer to store the target group
214 * Convert from a UMP packet @data to MIDI 1.0 bytes at @buf.
215 * The target group is stored at @group_ret.
217 * The function returns the number of bytes of MIDI 1.0 stream.
219 int snd_ump_convert_from_ump(const u32
*data
,
221 unsigned char *group_ret
)
223 *group_ret
= ump_message_group(*data
);
225 switch (ump_message_type(*data
)) {
226 case UMP_MSG_TYPE_SYSTEM
:
227 return cvt_ump_system_to_legacy(*data
, buf
);
228 case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE
:
229 return cvt_ump_midi1_to_legacy(*data
, buf
);
230 case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE
:
231 return cvt_ump_midi2_to_legacy((const union snd_ump_midi2_msg
*)data
,
233 case UMP_MSG_TYPE_DATA
:
234 return cvt_ump_sysex7_to_legacy(data
, buf
);
239 EXPORT_SYMBOL_GPL(snd_ump_convert_from_ump
);
242 * MIDI 1 byte stream -> UMP conversion
244 /* convert MIDI 1.0 SysEx to a UMP packet */
245 static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump
*cvt
,
246 unsigned char group
, u32
*data
, bool finish
)
248 unsigned char status
;
249 bool start
= cvt
->in_sysex
== 1;
253 status
= UMP_SYSEX_STATUS_SINGLE
;
255 status
= UMP_SYSEX_STATUS_START
;
257 status
= UMP_SYSEX_STATUS_END
;
259 status
= UMP_SYSEX_STATUS_CONTINUE
;
260 *data
= ump_compose(UMP_MSG_TYPE_DATA
, group
, status
, cvt
->len
);
262 for (i
= 0; i
< cvt
->len
; i
++) {
263 *data
|= cvt
->buf
[i
] << offset
;
278 /* convert to a UMP System message */
279 static int cvt_legacy_system_to_ump(struct ump_cvt_to_ump
*cvt
,
280 unsigned char group
, u32
*data
)
282 data
[0] = ump_compose(UMP_MSG_TYPE_SYSTEM
, group
, 0, cvt
->buf
[0]);
283 if (cvt
->cmd_bytes
> 1)
284 data
[0] |= cvt
->buf
[1] << 8;
285 if (cvt
->cmd_bytes
> 2)
286 data
[0] |= cvt
->buf
[2];
290 static void reset_rpn(struct ump_cvt_to_ump_bank
*cc
)
294 cc
->cc_rpn_msb
= cc
->cc_rpn_lsb
= 0;
295 cc
->cc_data_msb
= cc
->cc_data_lsb
= 0;
296 cc
->cc_data_msb_set
= cc
->cc_data_lsb_set
= 0;
299 static int fill_rpn(struct ump_cvt_to_ump_bank
*cc
,
300 union snd_ump_midi2_msg
*midi2
,
303 if (!(cc
->cc_data_lsb_set
|| cc
->cc_data_msb_set
))
305 /* when not flushing, wait for complete data set */
306 if (!flush
&& (!cc
->cc_data_lsb_set
|| !cc
->cc_data_msb_set
))
310 midi2
->rpn
.status
= UMP_MSG_STATUS_RPN
;
311 midi2
->rpn
.bank
= cc
->cc_rpn_msb
;
312 midi2
->rpn
.index
= cc
->cc_rpn_lsb
;
313 } else if (cc
->nrpn_set
) {
314 midi2
->rpn
.status
= UMP_MSG_STATUS_NRPN
;
315 midi2
->rpn
.bank
= cc
->cc_nrpn_msb
;
316 midi2
->rpn
.index
= cc
->cc_nrpn_lsb
;
321 midi2
->rpn
.data
= upscale_14_to_32bit((cc
->cc_data_msb
<< 7) |
328 /* convert to a MIDI 1.0 Channel Voice message */
329 static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump
*cvt
,
331 unsigned int protocol
,
332 u32
*data
, unsigned char bytes
)
334 const unsigned char *buf
= cvt
->buf
;
335 struct ump_cvt_to_ump_bank
*cc
;
336 union snd_ump_midi2_msg
*midi2
= (union snd_ump_midi2_msg
*)data
;
337 unsigned char status
, channel
;
340 BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg
) != 4);
341 BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg
) != 8);
343 /* for MIDI 1.0 UMP, it's easy, just pack it into UMP */
344 if (protocol
& SNDRV_UMP_EP_INFO_PROTO_MIDI1
) {
345 data
[0] = ump_compose(UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE
,
347 data
[0] |= buf
[1] << 8;
354 channel
= *buf
& 0x0f;
355 cc
= &cvt
->bank
[channel
];
357 /* special handling: treat note-on with 0 velocity as note-off */
358 if (status
== UMP_MSG_STATUS_NOTE_ON
&& !buf
[2])
359 status
= UMP_MSG_STATUS_NOTE_OFF
;
361 /* initialize the packet */
362 data
[0] = ump_compose(UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE
,
363 group
, status
, channel
);
367 case UMP_MSG_STATUS_NOTE_ON
:
368 case UMP_MSG_STATUS_NOTE_OFF
:
369 midi2
->note
.note
= buf
[1];
370 midi2
->note
.velocity
= upscale_7_to_16bit(buf
[2]);
372 case UMP_MSG_STATUS_POLY_PRESSURE
:
373 midi2
->paf
.note
= buf
[1];
374 midi2
->paf
.data
= upscale_7_to_32bit(buf
[2]);
376 case UMP_MSG_STATUS_CC
:
379 ret
= fill_rpn(cc
, midi2
, true);
381 cc
->cc_rpn_msb
= buf
[2];
382 if (cc
->cc_rpn_msb
== 0x7f && cc
->cc_rpn_lsb
== 0x7f)
386 ret
= fill_rpn(cc
, midi2
, true);
388 cc
->cc_rpn_lsb
= buf
[2];
389 if (cc
->cc_rpn_msb
== 0x7f && cc
->cc_rpn_lsb
== 0x7f)
392 case UMP_CC_NRPN_MSB
:
393 ret
= fill_rpn(cc
, midi2
, true);
395 cc
->cc_nrpn_msb
= buf
[2];
397 case UMP_CC_NRPN_LSB
:
398 ret
= fill_rpn(cc
, midi2
, true);
400 cc
->cc_nrpn_lsb
= buf
[2];
403 cc
->cc_data_msb_set
= 1;
404 cc
->cc_data_msb
= buf
[2];
405 return fill_rpn(cc
, midi2
, false);
406 case UMP_CC_BANK_SELECT
:
408 cc
->cc_bank_msb
= buf
[2];
410 case UMP_CC_BANK_SELECT_LSB
:
412 cc
->cc_bank_lsb
= buf
[2];
414 case UMP_CC_DATA_LSB
:
415 cc
->cc_data_lsb_set
= 1;
416 cc
->cc_data_lsb
= buf
[2];
417 return fill_rpn(cc
, midi2
, false);
419 midi2
->cc
.index
= buf
[1];
420 midi2
->cc
.data
= upscale_7_to_32bit(buf
[2]);
424 case UMP_MSG_STATUS_PROGRAM
:
425 midi2
->pg
.program
= buf
[1];
427 midi2
->pg
.bank_valid
= 1;
428 midi2
->pg
.bank_msb
= cc
->cc_bank_msb
;
429 midi2
->pg
.bank_lsb
= cc
->cc_bank_lsb
;
433 case UMP_MSG_STATUS_CHANNEL_PRESSURE
:
434 midi2
->caf
.data
= upscale_7_to_32bit(buf
[1]);
436 case UMP_MSG_STATUS_PITCH_BEND
:
437 midi2
->pb
.data
= upscale_14_to_32bit(buf
[1] | (buf
[2] << 7));
446 static int do_convert_to_ump(struct ump_cvt_to_ump
*cvt
, unsigned char group
,
447 unsigned int protocol
, unsigned char c
, u32
*data
)
449 /* bytes for 0x80-0xf0 */
450 static unsigned char cmd_bytes
[8] = {
451 3, 3, 3, 3, 2, 2, 3, 0
453 /* bytes for 0xf0-0xff */
454 static unsigned char system_bytes
[16] = {
455 0, 2, 3, 2, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1
459 if (c
== UMP_MIDI1_MSG_SYSEX_START
) {
464 if (c
== UMP_MIDI1_MSG_SYSEX_END
) {
467 return cvt_legacy_sysex_to_ump(cvt
, group
, data
, true);
470 if ((c
& 0xf0) == UMP_MIDI1_MSG_REALTIME
) {
471 bytes
= system_bytes
[c
& 0x0f];
475 data
[0] = ump_compose(UMP_MSG_TYPE_SYSTEM
, group
, 0, c
);
480 cvt
->cmd_bytes
= bytes
;
481 cvt
->in_sysex
= 0; /* abort SysEx */
486 bytes
= cmd_bytes
[(c
>> 4) & 7];
489 cvt
->cmd_bytes
= bytes
;
490 cvt
->in_sysex
= 0; /* abort SysEx */
495 cvt
->buf
[cvt
->len
++] = c
;
497 return cvt_legacy_sysex_to_ump(cvt
, group
, data
, false);
504 cvt
->buf
[cvt
->len
++] = c
;
505 if (cvt
->len
< cvt
->cmd_bytes
)
508 if ((cvt
->buf
[0] & 0xf0) == UMP_MIDI1_MSG_REALTIME
)
509 return cvt_legacy_system_to_ump(cvt
, group
, data
);
510 return cvt_legacy_cmd_to_ump(cvt
, group
, protocol
, data
, cvt
->cmd_bytes
);
514 * snd_ump_convert_to_ump - convert legacy MIDI byte to UMP packet
515 * @cvt: converter context
516 * @group: target UMP group
517 * @protocol: target UMP protocol
518 * @c: MIDI 1.0 byte data
520 * Feed a MIDI 1.0 byte @c and convert to a UMP packet if completed.
521 * The result is stored in the buffer in @cvt.
523 void snd_ump_convert_to_ump(struct ump_cvt_to_ump
*cvt
, unsigned char group
,
524 unsigned int protocol
, unsigned char c
)
526 cvt
->ump_bytes
= do_convert_to_ump(cvt
, group
, protocol
, c
, cvt
->ump
);
528 EXPORT_SYMBOL_GPL(snd_ump_convert_to_ump
);