diff options
author | Bad Diode <bd@badd10de.dev> | 2023-06-28 10:31:18 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2023-06-28 10:31:18 +0200 |
commit | 9bb91b5496b5a1bc730bdc70b3352a789c35d995 (patch) | |
tree | 0c0082fe97980756c667fe25fab9d0a8afb35365 /src/app.c | |
parent | 10513f80a94bde633922862db9ca71337889f0e5 (diff) | |
download | launchpad-polymaker-9bb91b5496b5a1bc730bdc70b3352a789c35d995.tar.gz launchpad-polymaker-9bb91b5496b5a1bc730bdc70b3352a789c35d995.zip |
Initial kbd implementation
Diffstat (limited to 'src/app.c')
-rw-r--r-- | src/app.c | 219 |
1 files changed, 209 insertions, 10 deletions
@@ -117,6 +117,29 @@ u16 font_numbers[10] = { | |||
117 | }; | 117 | }; |
118 | 118 | ||
119 | // Enums and structs. | 119 | // Enums and structs. |
120 | typedef enum Pads { | ||
121 | POLY_PAD_ROW_0 = 11 + 10 * 7 + 0, | ||
122 | POLY_PAD_ROW_1 = 11 + 10 * 6 + 0, | ||
123 | POLY_PAD_ROW_2 = 11 + 10 * 5 + 0, | ||
124 | POLY_PAD_ROW_3 = 11 + 10 * 4 + 0, | ||
125 | POLY_PAD_ROW_4 = 11 + 10 * 3 + 0, | ||
126 | POLY_PAD_ROW_5 = 11 + 10 * 2 + 0, | ||
127 | POLY_PAD_ROW_6 = 11 + 10 * 1 + 0, | ||
128 | POLY_PAD_ROW_7 = 11 + 10 * 0 + 0, | ||
129 | POLY_TOP_ROW = 11 + 10 * 8 + 0, | ||
130 | } Pads; | ||
131 | |||
132 | Pads row_idx[] = { | ||
133 | POLY_PAD_ROW_0, | ||
134 | POLY_PAD_ROW_1, | ||
135 | POLY_PAD_ROW_2, | ||
136 | POLY_PAD_ROW_3, | ||
137 | POLY_PAD_ROW_4, | ||
138 | POLY_PAD_ROW_5, | ||
139 | POLY_PAD_ROW_6, | ||
140 | POLY_PAD_ROW_7, | ||
141 | }; | ||
142 | |||
120 | typedef enum Button { | 143 | typedef enum Button { |
121 | POLY_PAD_ACTIVE = 11, | 144 | POLY_PAD_ACTIVE = 11, |
122 | POLY_PAD_STEAL = 11 + 7, | 145 | POLY_PAD_STEAL = 11 + 7, |
@@ -173,6 +196,7 @@ typedef enum Button { | |||
173 | typedef enum Mode { | 196 | typedef enum Mode { |
174 | MOD_POLY_MAIN = 0, | 197 | MOD_POLY_MAIN = 0, |
175 | MOD_POLY_SETUP = 1, | 198 | MOD_POLY_SETUP = 1, |
199 | MOD_POLY_KBD = 2, | ||
176 | } Mode; | 200 | } Mode; |
177 | 201 | ||
178 | typedef enum Stealing { | 202 | typedef enum Stealing { |
@@ -182,6 +206,19 @@ typedef enum Stealing { | |||
182 | POLY_STEAL_LOW = 3, | 206 | POLY_STEAL_LOW = 3, |
183 | } Stealing; | 207 | } Stealing; |
184 | 208 | ||
209 | typedef struct Color { | ||
210 | int r; | ||
211 | int g; | ||
212 | int b; | ||
213 | } Color; | ||
214 | |||
215 | Color colors[] = { | ||
216 | { MAXLED, MAXLED, MAXLED }, // White | ||
217 | { MAXLED, 0, 0 }, // Red | ||
218 | { 0, MAXLED, 0 }, // Green | ||
219 | { 0, 0, MAXLED }, // Blue | ||
220 | }; | ||
221 | |||
185 | typedef struct Voice { | 222 | typedef struct Voice { |
186 | u8 active; | 223 | u8 active; |
187 | u8 channel; | 224 | u8 channel; |
@@ -192,6 +229,8 @@ typedef struct Voice { | |||
192 | } Voice; | 229 | } Voice; |
193 | 230 | ||
194 | typedef struct State { | 231 | typedef struct State { |
232 | // Magic. | ||
233 | u32 magic; | ||
195 | // Polyphony. | 234 | // Polyphony. |
196 | u8 active; | 235 | u8 active; |
197 | u8 stealing; | 236 | u8 stealing; |
@@ -199,6 +238,8 @@ typedef struct State { | |||
199 | u8 ch_max; | 238 | u8 ch_max; |
200 | u8 ch_listen[16]; | 239 | u8 ch_listen[16]; |
201 | Mode mode; | 240 | Mode mode; |
241 | // Keyboard. | ||
242 | u8 kbd_octave; | ||
202 | } State; | 243 | } State; |
203 | 244 | ||
204 | // Globals. | 245 | // Globals. |
@@ -265,6 +306,87 @@ select_ch_sel(u8 target) { | |||
265 | state.ch_listen[target] = !state.ch_listen[target]; | 306 | state.ch_listen[target] = !state.ch_listen[target]; |
266 | } | 307 | } |
267 | 308 | ||
309 | int | ||
310 | kbd_find_note(int index) { | ||
311 | int note = -1; | ||
312 | int black = 0; | ||
313 | int row = 0; | ||
314 | |||
315 | // A | ||
316 | if (index >= POLY_PAD_ROW_0 && index <= (POLY_PAD_ROW_0 + 7)) { | ||
317 | row = POLY_PAD_ROW_0; | ||
318 | black = 1; | ||
319 | note = 12 * (state.kbd_octave + 5); | ||
320 | } | ||
321 | if (index >= POLY_PAD_ROW_1 && index <= (POLY_PAD_ROW_1 + 7)) { | ||
322 | row = POLY_PAD_ROW_1; | ||
323 | note = 12 * (state.kbd_octave + 5); | ||
324 | } | ||
325 | |||
326 | // B | ||
327 | if (index >= POLY_PAD_ROW_2 && index <= (POLY_PAD_ROW_2 + 7)) { | ||
328 | row = POLY_PAD_ROW_2; | ||
329 | black = 1; | ||
330 | note = 12 * (state.kbd_octave + 4); | ||
331 | } | ||
332 | if (index >= POLY_PAD_ROW_3 && index <= (POLY_PAD_ROW_3 + 7)) { | ||
333 | row = POLY_PAD_ROW_3; | ||
334 | note = 12 * (state.kbd_octave + 4); | ||
335 | } | ||
336 | |||
337 | // C | ||
338 | if (index >= POLY_PAD_ROW_4 && index <= (POLY_PAD_ROW_4 + 7)) { | ||
339 | row = POLY_PAD_ROW_4; | ||
340 | black = 1; | ||
341 | note = 12 * (state.kbd_octave + 3); | ||
342 | } | ||
343 | if (index >= POLY_PAD_ROW_5 && index <= (POLY_PAD_ROW_5 + 7)) { | ||
344 | row = POLY_PAD_ROW_5; | ||
345 | note = 12 * (state.kbd_octave + 3); | ||
346 | } | ||
347 | |||
348 | // D | ||
349 | if (index >= POLY_PAD_ROW_6 && index <= (POLY_PAD_ROW_6 + 7)) { | ||
350 | row = POLY_PAD_ROW_6; | ||
351 | black = 1; | ||
352 | note = 12 * (state.kbd_octave + 2); | ||
353 | } | ||
354 | if (index >= POLY_PAD_ROW_7 && index <= (POLY_PAD_ROW_7 + 7)) { | ||
355 | row = POLY_PAD_ROW_7; | ||
356 | note = 12 * (state.kbd_octave + 2); | ||
357 | } | ||
358 | |||
359 | // Invalid pad index. | ||
360 | if (note < 0) { | ||
361 | return -1; | ||
362 | } | ||
363 | |||
364 | // Find note offset. | ||
365 | if (black) { | ||
366 | switch (index - row) { | ||
367 | case 1: { return note + 1; } break; | ||
368 | case 2: { return note + 3; } break; | ||
369 | case 4: { return note + 6; } break; | ||
370 | case 5: { return note + 8; } break; | ||
371 | case 6: { return note + 10; } break; | ||
372 | default: { return -1; } break; | ||
373 | } | ||
374 | } else { | ||
375 | switch (index - row) { | ||
376 | case 0: { return note + 0; } break; | ||
377 | case 1: { return note + 2; } break; | ||
378 | case 2: { return note + 4; } break; | ||
379 | case 3: { return note + 5; } break; | ||
380 | case 4: { return note + 7; } break; | ||
381 | case 5: { return note + 9; } break; | ||
382 | case 6: { return note + 11; } break; | ||
383 | case 7: { return note + 12; } break; | ||
384 | default: { return -1; } break; | ||
385 | } | ||
386 | } | ||
387 | return -1; | ||
388 | } | ||
389 | |||
268 | void | 390 | void |
269 | app_surface_event(u8 type, u8 index, u8 value) { | 391 | app_surface_event(u8 type, u8 index, u8 value) { |
270 | switch (type) { | 392 | switch (type) { |
@@ -340,18 +462,57 @@ app_surface_event(u8 type, u8 index, u8 value) { | |||
340 | } | 462 | } |
341 | } | 463 | } |
342 | } break; | 464 | } break; |
465 | case MOD_POLY_KBD: { | ||
466 | // Transposition. | ||
467 | if (index >= POLY_TOP_ROW && index <= (POLY_TOP_ROW + 7)) { | ||
468 | switch (index - POLY_TOP_ROW) { | ||
469 | case 0: { | ||
470 | if (state.kbd_octave < 3) { | ||
471 | state.kbd_octave++; | ||
472 | } | ||
473 | } break; | ||
474 | case 1: { | ||
475 | if (state.kbd_octave > 0) { | ||
476 | state.kbd_octave--; | ||
477 | } | ||
478 | } break; | ||
479 | default: {} break; | ||
480 | } | ||
481 | return; | ||
482 | } | ||
483 | u8 channel = 0; | ||
484 | // TODO: Velocity on/off configuration. | ||
485 | // TODO: Velocity curve adjustment. | ||
486 | // TODO: Aftertouch? | ||
487 | // TODO: Different colors for different octaves. | ||
488 | // TODO: Per KBD channel selection. | ||
489 | int note = kbd_find_note(index); | ||
490 | if (note == -1) { | ||
491 | return; | ||
492 | } | ||
493 | |||
494 | if (value) { | ||
495 | hal_send_midi(USBSTANDALONE, NOTEON | channel, note, value); | ||
496 | hal_send_midi(USBMIDI, NOTEON | channel, note, value); | ||
497 | hal_send_midi(DINMIDI, NOTEON | channel, note, value); | ||
498 | } else { | ||
499 | hal_send_midi(USBSTANDALONE, NOTEOFF | channel, note, value); | ||
500 | hal_send_midi(USBMIDI, NOTEOFF | channel, note, value); | ||
501 | hal_send_midi(DINMIDI, NOTEOFF | channel, note, value); | ||
502 | } | ||
503 | } break; | ||
343 | } | 504 | } |
344 | } break; | 505 | } break; |
345 | case TYPESETUP: { | 506 | case TYPESETUP: { |
346 | if (value) { | 507 | if (value) { |
347 | switch (state.mode) { | 508 | switch (state.mode) { |
509 | case MOD_POLY_KBD: | ||
348 | case MOD_POLY_MAIN: { | 510 | case MOD_POLY_MAIN: { |
349 | state.mode = MOD_POLY_SETUP; | 511 | state.mode = MOD_POLY_SETUP; |
350 | } break; | 512 | } break; |
351 | case MOD_POLY_SETUP: { | 513 | case MOD_POLY_SETUP: { |
352 | state.mode = MOD_POLY_MAIN; | 514 | state.mode = MOD_POLY_KBD; |
353 | hal_write_flash(0, (u8*)&state, sizeof(State)); | 515 | hal_write_flash(0, (u8*)&state, sizeof(State)); |
354 | |||
355 | } break; | 516 | } break; |
356 | } | 517 | } |
357 | } | 518 | } |
@@ -575,6 +736,14 @@ draw_poly_main(void) { | |||
575 | 736 | ||
576 | void | 737 | void |
577 | draw_min_channel(void) { | 738 | draw_min_channel(void) { |
739 | // for (u8 i = 0; i < 8; i++) { | ||
740 | // if (state.ch_max < 8 && i > state.ch_max) { | ||
741 | // clr = colors[1]; | ||
742 | // } else if (i != state.ch_min){ | ||
743 | // clr = colors[2]; | ||
744 | // } | ||
745 | // hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, clr.r, clr.g, clr.b); | ||
746 | // } | ||
578 | for (u8 i = 0; i < 8; i++) { | 747 | for (u8 i = 0; i < 8; i++) { |
579 | if (i == state.ch_min) { | 748 | if (i == state.ch_min) { |
580 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, MAXLED, MAXLED, MAXLED); | 749 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, MAXLED, MAXLED, MAXLED); |
@@ -653,6 +822,25 @@ draw_poly_setup(void) { | |||
653 | } | 822 | } |
654 | 823 | ||
655 | void | 824 | void |
825 | draw_kbd(int loc, Color clr) { | ||
826 | if (loc < 0 || loc >= 7) { | ||
827 | return; | ||
828 | } | ||
829 | Color clr_top = clr; | ||
830 | Color clr_bot = (Color){clr_top.r / 4, clr_top.g / 4, clr_top.b / 4 }; | ||
831 | int idx_top = row_idx[loc]; | ||
832 | int idx_bot = row_idx[loc + 1]; | ||
833 | hal_plot_led(TYPEPAD, idx_top + 1, clr_bot.r, clr_bot.g, clr_bot.b); | ||
834 | hal_plot_led(TYPEPAD, idx_top + 2, clr_bot.r, clr_bot.g, clr_bot.b); | ||
835 | hal_plot_led(TYPEPAD, idx_top + 4, clr_bot.r, clr_bot.g, clr_bot.b); | ||
836 | hal_plot_led(TYPEPAD, idx_top + 5, clr_bot.r, clr_bot.g, clr_bot.b); | ||
837 | hal_plot_led(TYPEPAD, idx_top + 6, clr_bot.r, clr_bot.g, clr_bot.b); | ||
838 | for (u8 i = 0; i < 8; i++) { | ||
839 | hal_plot_led(TYPEPAD, idx_bot + i, clr_top.r, clr_top.g, clr_top.b); | ||
840 | } | ||
841 | } | ||
842 | |||
843 | void | ||
656 | draw_scene(void) { | 844 | draw_scene(void) { |
657 | switch (state.mode) { | 845 | switch (state.mode) { |
658 | case MOD_POLY_MAIN: { | 846 | case MOD_POLY_MAIN: { |
@@ -661,6 +849,12 @@ draw_scene(void) { | |||
661 | case MOD_POLY_SETUP: { | 849 | case MOD_POLY_SETUP: { |
662 | draw_poly_setup(); | 850 | draw_poly_setup(); |
663 | } break; | 851 | } break; |
852 | case MOD_POLY_KBD: { | ||
853 | draw_kbd(0, colors[0]); | ||
854 | draw_kbd(2, colors[1]); | ||
855 | draw_kbd(4, colors[2]); | ||
856 | draw_kbd(6, colors[3]); | ||
857 | } break; | ||
664 | } | 858 | } |
665 | } | 859 | } |
666 | 860 | ||
@@ -682,15 +876,20 @@ app_timer_event() { | |||
682 | void | 876 | void |
683 | app_init(const u16 *adc_raw) { | 877 | app_init(const u16 *adc_raw) { |
684 | // example - load button states from flash | 878 | // example - load button states from flash |
685 | state = (State){ | ||
686 | .active = 1, | ||
687 | .stealing = POLY_STEAL_OFF, | ||
688 | .ch_min = 0, | ||
689 | .ch_max = 7, | ||
690 | .ch_listen = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, | ||
691 | .mode = MOD_POLY_MAIN, | ||
692 | }; | ||
693 | hal_read_flash(0, (u8*)&state, sizeof(State)); | 879 | hal_read_flash(0, (u8*)&state, sizeof(State)); |
880 | if (state.magic != 0xbadd10de) { | ||
881 | state = (State){ | ||
882 | .magic = 0xbadd10de, | ||
883 | .active = 1, | ||
884 | .stealing = POLY_STEAL_OFF, | ||
885 | .ch_min = 0, | ||
886 | .ch_max = 7, | ||
887 | .ch_listen = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, | ||
888 | .mode = MOD_POLY_MAIN, | ||
889 | .kbd_octave = 0, | ||
890 | }; | ||
891 | } | ||
892 | state.mode = MOD_POLY_KBD; // DEBUG: | ||
694 | 893 | ||
695 | // store off the raw ADC frame pointer for later use | 894 | // store off the raw ADC frame pointer for later use |
696 | g_ADC = adc_raw; | 895 | g_ADC = adc_raw; |