summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-02-09 16:20:26 +0100
committerBad Diode <bd@badd10de.dev>2023-02-09 16:20:26 +0100
commitca67fe00a89b834328f2b48c35e0a534ee91a075 (patch)
tree9309a9cc5998e59ff0deb806f18c21fb608f2cd8
parent87e681d8f1981020fe3b5dd888e3f333ede2a065 (diff)
downloadlaunchpad-polymaker-ca67fe00a89b834328f2b48c35e0a534ee91a075.tar.gz
launchpad-polymaker-ca67fe00a89b834328f2b48c35e0a534ee91a075.zip
Add state saving to flash
-rw-r--r--src/app.c105
1 files changed, 61 insertions, 44 deletions
diff --git a/src/app.c b/src/app.c
index 29d114f..4f52d89 100644
--- a/src/app.c
+++ b/src/app.c
@@ -180,24 +180,30 @@ typedef struct Voice {
180 u8 d1; 180 u8 d1;
181 u8 d2; 181 u8 d2;
182 u8 port; 182 u8 port;
183 u8 age;
183} Voice; 184} Voice;
184 185
186typedef struct State {
187 // Polyphony.
188 u8 active;
189 u8 ch_min;
190 u8 ch_max;
191 u8 ch_listen[16];
192 Mode mode;
193} State;
194
185// Globals. 195// Globals.
186u16 frame = 0; 196u16 frame = 0;
187u16 n_voices = 0; 197u16 n_voices = 0;
188u8 active = 1;
189u8 ch_min = 0;
190u8 ch_max = 7;
191u8 ch_listen[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
192Mode mode = MOD_POLY_SETUP;
193Voice voices[16] = {0}; 198Voice voices[16] = {0};
199State state = {0};
200u32 note_counter = 0;
194 201
195// store ADC frame pointer 202// store ADC frame pointer
196static const u16 *g_ADC = 0; 203static const u16 *g_ADC = 0;
197 204
198void 205void
199poly_active_toggle(void) { 206poly_stop_all(void) {
200 // Stop existing playing notes.
201 for (u8 i = 0; i < 16; i++) { 207 for (u8 i = 0; i < 16; i++) {
202 Voice *voice = &voices[i]; 208 Voice *voice = &voices[i];
203 if (voice->active) { 209 if (voice->active) {
@@ -210,33 +216,39 @@ poly_active_toggle(void) {
210 } 216 }
211 } 217 }
212 n_voices = 0; 218 n_voices = 0;
213 active = !active; 219}
220
221void
222poly_active_toggle(void) {
223 poly_stop_all();
224 state.active = !state.active;
214} 225}
215 226
216void 227void
217select_ch_min(u8 target) { 228select_ch_min(u8 target) {
218 if (target <= ch_max) { 229 if (target <= state.ch_max) {
219 ch_min = target; 230 state.ch_min = target;
220 } 231 }
221} 232}
222 233
223void 234void
224select_ch_max(u8 target) { 235select_ch_max(u8 target) {
225 if (target >= ch_min) { 236 if (target >= state.ch_min) {
226 ch_max = target; 237 state.ch_max = target;
227 } 238 }
228} 239}
229 240
230void 241void
231select_ch_sel(u8 target) { 242select_ch_sel(u8 target) {
232 ch_listen[target] = !ch_listen[target]; 243 poly_stop_all();
244 state.ch_listen[target] = !state.ch_listen[target];
233} 245}
234 246
235void 247void
236app_surface_event(u8 type, u8 index, u8 value) { 248app_surface_event(u8 type, u8 index, u8 value) {
237 switch (type) { 249 switch (type) {
238 case TYPEPAD: { 250 case TYPEPAD: {
239 switch (mode) { 251 switch (state.mode) {
240 case MOD_POLY_MAIN: { 252 case MOD_POLY_MAIN: {
241 if (value && index == POLY_PAD_ACTIVE) { 253 if (value && index == POLY_PAD_ACTIVE) {
242 poly_active_toggle(); 254 poly_active_toggle();
@@ -306,45 +318,41 @@ app_surface_event(u8 type, u8 index, u8 value) {
306 } break; 318 } break;
307 case TYPESETUP: { 319 case TYPESETUP: {
308 if (value) { 320 if (value) {
309 switch (mode) { 321 switch (state.mode) {
310 case MOD_POLY_MAIN: { 322 case MOD_POLY_MAIN: {
311 mode = MOD_POLY_SETUP; 323 state.mode = MOD_POLY_SETUP;
312 } break; 324 } break;
313 case MOD_POLY_SETUP: { 325 case MOD_POLY_SETUP: {
314 mode = MOD_POLY_MAIN; 326 state.mode = MOD_POLY_MAIN;
315 // TODO: save to flash. 327 hal_write_flash(0, (u8*)&state, sizeof(State));
328
316 } break; 329 } break;
317 } 330 }
318 } 331 }
319 // if (value) {
320 // // Pressing the setup button will save the current buttons/pad
321 // // state to the flash. The flash memory is USER_AREA_SIZE bytes
322 // // long and can be organized however we need.
323 // hal_write_flash(0, buttons, BUTTON_COUNT);
324 // }
325 } break; 332 } break;
326 } 333 }
327} 334}
328 335
329void 336void
330app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { 337app_midi_event(u8 port, u8 status, u8 d1, u8 d2) {
331 if (!active) { 338 if (!state.active) {
332 hal_send_midi(port, status, d1, d2); 339 hal_send_midi(port, status, d1, d2);
333 return; 340 return;
334 } 341 }
335 u8 channel = status & 0x0F; 342 u8 channel = status & 0x0F;
336 // If the channel isn't set to listen, just passthrough the message. 343 // If the channel isn't set to listen, just passthrough the message.
337 if (!ch_listen[channel]) { 344 if (!state.ch_listen[channel]) {
338 hal_send_midi(port, status, d1, d2); 345 hal_send_midi(port, status, d1, d2);
339 return; 346 return;
340 } 347 }
341 if ((status & 0xF0) == NOTEON) { 348 if ((status & 0xF0) == NOTEON) {
342 if (n_voices == (ch_max - ch_min + 1)) { 349 // TODO: Only do this if voice stealing is disabled.
350 if (n_voices == (state.ch_max - state.ch_min + 1)) {
343 return; 351 return;
344 } 352 }
345 353
346 // Find if the note was already pressed. 354 // Find if the note was already pressed.
347 for (u8 i = ch_min; i <= ch_max; i++) { 355 for (u8 i = state.ch_min; i <= state.ch_max; i++) {
348 Voice *voice = &voices[i]; 356 Voice *voice = &voices[i];
349 // Stop voice if needed. 357 // Stop voice if needed.
350 if (voice->active && voice->d1 == d1) { 358 if (voice->active && voice->d1 == d1) {
@@ -358,7 +366,7 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) {
358 } 366 }
359 367
360 // Find next free voice slot. 368 // Find next free voice slot.
361 for (u8 i = ch_min; i <= ch_max; i++) { 369 for (u8 i = state.ch_min; i <= state.ch_max; i++) {
362 Voice *voice = &voices[i]; 370 Voice *voice = &voices[i];
363 // Register voice. 371 // Register voice.
364 if (!voice->active) { 372 if (!voice->active) {
@@ -371,17 +379,17 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) {
371 NOTEON | voice->channel, 379 NOTEON | voice->channel,
372 voice->d1, 380 voice->d1,
373 voice->d2); 381 voice->d2);
382 voice->age = note_counter++;
374 break; 383 break;
375 } 384 }
376 } 385 }
377 386
378 // TODO: if there are no free slots choose the oldest active one. 387 // TODO: if there are no free slots choose the oldest active one.
379 // TODO: - Send noteoff message for the previous active channel. 388 // TODO: - Send noteoff message for the previous active channel.
380 // TODO: send noteon message.
381 // TODO: Round robin scheduling? 389 // TODO: Round robin scheduling?
382 n_voices++; 390 n_voices++;
383 } else if ((status & 0xF0) == NOTEOFF) { 391 } else if ((status & 0xF0) == NOTEOFF) {
384 for (u8 i = ch_min; i <= ch_max; i++) { 392 for (u8 i = state.ch_min; i <= state.ch_max; i++) {
385 Voice *voice = &voices[i]; 393 Voice *voice = &voices[i];
386 // Register voice. 394 // Register voice.
387 if (voice->active && voice->d1 == d1) { 395 if (voice->active && voice->d1 == d1) {
@@ -391,6 +399,8 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) {
391 break; 399 break;
392 } 400 }
393 } 401 }
402 } else {
403 hal_send_midi(port, status, d1, d2);
394 } 404 }
395} 405}
396 406
@@ -451,7 +461,7 @@ clear_pads(void) {
451 461
452void 462void
453draw_poly_active_button(void) { 463draw_poly_active_button(void) {
454 if (active) { 464 if (state.active) {
455 hal_plot_led(TYPEPAD, 11, 0, MAXLED, 0); 465 hal_plot_led(TYPEPAD, 11, 0, MAXLED, 0);
456 } else { 466 } else {
457 hal_plot_led(TYPEPAD, 11, MAXLED, 0, 0); 467 hal_plot_led(TYPEPAD, 11, MAXLED, 0, 0);
@@ -468,22 +478,22 @@ draw_poly_main(void) {
468void 478void
469draw_min_channel(void) { 479draw_min_channel(void) {
470 for (u8 i = 0; i < 8; i++) { 480 for (u8 i = 0; i < 8; i++) {
471 if (i == ch_min) { 481 if (i == state.ch_min) {
472 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, MAXLED, MAXLED, MAXLED); 482 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, MAXLED, MAXLED, MAXLED);
473 continue; 483 continue;
474 } 484 }
475 if (ch_max < 8 && i > ch_max) { 485 if (state.ch_max < 8 && i > state.ch_max) {
476 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, 5, 0, 0); 486 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, 5, 0, 0);
477 continue; 487 continue;
478 } 488 }
479 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, MAXLED / 2, 0, 0); 489 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_0 + i, MAXLED / 2, 0, 0);
480 } 490 }
481 for (u8 i = 0; i < 8; i++) { 491 for (u8 i = 0; i < 8; i++) {
482 if ((i + 8) == ch_min) { 492 if ((i + 8) == state.ch_min) {
483 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_8 + i, MAXLED, MAXLED, MAXLED); 493 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_8 + i, MAXLED, MAXLED, MAXLED);
484 continue; 494 continue;
485 } 495 }
486 if (ch_max < 16 && (i + 8) > ch_max) { 496 if (state.ch_max < 16 && (i + 8) > state.ch_max) {
487 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_8 + i, 5, 0, 0); 497 hal_plot_led(TYPEPAD, POLY_PAD_MIN_CH_8 + i, 5, 0, 0);
488 continue; 498 continue;
489 } 499 }
@@ -494,22 +504,22 @@ draw_min_channel(void) {
494void 504void
495draw_max_channel(void) { 505draw_max_channel(void) {
496 for (u8 i = 0; i < 8; i++) { 506 for (u8 i = 0; i < 8; i++) {
497 if (i == ch_max) { 507 if (i == state.ch_max) {
498 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, MAXLED, MAXLED, MAXLED); 508 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, MAXLED, MAXLED, MAXLED);
499 continue; 509 continue;
500 } 510 }
501 if (i < ch_min) { 511 if (i < state.ch_min) {
502 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, 5, 5, 0); 512 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, 5, 5, 0);
503 continue; 513 continue;
504 } 514 }
505 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, MAXLED / 2, MAXLED / 2, 0); 515 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_0 + i, MAXLED / 2, MAXLED / 2, 0);
506 } 516 }
507 for (u8 i = 0; i < 8; i++) { 517 for (u8 i = 0; i < 8; i++) {
508 if ((i + 8) == ch_max) { 518 if ((i + 8) == state.ch_max) {
509 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_8 + i, MAXLED, MAXLED, MAXLED); 519 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_8 + i, MAXLED, MAXLED, MAXLED);
510 continue; 520 continue;
511 } 521 }
512 if (ch_min >= 8 && (i + 8) < ch_min) { 522 if (state.ch_min >= 8 && (i + 8) < state.ch_min) {
513 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_8 + i, 5, 5, 0); 523 hal_plot_led(TYPEPAD, POLY_PAD_MAX_CH_8 + i, 5, 5, 0);
514 continue; 524 continue;
515 } 525 }
@@ -520,14 +530,14 @@ draw_max_channel(void) {
520void 530void
521draw_sel_channel(void) { 531draw_sel_channel(void) {
522 for (u8 i = 0; i < 8; i++) { 532 for (u8 i = 0; i < 8; i++) {
523 if (ch_listen[i]) { 533 if (state.ch_listen[i]) {
524 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_0 + i, 20, 0, 40); 534 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_0 + i, 20, 0, 40);
525 } else { 535 } else {
526 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_0 + i, 2, 0, 5); 536 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_0 + i, 2, 0, 5);
527 } 537 }
528 } 538 }
529 for (u8 i = 0; i < 8; i++) { 539 for (u8 i = 0; i < 8; i++) {
530 if (ch_listen[i + 8]) { 540 if (state.ch_listen[i + 8]) {
531 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_8 + i, 20, 0, 40); 541 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_8 + i, 20, 0, 40);
532 } else { 542 } else {
533 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_8 + i, 2, 0, 5); 543 hal_plot_led(TYPEPAD, POLY_PAD_SEL_CH_8 + i, 2, 0, 5);
@@ -546,7 +556,7 @@ draw_poly_setup(void) {
546 556
547void 557void
548draw_scene(void) { 558draw_scene(void) {
549 switch (mode) { 559 switch (state.mode) {
550 case MOD_POLY_MAIN: { 560 case MOD_POLY_MAIN: {
551 draw_poly_main(); 561 draw_poly_main();
552 } break; 562 } break;
@@ -574,7 +584,14 @@ app_timer_event() {
574void 584void
575app_init(const u16 *adc_raw) { 585app_init(const u16 *adc_raw) {
576 // example - load button states from flash 586 // example - load button states from flash
577 // hal_read_flash(0, buttons, BUTTON_COUNT); 587 state = (State){
588 .active = 1,
589 .ch_min = 0,
590 .ch_max = 7,
591 .ch_listen = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
592 .mode = MOD_POLY_MAIN,
593 };
594 hal_read_flash(0, (u8*)&state, sizeof(State));
578 595
579 // store off the raw ADC frame pointer for later use 596 // store off the raw ADC frame pointer for later use
580 g_ADC = adc_raw; 597 g_ADC = adc_raw;