// 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 "frames/ui.h" #include #include "frames/keyframer.h" #include "frames/poly_lfo.h" #include "frames/euclidean.h" namespace frames { using namespace std; using namespace stmlib; const uint16_t kAdcThreshold = 1 << (16 - 10); // 10 bits const int32_t kLongPressDuration = 800; const int32_t kVeryLongPressDuration = 2000; const uint16_t kKeyframeGridTolerance = 2048; const uint8_t kDividersSteps = 7; const uint16_t kDeadZone = 2048; // 0..32767 void Ui::Init(Keyframer* keyframer, PolyLfo* poly_lfo, Euclidean euclidean[kNumChannels]) { factory_testing_switch_.Init(); channel_leds_.Init(); keyframe_led_.Init(); rgb_led_.Init(); switches_.Init(); adc_.Init(false); fill(&adc_value_[0], &adc_value_[kNumAdcChannels], 0); keyframer_ = keyframer; poly_lfo_ = poly_lfo; euclidean_ = euclidean; mode_ = factory_testing_switch_.Read() ? UI_MODE_SPLASH : UI_MODE_FACTORY_TESTING; animation_counter_ = 0; ignore_releases_ = 0; active_slot_ = 0; FindNearestKeyframe(); active_keyframe_lock_ = false; feature_mode_ = static_cast(keyframer_->feature_mode_); for (int i=0; ieuclidean_length_[i]); euclidean_[i].set_shape(keyframer_->euclidean_shape_[i]); } sequencer_step = 0; step_divider = 1; step_random = 0; shift_divider = 1; shift_random = 0; feedback_random = 0; active_registers = kMaxRegisters; for (int i=0; iCalibrate(frame_modulation()); FlushEvents(); } } void Ui::Poll() { 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(); detect_very_long_press_[i] = false; } if (switches_.pressed(i)) { int32_t pressed_time = system_clock.milliseconds() - press_time_[i]; if (!detect_very_long_press_[i]) { if (pressed_time > kLongPressDuration) { queue_.AddEvent(CONTROL_SWITCH, i, pressed_time); detect_very_long_press_[i] = true; } } else { if (pressed_time > kVeryLongPressDuration) { queue_.AddEvent(CONTROL_SWITCH, i, pressed_time); detect_very_long_press_[i] = false; press_time_[i] = 0; } } } if (switches_.released(i) && press_time_[i] != 0 && !detect_very_long_press_[i]) { queue_.AddEvent( CONTROL_SWITCH, i, system_clock.milliseconds() - press_time_[i] + 1); press_time_[i] = 0; detect_very_long_press_[i] = false; } } for (uint8_t i = 0; i <= kFrameAdcChannel; ++i) { int32_t value = (31 * adc_filtered_value_[i] + adc_.value(i)) >> 5; adc_filtered_value_[i] = value; int32_t current_value = static_cast(adc_value_[i]); if (value >= current_value + kAdcThreshold || value <= current_value - kAdcThreshold) { queue_.AddEvent(CONTROL_POT, i, value); adc_value_[i] = value; } } switch (mode_) { case UI_MODE_SPLASH: animation_counter_ += 128; channel_leds_.set_channel(0, (animation_counter_ + 49152) >> 8); channel_leds_.set_channel(1, (animation_counter_ + 32768) >> 8); channel_leds_.set_channel(2, (animation_counter_ + 16384) >> 8); channel_leds_.set_channel(3, (animation_counter_ + 0) >> 8); rgb_led_.set_color(255, 255, 255); break; case UI_MODE_FEATURE_SWITCH: animation_counter_ += 1024; rgb_led_.set_color(255, 255, 255); rgb_led_.Dim(animation_counter_); for (int i=0; i<4; i++) channel_leds_.set_channel(i, 0); switch (feature_mode_) { case FEAT_MODE_KEYFRAMER: break; case FEAT_MODE_KEYFRAME_LOOPER: rgb_led_.set_color(255, 0, 0); rgb_led_.Dim(animation_counter_); break; case FEAT_MODE_SEQ_MAIN: channel_leds_.set_channel(0, 255); break; case FEAT_MODE_SEQ_SHIFT_REGISTER: channel_leds_.set_channel(1, 255); break; case FEAT_MODE_SEQ_STEP_EDIT: break; case FEAT_MODE_POLY_LFO: channel_leds_.set_channel(2, 255); break; case FEAT_MODE_EUCLIDEAN: channel_leds_.set_channel(3, 255); break; } break; case UI_MODE_SAVE_CONFIRMATION: animation_counter_ -= 256; if (active_slot_ == 0) { channel_leds_.set_channel(0, (animation_counter_ + 0) >> 8); channel_leds_.set_channel(1, (animation_counter_ + 16384) >> 8); channel_leds_.set_channel(2, (animation_counter_ + 32768) >> 8); channel_leds_.set_channel(3, (animation_counter_ + 49152) >> 8); } else { for (int i=0; i<4; i++) channel_leds_.set_channel(i, active_slot_ == i+1 ? animation_counter_ >> 8 : 0); } rgb_led_.set_color(0, 255, 0); break; case UI_MODE_ERASE_CONFIRMATION: animation_counter_ -= 256; if (active_slot_ == 0) { channel_leds_.set_channel(0, (animation_counter_ + 0) >> 8); channel_leds_.set_channel(1, (animation_counter_ + 16384) >> 8); channel_leds_.set_channel(2, (animation_counter_ + 32768) >> 8); channel_leds_.set_channel(3, (animation_counter_ + 49152) >> 8); } else { for (int i=0; i<4; i++) channel_leds_.set_channel(i, active_slot_ == i+1 ? animation_counter_ >> 8 : 0); } rgb_led_.set_color(255, 0, 0); break; case UI_MODE_FACTORY_TESTING: channel_leds_.set_channel(0, keyframer_->level(0) >> 8); channel_leds_.set_channel(1, keyframer_->level(1) >> 8); channel_leds_.set_channel(2, keyframer_->level(2) >> 8); channel_leds_.set_channel(3, keyframer_->level(3) >> 8); if (frame() < 4096) { rgb_led_.set_color(255, 0, 0); } else if (frame() > 61440) { rgb_led_.set_color(0, 255, 0); } else { uint8_t v = frame_modulation() >> 8; rgb_led_.set_color(0, 0, v); } if (test_led_) { keyframe_led_.High(); } else { keyframe_led_.Low(); } break; case UI_MODE_NORMAL: if (feature_mode_ == FEAT_MODE_POLY_LFO) { channel_leds_.set_channel(0, poly_lfo_->level(0)); channel_leds_.set_channel(1, poly_lfo_->level(1)); channel_leds_.set_channel(2, poly_lfo_->level(2)); channel_leds_.set_channel(3, poly_lfo_->level(3)); rgb_led_.set_color(poly_lfo_->color()); if (poly_lfo_->level(0) > 128) { keyframe_led_.High(); } else { keyframe_led_.Low(); } } else if (feature_mode_ == FEAT_MODE_EUCLIDEAN) { channel_leds_.set_channel(0, euclidean_[0].level()); channel_leds_.set_channel(1, euclidean_[1].level()); channel_leds_.set_channel(2, euclidean_[2].level()); channel_leds_.set_channel(3, euclidean_[3].level()); } else if (feature_mode_ == FEAT_MODE_SEQ_STEP_EDIT) { channel_leds_.set_channel(0, keyframer_->level(0) >> 8); channel_leds_.set_channel(1, keyframer_->level(1) >> 8); channel_leds_.set_channel(2, keyframer_->level(2) >> 8); channel_leds_.set_channel(3, keyframer_->level(3) >> 8); rgb_led_.set_color(keyframer_->color()); ++keyframe_led_pwm_counter_; if ((keyframe_led_pwm_counter_ & 15) >= 15) { keyframe_led_.High(); } else { keyframe_led_.Low(); } break; } else if (feature_mode_ == FEAT_MODE_SEQ_SHIFT_REGISTER) { channel_leds_.set_channel(0, shift_register[0] >> 8); channel_leds_.set_channel(1, shift_register[1] >> 8); channel_leds_.set_channel(2, shift_register[2] >> 8); channel_leds_.set_channel(3, shift_register[3] >> 8); rgb_led_.set_color(keyframer_->color()); if (active_keyframe_ == -1) { keyframe_led_.Low(); } else { animation_counter_ += 256; int32_t distance = frame() - \ keyframer_->keyframe(active_keyframe_).timestamp; distance = min(distance * distance >> 18, int32_t(15)); ++keyframe_led_pwm_counter_; if ((keyframe_led_pwm_counter_ & 15) >= distance) { keyframe_led_.High(); } else { keyframe_led_.Low(); } if (active_keyframe_lock_) { if (animation_counter_ & 0x8000) { keyframe_led_.High(); } else { keyframe_led_.Low(); } } } } else { channel_leds_.set_channel(0, keyframer_->level(0) >> 8); channel_leds_.set_channel(1, keyframer_->level(1) >> 8); channel_leds_.set_channel(2, keyframer_->level(2) >> 8); channel_leds_.set_channel(3, keyframer_->level(3) >> 8); rgb_led_.set_color(keyframer_->color()); if (active_keyframe_ == -1) { keyframe_led_.Low(); } else { animation_counter_ += 256; int32_t distance = frame() - \ keyframer_->keyframe(active_keyframe_).timestamp; distance = min(distance * distance >> 18, int32_t(15)); ++keyframe_led_pwm_counter_; if ((keyframe_led_pwm_counter_ & 15) >= distance) { keyframe_led_.High(); } else { keyframe_led_.Low(); } if (active_keyframe_lock_) { if (animation_counter_ & 0x8000) { keyframe_led_.High(); } else { keyframe_led_.Low(); } } } } break; case UI_MODE_EDIT_LENGTH: channel_leds_.set_channel(0, euclidean_[0].level()); channel_leds_.set_channel(1, euclidean_[1].level()); channel_leds_.set_channel(2, euclidean_[2].level()); channel_leds_.set_channel(3, euclidean_[3].level()); rgb_led_.set_color(255, 16, 32); break; case UI_MODE_EDIT_SHAPE: channel_leds_.set_channel(0, euclidean_[0].level()); channel_leds_.set_channel(1, euclidean_[1].level()); channel_leds_.set_channel(2, euclidean_[2].level()); channel_leds_.set_channel(3, euclidean_[3].level()); rgb_led_.set_color(16, 192, 32); break; case UI_MODE_EDIT_RESPONSE: case UI_MODE_EDIT_EASING: { animation_counter_ += 48; for (uint8_t i = 0; i < 4; ++i) { channel_leds_.set_channel(i, active_channel_ == i ? 255 : 0); } if (mode_ == UI_MODE_EDIT_EASING) { rgb_led_.set_color(255, 16, 32); } else { rgb_led_.set_color(16, 192, 32); } uint16_t brightness = active_channel_ == -1 ? 65535 : keyframer_->SampleAnimation(active_channel_, animation_counter_, mode_ == UI_MODE_EDIT_EASING); rgb_led_.Dim(brightness); } break; } rgb_led_.Write(); channel_leds_.Write(); } void Ui::FlushEvents() { queue_.Flush(); } void Ui::OnSwitchPressed(const Event& e) { test_led_ = true; // double press -> feature switch mode if ((e.control_id == 0 && switches_.pressed_immediate(1)) || (e.control_id == 1 && switches_.pressed_immediate(0))) { mode_ = UI_MODE_FEATURE_SWITCH; ignore_releases_ = 2; } } void Ui::OnSwitchReleased(const Event& e) { if (mode_ == UI_MODE_FACTORY_TESTING) { test_led_ = false; } else { if (active_keyframe_lock_) { active_keyframe_lock_ = false; FindNearestKeyframe(); return; } // hack for double presses if (ignore_releases_ > 0) { ignore_releases_--; return; } switch (e.control_id) { case SWITCH_ADD_FRAME: if (e.data > kVeryLongPressDuration) { mode_ = UI_MODE_SAVE_CONFIRMATION; } else if (e.data > kLongPressDuration) { if (feature_mode_ == FEAT_MODE_KEYFRAMER || feature_mode_ == FEAT_MODE_KEYFRAME_LOOPER) { mode_ = UI_MODE_EDIT_EASING; active_channel_ = -1; } else if (feature_mode_ == FEAT_MODE_EUCLIDEAN) { mode_ = UI_MODE_EDIT_LENGTH; } else if (feature_mode_ == FEAT_MODE_SEQ_MAIN) { keyframer_->Randomize(); } } else { if (mode_ == UI_MODE_NORMAL && (feature_mode_ == FEAT_MODE_KEYFRAMER || feature_mode_ == FEAT_MODE_KEYFRAME_LOOPER || feature_mode_ == FEAT_MODE_SEQ_MAIN || feature_mode_ == FEAT_MODE_SEQ_SHIFT_REGISTER)) { if (active_keyframe_ == -1) { keyframer_->AddKeyframe(frame(), &adc_value_[0]); } else { // This abandoned feature allowed to select and continue editing // a keyframe with the 4 knobs on the top even when the big // frame knob is being played. For that, we select the keyframe // we are interested in, we press "add", and this "locks" the // 4 pots at the top of the module for editing this keyframe. //active_keyframe_lock_ = true; } FindNearestKeyframe(); } else if (mode_ == UI_MODE_SAVE_CONFIRMATION) { // confirming save -> write to active slot keyframer_->feature_mode_ = feature_mode_; keyframer_->Save(active_slot_); mode_ = UI_MODE_SPLASH; } else if (mode_ == UI_MODE_FEATURE_SWITCH) { mode_ = UI_MODE_NORMAL; } else if (feature_mode_ == FEAT_MODE_POLY_LFO) { poly_lfo_->Reset(); } else if (feature_mode_ == FEAT_MODE_SEQ_STEP_EDIT) { feature_mode_ = FEAT_MODE_SEQ_MAIN; } else { mode_ = UI_MODE_NORMAL; } } break; case SWITCH_DELETE_FRAME: if (e.data > kVeryLongPressDuration) { mode_ = UI_MODE_ERASE_CONFIRMATION; } else if (e.data > kLongPressDuration) { if (feature_mode_ == FEAT_MODE_SEQ_MAIN) { feature_mode_ = FEAT_MODE_SEQ_STEP_EDIT; } else if (feature_mode_ == FEAT_MODE_KEYFRAMER || feature_mode_ == FEAT_MODE_KEYFRAME_LOOPER) { mode_ = UI_MODE_EDIT_RESPONSE; active_channel_ = -1; } else if (feature_mode_ == FEAT_MODE_EUCLIDEAN) { mode_ = UI_MODE_EDIT_SHAPE; } } else if (mode_ == UI_MODE_ERASE_CONFIRMATION) { if (active_slot_ == 0) { keyframer_->Clear(); FindNearestKeyframe(); SyncWithPots(); feature_mode_ = FEAT_MODE_KEYFRAMER; } else { keyframer_->Load(active_slot_); feature_mode_ = static_cast(keyframer_->feature_mode_); for (int i=0; ieuclidean_length_[i]); euclidean_[i].set_shape(keyframer_->euclidean_shape_[i]); } } mode_ = UI_MODE_SPLASH; } else { if (mode_ == UI_MODE_NORMAL && (feature_mode_ == FEAT_MODE_KEYFRAMER || feature_mode_ == FEAT_MODE_KEYFRAME_LOOPER || feature_mode_ == FEAT_MODE_SEQ_MAIN || feature_mode_ == FEAT_MODE_SEQ_SHIFT_REGISTER)) { if (active_keyframe_ != -1) { keyframer_->RemoveKeyframe( keyframer_->keyframe(active_keyframe_).timestamp); } FindNearestKeyframe(); SyncWithPots(); } else if (mode_ == UI_MODE_FEATURE_SWITCH) { mode_ = UI_MODE_NORMAL; } else if (feature_mode_ == FEAT_MODE_POLY_LFO) { poly_lfo_->Randomize(); } else if (feature_mode_ == FEAT_MODE_SEQ_STEP_EDIT) { feature_mode_ = FEAT_MODE_SEQ_MAIN; } else { mode_ = UI_MODE_NORMAL; } } break; } } } void Ui::ParseShiftSequencer(uint16_t control_id, int32_t data) { // knob 1 is sequence order randomization, knobs 2 and 3 // are divisors for the shift register, knob 4 randomizes // feedback value switch (control_id) { case 0: if (active_keyframe_ != -1) { Keyframe* k = keyframer_->mutable_keyframe(active_keyframe_); k->values[0] = data; } else { keyframer_->set_immediate(0, data); } break; case 1: if (data < 32768) { step_random = 0; step_divider = (((32768 - data) * kDividersSteps >> 15) + 1) % kDividersSteps; } else { int16_t rnd = data - 32768; rnd = (rnd * rnd) >> 15; step_random = rnd >> 7; step_divider = 1; } break; case 2: if (data < 32768) { shift_random = 0; shift_divider = (((32768 - data) * kDividersSteps >> 15) + 1) % kDividersSteps; } else { int16_t rnd = data - 32768; rnd = (rnd * rnd) >> 15; shift_random = rnd >> 7; shift_divider = 1; } break; case 3: if (data < 32768) { feedback_random = 0; int16_t rnd = 32768 - data; rnd = (rnd * rnd) >> 15; sequencer_random = rnd >> 7; } else { int16_t rnd = data - 32768; rnd = (rnd * rnd) >> 15; feedback_random = rnd >> 7; sequencer_random = 0; } break; feedback_random = data >> 8; break; case kFrameAdcChannel: if (!active_keyframe_lock_) { FindNearestKeyframe(); } // big knob also sets number of registers active_registers = (data * kMaxRegisters >> 16) + 1; } } void Ui::ParsePolyLFO(uint16_t control_id, int32_t data) { switch (control_id) { case 0: poly_lfo_->set_shape(data); break; case 1: poly_lfo_->set_shape_spread(data); break; case 2: poly_lfo_->set_spread(data); break; case 3: poly_lfo_->set_coupling(data); break; } } void Ui::ParseEuclidean(uint16_t control_id, int32_t data) { if (mode_ == UI_MODE_EDIT_LENGTH) { switch (control_id) { case 0: case 1: case 2: case 3: { uint8_t len = (data >> 12) + 1; euclidean_[control_id].set_length(len); keyframer_->euclidean_length_[control_id] = len; } break; case kFrameAdcChannel: for (int i=0; ieuclidean_shape_[control_id] = data; } break; case kFrameAdcChannel: for (int i=0; iset_immediate(e.control_id, e.data); break; } } else if (mode_ == UI_MODE_FEATURE_SWITCH) { switch (e.control_id) { case kFrameAdcChannel: feature_mode_ = e.data > 60000 ? FEAT_MODE_KEYFRAME_LOOPER : FEAT_MODE_KEYFRAMER; SyncWithPots(); break; case 0: feature_mode_ = FEAT_MODE_SEQ_MAIN; SyncWithPots(); break; case 1: feature_mode_ = FEAT_MODE_SEQ_SHIFT_REGISTER; SyncWithPotsShiftSequencer(); break; case 2: feature_mode_ = FEAT_MODE_POLY_LFO; SyncWithPotsPolyLFO(); break; case 3: feature_mode_ = FEAT_MODE_EUCLIDEAN; // TODO marche pas SyncWithPotsEuclidean(); break; } } else if (mode_ == UI_MODE_SAVE_CONFIRMATION || mode_ == UI_MODE_ERASE_CONFIRMATION) { switch (e.control_id) { case 0: case 1: case 2: case 3: active_slot_ = e.control_id + 1; break; case kFrameAdcChannel: active_slot_ = 0; } } else if (feature_mode_ == FEAT_MODE_POLY_LFO) { ParsePolyLFO(e.control_id, e.data); } else if (feature_mode_ == FEAT_MODE_SEQ_STEP_EDIT) { switch (e.control_id) { case 0: case 1: case 2: case 3: // the controls directly edit the value of the current step Keyframe* k = keyframer_->mutable_keyframe(sequencer_step); k->values[e.control_id] = e.data; } } else if (feature_mode_ == FEAT_MODE_EUCLIDEAN) { ParseEuclidean(e.control_id, e.data); } else if (feature_mode_ == FEAT_MODE_SEQ_SHIFT_REGISTER) { ParseShiftSequencer(e.control_id, e.data); } else { switch (e.control_id) { case 0: case 1: case 2: case 3: if (mode_ == UI_MODE_NORMAL || mode_ == UI_MODE_SPLASH) { if (active_keyframe_ != -1) { Keyframe* k = keyframer_->mutable_keyframe(active_keyframe_); k->values[e.control_id] = e.data; } else { keyframer_->set_immediate(e.control_id, e.data); } } else if (mode_ == UI_MODE_EDIT_RESPONSE) { active_channel_ = e.control_id; keyframer_->mutable_settings(e.control_id)->response = e.data >> 8; } else if (mode_ == UI_MODE_EDIT_EASING) { active_channel_ = e.control_id; keyframer_->mutable_settings(e.control_id)->easing_curve = \ static_cast(e.data * 6 >> 16); } break; case kFrameAdcChannel: if (!active_keyframe_lock_) { FindNearestKeyframe(); } break; case kFrameModulationAdcChannel: break; } } } void Ui::FindNearestKeyframe() { active_keyframe_ = keyframer_->FindNearestKeyframe( frame(), kKeyframeGridTolerance); } void Ui::SyncWithPots() { if (!keyframer_->num_keyframes()) { for (uint8_t i = 0; i < kNumChannels; ++i) { keyframer_->set_immediate(i, adc_filtered_value_[i]); } } } void Ui::SyncWithPotsShiftSequencer() { for (uint8_t i = 0; i < kNumChannels; ++i) { ParseShiftSequencer(i, adc_value_[i]); } } void Ui::SyncWithPotsEuclidean() { for (uint8_t i = 0; i < kNumChannels; ++i) { ParseEuclidean(i, adc_value_[i]); } } void Ui::SyncWithPotsPolyLFO() { for (uint8_t i = 0; i < kNumChannels; ++i) { ParsePolyLFO(i, adc_value_[i]); } } 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() > 500) { queue_.Touch(); if (mode_ == UI_MODE_SPLASH) { mode_ = UI_MODE_NORMAL; } } } } // namespace frames