Mistake on this page?
Report an issue in GitHub or email us
MIDIMessage.h
1 /*
2  * Copyright (c) 2018-2019, Arm Limited and affiliates.
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #ifndef MIDIMESSAGE_H
19 #define MIDIMESSAGE_H
20 
21 #include <stdint.h>
22 
23 #define MAX_MIDI_MESSAGE_SIZE 256 // Max message size. SysEx can be up to 65536 but 256 should be fine for most usage
24 
25 // MIDI Message Format
26 //
27 // [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ]
28 //
29 // MIDI Data Messages (Channel Specific)
30 //
31 // Message msg n m
32 // ---------------------------------------------
33 // Note Off 0x8 Key Velocity
34 // Note On 0x9 Key Velocity
35 // Polyphonic Aftertouch 0xA Key Pressure
36 // Control Change 0xB Controller Value
37 // Program Change 0xC Program -
38 // Channel Aftertouch 0xD Pressure -
39 // Pitch Wheel 0xE LSB MSB
40 
41 #define CABLE_NUM (0<<4)
42 
43 
44 /**
45  * \defgroup drivers_MIDIMessage MIDIMessage class
46  * \ingroup drivers-internal-api-usb
47  * @{
48  */
49 
50 /** A MIDI message container */
51 class MIDIMessage {
52 public:
53 
54  MIDIMessage() : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) {}
55 
56  MIDIMessage(uint8_t *buf) : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0)
57  {
58  for (int i = 0; i < 4; i++) {
59  data[i] = buf[i];
60  }
61  }
62 
63  /**
64  * Copy constructor
65  */
66  MIDIMessage(const MIDIMessage &other)
67  {
68  *this = other;
69  }
70 
71  /**
72  * Assignment operator
73  */
75  {
76  length = other.length;
77  for (int i = 0; i < length; i++) {
78  data[i] = other.data[i];
79  }
80 
81  return *this;
82  }
83 
84  ~MIDIMessage()
85  {
86  delete[] data;
87  }
88 
89  /**
90  * Set this MIDIMessage to a raw MIDI message
91  *
92  * @param buf is a true MIDI message (not USBMidi message)
93  * @param buf_len size of message
94  */
95  void from_raw(uint8_t *buf, int buf_len)
96  {
97  length = buf_len + 1;
98  if (length > MAX_MIDI_MESSAGE_SIZE) {
99  // Message is too big
100  length = 0;
101  return;
102  }
103 
104  // first byte keeped for retro-compatibility
105  data[0] = CABLE_NUM | 0x08;
106 
107  for (int i = 0; i < buf_len; i++) {
108  data[i + 1] = buf[i];
109  }
110  }
111 
112  // create messages
113 
114  /** Create a NoteOff message
115  * @param key Key ID
116  * @param velocity Key velocity (0-127, default = 127)
117  * @param channel Key channel (0-15, default 0)
118  * @returns A MIDIMessage
119  */
120  static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0)
121  {
122  MIDIMessage msg;
123  msg.data[0] = CABLE_NUM | 0x08;
124  msg.data[1] = 0x80 | (channel & 0x0F);
125  msg.data[2] = key & 0x7F;
126  msg.data[3] = velocity & 0x7F;
127  msg.length = 4;
128  return msg;
129  }
130 
131  /** Create a NoteOn message
132  * @param key Key ID
133  * @param velocity Key velocity (0-127, default = 127)
134  * @param channel Key channel (0-15, default 0)
135  * @returns A MIDIMessage
136  */
137  static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0)
138  {
139  MIDIMessage msg;
140  msg.data[0] = CABLE_NUM | 0x09;
141  msg.data[1] = 0x90 | (channel & 0x0F);
142  msg.data[2] = key & 0x7F;
143  msg.data[3] = velocity & 0x7F;
144  msg.length = 4;
145  return msg;
146  }
147 
148  /** Create a PolyPhonic Aftertouch message
149  * @param key Key ID
150  * @param pressure Aftertouch pressure (0-127)
151  * @param channel Key channel (0-15, default 0)
152  * @returns A MIDIMessage
153  */
155  {
156  MIDIMessage msg;
157  msg.data[0] = CABLE_NUM | 0x0A;
158  msg.data[1] = 0xA0 | (channel & 0x0F);
159  msg.data[2] = key & 0x7F;
160  msg.data[3] = pressure & 0x7F;
161  msg.length = 4;
162  return msg;
163  }
164 
165  /** Create a Control Change message
166  * @param control Controller ID
167  * @param value Controller value (0-127)
168  * @param channel Controller channel (0-15, default 0)
169  * @returns A MIDIMessage
170  */
171  static MIDIMessage ControlChange(int control, int value, int channel = 0)
172  {
173  MIDIMessage msg;
174  msg.data[0] = CABLE_NUM | 0x0B;
175  msg.data[1] = 0xB0 | (channel & 0x0F);
176  msg.data[2] = control & 0x7F;
177  msg.data[3] = value & 0x7F;
178  msg.length = 4;
179  return msg;
180  }
181 
182  /** Create a Program Change message
183  * @param program Program ID
184  * @param channel Channel (0-15, default 0)
185  * @returns A MIDIMessage
186  */
188  {
189  MIDIMessage msg;
190  msg.data[0] = CABLE_NUM | 0x0C;
191  msg.data[1] = 0xC0 | (channel & 0x0F);
192  msg.data[2] = program & 0x7F;
193  msg.data[3] = 0x00;
194  msg.length = 4;
195  return msg;
196  }
197 
198  /** Create a Channel Aftertouch message
199  * @param pressure Pressure
200  * @param channel Key channel (0-15, default 0)
201  * @returns A MIDIMessage
202  */
204  {
205  MIDIMessage msg;
206  msg.data[0] = CABLE_NUM | 0x0D;
207  msg.data[1] = 0xD0 | (channel & 0x0F);
208  msg.data[2] = pressure & 0x7F;
209  msg.data[3] = 0x00;
210  msg.length = 4;
211  return msg;
212  }
213 
214  /** Create a Pitch Wheel message
215  * @param pitch Pitch (-8192 - 8191, default = 0)
216  * @param channel Channel (0-15, default 0)
217  * @returns A MIDIMessage
218  */
219  static MIDIMessage PitchWheel(int pitch = 0, int channel = 0)
220  {
221  MIDIMessage msg;
222  int p = pitch + 8192; // 0 - 16383, 8192 is center
223  msg.data[0] = CABLE_NUM | 0x0E;
224  msg.data[1] = 0xE0 | (channel & 0x0F);
225  msg.data[2] = p & 0x7F;
226  msg.data[3] = (p >> 7) & 0x7F;
227  msg.length = 4;
228  return msg;
229  }
230 
231  /** Create an All Notes Off message
232  * @param channel Channel (0-15, default 0)
233  * @returns A MIDIMessage
234  */
236  {
237  return ControlChange(123, 0, channel);
238  }
239 
240  /** Create a SysEx message
241  * @param data SysEx data (including 0xF0 .. 0xF7)
242  * @param len SysEx data length
243  * @returns A MIDIMessage
244  */
245  static MIDIMessage SysEx(uint8_t *data, int len)
246  {
247  MIDIMessage msg;
248  msg.from_raw(data, len);
249  return msg;
250  }
251 
252  // decode messages
253 
254  /** MIDI Message Types */
256  ErrorType,
257  NoteOffType,
258  NoteOnType,
259  PolyphonicAftertouchType,
260  ControlChangeType,
261  ProgramChangeType,
262  ChannelAftertouchType,
263  PitchWheelType,
264  ResetAllControllersType,
265  AllNotesOffType,
266  SysExType
267  };
268 
269  /** Read the message type
270  *
271  * @returns MIDIMessageType
272  */
274  {
275  MIDIMessageType message_type;
276  uint8_t min_size;
277  switch ((data[1] >> 4) & 0xF) {
278  case 0x8:
279  // message, channel
280  // key
281  // velocity
282  min_size = 3;
283  message_type = NoteOffType;
284  break;
285  case 0x9:
286  // message, channel
287  // key
288  // velocity
289  min_size = 3;
290  message_type = NoteOnType;
291  break;
292  case 0xA:
293  // message, channel
294  // key
295  // pressure
296  min_size = 3;
297  message_type = PolyphonicAftertouchType;
298  break;
299  case 0xB:
300  // message, channel
301  // controller
302  min_size = 2;
303  if ((data[2] & 0x7F) < 120) { // standard controllers
304  message_type = ControlChangeType;
305  } else if ((data[2] & 0x7F) == 121) {
306  message_type = ResetAllControllersType;
307  } else if ((data[2] & 0x7F) == 123) {
308  message_type = AllNotesOffType;
309  } else {
310  message_type = ErrorType; // unsupported atm
311  }
312  break;
313  case 0xC:
314  // message, channel
315  // program
316  min_size = 2;
317  message_type = ProgramChangeType;
318  break;
319  case 0xD:
320  // message, channel
321  // pressure
322  min_size = 2;
323  message_type = ChannelAftertouchType;
324  break;
325  case 0xE:
326  // message, channel
327  // pitch lsb
328  // pitch msb
329  min_size = 3;
330  message_type = PitchWheelType;
331  break;
332  case 0xF:
333  min_size = 2;
334  message_type = SysExType;
335  break;
336  default:
337  message_type = ErrorType;
338  break;
339  }
340 
341 
342  if (length < min_size) {
343  // too small to be a valid message
344  message_type = ErrorType;
345  }
346  return message_type;
347  }
348 
349  /**
350  * Read the channel number
351  *
352  * @return channel number or -1 on error
353  */
354 
355  int channel()
356  {
357  return (data[1] & 0x0F);
358  }
359 
360  /**
361  * Read the key ID
362  *
363  * @return key ID or -1 on error
364  */
365  int key()
366  {
367  MIDIMessageType msg_type = type();
368  if ((msg_type != NoteOffType) &&
369  (msg_type != NoteOnType) &&
370  (msg_type != PolyphonicAftertouchType)) {
371  return -1;
372  }
373 
374  return data[2] & 0x7F;
375  }
376 
377  /**
378  * Read the velocity
379  *
380  * @return velocity or -1 on error
381  */
382  int velocity()
383  {
384  MIDIMessageType msg_type = type();
385  if ((msg_type != NoteOffType) &&
386  (msg_type != NoteOnType)) {
387  return -1;
388  }
389 
390  return data[3] & 0x7F;
391  }
392 
393  /**
394  * Read the controller value
395  *
396  * @return controller value or -1 on error
397  */
398  int value()
399  {
400  MIDIMessageType msg_type = type();
401  if ((msg_type != ControlChangeType) &&
402  (msg_type != ResetAllControllersType) &&
403  (msg_type != AllNotesOffType)) {
404  return -1;
405  }
406 
407  return data[3] & 0x7F;
408  }
409 
410  /**
411  * Read the aftertouch pressure
412  *
413  * @return aftertouch pressure or -1 on error
414  */
415  int pressure()
416  {
417  MIDIMessageType msg_type = type();
418  if ((msg_type != PolyphonicAftertouchType) &&
419  (msg_type != ChannelAftertouchType)) {
420  return -1;
421  }
422 
423  if (type() == PolyphonicAftertouchType) {
424  return data[3] & 0x7F;
425  } else {
426  return data[2] & 0x7F;
427  }
428  }
429 
430  /**
431  * Read the controller number
432  *
433  * @return controller number or -1 on error
434  */
436  {
437  MIDIMessageType msg_type = type();
438  if ((msg_type != ControlChangeType) &&
439  (msg_type != ResetAllControllersType) &&
440  (msg_type != AllNotesOffType)) {
441  return -1;
442  }
443 
444  return data[2] & 0x7F;
445  }
446 
447  /**
448  * Read the program number
449  *
450  * @return program number or -1 on error
451  */
452  int program()
453  {
454  MIDIMessageType msg_type = type();
455  if (msg_type != ProgramChangeType) {
456  return -1;
457  }
458 
459  return data[2] & 0x7F;
460  }
461 
462  /**
463  * Read the pitch value
464  *
465  * @return pitch value or -1 on error
466  */
467  int pitch()
468  {
469  MIDIMessageType msg_type = type();
470  if (msg_type != PitchWheelType) {
471  return -1;
472  }
473 
474  int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F);
475  return p - 8192; // 0 - 16383, 8192 is center
476  }
477 
478  uint8_t *data;
479  uint16_t length;
480 };
481 
482 /** @}*/
483 
484 #endif
MIDIMessageType type()
Read the message type.
Definition: MIDIMessage.h:273
static MIDIMessage NoteOn(int key, int velocity=127, int channel=0)
Create a NoteOn message.
Definition: MIDIMessage.h:137
static MIDIMessage ProgramChange(int program, int channel=0)
Create a Program Change message.
Definition: MIDIMessage.h:187
int controller()
Read the controller number.
Definition: MIDIMessage.h:435
static MIDIMessage NoteOff(int key, int velocity=127, int channel=0)
Create a NoteOff message.
Definition: MIDIMessage.h:120
static MIDIMessage AllNotesOff(int channel=0)
Create an All Notes Off message.
Definition: MIDIMessage.h:235
static MIDIMessage SysEx(uint8_t *data, int len)
Create a SysEx message.
Definition: MIDIMessage.h:245
int pressure()
Read the aftertouch pressure.
Definition: MIDIMessage.h:415
static MIDIMessage ControlChange(int control, int value, int channel=0)
Create a Control Change message.
Definition: MIDIMessage.h:171
int key()
Read the key ID.
Definition: MIDIMessage.h:365
int channel()
Read the channel number.
Definition: MIDIMessage.h:355
static MIDIMessage ChannelAftertouch(int pressure, int channel=0)
Create a Channel Aftertouch message.
Definition: MIDIMessage.h:203
void from_raw(uint8_t *buf, int buf_len)
Set this MIDIMessage to a raw MIDI message.
Definition: MIDIMessage.h:95
MIDIMessage & operator=(const MIDIMessage &other)
Assignment operator.
Definition: MIDIMessage.h:74
A MIDI message container.
Definition: MIDIMessage.h:51
static MIDIMessage PitchWheel(int pitch=0, int channel=0)
Create a Pitch Wheel message.
Definition: MIDIMessage.h:219
static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel=0)
Create a PolyPhonic Aftertouch message.
Definition: MIDIMessage.h:154
int program()
Read the program number.
Definition: MIDIMessage.h:452
int value()
Read the controller value.
Definition: MIDIMessage.h:398
int pitch()
Read the pitch value.
Definition: MIDIMessage.h:467
int velocity()
Read the velocity.
Definition: MIDIMessage.h:382
MIDIMessageType
MIDI Message Types.
Definition: MIDIMessage.h:255
MIDIMessage(const MIDIMessage &other)
Copy constructor.
Definition: MIDIMessage.h:66
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.