~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/core/ump_convert.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php