diff options
author | Bad Diode <bd@badd10de.dev> | 2023-02-09 15:11:22 +0100 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2023-02-09 15:11:22 +0100 |
commit | 6e331bd4b16b17cb9a5d0b38aab0099b9cd23d06 (patch) | |
tree | a3d482a86ea9aacf5707c705f3a2a8b00256957f /src | |
parent | 79b116d19fa4de2f7b5450acfe77a54524505b7d (diff) | |
download | launchpad-polymaker-6e331bd4b16b17cb9a5d0b38aab0099b9cd23d06.tar.gz launchpad-polymaker-6e331bd4b16b17cb9a5d0b38aab0099b9cd23d06.zip |
Add midi range selection
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 311 |
1 files changed, 252 insertions, 59 deletions
@@ -32,29 +32,7 @@ | |||
32 | 32 | ||
33 | #include "app.h" | 33 | #include "app.h" |
34 | 34 | ||
35 | // Housekeeping and debugging. | 35 | // Font. |
36 | u16 frame = 0; | ||
37 | u16 n_voices = 0; | ||
38 | u8 active = 1; | ||
39 | u8 ch_min = 4; | ||
40 | u8 ch_max = 8; | ||
41 | |||
42 | typedef struct Voice { | ||
43 | u8 active; | ||
44 | u8 channel; | ||
45 | u8 d1; | ||
46 | u8 d2; | ||
47 | u8 port; | ||
48 | } Voice; | ||
49 | |||
50 | Voice voices[16] = {0}; | ||
51 | |||
52 | // store ADC frame pointer | ||
53 | static const u16 *g_ADC = 0; | ||
54 | |||
55 | // buffer to store pad states for flash save | ||
56 | #define BUTTON_COUNT 100 | ||
57 | u8 buttons[BUTTON_COUNT] = {0}; | ||
58 | u16 font_numbers[10] = { | 36 | u16 font_numbers[10] = { |
59 | /* | 37 | /* |
60 | * 0: 111 | 1111 0110 1101 1110 | 38 | * 0: 111 | 1111 0110 1101 1110 |
@@ -138,42 +116,168 @@ u16 font_numbers[10] = { | |||
138 | 0xF792, | 116 | 0xF792, |
139 | }; | 117 | }; |
140 | 118 | ||
141 | enum Buttons { | 119 | // Enums and structs. |
120 | typedef enum Button { | ||
142 | POLY_PAD_ACTIVE = 11, | 121 | POLY_PAD_ACTIVE = 11, |
143 | }; | 122 | POLY_PAD_MIN_CH_0 = 11 + 10 * 7 + 0, |
123 | POLY_PAD_MIN_CH_1 = 11 + 10 * 7 + 1, | ||
124 | POLY_PAD_MIN_CH_2 = 11 + 10 * 7 + 2, | ||
125 | POLY_PAD_MIN_CH_3 = 11 + 10 * 7 + 3, | ||
126 | POLY_PAD_MIN_CH_4 = 11 + 10 * 7 + 4, | ||
127 | POLY_PAD_MIN_CH_5 = 11 + 10 * 7 + 5, | ||
128 | POLY_PAD_MIN_CH_6 = 11 + 10 * 7 + 6, | ||
129 | POLY_PAD_MIN_CH_7 = 11 + 10 * 7 + 7, | ||
130 | POLY_PAD_MIN_CH_8 = 11 + 10 * 6 + 0, | ||
131 | POLY_PAD_MIN_CH_9 = 11 + 10 * 6 + 1, | ||
132 | POLY_PAD_MIN_CH_A = 11 + 10 * 6 + 2, | ||
133 | POLY_PAD_MIN_CH_B = 11 + 10 * 6 + 3, | ||
134 | POLY_PAD_MIN_CH_C = 11 + 10 * 6 + 4, | ||
135 | POLY_PAD_MIN_CH_D = 11 + 10 * 6 + 5, | ||
136 | POLY_PAD_MIN_CH_E = 11 + 10 * 6 + 6, | ||
137 | POLY_PAD_MIN_CH_F = 11 + 10 * 6 + 7, | ||
138 | POLY_PAD_MAX_CH_0 = 11 + 10 * 5 + 0, | ||
139 | POLY_PAD_MAX_CH_1 = 11 + 10 * 5 + 1, | ||
140 | POLY_PAD_MAX_CH_2 = 11 + 10 * 5 + 2, | ||
141 | POLY_PAD_MAX_CH_3 = 11 + 10 * 5 + 3, | ||
142 | POLY_PAD_MAX_CH_4 = 11 + 10 * 5 + 4, | ||
143 | POLY_PAD_MAX_CH_5 = 11 + 10 * 5 + 5, | ||
144 | POLY_PAD_MAX_CH_6 = 11 + 10 * 5 + 6, | ||
145 | POLY_PAD_MAX_CH_7 = 11 + 10 * 5 + 7, | ||
146 | POLY_PAD_MAX_CH_8 = 11 + 10 * 4 + 0, | ||
147 | POLY_PAD_MAX_CH_9 = 11 + 10 * 4 + 1, | ||
148 | POLY_PAD_MAX_CH_A = 11 + 10 * 4 + 2, | ||
149 | POLY_PAD_MAX_CH_B = 11 + 10 * 4 + 3, | ||
150 | POLY_PAD_MAX_CH_C = 11 + 10 * 4 + 4, | ||
151 | POLY_PAD_MAX_CH_D = 11 + 10 * 4 + 5, | ||
152 | POLY_PAD_MAX_CH_E = 11 + 10 * 4 + 6, | ||
153 | POLY_PAD_MAX_CH_F = 11 + 10 * 4 + 7, | ||
154 | } Button; | ||
155 | |||
156 | typedef enum Mode { | ||
157 | MOD_POLY_MAIN = 0, | ||
158 | MOD_POLY_SETUP = 1, | ||
159 | } Mode; | ||
160 | |||
161 | typedef struct Voice { | ||
162 | u8 active; | ||
163 | u8 channel; | ||
164 | u8 d1; | ||
165 | u8 d2; | ||
166 | u8 port; | ||
167 | } Voice; | ||
168 | |||
169 | // Globals. | ||
170 | u16 frame = 0; | ||
171 | u16 n_voices = 0; | ||
172 | u8 active = 1; | ||
173 | u8 ch_min = 0; | ||
174 | u8 ch_max = 4; | ||
175 | Mode mode = MOD_POLY_SETUP; | ||
176 | Voice voices[16] = {0}; | ||
177 | |||
178 | // store ADC frame pointer | ||
179 | static const u16 *g_ADC = 0; | ||
180 | |||
181 | void | ||
182 | poly_active_toggle(void) { | ||
183 | // Stop existing playing notes. | ||
184 | for (u8 i = 0; i < 16; i++) { | ||
185 | Voice *voice = &voices[i]; | ||
186 | if (voice->active) { | ||
187 | voice->active = 0; | ||
188 | hal_send_midi( | ||
189 | voice->port, | ||
190 | NOTEOFF | voice->channel, | ||
191 | voice->d1, | ||
192 | voice->d2); | ||
193 | } | ||
194 | } | ||
195 | n_voices = 0; | ||
196 | active = !active; | ||
197 | } | ||
198 | |||
199 | void | ||
200 | select_ch_min(u8 target) { | ||
201 | if (target <= ch_max) { | ||
202 | ch_min = target; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | void | ||
207 | select_ch_max(u8 target) { | ||
208 | if (target >= ch_min) { | ||
209 | ch_max = target; | ||
210 | } | ||
211 | } | ||
144 | 212 | ||
145 | void | 213 | void |
146 | app_surface_event(u8 type, u8 index, u8 value) { | 214 | app_surface_event(u8 type, u8 index, u8 value) { |
147 | switch (type) { | 215 | switch (type) { |
148 | case TYPEPAD: { | 216 | case TYPEPAD: { |
149 | if (value && index == POLY_PAD_ACTIVE) { | 217 | switch (mode) { |
150 | // Stop existing playing notes. | 218 | case MOD_POLY_MAIN: { |
151 | for (u8 i = 0; i < 16; i++) { | 219 | if (value && index == POLY_PAD_ACTIVE) { |
152 | Voice *voice = &voices[i]; | 220 | poly_active_toggle(); |
153 | if (voice->active) { | ||
154 | voice->active = 0; | ||
155 | hal_send_midi( | ||
156 | voice->port, | ||
157 | NOTEOFF | voice->channel, | ||
158 | voice->d1, | ||
159 | voice->d2); | ||
160 | } | 221 | } |
161 | } | 222 | } break; |
162 | n_voices = 0; | 223 | case MOD_POLY_SETUP: { |
163 | active = !active; | 224 | if (value) { |
225 | switch (index) { | ||
226 | // Handle min channel selection. | ||
227 | case POLY_PAD_MIN_CH_0: { select_ch_min(0); } break; | ||
228 | case POLY_PAD_MIN_CH_1: { select_ch_min(1); } break; | ||
229 | case POLY_PAD_MIN_CH_2: { select_ch_min(2); } break; | ||
230 | case POLY_PAD_MIN_CH_3: { select_ch_min(3); } break; | ||
231 | case POLY_PAD_MIN_CH_4: { select_ch_min(4); } break; | ||
232 | case POLY_PAD_MIN_CH_5: { select_ch_min(5); } break; | ||
233 | case POLY_PAD_MIN_CH_6: { select_ch_min(6); } break; | ||
234 | case POLY_PAD_MIN_CH_7: { select_ch_min(7); } break; | ||
235 | case POLY_PAD_MIN_CH_8: { select_ch_min(8); } break; | ||
236 | case POLY_PAD_MIN_CH_9: { select_ch_min(9); } break; | ||
237 | case POLY_PAD_MIN_CH_A: { select_ch_min(10); } break; | ||
238 | case POLY_PAD_MIN_CH_B: { select_ch_min(11); } break; | ||
239 | case POLY_PAD_MIN_CH_C: { select_ch_min(12); } break; | ||
240 | case POLY_PAD_MIN_CH_D: { select_ch_min(13); } break; | ||
241 | case POLY_PAD_MIN_CH_E: { select_ch_min(14); } break; | ||
242 | case POLY_PAD_MIN_CH_F: { select_ch_min(15); } break; | ||
243 | // Handle max channel selection. | ||
244 | case POLY_PAD_MAX_CH_0: { select_ch_max(0); } break; | ||
245 | case POLY_PAD_MAX_CH_1: { select_ch_max(1); } break; | ||
246 | case POLY_PAD_MAX_CH_2: { select_ch_max(2); } break; | ||
247 | case POLY_PAD_MAX_CH_3: { select_ch_max(3); } break; | ||
248 | case POLY_PAD_MAX_CH_4: { select_ch_max(4); } break; | ||
249 | case POLY_PAD_MAX_CH_5: { select_ch_max(5); } break; | ||
250 | case POLY_PAD_MAX_CH_6: { select_ch_max(6); } break; | ||
251 | case POLY_PAD_MAX_CH_7: { select_ch_max(7); } break; | ||
252 | case POLY_PAD_MAX_CH_8: { select_ch_max(8); } break; | ||
253 | case POLY_PAD_MAX_CH_9: { select_ch_max(9); } break; | ||
254 | case POLY_PAD_MAX_CH_A: { select_ch_max(10); } break; | ||
255 | case POLY_PAD_MAX_CH_B: { select_ch_max(11); } break; | ||
256 | case POLY_PAD_MAX_CH_C: { select_ch_max(12); } break; | ||
257 | case POLY_PAD_MAX_CH_D: { select_ch_max(13); } break; | ||
258 | case POLY_PAD_MAX_CH_E: { select_ch_max(14); } break; | ||
259 | case POLY_PAD_MAX_CH_F: { select_ch_max(15); } break; | ||
260 | // TODO: Handle listen channel selection. | ||
261 | // Handle active toggle. | ||
262 | case POLY_PAD_ACTIVE: { poly_active_toggle(); } break; | ||
263 | default: break; | ||
264 | } | ||
265 | } | ||
266 | } break; | ||
164 | } | 267 | } |
165 | // // toggle it and store it off, so we can save to flash if we want to | ||
166 | // if (value) { | ||
167 | // buttons[index] = MAXLED * !buttons[index]; | ||
168 | // } | ||
169 | |||
170 | // // example - light / extinguish pad LEDs | ||
171 | // hal_plot_led(TYPEPAD, index, 0, 0, buttons[index]); | ||
172 | |||
173 | // // example - send MIDI | ||
174 | // hal_send_midi(DINMIDI, NOTEON | 0, index, value); | ||
175 | } break; | 268 | } break; |
176 | case TYPESETUP: { | 269 | case TYPESETUP: { |
270 | if (value) { | ||
271 | switch (mode) { | ||
272 | case MOD_POLY_MAIN: { | ||
273 | mode = MOD_POLY_SETUP; | ||
274 | } break; | ||
275 | case MOD_POLY_SETUP: { | ||
276 | mode = MOD_POLY_MAIN; | ||
277 | // TODO: save to flash. | ||
278 | } break; | ||
279 | } | ||
280 | } | ||
177 | // if (value) { | 281 | // if (value) { |
178 | // // Pressing the setup button will save the current buttons/pad | 282 | // // Pressing the setup button will save the current buttons/pad |
179 | // // state to the flash. The flash memory is USER_AREA_SIZE bytes | 283 | // // state to the flash. The flash memory is USER_AREA_SIZE bytes |
@@ -192,12 +296,12 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { | |||
192 | } | 296 | } |
193 | u8 channel = status & 0x0F; | 297 | u8 channel = status & 0x0F; |
194 | if ((status & 0xF0) == NOTEON) { | 298 | if ((status & 0xF0) == NOTEON) { |
195 | if (n_voices == (ch_max - ch_min)) { | 299 | if (n_voices == (ch_max - ch_min + 1)) { |
196 | return; | 300 | return; |
197 | } | 301 | } |
198 | 302 | ||
199 | // Find if the note was already pressed. | 303 | // Find if the note was already pressed. |
200 | for (u8 i = ch_min; i < ch_max; i++) { | 304 | for (u8 i = ch_min; i <= ch_max; i++) { |
201 | Voice *voice = &voices[i]; | 305 | Voice *voice = &voices[i]; |
202 | // Stop voice if needed. | 306 | // Stop voice if needed. |
203 | if (voice->active && voice->d1 == d1) { | 307 | if (voice->active && voice->d1 == d1) { |
@@ -211,7 +315,7 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { | |||
211 | } | 315 | } |
212 | 316 | ||
213 | // Find next free voice slot. | 317 | // Find next free voice slot. |
214 | for (u8 i = ch_min; i < ch_max; i++) { | 318 | for (u8 i = ch_min; i <= ch_max; i++) { |
215 | Voice *voice = &voices[i]; | 319 | Voice *voice = &voices[i]; |
216 | // Register voice. | 320 | // Register voice. |
217 | if (!voice->active) { | 321 | if (!voice->active) { |
@@ -234,7 +338,7 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { | |||
234 | // TODO: Round robin scheduling? | 338 | // TODO: Round robin scheduling? |
235 | n_voices++; | 339 | n_voices++; |
236 | } else if ((status & 0xF0) == NOTEOFF) { | 340 | } else if ((status & 0xF0) == NOTEOFF) { |
237 | for (u8 i = ch_min; i < ch_max; i++) { | 341 | for (u8 i = ch_min; i <= ch_max; i++) { |
238 | Voice *voice = &voices[i]; | 342 | Voice *voice = &voices[i]; |
239 | // Register voice. | 343 | // Register voice. |
240 | if (voice->active && voice->d1 == d1) { | 344 | if (voice->active && voice->d1 == d1) { |
@@ -303,10 +407,7 @@ clear_pads(void) { | |||
303 | } | 407 | } |
304 | 408 | ||
305 | void | 409 | void |
306 | draw_poly_scene(void) { | 410 | draw_poly_active_button(void) { |
307 | print_number((n_voices / 10) % 10, MAXLED, MAXLED, MAXLED, 0, 0); | ||
308 | print_number( n_voices % 10, MAXLED, MAXLED, MAXLED, 4, 0); | ||
309 | |||
310 | if (active) { | 411 | if (active) { |
311 | hal_plot_led(TYPEPAD, 11, 0, MAXLED, 0); | 412 | hal_plot_led(TYPEPAD, 11, 0, MAXLED, 0); |
312 | } else { | 413 | } else { |
@@ -315,10 +416,102 @@ draw_poly_scene(void) { | |||
315 | } | 416 | } |
316 | 417 | ||
317 | void | 418 | void |
419 | draw_poly_main(void) { | ||
420 | print_number((n_voices / 10) % 10, MAXLED, MAXLED, MAXLED, 0, 0); | ||
421 | print_number( n_voices % 10, MAXLED, MAXLED, MAXLED, 4, 0); | ||
422 | draw_poly_active_button(); | ||
423 | } | ||
424 | |||
425 | void | ||
426 | draw_min_channel(void) { | ||
427 | for (u8 i = 0; i < 8; i++) { | ||
428 | if (i == ch_min) { | ||
429 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, MAXLED, MAXLED, MAXLED); | ||
430 | continue; | ||
431 | } | ||
432 | if (ch_max < 8 && i > ch_max) { | ||
433 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, 0, 0, 5); | ||
434 | continue; | ||
435 | } | ||
436 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, 0, 0, MAXLED / 2); | ||
437 | } | ||
438 | for (u8 i = 0; i < 8; i++) { | ||
439 | if ((i + 8) == ch_min) { | ||
440 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_8 + i, MAXLED, MAXLED, MAXLED); | ||
441 | continue; | ||
442 | } | ||
443 | if (ch_max < 16 && (i + 8) > ch_max) { | ||
444 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_8 + i, 0, 0, 5); | ||
445 | continue; | ||
446 | } | ||
447 | hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_8 + i, 0, 0, MAXLED / 2); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | void | ||
452 | draw_max_channel(void) { | ||
453 | // for (u8 i = 0; i < 8; i++) { | ||
454 | // u8 color = 5; | ||
455 | // if (i == ch_max) { | ||
456 | // color = MAXLED; | ||
457 | // } | ||
458 | // hal_plot_led(TYPEPAD, 11 + 10 * 5 + i, color, 10, 10); | ||
459 | // color = 5; | ||
460 | // if (i + 8 == ch_max) { | ||
461 | // color = MAXLED; | ||
462 | // } | ||
463 | // hal_plot_led(TYPEPAD, 11 + 10 * 4 + i, color, 10, 10); | ||
464 | // } | ||
465 | for (u8 i = 0; i < 8; i++) { | ||
466 | if (i == ch_max) { | ||
467 | hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, MAXLED, MAXLED, MAXLED); | ||
468 | continue; | ||
469 | } | ||
470 | if (i < ch_min) { | ||
471 | hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, 5, 0, 0); | ||
472 | continue; | ||
473 | } | ||
474 | hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, MAXLED / 2, 0, 0); | ||
475 | } | ||
476 | for (u8 i = 0; i < 8; i++) { | ||
477 | if ((i + 8) == ch_max) { | ||
478 | hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_8 + i, MAXLED, MAXLED, MAXLED); | ||
479 | continue; | ||
480 | } | ||
481 | if (ch_min >= 8 && (i + 8) < ch_min) { | ||
482 | hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_8 + i, 5, 0, 0); | ||
483 | continue; | ||
484 | } | ||
485 | hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_8 + i, MAXLED / 2, 0, 0); | ||
486 | } | ||
487 | } | ||
488 | |||
489 | void | ||
490 | draw_poly_setup(void) { | ||
491 | // TODO: ... | ||
492 | draw_min_channel(); | ||
493 | draw_max_channel(); | ||
494 | // draw_listen_channel(); | ||
495 | draw_poly_active_button(); | ||
496 | } | ||
497 | |||
498 | void | ||
499 | draw_scene(void) { | ||
500 | switch (mode) { | ||
501 | case MOD_POLY_MAIN: { | ||
502 | draw_poly_main(); | ||
503 | } break; | ||
504 | case MOD_POLY_SETUP: { | ||
505 | draw_poly_setup(); | ||
506 | } break; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | void | ||
318 | render(void) { | 511 | render(void) { |
319 | if (frame++ == 1000 / 10) { | 512 | if (frame++ == 1000 / 10) { |
320 | clear_pads(); | 513 | clear_pads(); |
321 | draw_poly_scene(); | 514 | draw_scene(); |
322 | frame = 0; | 515 | frame = 0; |
323 | } | 516 | } |
324 | } | 517 | } |
@@ -332,7 +525,7 @@ app_timer_event() { | |||
332 | void | 525 | void |
333 | app_init(const u16 *adc_raw) { | 526 | app_init(const u16 *adc_raw) { |
334 | // example - load button states from flash | 527 | // example - load button states from flash |
335 | hal_read_flash(0, buttons, BUTTON_COUNT); | 528 | // hal_read_flash(0, buttons, BUTTON_COUNT); |
336 | 529 | ||
337 | // store off the raw ADC frame pointer for later use | 530 | // store off the raw ADC frame pointer for later use |
338 | g_ADC = adc_raw; | 531 | g_ADC = adc_raw; |