Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/core/ump_convert.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Helpers for UMP <-> MIDI 1.0 byte stream conversion
4
*/
5
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>
12
13
/*
14
* Upgrade / downgrade value bits
15
*/
16
static u8 downscale_32_to_7bit(u32 src)
17
{
18
return src >> 25;
19
}
20
21
static u16 downscale_32_to_14bit(u32 src)
22
{
23
return src >> 18;
24
}
25
26
static u8 downscale_16_to_7bit(u16 src)
27
{
28
return src >> 9;
29
}
30
31
static u16 upscale_7_to_16bit(u8 src)
32
{
33
u16 val, repeat;
34
35
val = (u16)src << 9;
36
if (src <= 0x40)
37
return val;
38
repeat = src & 0x3f;
39
return val | (repeat << 3) | (repeat >> 3);
40
}
41
42
static u32 upscale_7_to_32bit(u8 src)
43
{
44
u32 val, repeat;
45
46
val = src << 25;
47
if (src <= 0x40)
48
return val;
49
repeat = src & 0x3f;
50
return val | (repeat << 19) | (repeat << 13) |
51
(repeat << 7) | (repeat << 1) | (repeat >> 5);
52
}
53
54
static u32 upscale_14_to_32bit(u16 src)
55
{
56
u32 val, repeat;
57
58
val = src << 18;
59
if (src <= 0x2000)
60
return val;
61
repeat = src & 0x1fff;
62
return val | (repeat << 5) | (repeat >> 8);
63
}
64
65
/*
66
* UMP -> MIDI 1 byte stream conversion
67
*/
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)
70
{
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;
76
return 2;
77
case UMP_SYSTEM_STATUS_SONG_POSITION:
78
buf[1] = (data >> 8) & 0x7f;
79
buf[2] = data & 0x7f;
80
return 3;
81
default:
82
return 1;
83
}
84
}
85
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)
88
{
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:
94
return 2;
95
default:
96
buf[2] = data & 0xff;
97
return 3;
98
}
99
}
100
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,
103
unsigned char *buf)
104
{
105
unsigned char status = midi2->note.status;
106
unsigned char channel = midi2->note.channel;
107
u16 v;
108
109
buf[0] = (status << 4) | channel;
110
switch (status) {
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])
116
buf[2] = 1;
117
return 3;
118
case UMP_MSG_STATUS_POLY_PRESSURE:
119
buf[1] = midi2->paf.note;
120
buf[2] = downscale_32_to_7bit(midi2->paf.data);
121
return 3;
122
case UMP_MSG_STATUS_CC:
123
buf[1] = midi2->cc.index;
124
buf[2] = downscale_32_to_7bit(midi2->cc.data);
125
return 3;
126
case UMP_MSG_STATUS_CHANNEL_PRESSURE:
127
buf[1] = downscale_32_to_7bit(midi2->caf.data);
128
return 2;
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;
139
return 8;
140
}
141
buf[1] = midi2->pg.program;
142
return 2;
143
case UMP_MSG_STATUS_PITCH_BEND:
144
v = downscale_32_to_14bit(midi2->pb.data);
145
buf[1] = v & 0x7f;
146
buf[2] = v >> 7;
147
return 3;
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;
153
buf[3] = buf[0];
154
buf[4] = status == UMP_MSG_STATUS_RPN ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB;
155
buf[5] = midi2->rpn.index;
156
buf[6] = buf[0];
157
buf[7] = UMP_CC_DATA;
158
v = downscale_32_to_14bit(midi2->rpn.data);
159
buf[8] = v >> 7;
160
buf[9] = buf[0];
161
buf[10] = UMP_CC_DATA_LSB;
162
buf[11] = v & 0x7f;
163
return 12;
164
default:
165
return 0;
166
}
167
}
168
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)
171
{
172
unsigned char status;
173
unsigned char bytes;
174
int size, offset;
175
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);
180
if (bytes > 6)
181
return 0; // skip
182
183
size = 0;
184
if (status == UMP_SYSEX_STATUS_SINGLE ||
185
status == UMP_SYSEX_STATUS_START) {
186
buf[0] = UMP_MIDI1_MSG_SYSEX_START;
187
size = 1;
188
}
189
190
offset = 8;
191
for (; bytes; bytes--, size++) {
192
buf[size] = (*data >> offset) & 0x7f;
193
if (!offset) {
194
offset = 24;
195
data++;
196
} else {
197
offset -= 8;
198
}
199
}
200
201
if (status == UMP_SYSEX_STATUS_SINGLE ||
202
status == UMP_SYSEX_STATUS_END)
203
buf[size++] = UMP_MIDI1_MSG_SYSEX_END;
204
205
return size;
206
}
207
208
/**
209
* snd_ump_convert_from_ump - convert from UMP to legacy MIDI
210
* @data: UMP packet
211
* @buf: buffer to store legacy MIDI data
212
* @group_ret: pointer to store the target group
213
*
214
* Convert from a UMP packet @data to MIDI 1.0 bytes at @buf.
215
* The target group is stored at @group_ret.
216
*
217
* The function returns the number of bytes of MIDI 1.0 stream.
218
*/
219
int snd_ump_convert_from_ump(const u32 *data,
220
unsigned char *buf,
221
unsigned char *group_ret)
222
{
223
*group_ret = ump_message_group(*data);
224
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,
232
buf);
233
case UMP_MSG_TYPE_DATA:
234
return cvt_ump_sysex7_to_legacy(data, buf);
235
}
236
237
return 0;
238
}
239
EXPORT_SYMBOL_GPL(snd_ump_convert_from_ump);
240
241
/*
242
* MIDI 1 byte stream -> UMP conversion
243
*/
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)
247
{
248
unsigned char status;
249
bool start = cvt->in_sysex == 1;
250
int i, offset;
251
252
if (start && finish)
253
status = UMP_SYSEX_STATUS_SINGLE;
254
else if (start)
255
status = UMP_SYSEX_STATUS_START;
256
else if (finish)
257
status = UMP_SYSEX_STATUS_END;
258
else
259
status = UMP_SYSEX_STATUS_CONTINUE;
260
*data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len);
261
offset = 8;
262
for (i = 0; i < cvt->len; i++) {
263
*data |= cvt->buf[i] << offset;
264
if (!offset) {
265
offset = 24;
266
data++;
267
} else
268
offset -= 8;
269
}
270
cvt->len = 0;
271
if (finish)
272
cvt->in_sysex = 0;
273
else
274
cvt->in_sysex++;
275
return 8;
276
}
277
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)
281
{
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];
287
return 4;
288
}
289
290
static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
291
{
292
cc->rpn_set = 0;
293
cc->nrpn_set = 0;
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;
297
}
298
299
static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
300
union snd_ump_midi2_msg *midi2,
301
bool flush)
302
{
303
if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
304
return 0; // skip
305
/* when not flushing, wait for complete data set */
306
if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
307
return 0; // skip
308
309
if (cc->rpn_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;
317
} else {
318
return 0; // skip
319
}
320
321
midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
322
cc->cc_data_lsb);
323
324
reset_rpn(cc);
325
return 1;
326
}
327
328
/* convert to a MIDI 1.0 Channel Voice message */
329
static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt,
330
unsigned char group,
331
unsigned int protocol,
332
u32 *data, unsigned char bytes)
333
{
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;
338
int ret;
339
340
BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4);
341
BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg) != 8);
342
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,
346
group, 0, buf[0]);
347
data[0] |= buf[1] << 8;
348
if (bytes > 2)
349
data[0] |= buf[2];
350
return 4;
351
}
352
353
status = *buf >> 4;
354
channel = *buf & 0x0f;
355
cc = &cvt->bank[channel];
356
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;
360
361
/* initialize the packet */
362
data[0] = ump_compose(UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE,
363
group, status, channel);
364
data[1] = 0;
365
366
switch (status) {
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]);
371
break;
372
case UMP_MSG_STATUS_POLY_PRESSURE:
373
midi2->paf.note = buf[1];
374
midi2->paf.data = upscale_7_to_32bit(buf[2]);
375
break;
376
case UMP_MSG_STATUS_CC:
377
switch (buf[1]) {
378
case UMP_CC_RPN_MSB:
379
ret = fill_rpn(cc, midi2, true);
380
cc->rpn_set = 1;
381
cc->cc_rpn_msb = buf[2];
382
if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
383
reset_rpn(cc);
384
return ret;
385
case UMP_CC_RPN_LSB:
386
ret = fill_rpn(cc, midi2, true);
387
cc->rpn_set = 1;
388
cc->cc_rpn_lsb = buf[2];
389
if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
390
reset_rpn(cc);
391
return ret;
392
case UMP_CC_NRPN_MSB:
393
ret = fill_rpn(cc, midi2, true);
394
cc->nrpn_set = 1;
395
cc->cc_nrpn_msb = buf[2];
396
return ret;
397
case UMP_CC_NRPN_LSB:
398
ret = fill_rpn(cc, midi2, true);
399
cc->nrpn_set = 1;
400
cc->cc_nrpn_lsb = buf[2];
401
return ret;
402
case UMP_CC_DATA:
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:
407
cc->bank_set = 1;
408
cc->cc_bank_msb = buf[2];
409
return 0; // skip
410
case UMP_CC_BANK_SELECT_LSB:
411
cc->bank_set = 1;
412
cc->cc_bank_lsb = buf[2];
413
return 0; // skip
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);
418
default:
419
midi2->cc.index = buf[1];
420
midi2->cc.data = upscale_7_to_32bit(buf[2]);
421
break;
422
}
423
break;
424
case UMP_MSG_STATUS_PROGRAM:
425
midi2->pg.program = buf[1];
426
if (cc->bank_set) {
427
midi2->pg.bank_valid = 1;
428
midi2->pg.bank_msb = cc->cc_bank_msb;
429
midi2->pg.bank_lsb = cc->cc_bank_lsb;
430
cc->bank_set = 0;
431
}
432
break;
433
case UMP_MSG_STATUS_CHANNEL_PRESSURE:
434
midi2->caf.data = upscale_7_to_32bit(buf[1]);
435
break;
436
case UMP_MSG_STATUS_PITCH_BEND:
437
midi2->pb.data = upscale_14_to_32bit(buf[1] | (buf[2] << 7));
438
break;
439
default:
440
return 0;
441
}
442
443
return 8;
444
}
445
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)
448
{
449
/* bytes for 0x80-0xf0 */
450
static unsigned char cmd_bytes[8] = {
451
3, 3, 3, 3, 2, 2, 3, 0
452
};
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
456
};
457
unsigned char bytes;
458
459
if (c == UMP_MIDI1_MSG_SYSEX_START) {
460
cvt->in_sysex = 1;
461
cvt->len = 0;
462
return 0;
463
}
464
if (c == UMP_MIDI1_MSG_SYSEX_END) {
465
if (!cvt->in_sysex)
466
return 0; /* skip */
467
return cvt_legacy_sysex_to_ump(cvt, group, data, true);
468
}
469
470
if ((c & 0xf0) == UMP_MIDI1_MSG_REALTIME) {
471
bytes = system_bytes[c & 0x0f];
472
if (!bytes)
473
return 0; /* skip */
474
if (bytes == 1) {
475
data[0] = ump_compose(UMP_MSG_TYPE_SYSTEM, group, 0, c);
476
return 4;
477
}
478
cvt->buf[0] = c;
479
cvt->len = 1;
480
cvt->cmd_bytes = bytes;
481
cvt->in_sysex = 0; /* abort SysEx */
482
return 0;
483
}
484
485
if (c & 0x80) {
486
bytes = cmd_bytes[(c >> 4) & 7];
487
cvt->buf[0] = c;
488
cvt->len = 1;
489
cvt->cmd_bytes = bytes;
490
cvt->in_sysex = 0; /* abort SysEx */
491
return 0;
492
}
493
494
if (cvt->in_sysex) {
495
cvt->buf[cvt->len++] = c;
496
if (cvt->len == 6)
497
return cvt_legacy_sysex_to_ump(cvt, group, data, false);
498
return 0;
499
}
500
501
if (!cvt->len)
502
return 0;
503
504
cvt->buf[cvt->len++] = c;
505
if (cvt->len < cvt->cmd_bytes)
506
return 0;
507
cvt->len = 1;
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);
511
}
512
513
/**
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
519
*
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.
522
*/
523
void snd_ump_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
524
unsigned int protocol, unsigned char c)
525
{
526
cvt->ump_bytes = do_convert_to_ump(cvt, group, protocol, c, cvt->ump);
527
}
528
EXPORT_SYMBOL_GPL(snd_ump_convert_to_ump);
529
530