Files
-
blinds / hardware_design / pcb / blinds_v60.brd
-
blinds / hardware_design / pcb / blinds_v60.sch
-
braids / hardware_design / pcb / braids_v50.brd
-
braids / hardware_design / pcb / braids_v50.sch
-
branches / hardware_design / pcb / branches_v40.brd
-
branches / hardware_design / pcb / branches_v40.sch
-
clouds / hardware_design / pcb / clouds_v30.brd
-
clouds / hardware_design / pcb / clouds_v30.sch
-
ears / hardware_design / panel / ears_panel_v30.brd
-
ears / hardware_design / panel / ears_panel_v30.sch
-
ears / hardware_design / pcb / ears_v40.brd
-
ears / hardware_design / pcb / ears_v40.sch
-
edges / hardware_design / pcb / edges_expander_v01.brd
-
edges / hardware_design / pcb / edges_expander_v01.sch
-
edges / hardware_design / pcb / edges_v20.brd
-
edges / hardware_design / pcb / edges_v20.sch
-
elements / hardware_design / pcb / elements_v02.brd
-
elements / hardware_design / pcb / elements_v02.sch
-
frames / hardware_design / pcb / frames_v03.brd
-
frames / hardware_design / pcb / frames_v03.sch
-
grids / hardware_design / pcb / grids_v02.brd
-
grids / hardware_design / pcb / grids_v02.sch
-
kinks / hardware_design / pcb / kinks_v41.brd
-
kinks / hardware_design / pcb / kinks_v41.sch
-
links / hardware_design / pcb / links_v40.brd
-
links / hardware_design / pcb / links_v40.sch
-
marbles / hardware_design / pcb / marbles_v70.brd
-
marbles / hardware_design / pcb / marbles_v70.sch
-
peaks / hardware_design / pcb / peaks_v30.brd
-
peaks / hardware_design / pcb / peaks_v30.sch
-
plaits / hardware_design / pcb / plaits_v50.brd
-
plaits / hardware_design / pcb / plaits_v50.sch
-
rings / hardware_design / pcb / rings_v30.brd
-
rings / hardware_design / pcb / rings_v30.sch
-
ripples / hardware_design / pcb / ripples_v40.brd
-
ripples / hardware_design / pcb / ripples_v40.sch
-
shades / hardware_design / pcb / shades_v30.brd
-
shades / hardware_design / pcb / shades_v30.sch
-
shelves / hardware_design / pcb / shelves_expander_v10.brd
-
shelves / hardware_design / pcb / shelves_expander_v10.sch
-
shelves / hardware_design / pcb / shelves_v05.brd
-
shelves / hardware_design / pcb / shelves_v05.sch
-
stages / hardware_design / pcb / stages_v70.brd
-
stages / hardware_design / pcb / stages_v70.sch
-
streams / hardware_design / pcb / streams_v02_bargraph.brd
-
streams / hardware_design / pcb / streams_v02_bargraph.sch
-
streams / hardware_design / pcb / streams_v05.brd
-
streams / hardware_design / pcb / streams_v05.sch
-
tides / hardware_design / pcb / tides_v40.brd
-
tides / hardware_design / pcb / tides_v40.sch
-
veils / hardware_design / pcb / veils_v40.brd
-
veils / hardware_design / pcb / veils_v40.sch
-
volts / hardware_design / pcb / volts_v01.brd
-
volts / hardware_design / pcb / volts_v01.sch
-
warps / hardware_design / pcb / warps_v30.brd
-
warps / hardware_design / pcb / warps_v30.sch
-
yarns / hardware_design / pcb / yarns_v03.brd
-
yarns / hardware_design / pcb / yarns_v03.sch
Last update 6 years 1 month
by
Olivier Gillet
midi_handler.cc// Copyright 2013 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // See http://creativecommons.org/licenses/MIT/ for more information. // // ----------------------------------------------------------------------------- // // MIDI event handler. #include "yarns/midi_handler.h" #include <algorithm> #include "yarns/multi.h" #ifndef TEST #include "yarns/storage_manager.h" #endif // TEST namespace yarns { using namespace std; /* static */ MidiHandler::MidiBuffer MidiHandler::input_buffer_; /* static */ MidiHandler::MidiBuffer MidiHandler::output_buffer_; /* static */ MidiHandler::SmallMidiBuffer MidiHandler::high_priority_output_buffer_; /* static */ stmlib_midi::MidiStreamParser<MidiHandler> MidiHandler::parser_; /* static */ const MidiHandler::SysExDescription MidiHandler::accepted_sysex_[] = { { { 0xf0, 0x00, 0x21, 0x02, 0x00, 0x0b }, 6, 0xff, &MidiHandler::HandleYarnsSpecificMessage }, { { 0xf0, 0x7f, 0xff, 0x08, 0x08 }, 5, 21, &MidiHandler::HandleScaleOctaveTuning1ByteForm }, { { 0xf0, 0x7e, 0xff, 0x08, 0x08 }, 5, 21, &MidiHandler::HandleScaleOctaveTuning1ByteForm }, { { 0xf0, 0x7f, 0xff, 0x08, 0x09 }, 5, 33, &MidiHandler::HandleScaleOctaveTuning2ByteForm }, { { 0xf0, 0x7e, 0xff, 0x08, 0x09 }, 5, 33, &MidiHandler::HandleScaleOctaveTuning2ByteForm } }; /* static */ uint8_t MidiHandler::sysex_rx_write_ptr_; /* static */ uint8_t MidiHandler::previous_packet_index_; /* static */ uint8_t MidiHandler::sysex_rx_buffer_[kSysexRxBufferSize]; /* static */ uint8_t MidiHandler::calibration_voice_; /* static */ uint8_t MidiHandler::calibration_note_; /* static */ bool MidiHandler::factory_testing_requested_; /* static */ void MidiHandler::Init() { input_buffer_.Init(); output_buffer_.Init(); high_priority_output_buffer_.Init(); sysex_rx_write_ptr_ = 0; previous_packet_index_ = 0; calibration_voice_ = 0xff; calibration_note_ = 0xff; factory_testing_requested_ = false; } /* static */ void MidiHandler::DecodeSysExMessage() { uint8_t length = sysex_rx_write_ptr_; if (sysex_rx_buffer_[length - 1] != 0xf7) { // Discard long messages that have been truncated. return; } for (uint8_t i = 0; i < sizeof(accepted_sysex_) / sizeof(SysExDescription); ++i) { const SysExDescription& description = accepted_sysex_[i]; if (description.expected_size == length || description.expected_size == 0xff) { bool match = true; for (uint8_t j = 0; j < description.prefix_length; ++j) { if (description.prefix[j] != sysex_rx_buffer_[j] && description.prefix[j] != 0xff) { match = false; break; } } if (match) { (*description.handler)(); break; } } } } /* static */ void MidiHandler::HandleScaleOctaveTuning1ByteForm() { for (uint8_t pitch_class = 0; pitch_class < 12; ++pitch_class) { int16_t correction = sysex_rx_buffer_[8 + pitch_class]; correction -= 64; correction = (correction * 128 + (correction > 0 ? 64 : -64)) / 100; multi.set_custom_pitch(pitch_class, correction); } } /* static */ void MidiHandler::HandleScaleOctaveTuning2ByteForm() { for (uint8_t pitch_class = 0; pitch_class < 12; ++pitch_class) { int16_t correction = sysex_rx_buffer_[8 + pitch_class * 2] << 7; correction += sysex_rx_buffer_[9 + pitch_class * 2]; correction -= 8192; correction >>= 6; multi.set_custom_pitch(pitch_class, correction); } } enum SysExCommand { SYSEX_COMMAND_DUMP_PACKET = 1, SYSEX_COMMAND_REQUEST_PACKETS = 17, SYSEX_COMMAND_FACTORY_TESTING_MODE = 32, SYSEX_COMMAND_CALIBRATE = 33, }; /* static */ void MidiHandler::HandleYarnsSpecificMessage() { #ifndef TEST uint8_t command = sysex_rx_buffer_[6]; if (command == SYSEX_COMMAND_DUMP_PACKET) { uint8_t packet_index = sysex_rx_buffer_[7]; // Handle packet reception. if (packet_index != 0 && packet_index != previous_packet_index_ + 1) { // Packet not in sequence! return; } previous_packet_index_ = packet_index; // Denibblize. uint8_t* data = &sysex_rx_buffer_[8]; uint8_t* byte_ptr = data; uint8_t* nibble_ptr = data; uint8_t checksum = 0; while (*nibble_ptr != 0xf7 && static_cast<size_t>(byte_ptr - data) <= kSysexMaxChunkSize + 1) { *byte_ptr = (*nibble_ptr++) << 4; *byte_ptr |= (*nibble_ptr++); // Warning! The last byte of the block, which is the checksum // is summed here! checksum += *byte_ptr++; } size_t size = byte_ptr - data - 1; checksum -= data[size]; if (checksum != data[size]) { previous_packet_index_ = 0xff; return; } if (size != 0) { storage_manager.AppendData(data, size, packet_index == 0); } else if (packet_index) { storage_manager.DeserializeMulti(); } } else if (command == SYSEX_COMMAND_REQUEST_PACKETS) { if (sysex_rx_buffer_[7] == 0 && sysex_rx_buffer_[8] == 0 && sysex_rx_buffer_[9] == 0 && sysex_rx_buffer_[10] == 0xf7) { storage_manager.SysExSendMulti(); } } else if (command == SYSEX_COMMAND_FACTORY_TESTING_MODE) { if (sysex_rx_buffer_[7] == 0 && sysex_rx_buffer_[8] == 0 && sysex_rx_buffer_[9] == 0 && sysex_rx_buffer_[10] == 0xf7) { factory_testing_requested_ = true; } } else if (command == SYSEX_COMMAND_CALIBRATE) { calibration_voice_ = sysex_rx_buffer_[7] >> 4; calibration_note_ = sysex_rx_buffer_[7] & 0xf; if (calibrating()) { uint16_t dac_code = (sysex_rx_buffer_[8] << 12) | (sysex_rx_buffer_[9] << 8) | (sysex_rx_buffer_[10] << 4) | (sysex_rx_buffer_[11] << 0); Voice* voice = multi.mutable_voice(calibration_voice_); voice->set_calibration_dac_code(calibration_note_, dac_code); } else { storage_manager.SaveCalibration(); } } #endif // TEST } /* static */ void MidiHandler::SysExSendPacket( uint8_t packet_index, const uint8_t* data, size_t size) { Flush(); for (uint8_t i = 0; i < 6; ++i) { SendBlocking(accepted_sysex_[0].prefix[i]); } SendBlocking(SYSEX_COMMAND_DUMP_PACKET); SendBlocking(packet_index); // Outputs the data. uint8_t checksum = 0; for (uint8_t i = 0; i < size; ++i) { checksum += data[i]; SendBlocking(data[i] >> 4); SendBlocking(data[i] & 0x0f); } // Outputs a checksum. SendBlocking(checksum >> 4); SendBlocking(checksum & 0x0f); // End of SysEx block. SendBlocking(0xf7); Flush(); } /* static */ void MidiHandler::SysExSendPackets(const uint8_t* data, size_t size) { uint8_t block_index = 0; while (size) { size_t chunk_size = min(size, kSysexMaxChunkSize); SysExSendPacket(block_index, data, chunk_size); size -= chunk_size; data += chunk_size; ++block_index; } // Send a NULL packet to indicate end of transmission. SysExSendPacket(block_index, NULL, 0); } /* extern */ MidiHandler midi_handler; } // namespace yarns