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