No Description
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.

ui.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /*
  2. * ui.c
  3. *
  4. * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * See <http://www.gnu.org/licenses/>.
  17. */
  18. #include <stdio.h>
  19. #include <inttypes.h>
  20. #include <math.h>
  21. #include "pico/stdlib.h"
  22. #include "config.h"
  23. #include "adc.h"
  24. #include "buttons.h"
  25. #include "lcd.h"
  26. #include "led.h"
  27. #include "log.h"
  28. #include "mem.h"
  29. #include "pulse.h"
  30. #include "sequence.h"
  31. #include "usb.h"
  32. #include "usb_midi.h"
  33. #include "ui.h"
  34. enum ui_settings {
  35. SETTING_MODE = 0,
  36. // loop station
  37. SETTING_SPEED,
  38. // drum machine
  39. SETTING_BPM,
  40. SETTING_LENGTH,
  41. SETTING_BANK,
  42. SETTING_CHANNEL,
  43. // midi
  44. SETTING_CH_RX,
  45. SETTING_CH_TX,
  46. SETTING_NUM_MODES
  47. };
  48. static bool allowed_settings[MACHINE_NUM_MODES][SETTING_NUM_MODES] = {
  49. // MODE_LOOPSTATION
  50. {
  51. true, // SETTING_MODE
  52. true,
  53. false, false, false, false,
  54. false, false,
  55. },
  56. // MODE_DRUMMACHINE
  57. {
  58. true, // SETTING_MODE
  59. false,
  60. true, true, true, true,
  61. false, false,
  62. },
  63. // MODE_MIDI
  64. {
  65. true, // SETTING_MODE
  66. false,
  67. false, false, false, false,
  68. true, true,
  69. },
  70. };
  71. static bool rec_held_down = false;
  72. static enum ui_settings ui_setting = 0;
  73. static enum machine_modes machine_mode = 0;
  74. static uint32_t last_bat_fetch = 0;
  75. static float last_voltage = 0.0f;
  76. static float last_percentage = 0.0f;
  77. static uint8_t midi_rx = 0, midi_tx = 0;
  78. enum machine_modes ui_get_machinemode(void) {
  79. return machine_mode;
  80. }
  81. static void ui_redraw(void) {
  82. char mode[64] = {0};
  83. char val[64] = {0};
  84. char bat[64] = {0};
  85. switch (ui_setting) {
  86. case SETTING_MODE: {
  87. if ((machine_mode == MODE_LOOPSTATION) && (sequence_get_us() != 0)) {
  88. snprintf(mode, sizeof(mode) - 1, "Mode: %"PRIu64"ms", sequence_get_us() / 1000);
  89. } else {
  90. snprintf(mode, sizeof(mode) - 1, "Mode:");
  91. }
  92. switch (machine_mode) {
  93. case MODE_LOOPSTATION: {
  94. snprintf(val, sizeof(val) - 1, "Loop");
  95. break;
  96. }
  97. case MODE_DRUMMACHINE: {
  98. snprintf(val, sizeof(val) - 1, "Drum");
  99. break;
  100. }
  101. case MODE_MIDI: {
  102. snprintf(val, sizeof(val) - 1, "MIDI");
  103. break;
  104. }
  105. default: {
  106. printf("%s: invalid machine mode: %d\n", __func__, machine_mode);
  107. machine_mode = 0;
  108. ui_redraw();
  109. return;
  110. }
  111. }
  112. break;
  113. }
  114. case SETTING_SPEED:
  115. case SETTING_BPM: {
  116. if ((machine_mode == MODE_LOOPSTATION) && (sequence_get_us() != 0)) {
  117. snprintf(mode, sizeof(mode) - 1, "BPM: %"PRIu64"ms", sequence_get_us() / 1000);
  118. } else {
  119. snprintf(mode, sizeof(mode) - 1, "BPM:");
  120. }
  121. snprintf(val, sizeof(val) - 1, "%"PRIu32, sequence_get_bpm());
  122. break;
  123. }
  124. case SETTING_LENGTH: {
  125. snprintf(mode, sizeof(mode) - 1, "Length:");
  126. snprintf(val, sizeof(val) - 1, "%"PRIu32, sequence_get_beats());
  127. break;
  128. }
  129. case SETTING_BANK: {
  130. snprintf(mode, sizeof(mode) - 1, "Bank:");
  131. snprintf(val, sizeof(val) - 1, "%"PRIu32, sequence_get_bank() + 1);
  132. break;
  133. }
  134. case SETTING_CH_RX: {
  135. snprintf(mode, sizeof(mode) - 1, "Rx-Ch:");
  136. snprintf(val, sizeof(val) - 1, "%"PRIu8, midi_rx + 1);
  137. break;
  138. }
  139. case SETTING_CH_TX: {
  140. snprintf(mode, sizeof(mode) - 1, "Tx-Ch:");
  141. snprintf(val, sizeof(val) - 1, "%"PRIu8, midi_tx + 1);
  142. break;
  143. }
  144. case SETTING_CHANNEL: {
  145. snprintf(mode, sizeof(mode) - 1, "Channel:");
  146. snprintf(val, sizeof(val) - 1, "todo");
  147. break;
  148. }
  149. default: {
  150. debug("invalid setting: %d", ui_setting);
  151. ui_setting = 0;
  152. ui_redraw();
  153. return;
  154. }
  155. }
  156. snprintf(bat, sizeof(bat) - 1, "Bat: %.1f%% (%.2fV)", last_percentage, last_voltage);
  157. lcd_draw(mode, val, bat);
  158. }
  159. static void ui_buttons_loopstation(enum buttons btn, bool val) {
  160. switch (btn) {
  161. case BTN_A:
  162. case BTN_B:
  163. case BTN_C:
  164. case BTN_E:
  165. case BTN_F:
  166. case BTN_G: {
  167. if (val) {
  168. sequence_handle_button_loopstation(btn, rec_held_down);
  169. }
  170. break;
  171. }
  172. case BTN_REC: {
  173. // reset sequence
  174. sequence_init();
  175. ui_redraw();
  176. break;
  177. }
  178. case BTN_D:
  179. case BTN_H: {
  180. rec_held_down = val;
  181. sequence_looptime(!val);
  182. if (!val) {
  183. ui_redraw();
  184. }
  185. break;
  186. }
  187. default: {
  188. debug("invalid btn: %d", btn);
  189. break;
  190. }
  191. }
  192. }
  193. static void ui_buttons_drummachine(enum buttons btn, bool val) {
  194. switch (btn) {
  195. case BTN_A:
  196. case BTN_B:
  197. case BTN_C:
  198. case BTN_D:
  199. case BTN_E:
  200. case BTN_F:
  201. case BTN_G:
  202. case BTN_H:
  203. case BTN_REC: {
  204. if (val) {
  205. sequence_handle_button_drummachine(btn);
  206. }
  207. break;
  208. }
  209. default: {
  210. debug("invalid btn: %d", btn);
  211. break;
  212. }
  213. }
  214. }
  215. static void ui_buttons_midi(enum buttons btn, bool val) {
  216. switch (btn) {
  217. case BTN_A:
  218. case BTN_B:
  219. case BTN_C:
  220. case BTN_D:
  221. case BTN_E:
  222. case BTN_F:
  223. case BTN_G:
  224. case BTN_H: {
  225. if (val) {
  226. usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
  227. pulse_trigger_led(btn - BTN_A, mem_data()->ch_timings[0]);
  228. }
  229. }
  230. default:
  231. break;
  232. }
  233. }
  234. static void ui_buttons(enum buttons btn, bool val) {
  235. switch (btn) {
  236. case BTN_CLICK: {
  237. if (val) {
  238. // only allow settings for this mode
  239. do {
  240. ui_setting = (ui_setting + 1) % SETTING_NUM_MODES;
  241. } while (!allowed_settings[machine_mode][ui_setting]);
  242. ui_redraw();
  243. }
  244. break;
  245. }
  246. default: {
  247. switch (machine_mode) {
  248. case MODE_LOOPSTATION: {
  249. ui_buttons_loopstation(btn, val);
  250. break;
  251. }
  252. case MODE_DRUMMACHINE: {
  253. ui_buttons_drummachine(btn, val);
  254. break;
  255. }
  256. case MODE_MIDI: {
  257. ui_buttons_midi(btn, val);
  258. break;
  259. }
  260. default: {
  261. debug("invalid mode: %d", machine_mode);
  262. machine_mode = 0;
  263. ui_buttons(btn, val);
  264. break;
  265. }
  266. }
  267. break;
  268. }
  269. }
  270. }
  271. void ui_encoder(int32_t val) {
  272. if (val == 0) {
  273. return;
  274. }
  275. switch (ui_setting) {
  276. case SETTING_MODE: {
  277. int32_t tmp = machine_mode + val;
  278. KEEP_IN_RANGE(tmp, 0, MACHINE_NUM_MODES);
  279. // midi only when connected to pc
  280. if ((tmp == MODE_MIDI) && !usb_is_connected()) {
  281. tmp = (tmp + 1) % MACHINE_NUM_MODES;
  282. }
  283. enum machine_modes prev_mode = machine_mode;
  284. machine_mode = tmp;
  285. if (prev_mode != machine_mode) {
  286. // reset sequence
  287. sequence_init();
  288. // turn off all LEDs
  289. for (uint i = 0; i < LED_COUNT; i++) {
  290. led_set(i, false);
  291. }
  292. if (machine_mode == MODE_LOOPSTATION) {
  293. sequence_set_beats(MAX_BEATS);
  294. // enable static LEDs in loopstation mode
  295. led_set(NUM_CHANNELS, true);
  296. led_set(NUM_CHANNELS + 4, true);
  297. } else if (machine_mode == MODE_DRUMMACHINE) {
  298. sequence_set_beats(LED_COUNT);
  299. sequence_set_bpm(120);
  300. }
  301. }
  302. break;
  303. }
  304. case SETTING_SPEED:
  305. case SETTING_BPM: {
  306. int32_t tmp = sequence_get_bpm() + val;
  307. KEEP_IN_RANGE(tmp, 1, MAX_BPM);
  308. sequence_set_bpm(tmp);
  309. break;
  310. }
  311. case SETTING_LENGTH: {
  312. int32_t tmp = sequence_get_beats() + val;
  313. KEEP_IN_RANGE(tmp, 1, MAX_BEATS);
  314. sequence_set_beats(tmp);
  315. break;
  316. }
  317. case SETTING_BANK: {
  318. int32_t tmp = sequence_get_bank() + val;
  319. KEEP_IN_RANGE(tmp, 0, (int32_t)sequence_get_max_banks());
  320. sequence_set_bank(tmp);
  321. break;
  322. }
  323. case SETTING_CH_RX: {
  324. int32_t tmp = midi_rx + val;
  325. KEEP_IN_RANGE(tmp, 0, MIDI_MAX_CH);
  326. midi_rx = tmp;
  327. break;
  328. }
  329. case SETTING_CH_TX: {
  330. int32_t tmp = midi_tx + val;
  331. KEEP_IN_RANGE(tmp, 0, MIDI_MAX_CH);
  332. midi_tx = tmp;
  333. break;
  334. }
  335. case SETTING_CHANNEL: {
  336. // TODO
  337. break;
  338. }
  339. default: {
  340. debug("invalid setting: %d", ui_setting);
  341. ui_setting = 0;
  342. ui_encoder(val);
  343. return;
  344. }
  345. }
  346. ui_redraw();
  347. }
  348. void ui_midi_set(uint8_t channel, uint8_t note, uint8_t velocity) {
  349. if (machine_mode != MODE_MIDI) {
  350. return;
  351. }
  352. if (channel != midi_rx) {
  353. return;
  354. }
  355. note = note % NUM_CHANNELS;
  356. if (velocity > 0) {
  357. pulse_trigger_out(note, mem_data()->ch_timings[note]);
  358. }
  359. }
  360. void ui_init(void) {
  361. buttons_callback(ui_buttons);
  362. // enable static LEDs in loopstation mode
  363. machine_mode = MODE_LOOPSTATION;
  364. led_set(NUM_CHANNELS, true);
  365. led_set(NUM_CHANNELS + 4, true);
  366. ui_redraw();
  367. }
  368. void ui_run(void) {
  369. uint32_t now = to_ms_since_boot(get_absolute_time());
  370. if (now >= (last_bat_fetch + BAT_FETCH_MS)) {
  371. last_bat_fetch = now;
  372. float volt = bat_get();
  373. float percentage = bat_to_percent(volt);
  374. if ((fabsf(volt - last_voltage) >= 0.01f)
  375. || (fabsf(percentage - last_percentage) >= 0.1f)) {
  376. last_voltage = volt;
  377. last_percentage = percentage;
  378. ui_redraw();
  379. }
  380. }
  381. }