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 /** A MIDI message container */
44 class MIDIMessage {
45 public:
46 
47  MIDIMessage() : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) {}
48 
49  MIDIMessage(uint8_t *buf) : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0)
50  {
51  for (int i = 0; i < 4; i++) {
52  data[i] = buf[i];
53  }
54  }
55 
56  /**
57  * Copy constructor
58  */
59  MIDIMessage(const MIDIMessage &other)
60  {
61  *this = other;
62  }
63 
64  /**
65  * Assignment operator
66  */
68  {
69  length = other.length;
70  for (int i = 0; i < length; i++) {
71  data[i] = other.data[i];
72  }
73 
74  return *this;
75  }
76 
77  ~MIDIMessage()
78  {
79  delete[] data;
80  }
81 
82  /**
83  * Set this MIDIMessage to a raw MIDI message
84  *
85  * @param buf is a true MIDI message (not USBMidi message)
86  * @param buf_len size of message
87  */
88  void from_raw(uint8_t *buf, int buf_len)
89  {
90  length = buf_len + 1;
91  if (length > MAX_MIDI_MESSAGE_SIZE) {
92  // Message is too big
93  length = 0;
94  return;
95  }
96 
97  // first byte keeped for retro-compatibility
98  data[0] = CABLE_NUM | 0x08;
99 
100  for (int i = 0; i < buf_len; i++) {
101  data[i + 1] = buf[i];
102  }
103  }
104 
105  // create messages
106 
107  /** Create a NoteOff message
108  * @param key Key ID
109  * @param velocity Key velocity (0-127, default = 127)
110  * @param channel Key channel (0-15, default 0)
111  * @returns A MIDIMessage
112  */
113  static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0)
114  {
115  MIDIMessage msg;
116  msg.data[0] = CABLE_NUM | 0x08;
117  msg.data[1] = 0x80 | (channel & 0x0F);
118  msg.data[2] = key & 0x7F;
119  msg.data[3] = velocity & 0x7F;
120  msg.length = 4;
121  return msg;
122  }
123 
124  /** Create a NoteOn message
125  * @param key Key ID
126  * @param velocity Key velocity (0-127, default = 127)
127  * @param channel Key channel (0-15, default 0)
128  * @returns A MIDIMessage
129  */
130  static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0)
131  {
132  MIDIMessage msg;
133  msg.data[0] = CABLE_NUM | 0x09;
134  msg.data[1] = 0x90 | (channel & 0x0F);
135  msg.data[2] = key & 0x7F;
136  msg.data[3] = velocity & 0x7F;
137  msg.length = 4;
138  return msg;
139  }
140 
141  /** Create a PolyPhonic Aftertouch message
142  * @param key Key ID
143  * @param pressure Aftertouch pressure (0-127)
144  * @param channel Key channel (0-15, default 0)
145  * @returns A MIDIMessage
146  */
148  {
149  MIDIMessage msg;
150  msg.data[0] = CABLE_NUM | 0x0A;
151  msg.data[1] = 0xA0 | (channel & 0x0F);
152  msg.data[2] = key & 0x7F;
153  msg.data[3] = pressure & 0x7F;
154  msg.length = 4;
155  return msg;
156  }
157 
158  /** Create a Control Change message
159  * @param control Controller ID
160  * @param value Controller value (0-127)
161  * @param channel Controller channel (0-15, default 0)
162  * @returns A MIDIMessage
163  */
164  static MIDIMessage ControlChange(int control, int value, int channel = 0)
165  {
166  MIDIMessage msg;
167  msg.data[0] = CABLE_NUM | 0x0B;
168  msg.data[1] = 0xB0 | (channel & 0x0F);
169  msg.data[2] = control & 0x7F;
170  msg.data[3] = value & 0x7F;
171  msg.length = 4;
172  return msg;
173  }
174 
175  /** Create a Program Change message
176  * @param program Program ID
177  * @param channel Channel (0-15, default 0)
178  * @returns A MIDIMessage
179  */
181  {
182  MIDIMessage msg;
183  msg.data[0] = CABLE_NUM | 0x0C;
184  msg.data[1] = 0xC0 | (channel & 0x0F);
185  msg.data[2] = program & 0x7F;
186  msg.data[3] = 0x00;
187  msg.length = 4;
188  return msg;
189  }
190 
191  /** Create a Channel Aftertouch message
192  * @param pressure Pressure
193  * @param channel Key channel (0-15, default 0)
194  * @returns A MIDIMessage
195  */
197  {
198  MIDIMessage msg;
199  msg.data[0] = CABLE_NUM | 0x0D;
200  msg.data[1] = 0xD0 | (channel & 0x0F);
201  msg.data[2] = pressure & 0x7F;
202  msg.data[3] = 0x00;
203  msg.length = 4;
204  return msg;
205  }
206 
207  /** Create a Pitch Wheel message
208  * @param pitch Pitch (-8192 - 8191, default = 0)
209  * @param channel Channel (0-15, default 0)
210  * @returns A MIDIMessage
211  */
212  static MIDIMessage PitchWheel(int pitch = 0, int channel = 0)
213  {
214  MIDIMessage msg;
215  int p = pitch + 8192; // 0 - 16383, 8192 is center
216  msg.data[0] = CABLE_NUM | 0x0E;
217  msg.data[1] = 0xE0 | (channel & 0x0F);
218  msg.data[2] = p & 0x7F;
219  msg.data[3] = (p >> 7) & 0x7F;
220  msg.length = 4;
221  return msg;
222  }
223 
224  /** Create an All Notes Off message
225  * @param channel Channel (0-15, default 0)
226  * @returns A MIDIMessage
227  */
229  {
230  return ControlChange(123, 0, channel);
231  }
232 
233  /** Create a SysEx message
234  * @param data SysEx data (including 0xF0 .. 0xF7)
235  * @param len SysEx data length
236  * @returns A MIDIMessage
237  */
238  static MIDIMessage SysEx(uint8_t *data, int len)
239  {
240  MIDIMessage msg;
241  msg.from_raw(data, len);
242  return msg;
243  }
244 
245  // decode messages
246 
247  /** MIDI Message Types */
249  ErrorType,
250  NoteOffType,
251  NoteOnType,
252  PolyphonicAftertouchType,
253  ControlChangeType,
254  ProgramChangeType,
255  ChannelAftertouchType,
256  PitchWheelType,
257  ResetAllControllersType,
258  AllNotesOffType,
259  SysExType
260  };
261 
262  /** Read the message type
263  *
264  * @returns MIDIMessageType
265  */
267  {
268  MIDIMessageType message_type;
269  uint8_t min_size;
270  switch ((data[1] >> 4) & 0xF) {
271  case 0x8:
272  // message, channel
273  // key
274  // velocity
275  min_size = 3;
276  message_type = NoteOffType;
277  break;
278  case 0x9:
279  // message, channel
280  // key
281  // velocity
282  min_size = 3;
283  message_type = NoteOnType;
284  break;
285  case 0xA:
286  // message, channel
287  // key
288  // pressure
289  min_size = 3;
290  message_type = PolyphonicAftertouchType;
291  break;
292  case 0xB:
293  // message, channel
294  // controller
295  min_size = 2;
296  if ((data[2] & 0x7F) < 120) { // standard controllers
297  message_type = ControlChangeType;
298  } else if ((data[2] & 0x7F) == 121) {
299  message_type = ResetAllControllersType;
300  } else if ((data[2] & 0x7F) == 123) {
301  message_type = AllNotesOffType;
302  } else {
303  message_type = ErrorType; // unsupported atm
304  }
305  break;
306  case 0xC:
307  // message, channel
308  // program
309  min_size = 2;
310  message_type = ProgramChangeType;
311  break;
312  case 0xD:
313  // message, channel
314  // pressure
315  min_size = 2;
316  message_type = ChannelAftertouchType;
317  break;
318  case 0xE:
319  // message, channel
320  // pitch lsb
321  // pitch msb
322  min_size = 3;
323  message_type = PitchWheelType;
324  break;
325  case 0xF:
326  min_size = 2;
327  message_type = SysExType;
328  break;
329  default:
330  message_type = ErrorType;
331  break;
332  }
333 
334 
335  if (length < min_size) {
336  // too small to be a valid message
337  message_type = ErrorType;
338  }
339  return message_type;
340  }
341 
342  /**
343  * Read the channel number
344  *
345  * @return channel number or -1 on error
346  */
347 
348  int channel()
349  {
350  return (data[1] & 0x0F);
351  }
352 
353  /**
354  * Read the key ID
355  *
356  * @return key ID or -1 on error
357  */
358  int key()
359  {
360  MIDIMessageType msg_type = type();
361  if ((msg_type != NoteOffType) &&
362  (msg_type != NoteOnType) &&
363  (msg_type != PolyphonicAftertouchType)) {
364  return -1;
365  }
366 
367  return data[2] & 0x7F;
368  }
369 
370  /**
371  * Read the velocity
372  *
373  * @return velocity or -1 on error
374  */
375  int velocity()
376  {
377  MIDIMessageType msg_type = type();
378  if ((msg_type != NoteOffType) &&
379  (msg_type != NoteOnType)) {
380  return -1;
381  }
382 
383  return data[3] & 0x7F;
384  }
385 
386  /**
387  * Read the controller value
388  *
389  * @return controller value or -1 on error
390  */
391  int value()
392  {
393  MIDIMessageType msg_type = type();
394  if ((msg_type != ControlChangeType) &&
395  (msg_type != ResetAllControllersType) &&
396  (msg_type != AllNotesOffType)) {
397  return -1;
398  }
399 
400  return data[3] & 0x7F;
401  }
402 
403  /**
404  * Read the aftertouch pressure
405  *
406  * @return aftertouch pressure or -1 on error
407  */
408  int pressure()
409  {
410  MIDIMessageType msg_type = type();
411  if ((msg_type != PolyphonicAftertouchType) &&
412  (msg_type != ChannelAftertouchType)) {
413  return -1;
414  }
415 
416  if (type() == PolyphonicAftertouchType) {
417  return data[3] & 0x7F;
418  } else {
419  return data[2] & 0x7F;
420  }
421  }
422 
423  /**
424  * Read the controller number
425  *
426  * @return controller number or -1 on error
427  */
429  {
430  MIDIMessageType msg_type = type();
431  if ((msg_type != ControlChangeType) &&
432  (msg_type != ResetAllControllersType) &&
433  (msg_type != AllNotesOffType)) {
434  return -1;
435  }
436 
437  return data[2] & 0x7F;
438  }
439 
440  /**
441  * Read the program number
442  *
443  * @return program number or -1 on error
444  */
445  int program()
446  {
447  MIDIMessageType msg_type = type();
448  if (msg_type != ProgramChangeType) {
449  return -1;
450  }
451 
452  return data[2] & 0x7F;
453  }
454 
455  /**
456  * Read the pitch value
457  *
458  * @return pitch value or -1 on error
459  */
460  int pitch()
461  {
462  MIDIMessageType msg_type = type();
463  if (msg_type != PitchWheelType) {
464  return -1;
465  }
466 
467  int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F);
468  return p - 8192; // 0 - 16383, 8192 is center
469  }
470 
471  uint8_t *data;
472  uint16_t length;
473 };
474 
475 #endif
MIDIMessageType type()
Read the message type.
Definition: MIDIMessage.h:266
static MIDIMessage NoteOn(int key, int velocity=127, int channel=0)
Create a NoteOn message.
Definition: MIDIMessage.h:130
static MIDIMessage ProgramChange(int program, int channel=0)
Create a Program Change message.
Definition: MIDIMessage.h:180
int controller()
Read the controller number.
Definition: MIDIMessage.h:428
static MIDIMessage NoteOff(int key, int velocity=127, int channel=0)
Create a NoteOff message.
Definition: MIDIMessage.h:113
static MIDIMessage AllNotesOff(int channel=0)
Create an All Notes Off message.
Definition: MIDIMessage.h:228
static MIDIMessage SysEx(uint8_t *data, int len)
Create a SysEx message.
Definition: MIDIMessage.h:238
int pressure()
Read the aftertouch pressure.
Definition: MIDIMessage.h:408
static MIDIMessage ControlChange(int control, int value, int channel=0)
Create a Control Change message.
Definition: MIDIMessage.h:164
int key()
Read the key ID.
Definition: MIDIMessage.h:358
int channel()
Read the channel number.
Definition: MIDIMessage.h:348
static MIDIMessage ChannelAftertouch(int pressure, int channel=0)
Create a Channel Aftertouch message.
Definition: MIDIMessage.h:196
void from_raw(uint8_t *buf, int buf_len)
Set this MIDIMessage to a raw MIDI message.
Definition: MIDIMessage.h:88
MIDIMessage & operator=(const MIDIMessage &other)
Assignment operator.
Definition: MIDIMessage.h:67
A MIDI message container.
Definition: MIDIMessage.h:44
static MIDIMessage PitchWheel(int pitch=0, int channel=0)
Create a Pitch Wheel message.
Definition: MIDIMessage.h:212
static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel=0)
Create a PolyPhonic Aftertouch message.
Definition: MIDIMessage.h:147
int program()
Read the program number.
Definition: MIDIMessage.h:445
int value()
Read the controller value.
Definition: MIDIMessage.h:391
int pitch()
Read the pitch value.
Definition: MIDIMessage.h:460
int velocity()
Read the velocity.
Definition: MIDIMessage.h:375
MIDIMessageType
MIDI Message Types.
Definition: MIDIMessage.h:248
MIDIMessage(const MIDIMessage &other)
Copy constructor.
Definition: MIDIMessage.h:59
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.