diff options
author | Bad Diode <bd@badd10de.dev> | 2023-02-09 16:20:26 +0100 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2023-02-09 16:20:26 +0100 |
commit | ca67fe00a89b834328f2b48c35e0a534ee91a075 (patch) | |
tree | 9309a9cc5998e59ff0deb806f18c21fb608f2cd8 /src/app.c | |
parent | 87e681d8f1981020fe3b5dd888e3f333ede2a065 (diff) | |
download | launchpad-polymaker-ca67fe00a89b834328f2b48c35e0a534ee91a075.tar.gz launchpad-polymaker-ca67fe00a89b834328f2b48c35e0a534ee91a075.zip |
Add state saving to flash
Diffstat (limited to 'src/app.c')
-rw-r--r-- | src/app.c | 105 |
1 files changed, 61 insertions, 44 deletions
@@ -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 | ||
186 | typedef 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. |
186 | u16 frame = 0; | 196 | u16 frame = 0; |
187 | u16 n_voices = 0; | 197 | u16 n_voices = 0; |
188 | u8 active = 1; | ||
189 | u8 ch_min = 0; | ||
190 | u8 ch_max = 7; | ||
191 | u8 ch_listen[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; | ||
192 | Mode mode = MOD_POLY_SETUP; | ||
193 | Voice voices[16] = {0}; | 198 | Voice voices[16] = {0}; |
199 | State state = {0}; | ||
200 | u32 note_counter = 0; | ||
194 | 201 | ||
195 | // store ADC frame pointer | 202 | // store ADC frame pointer |
196 | static const u16 *g_ADC = 0; | 203 | static const u16 *g_ADC = 0; |
197 | 204 | ||
198 | void | 205 | void |
199 | poly_active_toggle(void) { | 206 | poly_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 | |||
221 | void | ||
222 | poly_active_toggle(void) { | ||
223 | poly_stop_all(); | ||
224 | state.active = !state.active; | ||
214 | } | 225 | } |
215 | 226 | ||
216 | void | 227 | void |
217 | select_ch_min(u8 target) { | 228 | select_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 | ||
223 | void | 234 | void |
224 | select_ch_max(u8 target) { | 235 | select_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 | ||
230 | void | 241 | void |
231 | select_ch_sel(u8 target) { | 242 | select_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 | ||
235 | void | 247 | void |
236 | app_surface_event(u8 type, u8 index, u8 value) { | 248 | app_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 | ||
329 | void | 336 | void |
330 | app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { | 337 | app_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 | ||
452 | void | 462 | void |
453 | draw_poly_active_button(void) { | 463 | draw_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) { | |||
468 | void | 478 | void |
469 | draw_min_channel(void) { | 479 | draw_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) { | |||
494 | void | 504 | void |
495 | draw_max_channel(void) { | 505 | draw_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) { | |||
520 | void | 530 | void |
521 | draw_sel_channel(void) { | 531 | draw_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 | ||
547 | void | 557 | void |
548 | draw_scene(void) { | 558 | draw_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() { | |||
574 | void | 584 | void |
575 | app_init(const u16 *adc_raw) { | 585 | app_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; |