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