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 #include "yarns/midi_handler.h"
30 
31 #include <algorithm>
32 
33 #include "yarns/multi.h"
34 #ifndef TEST
35 #include "yarns/storage_manager.h"
36 #endif // TEST
37 
38 namespace yarns {
39 
40 using namespace std;
41 
42 /* static */
43 MidiHandler::MidiBuffer MidiHandler::input_buffer_;
44 
45 /* static */
46 MidiHandler::MidiBuffer MidiHandler::output_buffer_;
47 
48 /* static */
49 MidiHandler::SmallMidiBuffer MidiHandler::high_priority_output_buffer_;
50 
51 /* static */
52 stmlib_midi::MidiStreamParser<MidiHandler> MidiHandler::parser_;
53 
54 /* static */
55 const MidiHandler::SysExDescription MidiHandler::accepted_sysex_[] = {
56   { { 0xf0, 0x00, 0x21, 0x02, 0x00, 0x0b }, 6, 0xff,
57       &MidiHandler::HandleYarnsSpecificMessage },
58   { { 0xf0, 0x7f, 0xff, 0x08, 0x08 }, 5, 21,
59       &MidiHandler::HandleScaleOctaveTuning1ByteForm },
60   { { 0xf0, 0x7e, 0xff, 0x08, 0x08 }, 5, 21,
61       &MidiHandler::HandleScaleOctaveTuning1ByteForm },
62   { { 0xf0, 0x7f, 0xff, 0x08, 0x09 }, 5, 33,
63       &MidiHandler::HandleScaleOctaveTuning2ByteForm },
64   { { 0xf0, 0x7e, 0xff, 0x08, 0x09 }, 5, 33,
65       &MidiHandler::HandleScaleOctaveTuning2ByteForm }
66 };
67 
68 /* static */
69 uint8_t MidiHandler::sysex_rx_write_ptr_;
70 
71 /* static */
72 uint8_t MidiHandler::previous_packet_index_;
73 
74 /* static */
75 uint8_t MidiHandler::sysex_rx_buffer_[kSysexRxBufferSize];
76 
77 /* static */
78 uint8_t MidiHandler::calibration_voice_;
79 
80 /* static */
81 uint8_t MidiHandler::calibration_note_;
82 
83 /* static */
84 bool MidiHandler::factory_testing_requested_;
85 
86 /* static */
Init()87 void MidiHandler::Init() {
88   input_buffer_.Init();
89   output_buffer_.Init();
90   high_priority_output_buffer_.Init();
91   sysex_rx_write_ptr_ = 0;
92   previous_packet_index_ = 0;
93   calibration_voice_ = 0xff;
94   calibration_note_ = 0xff;
95   factory_testing_requested_ = false;
96 }
97 
98 /* static */
DecodeSysExMessage()99 void MidiHandler::DecodeSysExMessage() {
100   uint8_t length = sysex_rx_write_ptr_;
101 
102   if (sysex_rx_buffer_[length - 1] != 0xf7) {
103     // Discard long messages that have been truncated.
104     return;
105   }
106 
107   for (uint8_t i = 0;
108        i < sizeof(accepted_sysex_) / sizeof(SysExDescription);
109        ++i) {
110     const SysExDescription& description = accepted_sysex_[i];
111     if (description.expected_size == length ||
112         description.expected_size == 0xff) {
113       bool match = true;
114       for (uint8_t j = 0; j < description.prefix_length; ++j) {
115         if (description.prefix[j] != sysex_rx_buffer_[j] &&
116             description.prefix[j] != 0xff) {
117           match = false;
118           break;
119         }
120       }
121       if (match) {
122         (*description.handler)();
123         break;
124       }
125     }
126   }
127 }
128 
129 /* static */
HandleScaleOctaveTuning1ByteForm()130 void MidiHandler::HandleScaleOctaveTuning1ByteForm() {
131   for (uint8_t pitch_class = 0; pitch_class < 12; ++pitch_class) {
132     int16_t correction = sysex_rx_buffer_[8 + pitch_class];
133     correction -= 64;
134     correction = (correction * 128 + (correction > 0 ? 64 : -64)) / 100;
135     multi.set_custom_pitch(pitch_class, correction);
136   }
137 }
138 
139 /* static */
HandleScaleOctaveTuning2ByteForm()140 void MidiHandler::HandleScaleOctaveTuning2ByteForm() {
141   for (uint8_t pitch_class = 0; pitch_class < 12; ++pitch_class) {
142     int16_t correction = sysex_rx_buffer_[8 + pitch_class * 2] << 7;
143     correction += sysex_rx_buffer_[9 + pitch_class * 2];
144     correction -= 8192;
145     correction >>= 6;
146     multi.set_custom_pitch(pitch_class, correction);
147   }
148 }
149 
150 
151 enum SysExCommand {
152   SYSEX_COMMAND_DUMP_PACKET = 1,
153   SYSEX_COMMAND_REQUEST_PACKETS = 17,
154   SYSEX_COMMAND_FACTORY_TESTING_MODE = 32,
155   SYSEX_COMMAND_CALIBRATE = 33,
156 };
157 
158 /* static */
HandleYarnsSpecificMessage()159 void MidiHandler::HandleYarnsSpecificMessage() {
160 #ifndef TEST
161   uint8_t command = sysex_rx_buffer_[6];
162   if (command == SYSEX_COMMAND_DUMP_PACKET) {
163     uint8_t packet_index = sysex_rx_buffer_[7];
164 
165     // Handle packet reception.
166     if (packet_index != 0 && packet_index != previous_packet_index_ + 1) {
167       // Packet not in sequence!
168       return;
169     }
170     previous_packet_index_ = packet_index;
171 
172     // Denibblize.
173     uint8_t* data = &sysex_rx_buffer_[8];
174     uint8_t* byte_ptr = data;
175     uint8_t* nibble_ptr = data;
176     uint8_t checksum = 0;
177     while (*nibble_ptr != 0xf7 &&
178            static_cast<size_t>(byte_ptr - data) <= kSysexMaxChunkSize + 1) {
179       *byte_ptr = (*nibble_ptr++) << 4;
180       *byte_ptr |= (*nibble_ptr++);
181       // Warning! The last byte of the block, which is the checksum
182       // is summed here!
183       checksum += *byte_ptr++;
184     }
185     size_t size = byte_ptr - data - 1;
186     checksum -= data[size];
187     if (checksum != data[size]) {
188       previous_packet_index_ = 0xff;
189       return;
190     }
191     if (size != 0) {
192       storage_manager.AppendData(data, size, packet_index == 0);
193     } else if (packet_index) {
194       storage_manager.DeserializeMulti();
195     }
196   } else if (command == SYSEX_COMMAND_REQUEST_PACKETS) {
197     if (sysex_rx_buffer_[7] == 0 &&
198         sysex_rx_buffer_[8] == 0 &&
199         sysex_rx_buffer_[9] == 0 &&
200         sysex_rx_buffer_[10] == 0xf7) {
201       storage_manager.SysExSendMulti();
202     }
203   } else if (command == SYSEX_COMMAND_FACTORY_TESTING_MODE) {
204     if (sysex_rx_buffer_[7] == 0 &&
205         sysex_rx_buffer_[8] == 0 &&
206         sysex_rx_buffer_[9] == 0 &&
207         sysex_rx_buffer_[10] == 0xf7) {
208       factory_testing_requested_ = true;
209     }
210   } else if (command == SYSEX_COMMAND_CALIBRATE) {
211     calibration_voice_ = sysex_rx_buffer_[7] >> 4;
212     calibration_note_ = sysex_rx_buffer_[7] & 0xf;
213     if (calibrating()) {
214       uint16_t dac_code = (sysex_rx_buffer_[8] << 12) | (sysex_rx_buffer_[9] << 8) |
215         (sysex_rx_buffer_[10] << 4) | (sysex_rx_buffer_[11] << 0);
216       Voice* voice = multi.mutable_voice(calibration_voice_);
217       voice->set_calibration_dac_code(calibration_note_, dac_code);
218     } else {
219       storage_manager.SaveCalibration();
220     }
221   }
222 #endif  // TEST
223 }
224 
225 /* static */
SysExSendPacket(uint8_t packet_index,const uint8_t * data,size_t size)226 void MidiHandler::SysExSendPacket(
227     uint8_t packet_index,
228     const uint8_t* data,
229     size_t size) {
230   Flush();
231 
232   for (uint8_t i = 0; i < 6; ++i) {
233     SendBlocking(accepted_sysex_[0].prefix[i]);
234   }
235   SendBlocking(SYSEX_COMMAND_DUMP_PACKET);
236   SendBlocking(packet_index);
237 
238   // Outputs the data.
239   uint8_t checksum = 0;
240   for (uint8_t i = 0; i < size; ++i) {
241     checksum += data[i];
242     SendBlocking(data[i] >> 4);
243     SendBlocking(data[i] & 0x0f);
244   }
245   // Outputs a checksum.
246   SendBlocking(checksum >> 4);
247   SendBlocking(checksum & 0x0f);
248 
249   // End of SysEx block.
250   SendBlocking(0xf7);
251   Flush();
252 }
253 
254 /* static */
SysExSendPackets(const uint8_t * data,size_t size)255 void MidiHandler::SysExSendPackets(const uint8_t* data, size_t size) {
256   uint8_t block_index = 0;
257   while (size) {
258     size_t chunk_size = min(size, kSysexMaxChunkSize);
259     SysExSendPacket(block_index, data, chunk_size);
260     size -= chunk_size;
261     data += chunk_size;
262     ++block_index;
263   }
264   // Send a NULL packet to indicate end of transmission.
265   SysExSendPacket(block_index, NULL, 0);
266 }
267 
268 /* extern */
269 MidiHandler midi_handler;
270 
271 }  // namespace yarns