You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

744 lines
21KB

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