1 // Copyright 2013 Emilie Gillet. 2 // 3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 // 23 // See http://creativecommons.org/licenses/MIT/ for more information. 24 // 25 // ----------------------------------------------------------------------------- 26 // 27 // Decoding of MIDI messages. 28 29 #ifndef STMLIB_MIDI_H_ 30 #define STMLIB_MIDI_H_ 31 32 namespace stmlib_midi { 33 34 const uint8_t kCCModulationWheelMsb = 0x01; 35 const uint8_t kCCBreathController = 0x02; 36 const uint8_t kCCFootPedalMsb = 0x04; 37 const uint8_t kCCPortamentoTimeMsb = 0x05; 38 const uint8_t kCCDataEntryMsb = 0x06; 39 const uint8_t kCCVolume = 0x07; 40 const uint8_t kCCBankLsb = 0x20; 41 const uint8_t kCCDataEntryLsb = 0x26; 42 const uint8_t kCCHoldPedal = 0x40; 43 const uint8_t kCCHarmonicIntensity = 0x47; 44 const uint8_t kCCRelease = 0x48; 45 const uint8_t kCCAttack = 0x49; 46 const uint8_t kCCBrightness = 0x4a; 47 const uint8_t kCCDataIncrement = 0x60; 48 const uint8_t kCCDataDecrement = 0x61; 49 const uint8_t kCCNrpnLsb = 0x62; 50 const uint8_t kCCNrpnMsb = 0x63; 51 const uint8_t kCCOmniModeOff = 0x7c; 52 const uint8_t kCCOmniModeOn = 0x7d; 53 const uint8_t kCCMonoModeOn = 0x7e; 54 const uint8_t kCCPolyModeOn = 0x7f; 55 56 template<typename Handler> 57 class MidiStreamParser { 58 public: MidiStreamParser()59 MidiStreamParser() { 60 running_status_ = 0; 61 data_size_ = 0; 62 expected_data_size_ = 0; 63 } PushByte(uint8_t byte)64 void PushByte(uint8_t byte) { 65 // Active sensing messages are filtered at the source, the hard way... 66 if (byte == 0xfe) { 67 return; 68 } 69 Handler::RawByte(byte); 70 // Realtime messages are immediately passed-through, and do not modify the 71 // state of the parser. 72 if (byte >= 0xf8) { 73 MessageReceived(byte); 74 } else { 75 if (byte >= 0x80) { 76 uint8_t hi = byte & 0xf0; 77 uint8_t lo = byte & 0x0f; 78 data_size_ = 0; 79 expected_data_size_ = 1; 80 switch (hi) { 81 case 0x80: 82 case 0x90: 83 case 0xa0: 84 case 0xb0: 85 expected_data_size_ = 2; 86 break; 87 case 0xc0: 88 case 0xd0: 89 break; // default data size of 1. 90 case 0xe0: 91 expected_data_size_ = 2; 92 break; 93 case 0xf0: 94 if (lo > 0 && lo < 3) { 95 expected_data_size_ = 2; 96 } else if (lo >= 4) { 97 expected_data_size_ = 0; 98 } 99 break; 100 } 101 if (byte == 0xf7) { 102 if (running_status_ == 0xf0) { 103 Handler::SysExEnd(); 104 } 105 running_status_ = 0; 106 } else if (byte == 0xf0) { 107 running_status_ = 0xf0; 108 Handler::SysExStart(); 109 } else { 110 running_status_ = byte; 111 } 112 } else { 113 data_[data_size_++] = byte; 114 } 115 if (data_size_ >= expected_data_size_) { 116 MessageReceived(running_status_); 117 data_size_ = 0; 118 if (running_status_ > 0xf0) { 119 expected_data_size_ = 0; 120 running_status_ = 0; 121 } 122 } 123 } 124 } 125 126 private: MessageReceived(uint8_t status)127 void MessageReceived(uint8_t status) { 128 if (!status) { 129 Handler::BozoByte(data_[0]); 130 } 131 132 uint8_t hi = status & 0xf0; 133 uint8_t lo = status & 0x0f; 134 135 // If this is a channel-specific message, check first that the receiver is 136 // tuned to this channel. 137 if (hi != 0xf0 && !Handler::CheckChannel(lo)) { 138 Handler::RawMidiData(status, data_, data_size_, 0); 139 return; 140 } 141 if (status != 0xf0 && status != 0xf7) { 142 Handler::RawMidiData(status, data_, data_size_, 1); 143 } 144 switch (hi) { 145 case 0x80: 146 Handler::NoteOff(lo, data_[0], data_[1]); 147 break; 148 149 case 0x90: 150 if (data_[1]) { 151 Handler::NoteOn(lo, data_[0], data_[1]); 152 } else { 153 Handler::NoteOff(lo, data_[0], 0); 154 } 155 break; 156 157 case 0xa0: 158 Handler::Aftertouch(lo, data_[0], data_[1]); 159 break; 160 161 case 0xb0: 162 Handler::ControlChange(lo, data_[0], data_[1]); 163 break; 164 165 case 0xc0: 166 Handler::ProgramChange(lo, data_[0]); 167 break; 168 169 case 0xd0: 170 Handler::Aftertouch(lo, data_[0]); 171 break; 172 173 case 0xe0: 174 Handler::PitchBend(lo, (static_cast<uint16_t>(data_[1]) << 7) + data_[0]); 175 break; 176 177 case 0xf0: 178 switch(lo) { 179 case 0x0: 180 Handler::SysExByte(data_[0]); 181 break; 182 case 0x1: 183 case 0x2: 184 case 0x3: 185 case 0x4: 186 case 0x5: 187 case 0x6: 188 // TODO(pichenettes): implement this if it makes sense. 189 break; 190 case 0x8: 191 Handler::Clock(); 192 break; 193 case 0x9: 194 break; 195 case 0xa: 196 Handler::Start(); 197 break; 198 case 0xb: 199 Handler::Continue(); 200 break; 201 case 0xc: 202 Handler::Stop(); 203 break; 204 case 0xf: 205 Handler::Reset(); 206 break; 207 } 208 break; 209 } 210 } 211 212 uint8_t running_status_; 213 uint8_t data_[3]; 214 uint8_t data_size_; // Number of non-status byte received. 215 uint8_t expected_data_size_; // Expected number of non-status bytes. 216 217 DISALLOW_COPY_AND_ASSIGN(MidiStreamParser); 218 }; 219 220 } // namespace stmlib_midi 221 222 #endif // STMLIB_MIDI_H_ 223