Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MIDIMessage.h Source File

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