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