1 // Copyright 2013 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.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 // User interface.
28 
29 #include "stmlib/system/system_clock.h"
30 
31 #include "yarns/multi.h"
32 #include "yarns/ui.h"
33 #include "yarns/voice.h"
34 
35 #include <cstring>
36 
37 namespace yarns {
38 
39 using namespace std;
40 using namespace stmlib;
41 
42 const uint32_t kEncoderLongPressTime = 600;
43 const uint8_t kNumScrolls = 1;
44 const uint8_t kScrollingDelaySeconds = 1;
45 const uint16_t kScrollingCharacterTime = 140;
46 
47 /* static */
48 const Ui::Command Ui::commands_[] = {
49   { "*LOAD*", UI_MODE_LOAD_SELECT_PROGRAM, NULL },
50   { "*SAVE*", UI_MODE_SAVE_SELECT_PROGRAM, NULL },
51   { "*INIT*", UI_MODE_PARAMETER_SELECT, &Ui::DoInitCommand },
52   { "*QUICK CONFIG*", UI_MODE_LEARNING, &Ui::DoLearnCommand },
53   { "*>SYSEX DUMP*", UI_MODE_PARAMETER_SELECT, &Ui::DoDumpCommand },
54   { "*CALIBRATE*", UI_MODE_CALIBRATION_SELECT_VOICE, NULL },
55   { "*EXIT*", UI_MODE_PARAMETER_SELECT, NULL },
56 };
57 
58 /* static */
59 Ui::Mode Ui::modes_[] = {
60   // UI_MODE_PARAMETER_SELECT
61   { &Ui::OnIncrementParameterSelect, &Ui::OnClick,
62     &Ui::PrintParameterName,
63     UI_MODE_PARAMETER_EDIT,
64     NULL, 0, 0 },
65 
66   // UI_MODE_PARAMETER_EDIT
67   { &Ui::OnIncrementParameterEdit, &Ui::OnClick,
68     &Ui::PrintParameterValue,
69     UI_MODE_PARAMETER_SELECT,
70     NULL, 0, 0 },
71 
72   // UI_MODE_MAIN_MENU
73   { &Ui::OnIncrement, &Ui::OnClickMainMenu,
74     &Ui::PrintMenuName,
75     UI_MODE_MAIN_MENU,
76     NULL, 0, MAIN_MENU_LAST - 1 },
77 
78   // UI_MODE_LOAD_SELECT_PROGRAM
79   { &Ui::OnIncrement, &Ui::OnClickLoadSave,
80     &Ui::PrintProgramNumber,
81     UI_MODE_MAIN_MENU,
82     NULL, 0, kNumPrograms },
83 
84   // UI_MODE_SAVE_SELECT_PROGRAM
85   { &Ui::OnIncrement, &Ui::OnClickLoadSave,
86     &Ui::PrintProgramNumber,
87     UI_MODE_MAIN_MENU,
88     NULL, 0, kNumPrograms },
89 
90   // UI_MODE_CALIBRATION_SELECT_VOICE
91   { &Ui::OnIncrement, &Ui::OnClickCalibrationSelectVoice,
92     &Ui::PrintCalibrationVoiceNumber,
93     UI_MODE_CALIBRATION_SELECT_VOICE,
94     NULL, 0, kNumVoices },
95 
96   // UI_MODE_CALIBRATION_SELECT_NOTE
97   { &Ui::OnIncrement, &Ui::OnClickCalibrationSelectNote,
98     &Ui::PrintCalibrationNote,
99     UI_MODE_CALIBRATION_SELECT_NOTE,
100     NULL, 0, kNumOctaves },
101 
102   // UI_MODE_CALIBRATION_ADJUST_LEVEL
103   { &Ui::OnIncrementCalibrationAdjustment, &Ui::OnClick,
104     &Ui::PrintCalibrationNote,
105     UI_MODE_CALIBRATION_SELECT_NOTE,
106     NULL, 0, 0 },
107 
108   // UI_MODE_SELECT_RECORDING_PART
109   { &Ui::OnIncrement, &Ui::OnClickSelectRecordingPart,
110     &Ui::PrintRecordingPart,
111     UI_MODE_RECORDING,
112     NULL, 0, 0 },
113 
114   // UI_MODE_RECORDING
115   { &Ui::OnIncrementRecording, &Ui::OnClickRecording,
116     &Ui::PrintRecordingStatus,
117     UI_MODE_RECORDING,
118     NULL, 0, 0 },
119 
120   // UI_MODE_OVERDUBBING
121   { &Ui::OnIncrementOverdubbing, &Ui::OnClickOverdubbing,
122     &Ui::PrintRecordingStatus,
123     UI_MODE_OVERDUBBING,
124     NULL, 0, 0 },
125 
126   // UI_MODE_PUSH_IT_SELECT_NOTE
127   { &Ui::OnIncrementPushItNote, &Ui::OnClick,
128     &Ui::PrintPushItNote,
129     UI_MODE_PARAMETER_SELECT,
130     NULL, 0, 127 },
131 
132   // UI_MODE_LEARNING
133   { &Ui::OnIncrement, &Ui::OnClickLearning,
134     &Ui::PrintLearning,
135     UI_MODE_PARAMETER_SELECT,
136     NULL, 0, 127 },
137 
138   // UI_MODE_FACTORY_TESTING
139   { &Ui::OnIncrementFactoryTesting, &Ui::OnClickFactoryTesting,
140     &Ui::PrintFactoryTesting,
141     UI_MODE_PARAMETER_SELECT,
142     NULL, 0, 99 },
143 
144   // UI_MODE_SPLASH
145   { &Ui::OnIncrementParameterSelect, &Ui::OnClick,
146     &Ui::PrintVersionNumber,
147     UI_MODE_PARAMETER_SELECT,
148     NULL, 0, 0 }
149 };
150 
Init()151 void Ui::Init() {
152   encoder_.Init();
153   display_.Init();
154   switches_.Init();
155   queue_.Init();
156   leds_.Init();
157 
158   previous_mode_ = mode_ = UI_MODE_SPLASH;
159   setting_index_ = 0;
160   previous_tap_time_ = 0;
161   tap_tempo_count_ = 0;
162 
163   start_stop_press_time_ = 0;
164 
165   push_it_note_ = 60;
166   modes_[UI_MODE_MAIN_MENU].incremented_variable = &command_index_;
167   modes_[UI_MODE_LOAD_SELECT_PROGRAM].incremented_variable = &program_index_;
168   modes_[UI_MODE_SAVE_SELECT_PROGRAM].incremented_variable = &program_index_;
169   modes_[UI_MODE_CALIBRATION_SELECT_VOICE].incremented_variable = \
170       &calibration_voice_;
171   modes_[UI_MODE_CALIBRATION_SELECT_NOTE].incremented_variable = \
172       &calibration_note_;
173   modes_[UI_MODE_SELECT_RECORDING_PART].incremented_variable = \
174       &recording_part_;
175   modes_[UI_MODE_FACTORY_TESTING].incremented_variable = \
176       &factory_testing_number_;
177   PrintVersionNumber();
178 }
179 
Poll()180 void Ui::Poll() {
181   encoder_.Debounce();
182 
183   // Handle press and long press on encoder.
184   if (encoder_.just_pressed()) {
185     encoder_press_time_ = system_clock.milliseconds();
186     encoder_long_press_event_sent_ = false;
187   }
188   if (!encoder_long_press_event_sent_) {
189     if (encoder_.pressed()) {
190       uint32_t duration = system_clock.milliseconds() - encoder_press_time_;
191       if (duration >= kEncoderLongPressTime && !encoder_long_press_event_sent_) {
192         queue_.AddEvent(CONTROL_ENCODER_LONG_CLICK, 0, 0);
193         encoder_long_press_event_sent_ = true;
194       }
195     } else if (encoder_.released()) {
196       queue_.AddEvent(CONTROL_ENCODER_CLICK, 0, 0);
197     }
198   }
199 
200   // Encoder increment.
201   int32_t increment = encoder_.increment();
202   if (increment != 0) {
203     queue_.AddEvent(CONTROL_ENCODER, 0, increment);
204   }
205 
206 
207   // Switch press and long press.
208   switches_.Debounce();
209   if (switches_.just_pressed(UI_SWITCH_REC)) {
210     queue_.AddEvent(CONTROL_SWITCH, UI_SWITCH_REC, 0);
211   }
212   if (switches_.just_pressed(UI_SWITCH_TAP_TEMPO)) {
213     queue_.AddEvent(CONTROL_SWITCH, UI_SWITCH_TAP_TEMPO, 0);
214   }
215   if (mode_ == UI_MODE_RECORDING || mode_ == UI_MODE_OVERDUBBING) {
216     if (switches_.just_pressed(UI_SWITCH_TIE)) {
217       queue_.AddEvent(CONTROL_SWITCH, UI_SWITCH_TIE, 0);
218     }
219   } else {
220     if (switches_.just_pressed(UI_SWITCH_START_STOP)) {
221       start_stop_press_time_ = system_clock.milliseconds();
222       long_press_event_sent_ = false;
223     }
224     if (!long_press_event_sent_) {
225       if (switches_.pressed(UI_SWITCH_START_STOP)) {
226         uint32_t duration = system_clock.milliseconds() - start_stop_press_time_;
227         if (duration >= kEncoderLongPressTime && !long_press_event_sent_) {
228           queue_.AddEvent(CONTROL_SWITCH_HOLD, UI_SWITCH_START_STOP, 0);
229           long_press_event_sent_ = true;
230         }
231       } else if (switches_.released(UI_SWITCH_START_STOP)
232                  && !long_press_event_sent_) {
233         queue_.AddEvent(CONTROL_SWITCH, UI_SWITCH_START_STOP, 0);
234       }
235     }
236   }
237   display_.RefreshSlow();
238 
239   // Read LED brightness from multi and copy to LEDs driver.
240   uint8_t leds_brightness[kNumVoices];
241   multi.GetLedsBrightness(leds_brightness);
242   if (mode_ == UI_MODE_FACTORY_TESTING) {
243     ++factory_testing_leds_counter_;
244     uint16_t x = factory_testing_leds_counter_;
245     leds_brightness[0] = (((x + 384) & 511) < 128) ? 255 : 0;
246     leds_brightness[1] = (((x + 256) & 511) < 128) ? 255 : 0;
247     leds_brightness[2] = (((x + 128) & 511) < 128) ? 255 : 0;
248     leds_brightness[3] = (((x + 000) & 511) < 128) ? 255 : 0;
249   } else if (mode_ == UI_MODE_SPLASH) {
250     leds_brightness[0] = 255;
251     leds_brightness[1] = 0;
252     leds_brightness[2] = 0;
253     leds_brightness[3] = 0;
254   }
255 
256   leds_.Write(leds_brightness);
257   leds_.Write();
258 }
259 
FlushEvents()260 void Ui::FlushEvents() {
261   queue_.Flush();
262 }
263 
264 // Display refresh functions.
265 const char* const calibration_strings[] = {
266   "-3", "-2", "-1", " 0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "OK"
267 };
268 
269 const char notes_long[] = "C DbD EbE F GbG AbA BbB ";
270 const char octave[] = "-0123456789";
271 
PrintParameterName()272 void Ui::PrintParameterName() {
273   display_.Print(setting().short_name, setting().name);
274 }
275 
PrintParameterValue()276 void Ui::PrintParameterValue() {
277   settings.Print(setting(), buffer_);
278   display_.Print(buffer_, buffer_);
279 }
280 
PrintMenuName()281 void Ui::PrintMenuName() {
282   display_.Print(commands_[command_index_].name);
283 }
284 
PrintProgramNumber()285 void Ui::PrintProgramNumber() {
286   if (program_index_ < kNumPrograms) {
287     strcpy(buffer_, "P1");
288     buffer_[1] += program_index_;
289     display_.Print(buffer_);
290   } else {
291     display_.Print("--");
292   }
293 }
294 
PrintCalibrationVoiceNumber()295 void Ui::PrintCalibrationVoiceNumber() {
296   if (calibration_voice_ < kNumVoices) {
297     strcpy(buffer_, "*1");
298     buffer_[1] += calibration_voice_;
299     display_.Print(buffer_);
300   } else {
301     display_.Print("OK");
302   }
303 }
304 
PrintCalibrationNote()305 void Ui::PrintCalibrationNote() {
306   display_.Print(
307       calibration_strings[calibration_note_],
308       calibration_strings[calibration_note_]);
309 }
310 
PrintRecordingPart()311 void Ui::PrintRecordingPart() {
312   strcpy(buffer_, "R1");
313   buffer_[1] += recording_part_;
314   display_.Print(buffer_);
315 }
316 
PrintRecordingStatus()317 void Ui::PrintRecordingStatus() {
318   if (push_it_) {
319     PrintPushItNote();
320   } else {
321     uint8_t n = recording_part().recording_step() + 1;
322     strcpy(buffer_, "00");
323     buffer_[0] += n / 10;
324     buffer_[1] += n % 10;
325     display_.Print(buffer_);
326   }
327 }
328 
PrintPushItNote()329 void Ui::PrintPushItNote() {
330   buffer_[0] = notes_long[2 * (push_it_note_ % 12)];
331   buffer_[1] = notes_long[1 + 2 * (push_it_note_ % 12)];
332   buffer_[1] = buffer_[1] == ' ' ? octave[push_it_note_ / 12] : buffer_[1];
333   buffer_[2] = '\0';
334   display_.Print(buffer_, buffer_);
335 }
336 
PrintLearning()337 void Ui::PrintLearning() {
338   display_.Print("++");
339 }
340 
PrintFactoryTesting()341 void Ui::PrintFactoryTesting() {
342   switch (factory_testing_display_) {
343     case UI_FACTORY_TESTING_DISPLAY_EMPTY:
344       display_.Print("\xff\xff");
345       break;
346 
347     case UI_FACTORY_TESTING_DISPLAY_NUMBER:
348       {
349         strcpy(buffer_, "00");
350         buffer_[0] += factory_testing_number_ / 10;
351         buffer_[1] += factory_testing_number_ % 10;
352         display_.Print(buffer_);
353       }
354       break;
355 
356     case UI_FACTORY_TESTING_DISPLAY_CLICK:
357       display_.Print("OK");
358       break;
359 
360     case UI_FACTORY_TESTING_DISPLAY_SW_1:
361     case UI_FACTORY_TESTING_DISPLAY_SW_2:
362     case UI_FACTORY_TESTING_DISPLAY_SW_3:
363       {
364         strcpy(buffer_, "B1");
365         buffer_[1] += factory_testing_display_ - UI_FACTORY_TESTING_DISPLAY_SW_1;
366         display_.Print(buffer_);
367       }
368       break;
369   }
370 }
371 
PrintVersionNumber()372 void Ui::PrintVersionNumber() {
373   display_.Print(".5");
374 }
375 
376 // Generic Handlers
OnLongClick(const Event & e)377 void Ui::OnLongClick(const Event& e) {
378   switch (mode_) {
379     case UI_MODE_MAIN_MENU:
380       mode_ = previous_mode_;
381       break;
382 
383     default:
384       previous_mode_ = mode_;
385       mode_ = UI_MODE_MAIN_MENU;
386       command_index_ = 0;
387       break;
388   }
389 }
390 
OnClick(const Event & e)391 void Ui::OnClick(const Event& e) {
392   mode_ = modes_[mode_].next_mode;
393 }
394 
OnIncrement(const Event & e)395 void Ui::OnIncrement(const Event& e) {
396   Mode* mode = &modes_[mode_];
397   if (!mode->incremented_variable) {
398     return;
399   }
400   int8_t v = *mode->incremented_variable;
401   v += e.data;
402   CONSTRAIN(v, mode->min_value, mode->max_value);
403   *mode->incremented_variable = v;
404 }
405 
406 // Specialized Handlers
OnClickMainMenu(const Event & e)407 void Ui::OnClickMainMenu(const Event& e) {
408   if (commands_[command_index_].function) {
409     (this->*commands_[command_index_].function)();
410   }
411   mode_ = commands_[command_index_].next_mode;
412 }
413 
OnClickLoadSave(const Event & e)414 void Ui::OnClickLoadSave(const Event& e) {
415   if (program_index_ == kNumPrograms) {
416     program_index_ = active_program_;  // Cancel
417   } else {
418     active_program_ = program_index_;
419     if (mode_ == UI_MODE_SAVE_SELECT_PROGRAM) {
420       storage_manager.SaveMulti(program_index_);
421     } else {
422       storage_manager.LoadMulti(program_index_);
423     }
424   }
425   mode_ = UI_MODE_PARAMETER_SELECT;
426 }
427 
OnClickCalibrationSelectVoice(const Event & e)428 void Ui::OnClickCalibrationSelectVoice(const Event& e) {
429   if (calibration_voice_ == kNumVoices) {
430     mode_ = UI_MODE_PARAMETER_SELECT;
431     calibration_voice_ = 0;
432     storage_manager.SaveCalibration();
433   } else {
434     mode_ = UI_MODE_CALIBRATION_SELECT_NOTE;
435   }
436   calibration_note_ = 0;
437 }
438 
OnClickCalibrationSelectNote(const Event & e)439 void Ui::OnClickCalibrationSelectNote(const Event& e) {
440   if (calibration_note_ == kNumOctaves) {
441     mode_ = UI_MODE_CALIBRATION_SELECT_VOICE;
442     calibration_note_ = 0;
443   } else {
444     mode_ = UI_MODE_CALIBRATION_ADJUST_LEVEL;
445   }
446 }
447 
OnClickSelectRecordingPart(const Event & e)448 void Ui::OnClickSelectRecordingPart(const Event& e) {
449   multi.StartRecording(recording_part_);
450   if (recording_part().overdubbing() || multi.running()) {
451     mode_ = UI_MODE_OVERDUBBING;
452   } else {
453     mode_ = UI_MODE_RECORDING;
454   }
455 }
456 
OnClickRecording(const Event & e)457 void Ui::OnClickRecording(const Event& e) {
458   if (push_it_) {
459     multi.PushItNoteOff(push_it_note_);
460     push_it_ = false;
461     mutable_recording_part()->RecordStep(SequencerStep(push_it_note_, 100));
462   } else {
463     multi.PushItNoteOn(push_it_note_);
464     push_it_ = true;
465   }
466 }
467 
OnClickOverdubbing(const Event & e)468 void Ui::OnClickOverdubbing(const Event& e) {
469   if (push_it_) {
470     push_it_ = false;
471     mutable_recording_part()->RecordStep(SequencerStep(push_it_note_, 100));
472   } else {
473     push_it_ = true;
474   }
475 }
476 
OnClickLearning(const Event & e)477 void Ui::OnClickLearning(const Event& e) {
478   multi.StopLearning();
479   mode_ = UI_MODE_PARAMETER_SELECT;
480 }
481 
OnClickFactoryTesting(const Event & e)482 void Ui::OnClickFactoryTesting(const Event& e) {
483   factory_testing_display_ = UI_FACTORY_TESTING_DISPLAY_CLICK;
484 }
485 
OnIncrementParameterSelect(const Event & e)486 void Ui::OnIncrementParameterSelect(const Event& e) {
487   setting_index_ += e.data;
488   if (setting_index_ < 0) {
489     setting_index_ = 0;
490   } else if (settings.menu()[setting_index_] == SETTING_LAST) {
491     --setting_index_;
492   }
493 }
494 
OnIncrementParameterEdit(const stmlib::Event & e)495 void Ui::OnIncrementParameterEdit(const stmlib::Event& e) {
496   settings.Increment(setting(), e.data);
497 }
498 
OnIncrementCalibrationAdjustment(const stmlib::Event & e)499 void Ui::OnIncrementCalibrationAdjustment(const stmlib::Event& e) {
500   Voice* voice = multi.mutable_voice(calibration_voice_);
501   int32_t code = voice->calibration_dac_code(calibration_note_);
502   code -= e.data * (switches_.pressed(2) ? 32 : 1);
503   CONSTRAIN(code, 0, 65535);
504   voice->set_calibration_dac_code(calibration_note_, code);
505 }
506 
OnIncrementRecording(const stmlib::Event & e)507 void Ui::OnIncrementRecording(const stmlib::Event& e) {
508   if (push_it_) {
509     OnIncrementPushItNote(e);
510   } else {
511     int8_t step = recording_part().recording_step();
512     step += e.data;
513     CONSTRAIN(step, 0, recording_part().num_steps());
514     mutable_recording_part()->set_recording_step(step);
515   }
516 }
517 
OnIncrementOverdubbing(const stmlib::Event & e)518 void Ui::OnIncrementOverdubbing(const stmlib::Event& e) {
519   if (push_it_) {
520     push_it_note_ += e.data;
521     CONSTRAIN(push_it_note_, 0, 127);
522     mutable_recording_part()->ModifyNoteAtCurrentStep(push_it_note_);
523   } else {
524     int8_t step = recording_part().recording_step();
525     step += e.data;
526     if (recording_part().overdubbing()) {
527       CONSTRAIN(step, 0, recording_part().num_steps() - 1);
528     } else {
529       CONSTRAIN(step, 0, kNumSteps - 1);
530     }
531     mutable_recording_part()->set_recording_step(step);
532   }
533 }
534 
OnIncrementPushItNote(const stmlib::Event & e)535 void Ui::OnIncrementPushItNote(const stmlib::Event& e) {
536   int16_t previous_note = push_it_note_;
537   push_it_note_ += e.data;
538   CONSTRAIN(push_it_note_, 0, 127);
539   if (push_it_note_ != previous_note) {
540     multi.PushItNoteOn(push_it_note_);
541     multi.PushItNoteOff(previous_note);
542   }
543 }
544 
OnIncrementFactoryTesting(const Event & e)545 void Ui::OnIncrementFactoryTesting(const Event& e) {
546   factory_testing_display_ = UI_FACTORY_TESTING_DISPLAY_NUMBER;
547   OnIncrement(e);
548 }
549 
OnSwitchPress(const Event & e)550 void Ui::OnSwitchPress(const Event& e) {
551   if (mode_ == UI_MODE_FACTORY_TESTING) {
552     factory_testing_display_ = static_cast<UiFactoryTestingDisplay>(
553         UI_FACTORY_TESTING_DISPLAY_SW_1 + e.control_id);
554     return;
555   }
556 
557   switch (e.control_id) {
558     case UI_SWITCH_REC:
559       {
560         if (mode_ == UI_MODE_RECORDING || mode_ == UI_MODE_OVERDUBBING) {
561           // Finish recording.
562           multi.StopRecording(recording_part_);
563           mode_ = previous_mode_;
564         } else if (mode_ == UI_MODE_SELECT_RECORDING_PART) {
565           // Cancel recording.
566           mode_ = previous_mode_;
567         } else {
568           previous_mode_ = mode_;
569           if (multi.num_active_parts() == 1) {
570             recording_part_ = 0;
571             multi.StartRecording(0);
572             mode_ = recording_part().overdubbing() ?
573                 UI_MODE_OVERDUBBING : UI_MODE_RECORDING;
574           } else {
575             // Go into channel selection mode.
576             mode_ = UI_MODE_SELECT_RECORDING_PART;
577             modes_[mode_].max_value = multi.num_active_parts() - 1;
578             if (recording_part_ >= modes_[mode_].max_value) {
579               recording_part_ = modes_[mode_].max_value;
580             }
581           }
582         }
583       }
584       break;
585 
586     case UI_SWITCH_START_STOP:
587       if (mode_ == UI_MODE_RECORDING || mode_ == UI_MODE_OVERDUBBING) {
588         if (push_it_ && mode_ == UI_MODE_RECORDING) {
589           multi.PushItNoteOff(push_it_note_);
590         }
591         push_it_ = false;
592         multi.mutable_part(recording_part_)->RecordStep(SEQUENCER_STEP_TIE);
593       } else {
594         if (push_it_) {
595           multi.PushItNoteOff(push_it_note_);
596           push_it_ = false;
597           if (mode_ == UI_MODE_PUSH_IT_SELECT_NOTE) {
598             mode_ = UI_MODE_PARAMETER_SELECT;
599           }
600         } else {
601           if (multi.latched()) {
602             multi.Unlatch();
603           } else {
604             if (!multi.running()) {
605               multi.Start(false);
606               if (multi.paques()) {
607                 multi.StartSong();
608               }
609             } else {
610               multi.Stop();
611             }
612           }
613         }
614       }
615       break;
616 
617     case UI_SWITCH_TAP_TEMPO:
618       if (mode_ == UI_MODE_RECORDING || mode_ == UI_MODE_OVERDUBBING) {
619         if (push_it_ && mode_ == UI_MODE_RECORDING) {
620           multi.PushItNoteOff(push_it_note_);
621         }
622         push_it_ = false;
623         multi.mutable_part(recording_part_)->RecordStep(SEQUENCER_STEP_REST);
624       } else {
625         TapTempo();
626       }
627       break;
628   }
629 }
630 
OnSwitchHeld(const Event & e)631 void Ui::OnSwitchHeld(const Event& e) {
632   switch (e.control_id) {
633     case UI_SWITCH_START_STOP:
634       if (!push_it_ && !multi.latched()) {
635         if (multi.running()) {
636           multi.Latch();
637         } else {
638           mode_ = UI_MODE_PUSH_IT_SELECT_NOTE;
639           push_it_ = true;
640           multi.PushItNoteOn(push_it_note_);
641         }
642       }
643       break;
644 
645     default:
646       break;
647   }
648 }
649 
DoInitCommand()650 void Ui::DoInitCommand() {
651   multi.Init(false);
652 }
653 
DoDumpCommand()654 void Ui::DoDumpCommand() {
655   storage_manager.SysExSendMulti();
656 }
657 
DoLearnCommand()658 void Ui::DoLearnCommand() {
659   multi.StartLearning();
660 }
661 
TapTempo()662 void Ui::TapTempo() {
663   uint32_t tap_time = system_clock.milliseconds();
664   uint32_t delta = tap_time - previous_tap_time_;
665   if (delta < 1500) {
666     if (delta < 250) {
667       delta = 250;
668     }
669     ++tap_tempo_count_;
670     tap_tempo_sum_ += delta;
671     multi.Set(MULTI_CLOCK_TEMPO, tap_tempo_count_ * 60000 / tap_tempo_sum_);
672   } else {
673     tap_tempo_count_ = 0;
674     tap_tempo_sum_ = 0;
675   }
676   previous_tap_time_ = tap_time;
677 }
678 
DoEvents()679 void Ui::DoEvents() {
680   bool refresh_display = false;
681   bool scroll_display = false;
682 
683   while (queue_.available()) {
684     Event e = queue_.PullEvent();
685     const Mode& mode = modes_[mode_];
686     if (e.control_type == CONTROL_ENCODER_CLICK) {
687       (this->*mode.on_click)(e);
688     } else if (e.control_type == CONTROL_ENCODER) {
689       (this->*mode.on_increment)(e);
690     } else if (e.control_type == CONTROL_ENCODER_LONG_CLICK) {
691       OnLongClick(e);
692     } else if (e.control_type == CONTROL_SWITCH) {
693       OnSwitchPress(e);
694     } else if (e.control_type == CONTROL_SWITCH_HOLD) {
695       OnSwitchHeld(e);
696     }
697     refresh_display = true;
698     scroll_display = true;
699   }
700   if (queue_.idle_time() > 600) {
701     if (!display_.scrolling()) {
702       factory_testing_display_ = UI_FACTORY_TESTING_DISPLAY_EMPTY;
703       refresh_display = true;
704     }
705   }
706   if (queue_.idle_time() > 400 && multi.latched()) {
707     display_.Print("//");
708   }
709   if (queue_.idle_time() > 50 &&
710       (mode_ == UI_MODE_RECORDING || mode_ == UI_MODE_OVERDUBBING)) {
711     refresh_display = true;
712   }
713 
714   if (mode_ == UI_MODE_LEARNING && !multi.learning()) {
715     OnClickLearning(Event());
716   }
717 
718   if (refresh_display) {
719     queue_.Touch();
720     (this->*modes_[mode_].refresh_display)();
721     if (scroll_display) {
722       display_.Scroll();
723     }
724     display_.set_blink(
725         mode_ == UI_MODE_CALIBRATION_ADJUST_LEVEL ||
726         mode_ == UI_MODE_SELECT_RECORDING_PART ||
727         mode_ == UI_MODE_LEARNING);
728     if (mode_ == UI_MODE_MAIN_MENU) {
729       display_.set_fade(160);
730     } else if (mode_ == UI_MODE_PARAMETER_EDIT &&
731                setting().unit == SETTING_UNIT_TEMPO) {
732       display_.set_fade(multi.tempo() * 235 >> 8);
733     } else {
734       display_.set_fade(0);
735     }
736 
737     if (mode_ == UI_MODE_SPLASH) {
738       mode_ = UI_MODE_PARAMETER_SELECT;
739     }
740   }
741 }
742 
743 }  // namespace yarns
744