Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
MIDIMessage.h
00001 /* 00002 * Copyright (c) 2018-2019, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #ifndef MIDIMESSAGE_H 00019 #define MIDIMESSAGE_H 00020 00021 #include <stdint.h> 00022 00023 #define MAX_MIDI_MESSAGE_SIZE 256 // Max message size. SysEx can be up to 65536 but 256 should be fine for most usage 00024 00025 // MIDI Message Format 00026 // 00027 // [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ] 00028 // 00029 // MIDI Data Messages (Channel Specific) 00030 // 00031 // Message msg n m 00032 // --------------------------------------------- 00033 // Note Off 0x8 Key Velocity 00034 // Note On 0x9 Key Velocity 00035 // Polyphonic Aftertouch 0xA Key Pressure 00036 // Control Change 0xB Controller Value 00037 // Program Change 0xC Program - 00038 // Channel Aftertouch 0xD Pressure - 00039 // Pitch Wheel 0xE LSB MSB 00040 00041 #define CABLE_NUM (0<<4) 00042 00043 00044 /** 00045 * \defgroup drivers_MIDIMessage MIDIMessage class 00046 * \ingroup drivers-internal-api-usb 00047 * @{ 00048 */ 00049 00050 /** A MIDI message container */ 00051 class MIDIMessage { 00052 public: 00053 00054 MIDIMessage() : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) {} 00055 00056 MIDIMessage(uint8_t *buf) : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) 00057 { 00058 for (int i = 0; i < 4; i++) { 00059 data[i] = buf[i]; 00060 } 00061 } 00062 00063 /** 00064 * Copy constructor 00065 */ 00066 MIDIMessage(const MIDIMessage &other) 00067 { 00068 *this = other; 00069 } 00070 00071 /** 00072 * Assignment operator 00073 */ 00074 MIDIMessage &operator=(const MIDIMessage &other) 00075 { 00076 length = other.length; 00077 for (int i = 0; i < length; i++) { 00078 data[i] = other.data[i]; 00079 } 00080 00081 return *this; 00082 } 00083 00084 ~MIDIMessage() 00085 { 00086 delete[] data; 00087 } 00088 00089 /** 00090 * Set this MIDIMessage to a raw MIDI message 00091 * 00092 * @param buf is a true MIDI message (not USBMidi message) 00093 * @param buf_len size of message 00094 */ 00095 void from_raw(uint8_t *buf, int buf_len) 00096 { 00097 length = buf_len + 1; 00098 if (length > MAX_MIDI_MESSAGE_SIZE) { 00099 // Message is too big 00100 length = 0; 00101 return; 00102 } 00103 00104 // first byte keeped for retro-compatibility 00105 data[0] = CABLE_NUM | 0x08; 00106 00107 for (int i = 0; i < buf_len; i++) { 00108 data[i + 1] = buf[i]; 00109 } 00110 } 00111 00112 // create messages 00113 00114 /** Create a NoteOff message 00115 * @param key Key ID 00116 * @param velocity Key velocity (0-127, default = 127) 00117 * @param channel Key channel (0-15, default 0) 00118 * @returns A MIDIMessage 00119 */ 00120 static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0) 00121 { 00122 MIDIMessage msg; 00123 msg.data[0] = CABLE_NUM | 0x08; 00124 msg.data[1] = 0x80 | (channel & 0x0F); 00125 msg.data[2] = key & 0x7F; 00126 msg.data[3] = velocity & 0x7F; 00127 msg.length = 4; 00128 return msg; 00129 } 00130 00131 /** Create a NoteOn message 00132 * @param key Key ID 00133 * @param velocity Key velocity (0-127, default = 127) 00134 * @param channel Key channel (0-15, default 0) 00135 * @returns A MIDIMessage 00136 */ 00137 static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0) 00138 { 00139 MIDIMessage msg; 00140 msg.data[0] = CABLE_NUM | 0x09; 00141 msg.data[1] = 0x90 | (channel & 0x0F); 00142 msg.data[2] = key & 0x7F; 00143 msg.data[3] = velocity & 0x7F; 00144 msg.length = 4; 00145 return msg; 00146 } 00147 00148 /** Create a PolyPhonic Aftertouch message 00149 * @param key Key ID 00150 * @param pressure Aftertouch pressure (0-127) 00151 * @param channel Key channel (0-15, default 0) 00152 * @returns A MIDIMessage 00153 */ 00154 static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel = 0) 00155 { 00156 MIDIMessage msg; 00157 msg.data[0] = CABLE_NUM | 0x0A; 00158 msg.data[1] = 0xA0 | (channel & 0x0F); 00159 msg.data[2] = key & 0x7F; 00160 msg.data[3] = pressure & 0x7F; 00161 msg.length = 4; 00162 return msg; 00163 } 00164 00165 /** Create a Control Change message 00166 * @param control Controller ID 00167 * @param value Controller value (0-127) 00168 * @param channel Controller channel (0-15, default 0) 00169 * @returns A MIDIMessage 00170 */ 00171 static MIDIMessage ControlChange(int control, int value, int channel = 0) 00172 { 00173 MIDIMessage msg; 00174 msg.data[0] = CABLE_NUM | 0x0B; 00175 msg.data[1] = 0xB0 | (channel & 0x0F); 00176 msg.data[2] = control & 0x7F; 00177 msg.data[3] = value & 0x7F; 00178 msg.length = 4; 00179 return msg; 00180 } 00181 00182 /** Create a Program Change message 00183 * @param program Program ID 00184 * @param channel Channel (0-15, default 0) 00185 * @returns A MIDIMessage 00186 */ 00187 static MIDIMessage ProgramChange(int program, int channel = 0) 00188 { 00189 MIDIMessage msg; 00190 msg.data[0] = CABLE_NUM | 0x0C; 00191 msg.data[1] = 0xC0 | (channel & 0x0F); 00192 msg.data[2] = program & 0x7F; 00193 msg.data[3] = 0x00; 00194 msg.length = 4; 00195 return msg; 00196 } 00197 00198 /** Create a Channel Aftertouch message 00199 * @param pressure Pressure 00200 * @param channel Key channel (0-15, default 0) 00201 * @returns A MIDIMessage 00202 */ 00203 static MIDIMessage ChannelAftertouch(int pressure, int channel = 0) 00204 { 00205 MIDIMessage msg; 00206 msg.data[0] = CABLE_NUM | 0x0D; 00207 msg.data[1] = 0xD0 | (channel & 0x0F); 00208 msg.data[2] = pressure & 0x7F; 00209 msg.data[3] = 0x00; 00210 msg.length = 4; 00211 return msg; 00212 } 00213 00214 /** Create a Pitch Wheel message 00215 * @param pitch Pitch (-8192 - 8191, default = 0) 00216 * @param channel Channel (0-15, default 0) 00217 * @returns A MIDIMessage 00218 */ 00219 static MIDIMessage PitchWheel(int pitch = 0, int channel = 0) 00220 { 00221 MIDIMessage msg; 00222 int p = pitch + 8192; // 0 - 16383, 8192 is center 00223 msg.data[0] = CABLE_NUM | 0x0E; 00224 msg.data[1] = 0xE0 | (channel & 0x0F); 00225 msg.data[2] = p & 0x7F; 00226 msg.data[3] = (p >> 7) & 0x7F; 00227 msg.length = 4; 00228 return msg; 00229 } 00230 00231 /** Create an All Notes Off message 00232 * @param channel Channel (0-15, default 0) 00233 * @returns A MIDIMessage 00234 */ 00235 static MIDIMessage AllNotesOff(int channel = 0) 00236 { 00237 return ControlChange(123, 0, channel); 00238 } 00239 00240 /** Create a SysEx message 00241 * @param data SysEx data (including 0xF0 .. 0xF7) 00242 * @param len SysEx data length 00243 * @returns A MIDIMessage 00244 */ 00245 static MIDIMessage SysEx(uint8_t *data, int len) 00246 { 00247 MIDIMessage msg; 00248 msg.from_raw(data, len); 00249 return msg; 00250 } 00251 00252 // decode messages 00253 00254 /** MIDI Message Types */ 00255 enum MIDIMessageType { 00256 ErrorType, 00257 NoteOffType, 00258 NoteOnType, 00259 PolyphonicAftertouchType, 00260 ControlChangeType, 00261 ProgramChangeType, 00262 ChannelAftertouchType, 00263 PitchWheelType, 00264 ResetAllControllersType, 00265 AllNotesOffType, 00266 SysExType 00267 }; 00268 00269 /** Read the message type 00270 * 00271 * @returns MIDIMessageType 00272 */ 00273 MIDIMessageType type() 00274 { 00275 MIDIMessageType message_type; 00276 uint8_t min_size; 00277 switch ((data[1] >> 4) & 0xF) { 00278 case 0x8: 00279 // message, channel 00280 // key 00281 // velocity 00282 min_size = 3; 00283 message_type = NoteOffType; 00284 break; 00285 case 0x9: 00286 // message, channel 00287 // key 00288 // velocity 00289 min_size = 3; 00290 message_type = NoteOnType; 00291 break; 00292 case 0xA: 00293 // message, channel 00294 // key 00295 // pressure 00296 min_size = 3; 00297 message_type = PolyphonicAftertouchType; 00298 break; 00299 case 0xB: 00300 // message, channel 00301 // controller 00302 min_size = 2; 00303 if ((data[2] & 0x7F) < 120) { // standard controllers 00304 message_type = ControlChangeType; 00305 } else if ((data[2] & 0x7F) == 121) { 00306 message_type = ResetAllControllersType; 00307 } else if ((data[2] & 0x7F) == 123) { 00308 message_type = AllNotesOffType; 00309 } else { 00310 message_type = ErrorType; // unsupported atm 00311 } 00312 break; 00313 case 0xC: 00314 // message, channel 00315 // program 00316 min_size = 2; 00317 message_type = ProgramChangeType; 00318 break; 00319 case 0xD: 00320 // message, channel 00321 // pressure 00322 min_size = 2; 00323 message_type = ChannelAftertouchType; 00324 break; 00325 case 0xE: 00326 // message, channel 00327 // pitch lsb 00328 // pitch msb 00329 min_size = 3; 00330 message_type = PitchWheelType; 00331 break; 00332 case 0xF: 00333 min_size = 2; 00334 message_type = SysExType; 00335 break; 00336 default: 00337 message_type = ErrorType; 00338 break; 00339 } 00340 00341 00342 if (length < min_size) { 00343 // too small to be a valid message 00344 message_type = ErrorType; 00345 } 00346 return message_type; 00347 } 00348 00349 /** 00350 * Read the channel number 00351 * 00352 * @return channel number or -1 on error 00353 */ 00354 00355 int channel() 00356 { 00357 return (data[1] & 0x0F); 00358 } 00359 00360 /** 00361 * Read the key ID 00362 * 00363 * @return key ID or -1 on error 00364 */ 00365 int key() 00366 { 00367 MIDIMessageType msg_type = type(); 00368 if ((msg_type != NoteOffType) && 00369 (msg_type != NoteOnType) && 00370 (msg_type != PolyphonicAftertouchType)) { 00371 return -1; 00372 } 00373 00374 return data[2] & 0x7F; 00375 } 00376 00377 /** 00378 * Read the velocity 00379 * 00380 * @return velocity or -1 on error 00381 */ 00382 int velocity() 00383 { 00384 MIDIMessageType msg_type = type(); 00385 if ((msg_type != NoteOffType) && 00386 (msg_type != NoteOnType)) { 00387 return -1; 00388 } 00389 00390 return data[3] & 0x7F; 00391 } 00392 00393 /** 00394 * Read the controller value 00395 * 00396 * @return controller value or -1 on error 00397 */ 00398 int value() 00399 { 00400 MIDIMessageType msg_type = type(); 00401 if ((msg_type != ControlChangeType) && 00402 (msg_type != ResetAllControllersType) && 00403 (msg_type != AllNotesOffType)) { 00404 return -1; 00405 } 00406 00407 return data[3] & 0x7F; 00408 } 00409 00410 /** 00411 * Read the aftertouch pressure 00412 * 00413 * @return aftertouch pressure or -1 on error 00414 */ 00415 int pressure() 00416 { 00417 MIDIMessageType msg_type = type(); 00418 if ((msg_type != PolyphonicAftertouchType) && 00419 (msg_type != ChannelAftertouchType)) { 00420 return -1; 00421 } 00422 00423 if (type() == PolyphonicAftertouchType) { 00424 return data[3] & 0x7F; 00425 } else { 00426 return data[2] & 0x7F; 00427 } 00428 } 00429 00430 /** 00431 * Read the controller number 00432 * 00433 * @return controller number or -1 on error 00434 */ 00435 int controller() 00436 { 00437 MIDIMessageType msg_type = type(); 00438 if ((msg_type != ControlChangeType) && 00439 (msg_type != ResetAllControllersType) && 00440 (msg_type != AllNotesOffType)) { 00441 return -1; 00442 } 00443 00444 return data[2] & 0x7F; 00445 } 00446 00447 /** 00448 * Read the program number 00449 * 00450 * @return program number or -1 on error 00451 */ 00452 int program() 00453 { 00454 MIDIMessageType msg_type = type(); 00455 if (msg_type != ProgramChangeType) { 00456 return -1; 00457 } 00458 00459 return data[2] & 0x7F; 00460 } 00461 00462 /** 00463 * Read the pitch value 00464 * 00465 * @return pitch value or -1 on error 00466 */ 00467 int pitch() 00468 { 00469 MIDIMessageType msg_type = type(); 00470 if (msg_type != PitchWheelType) { 00471 return -1; 00472 } 00473 00474 int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F); 00475 return p - 8192; // 0 - 16383, 8192 is center 00476 } 00477 00478 uint8_t *data; 00479 uint16_t length; 00480 }; 00481 00482 /** @}*/ 00483 00484 #endif
Generated on Tue Jul 12 2022 13:54:35 by
