1 // Copyright 2013 Olivier Gillet.
2 //
3 // Author: Olivier Gillet (ol.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 >> 7, pitch_bend & 0x7f);
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