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
ui.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. // // ----------------------------------------------------------------------------- // // User interface. #include "peaks/ui.h" #include "stmlib/system/storage.h" #include <algorithm> #include "peaks/calibration_data.h" namespace peaks { using namespace std; using namespace stmlib; const uint16_t kAdcThresholdUnlocked = 1 << (16 - 10); // 10 bits const uint16_t kAdcThresholdLocked = 1 << (16 - 8); // 8 bits const int32_t kLongPressDuration = 600; /* static */ const ProcessorFunction Ui::function_table_[FUNCTION_LAST][2] = { { PROCESSOR_FUNCTION_ENVELOPE, PROCESSOR_FUNCTION_ENVELOPE }, { PROCESSOR_FUNCTION_LFO, PROCESSOR_FUNCTION_LFO }, { PROCESSOR_FUNCTION_TAP_LFO, PROCESSOR_FUNCTION_TAP_LFO }, { PROCESSOR_FUNCTION_BASS_DRUM, PROCESSOR_FUNCTION_SNARE_DRUM }, { PROCESSOR_FUNCTION_MINI_SEQUENCER, PROCESSOR_FUNCTION_MINI_SEQUENCER }, { PROCESSOR_FUNCTION_PULSE_SHAPER, PROCESSOR_FUNCTION_PULSE_SHAPER }, { PROCESSOR_FUNCTION_PULSE_RANDOMIZER, PROCESSOR_FUNCTION_PULSE_RANDOMIZER }, { PROCESSOR_FUNCTION_FM_DRUM, PROCESSOR_FUNCTION_FM_DRUM }, }; Storage<0x8020000, 16> storage; void Ui::Init(CalibrationData* calibration_data) { calibration_data_ = calibration_data; leds_.Init(); switches_.Init(); adc_.Init(); system_clock.Tick(); fill(&adc_lp_[0], &adc_lp_[kNumAdcChannels], 0); fill(&adc_value_[0], &adc_value_[kNumAdcChannels], 0); fill(&adc_threshold_[0], &adc_threshold_[kNumAdcChannels], 0); fill(&snapped_[0], &snapped_[kNumAdcChannels], false); panel_gate_state_ = 0; calibrating_ = switches_.pressed_immediate(1); if (!storage.ParsimoniousLoad(&settings_, &version_token_)) { edit_mode_ = EDIT_MODE_TWIN; function_[0] = FUNCTION_ENVELOPE; function_[1] = FUNCTION_ENVELOPE; settings_.snap_mode = false; } else { edit_mode_ = static_cast<EditMode>(settings_.edit_mode); function_[0] = static_cast<Function>(settings_.function[0]); function_[1] = static_cast<Function>(settings_.function[1]); copy(&settings_.pot_value[0], &settings_.pot_value[8], &pot_value_[0]); if (edit_mode_ == EDIT_MODE_FIRST || edit_mode_ == EDIT_MODE_SECOND) { LockPots(); for (uint8_t i = 0; i < 4; ++i) { processors[0].set_parameter( i, static_cast<uint16_t>(pot_value_[i]) << 8); processors[1].set_parameter( i, static_cast<uint16_t>(pot_value_[i + 4]) << 8); } } } if (switches_.pressed_immediate(SWITCH_TWIN_MODE)) { settings_.snap_mode = !settings_.snap_mode; SaveState(); } ChangeControlMode(); SetFunction(0, function_[0]); SetFunction(1, function_[1]); double_press_counter_ = 0; } void Ui::LockPots() { fill( &adc_threshold_[0], &adc_threshold_[kNumAdcChannels], kAdcThresholdLocked); fill(&snapped_[0], &snapped_[kNumAdcChannels], false); } void Ui::SaveState() { settings_.edit_mode = edit_mode_; settings_.function[0] = function_[0]; settings_.function[1] = function_[1]; copy(&pot_value_[0], &pot_value_[8], &settings_.pot_value[0]); storage.ParsimoniousSave(settings_, &version_token_); } inline void Ui::RefreshLeds() { if (calibrating_) { leds_.set_pattern(0xf); leds_.set_twin_mode(true); leds_.set_levels(0, 0); return; } uint8_t flash = (system_clock.milliseconds() >> 7) & 7; switch (edit_mode_) { case EDIT_MODE_FIRST: leds_.set_twin_mode(flash == 1); break; case EDIT_MODE_SECOND: leds_.set_twin_mode(flash == 1 || flash == 3); break; default: leds_.set_twin_mode(edit_mode_ & 1); break; } if ((system_clock.milliseconds() & 256) && function() >= FUNCTION_FIRST_ALTERNATE_FUNCTION) { leds_.set_function(4); } else { leds_.set_function(function() & 3); } uint8_t b[2]; for (uint8_t i = 0; i < 2; ++i) { switch (function_[i]) { case FUNCTION_DRUM_GENERATOR: case FUNCTION_FM_DRUM_GENERATOR: b[i] = abs(brightness_[i]) >> 8; b[i] = b[i] > 255 ? 255 : b[i]; break; case FUNCTION_LFO: case FUNCTION_TAP_LFO: case FUNCTION_MINI_SEQUENCER: { int32_t brightness = int32_t(brightness_[i]) * 409 >> 8; brightness += 32768; brightness >>= 8; CONSTRAIN(brightness, 0, 255); b[i] = brightness; } break; default: b[i] = brightness_[i] >> 7; break; } } if (processors[0].function() == PROCESSOR_FUNCTION_NUMBER_STATION) { leds_.set_pattern( processors[0].number_station().digit() ^ \ processors[1].number_station().digit()); b[0] = processors[0].number_station().gate() ? 255 : 0; b[1] = processors[1].number_station().gate() ? 255 : 0; } leds_.set_levels(b[0], b[1]); } void Ui::PollPots() { for (uint8_t i = 0; i < kNumAdcChannels; ++i) { adc_lp_[i] = (int32_t(adc_.value(i)) + adc_lp_[i] * 7) >> 3; int32_t value = adc_lp_[i]; int32_t current_value = adc_value_[i]; if (value >= current_value + adc_threshold_[i] || value <= current_value - adc_threshold_[i] || !adc_threshold_[i]) { Event e; e.control_id = i; e.data = value; OnPotChanged(e); adc_value_[i] = value; adc_threshold_[i] = kAdcThresholdUnlocked; } } } void Ui::Poll() { system_clock.Tick(); switches_.Debounce(); for (uint8_t i = 0; i < kNumSwitches; ++i) { if (switches_.just_pressed(i)) { queue_.AddEvent(CONTROL_SWITCH, i, 0); press_time_[i] = system_clock.milliseconds(); } if (switches_.pressed(i) && press_time_[i] != 0 && i < SWITCH_GATE_TRIG_1) { int32_t pressed_time = system_clock.milliseconds() - press_time_[i]; if (pressed_time > kLongPressDuration) { if (switches_.pressed(1 - i)) { ++double_press_counter_; press_time_[0] = press_time_[1] = 0; if (double_press_counter_ == 3) { double_press_counter_ = 0; processors[0].set_function(PROCESSOR_FUNCTION_NUMBER_STATION); processors[1].set_function(PROCESSOR_FUNCTION_NUMBER_STATION); } } else { queue_.AddEvent(CONTROL_SWITCH, i, pressed_time); press_time_[i] = 0; // Inhibit next release event } } } if (switches_.released(i) && press_time_[i] != 0) { queue_.AddEvent( CONTROL_SWITCH, i, system_clock.milliseconds() - press_time_[i] + 1); } } RefreshLeds(); leds_.Write(); } void Ui::FlushEvents() { queue_.Flush(); } void Ui::OnSwitchPressed(const Event& e) { switch (e.control_id) { case SWITCH_TWIN_MODE: break; case SWITCH_FUNCTION: break; case SWITCH_GATE_TRIG_1: panel_gate_control_[0] = true; break; case SWITCH_GATE_TRIG_2: panel_gate_control_[1] = true; break; } } void Ui::ChangeControlMode() { uint16_t parameters[4]; for (int i = 0; i < 4; ++i) { parameters[i] = adc_value_[i]; } if (edit_mode_ == EDIT_MODE_SPLIT) { processors[0].CopyParameters(¶meters[0], 2); processors[1].CopyParameters(¶meters[2], 2); processors[0].set_control_mode(CONTROL_MODE_HALF); processors[1].set_control_mode(CONTROL_MODE_HALF); } else if (edit_mode_ == EDIT_MODE_TWIN) { processors[0].CopyParameters(¶meters[0], 4); processors[1].CopyParameters(¶meters[0], 4); processors[0].set_control_mode(CONTROL_MODE_FULL); processors[1].set_control_mode(CONTROL_MODE_FULL); } else { processors[0].set_control_mode(CONTROL_MODE_FULL); processors[1].set_control_mode(CONTROL_MODE_FULL); } } void Ui::SetFunction(uint8_t index, Function f) { if (edit_mode_ == EDIT_MODE_SPLIT || edit_mode_ == EDIT_MODE_TWIN) { function_[0] = function_[1] = f; processors[0].set_function(function_table_[f][0]); processors[1].set_function(function_table_[f][1]); } else { function_[index] = f; processors[index].set_function(function_table_[f][index]); } } void Ui::OnSwitchReleased(const Event& e) { if (calibrating_) { if (e.control_id == SWITCH_TWIN_MODE) { // Save calibration. calibration_data_->Save(); // Reset all settings to defaults. edit_mode_ = EDIT_MODE_TWIN; function_[0] = FUNCTION_ENVELOPE; function_[1] = FUNCTION_ENVELOPE; settings_.snap_mode = false; SaveState(); ChangeControlMode(); SetFunction(0, function_[0]); SetFunction(1, function_[1]); // Done with calibration. calibrating_ = false; } return; } switch (e.control_id) { case SWITCH_TWIN_MODE: if (e.data > kLongPressDuration) { edit_mode_ = static_cast<EditMode>( (edit_mode_ + EDIT_MODE_FIRST) % EDIT_MODE_LAST); function_[0] = function_[1]; processors[0].set_function(function_table_[function_[0]][0]); processors[1].set_function(function_table_[function_[0]][1]); LockPots(); } else { if (edit_mode_ <= EDIT_MODE_SPLIT) { edit_mode_ = static_cast<EditMode>(EDIT_MODE_SPLIT - edit_mode_); } else { edit_mode_ = static_cast<EditMode>(EDIT_MODE_SECOND - (edit_mode_ & 1)); LockPots(); } } ChangeControlMode(); SaveState(); break; case SWITCH_FUNCTION: { Function f = function(); if (e.data > kLongPressDuration) { f = static_cast<Function>((f + FUNCTION_FIRST_ALTERNATE_FUNCTION) % FUNCTION_LAST); } else { if (f <= FUNCTION_DRUM_GENERATOR) { f = static_cast<Function>((f + 1) & 3); } else { f = static_cast<Function>(((f + 1) & 3) + FUNCTION_FIRST_ALTERNATE_FUNCTION); } } SetFunction(edit_mode_ - EDIT_MODE_FIRST, f); SaveState(); } break; case SWITCH_GATE_TRIG_1: panel_gate_control_[0] = false; break; case SWITCH_GATE_TRIG_2: panel_gate_control_[1] = false; break; } } void Ui::OnPotChanged(const Event& e) { if (calibrating_) { pot_value_[e.control_id] = e.data >> 8; for (uint8_t i = 0; i < 2; ++i) { int32_t coarse = pot_value_[i * 2]; int32_t fine = pot_value_[i * 2 + 1]; int32_t offset = ((coarse - 128) << 3) + ((fine - 128) >> 1); calibration_data_->set_dac_offset(i, -offset); } return; } switch (edit_mode_) { case EDIT_MODE_TWIN: processors[0].set_parameter(e.control_id, e.data); processors[1].set_parameter(e.control_id, e.data); pot_value_[e.control_id] = e.data >> 8; break; case EDIT_MODE_SPLIT: if (e.control_id < 2) { processors[0].set_parameter(e.control_id, e.data); } else { processors[1].set_parameter(e.control_id - 2, e.data); } pot_value_[e.control_id] = e.data >> 8; break; case EDIT_MODE_FIRST: case EDIT_MODE_SECOND: { uint8_t index = e.control_id + (edit_mode_ - EDIT_MODE_FIRST) * 4; Processors* p = &processors[edit_mode_ - EDIT_MODE_FIRST]; int16_t delta = static_cast<int16_t>(pot_value_[index]) - \ static_cast<int16_t>(e.data >> 8); if (delta < 0) { delta = -delta; } if (!settings_.snap_mode || snapped_[e.control_id] || delta <= 2) { p->set_parameter(e.control_id, e.data); pot_value_[index] = e.data >> 8; snapped_[e.control_id] = true; } } break; case EDIT_MODE_LAST: break; } } void Ui::DoEvents() { while (queue_.available()) { Event e = queue_.PullEvent(); if (e.control_type == CONTROL_SWITCH) { if (e.data == 0) { OnSwitchPressed(e); } else { OnSwitchReleased(e); } } else if (e.control_type == CONTROL_POT) { OnPotChanged(e); } } if (queue_.idle_time() > 1000) { queue_.Touch(); } } } // namespace peaks