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 // Midi event handler. 28 29 #ifndef YARNS_MIDI_HANDLER_H_ 30 #define YARNS_MIDI_HANDLER_H_ 31 32 #include "stmlib/stmlib.h" 33 34 #include "stmlib/utils/ring_buffer.h" 35 #include "stmlib/midi/midi.h" 36 37 #include "yarns/multi.h" 38 39 namespace yarns { 40 41 const size_t kSysexMaxChunkSize = 64; 42 const size_t kSysexRxBufferSize = kSysexMaxChunkSize * 2 + 16; 43 44 class MidiHandler { 45 public: 46 typedef stmlib::RingBuffer<uint8_t, 128> MidiBuffer; 47 typedef stmlib::RingBuffer<uint8_t, 32> SmallMidiBuffer; 48 MidiHandler()49 MidiHandler() { } ~MidiHandler()50 ~MidiHandler() { } 51 52 static void Init(); 53 NoteOn(uint8_t channel,uint8_t note,uint8_t velocity)54 static void NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { 55 if (multi.NoteOn(channel, note, velocity) && !multi.direct_thru()) { 56 Send3(0x90 | channel, note, velocity); 57 } 58 } 59 NoteOff(uint8_t channel,uint8_t note,uint8_t velocity)60 static void NoteOff(uint8_t channel, uint8_t note, uint8_t velocity) { 61 if (multi.NoteOff(channel, note, velocity) && !multi.direct_thru()) { 62 Send3(0x80 | channel, note, 0); 63 } 64 } 65 Aftertouch(uint8_t channel,uint8_t note,uint8_t velocity)66 static void Aftertouch(uint8_t channel, uint8_t note, uint8_t velocity) { 67 if (multi.Aftertouch(channel, note, velocity) && !multi.direct_thru()) { 68 Send3(0xa0 | channel, note, velocity); 69 } 70 } 71 Aftertouch(uint8_t channel,uint8_t velocity)72 static void Aftertouch(uint8_t channel, uint8_t velocity) { 73 if (multi.Aftertouch(channel, velocity) && !multi.direct_thru()) { 74 Send2(0xd0 | channel, velocity); 75 } 76 } 77 ControlChange(uint8_t channel,uint8_t controller,uint8_t value)78 static void ControlChange( 79 uint8_t channel, 80 uint8_t controller, 81 uint8_t value) { 82 if (multi.ControlChange(channel, controller, value) && !multi.direct_thru()) { 83 Send3(0xb0 | channel, controller, value); 84 } 85 } 86 ProgramChange(uint8_t channel,uint8_t program)87 static void ProgramChange(uint8_t channel, uint8_t program) { 88 if (!multi.direct_thru()) { 89 Send2(0xc0 | channel, program); 90 } 91 } 92 PitchBend(uint8_t channel,uint16_t pitch_bend)93 static void PitchBend(uint8_t channel, uint16_t pitch_bend) { 94 if (multi.PitchBend(channel, pitch_bend) && !multi.direct_thru()) { 95 Send3(0xe0 | channel, pitch_bend & 0x7f, pitch_bend >> 7); 96 } 97 } 98 SysExStart()99 static void SysExStart() { 100 sysex_rx_write_ptr_ = 0; 101 ProcessSysExByte(0xf0); 102 } 103 SysExByte(uint8_t sysex_byte)104 static void SysExByte(uint8_t sysex_byte) { 105 ProcessSysExByte(sysex_byte); 106 } 107 SysExEnd()108 static void SysExEnd() { 109 ProcessSysExByte(0xf7); 110 DecodeSysExMessage(); 111 } 112 BozoByte(uint8_t bozo_byte)113 static void BozoByte(uint8_t bozo_byte) { } 114 Clock()115 static void Clock() { 116 if (!multi.internal_clock()) { 117 multi.Clock(); 118 } 119 } 120 Start()121 static void Start() { 122 if (!multi.internal_clock()) { 123 multi.Start(false); 124 } 125 } 126 Continue()127 static void Continue() { 128 if (!multi.internal_clock()) { 129 multi.Continue(); 130 } 131 } 132 Stop()133 static void Stop() { 134 if (!multi.internal_clock()) { 135 multi.Stop(); 136 } 137 } 138 Reset()139 static void Reset() { 140 multi.Reset(); 141 } 142 CheckChannel(uint8_t channel)143 static bool CheckChannel(uint8_t channel) { return true; } 144 RawByte(uint8_t byte)145 static void RawByte(uint8_t byte) { 146 if (multi.direct_thru()) { 147 if (byte != 0xfa && byte != 0xf8 && byte != 0xfc) { 148 output_buffer_.Overwrite(byte); 149 } 150 } 151 } 152 RawMidiData(uint8_t status,uint8_t * data,uint8_t data_size,uint8_t accepted_channel)153 static void RawMidiData( 154 uint8_t status, 155 uint8_t* data, 156 uint8_t data_size, 157 uint8_t accepted_channel) { 158 159 } 160 OnInternalNoteOn(uint8_t channel,uint8_t note,uint8_t velocity)161 static void OnInternalNoteOn( 162 uint8_t channel, 163 uint8_t note, 164 uint8_t velocity) { 165 Send3(0x90 | channel, note, velocity); 166 } 167 OnInternalNoteOff(uint8_t channel,uint8_t note)168 static void OnInternalNoteOff(uint8_t channel, uint8_t note) { 169 Send3(0x80 | channel, note, 0); 170 } 171 OnClock()172 static void OnClock() { 173 SendNow(0xf8); 174 } 175 OnStart()176 static void OnStart() { 177 SendNow(0xfa); 178 } 179 OnStop()180 static void OnStop() { 181 SendNow(0xfc); 182 } 183 PushByte(uint8_t byte)184 static void PushByte(uint8_t byte) { 185 input_buffer_.Overwrite(byte); 186 } 187 ProcessInput()188 static void ProcessInput() { 189 while (input_buffer_.readable()) { 190 parser_.PushByte(input_buffer_.ImmediateRead()); 191 } 192 } 193 mutable_output_buffer()194 static inline MidiBuffer* mutable_output_buffer() { return &output_buffer_; } mutable_high_priority_output_buffer()195 static inline SmallMidiBuffer* mutable_high_priority_output_buffer() { 196 return &high_priority_output_buffer_; 197 } 198 Send3(uint8_t byte_1,uint8_t byte_2,uint8_t byte_3)199 static inline void Send3(uint8_t byte_1, uint8_t byte_2, uint8_t byte_3) { 200 output_buffer_.Overwrite(byte_1); 201 output_buffer_.Overwrite(byte_2); 202 output_buffer_.Overwrite(byte_3); 203 } 204 Send2(uint8_t byte_1,uint8_t byte_2)205 static inline void Send2(uint8_t byte_1, uint8_t byte_2) { 206 output_buffer_.Overwrite(byte_1); 207 output_buffer_.Overwrite(byte_2); 208 } 209 Send1(uint8_t byte)210 static inline void Send1(uint8_t byte) { 211 output_buffer_.Overwrite(byte); 212 } 213 SendBlocking(uint8_t byte)214 static inline void SendBlocking(uint8_t byte) { 215 output_buffer_.Write(byte); 216 } 217 SendNow(uint8_t byte)218 static inline void SendNow(uint8_t byte) { 219 high_priority_output_buffer_.Overwrite(byte); 220 } 221 222 typedef void (*SysExHandlerFn)(); 223 struct SysExDescription { 224 uint8_t prefix[8]; 225 uint8_t prefix_length; 226 uint8_t expected_size; 227 SysExHandlerFn handler; 228 }; 229 Flush()230 static void Flush() { 231 while (output_buffer_.readable()); 232 } 233 234 static void SysExSendPackets(const uint8_t* data, size_t size); 235 calibrating()236 static inline bool calibrating() { 237 return calibration_voice_ < kNumVoices && calibration_note_ < kNumOctaves; 238 } calibration_voice()239 static inline uint8_t calibration_voice() { return calibration_voice_; } calibration_note()240 static inline uint8_t calibration_note() { return calibration_note_; } factory_testing_requested()241 static inline bool factory_testing_requested() { 242 return factory_testing_requested_; 243 } AcknowledgeFactoryTestingRequest()244 static void AcknowledgeFactoryTestingRequest() { 245 factory_testing_requested_ = false; 246 } 247 248 private: 249 static void SysExSendPacket( 250 uint8_t packet_index, 251 const uint8_t* data, 252 size_t size); 253 static void DecodeSysExMessage(); ProcessSysExByte(uint8_t sysex_byte)254 inline static void ProcessSysExByte(uint8_t sysex_byte) { 255 if (!multi.direct_thru()) { 256 Send1(sysex_byte); 257 } 258 if (sysex_rx_write_ptr_ < sizeof(sysex_rx_buffer_)) { 259 sysex_rx_buffer_[sysex_rx_write_ptr_++] = sysex_byte; 260 } 261 } 262 263 static void HandleScaleOctaveTuning1ByteForm(); 264 static void HandleScaleOctaveTuning2ByteForm(); 265 static void HandleYarnsSpecificMessage(); 266 267 static MidiBuffer input_buffer_; 268 static MidiBuffer output_buffer_; 269 static SmallMidiBuffer high_priority_output_buffer_; 270 static stmlib_midi::MidiStreamParser<MidiHandler> parser_; 271 272 static uint8_t sysex_rx_buffer_[kSysexRxBufferSize]; 273 static uint8_t sysex_rx_write_ptr_; 274 275 static uint8_t previous_packet_index_; 276 277 static uint8_t calibration_voice_; 278 static uint8_t calibration_note_; 279 280 static bool factory_testing_requested_; 281 282 static const SysExDescription accepted_sysex_[]; 283 284 DISALLOW_COPY_AND_ASSIGN(MidiHandler); 285 }; 286 287 extern MidiHandler midi_handler; 288 289 } // namespace yarns 290 291 #endif // YARNS_MIDI_HANDLER_H_ 292