diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 131 |
1 files changed, 113 insertions, 18 deletions
@@ -119,6 +119,7 @@ u16 font_numbers[10] = { | |||
119 | // Enums and structs. | 119 | // Enums and structs. |
120 | typedef enum Button { | 120 | typedef enum Button { |
121 | POLY_PAD_ACTIVE = 11, | 121 | POLY_PAD_ACTIVE = 11, |
122 | POLY_PAD_STEAL = 11 + 7, | ||
122 | POLY_PAD_MIN_CH_0 = 11 + 10 * 7 + 0, | 123 | POLY_PAD_MIN_CH_0 = 11 + 10 * 7 + 0, |
123 | POLY_PAD_MIN_CH_1 = 11 + 10 * 7 + 1, | 124 | POLY_PAD_MIN_CH_1 = 11 + 10 * 7 + 1, |
124 | POLY_PAD_MIN_CH_2 = 11 + 10 * 7 + 2, | 125 | POLY_PAD_MIN_CH_2 = 11 + 10 * 7 + 2, |
@@ -170,10 +171,17 @@ typedef enum Button { | |||
170 | } Button; | 171 | } Button; |
171 | 172 | ||
172 | typedef enum Mode { | 173 | typedef enum Mode { |
173 | MOD_POLY_MAIN = 0, | 174 | MOD_POLY_MAIN = 0, |
174 | MOD_POLY_SETUP = 1, | 175 | MOD_POLY_SETUP = 1, |
175 | } Mode; | 176 | } Mode; |
176 | 177 | ||
178 | typedef enum Stealing { | ||
179 | POLY_STEAL_OFF = 0, | ||
180 | POLY_STEAL_OLD = 1, | ||
181 | POLY_STEAL_HIGH = 2, | ||
182 | POLY_STEAL_LOW = 3, | ||
183 | } Stealing; | ||
184 | |||
177 | typedef struct Voice { | 185 | typedef struct Voice { |
178 | u8 active; | 186 | u8 active; |
179 | u8 channel; | 187 | u8 channel; |
@@ -186,10 +194,11 @@ typedef struct Voice { | |||
186 | typedef struct State { | 194 | typedef struct State { |
187 | // Polyphony. | 195 | // Polyphony. |
188 | u8 active; | 196 | u8 active; |
197 | u8 stealing; | ||
189 | u8 ch_min; | 198 | u8 ch_min; |
190 | u8 ch_max; | 199 | u8 ch_max; |
191 | u8 ch_listen[16]; | 200 | u8 ch_listen[16]; |
192 | Mode mode; | 201 | u8 mode; |
193 | } State; | 202 | } State; |
194 | 203 | ||
195 | // Globals. | 204 | // Globals. |
@@ -198,6 +207,8 @@ u16 n_voices = 0; | |||
198 | Voice voices[16] = {0}; | 207 | Voice voices[16] = {0}; |
199 | State state = {0}; | 208 | State state = {0}; |
200 | u32 note_counter = 0; | 209 | u32 note_counter = 0; |
210 | u8 midi_in_connected = 0; | ||
211 | u8 midi_out_connected = 0; | ||
201 | 212 | ||
202 | // store ADC frame pointer | 213 | // store ADC frame pointer |
203 | static const u16 *g_ADC = 0; | 214 | static const u16 *g_ADC = 0; |
@@ -225,6 +236,15 @@ poly_active_toggle(void) { | |||
225 | } | 236 | } |
226 | 237 | ||
227 | void | 238 | void |
239 | poly_steal_cycle(void) { | ||
240 | poly_stop_all(); | ||
241 | state.stealing += 1; | ||
242 | if (state.stealing > POLY_STEAL_LOW) { | ||
243 | state.stealing = POLY_STEAL_OFF; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | void | ||
228 | select_ch_min(u8 target) { | 248 | select_ch_min(u8 target) { |
229 | if (target <= state.ch_max) { | 249 | if (target <= state.ch_max) { |
230 | state.ch_min = target; | 250 | state.ch_min = target; |
@@ -250,8 +270,12 @@ app_surface_event(u8 type, u8 index, u8 value) { | |||
250 | case TYPEPAD: { | 270 | case TYPEPAD: { |
251 | switch (state.mode) { | 271 | switch (state.mode) { |
252 | case MOD_POLY_MAIN: { | 272 | case MOD_POLY_MAIN: { |
253 | if (value && index == POLY_PAD_ACTIVE) { | 273 | if (value) { |
254 | poly_active_toggle(); | 274 | switch (index) { |
275 | case POLY_PAD_ACTIVE: { poly_active_toggle(); } break; | ||
276 | case POLY_PAD_STEAL: { poly_steal_cycle(); } break; | ||
277 | default: break; | ||
278 | } | ||
255 | } | 279 | } |
256 | } break; | 280 | } break; |
257 | case MOD_POLY_SETUP: { | 281 | case MOD_POLY_SETUP: { |
@@ -310,6 +334,7 @@ app_surface_event(u8 type, u8 index, u8 value) { | |||
310 | case POLY_PAD_SEL_CH_F: { select_ch_sel(15); } break; | 334 | case POLY_PAD_SEL_CH_F: { select_ch_sel(15); } break; |
311 | // Handle active toggle. | 335 | // Handle active toggle. |
312 | case POLY_PAD_ACTIVE: { poly_active_toggle(); } break; | 336 | case POLY_PAD_ACTIVE: { poly_active_toggle(); } break; |
337 | case POLY_PAD_STEAL: { poly_steal_cycle(); } break; | ||
313 | default: break; | 338 | default: break; |
314 | } | 339 | } |
315 | } | 340 | } |
@@ -346,9 +371,51 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { | |||
346 | return; | 371 | return; |
347 | } | 372 | } |
348 | if ((status & 0xF0) == NOTEON) { | 373 | if ((status & 0xF0) == NOTEON) { |
349 | // TODO: Only do this if voice stealing is disabled. | ||
350 | if (n_voices == (state.ch_max - state.ch_min + 1)) { | 374 | if (n_voices == (state.ch_max - state.ch_min + 1)) { |
351 | return; | 375 | u8 selected = 0; |
376 | switch (state.stealing) { | ||
377 | case POLY_STEAL_OFF: { | ||
378 | return; | ||
379 | } break; | ||
380 | case POLY_STEAL_OLD: { | ||
381 | u32 age = -1; | ||
382 | for (u8 i = state.ch_min; i <= state.ch_max; i++) { | ||
383 | Voice *voice = &voices[i]; | ||
384 | if (voice->age < age) { | ||
385 | age = voice->age; | ||
386 | selected = i; | ||
387 | } | ||
388 | } | ||
389 | } break; | ||
390 | case POLY_STEAL_HIGH: { | ||
391 | u8 pitch = 0; | ||
392 | for (u8 i = state.ch_min; i <= state.ch_max; i++) { | ||
393 | Voice *voice = &voices[i]; | ||
394 | if (voice->d1 >= pitch) { | ||
395 | pitch = voice->d1; | ||
396 | selected = i; | ||
397 | } | ||
398 | } | ||
399 | } break; | ||
400 | case POLY_STEAL_LOW: { | ||
401 | u8 pitch = -1; | ||
402 | for (u8 i = state.ch_min; i <= state.ch_max; i++) { | ||
403 | Voice *voice = &voices[i]; | ||
404 | if (voice->d1 < pitch) { | ||
405 | pitch = voice->d1; | ||
406 | selected = i; | ||
407 | } | ||
408 | } | ||
409 | } break; | ||
410 | } | ||
411 | |||
412 | Voice *voice = &voices[selected]; | ||
413 | voice->active = 0; | ||
414 | hal_send_midi(voice->port, | ||
415 | NOTEOFF | voice->channel, | ||
416 | voice->d1, | ||
417 | voice->d2); | ||
418 | n_voices--; | ||
352 | } | 419 | } |
353 | 420 | ||
354 | // Find if the note was already pressed. | 421 | // Find if the note was already pressed. |
@@ -384,9 +451,6 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { | |||
384 | } | 451 | } |
385 | } | 452 | } |
386 | 453 | ||
387 | // TODO: if there are no free slots choose the oldest active one. | ||
388 | // TODO: - Send noteoff message for the previous active channel. | ||
389 | // TODO: Round robin scheduling? | ||
390 | n_voices++; | 454 | n_voices++; |
391 | } else if ((status & 0xF0) == NOTEOFF) { | 455 | } else if ((status & 0xF0) == NOTEOFF) { |
392 | for (u8 i = state.ch_min; i <= state.ch_max; i++) { | 456 | for (u8 i = state.ch_min; i <= state.ch_max; i++) { |
@@ -406,22 +470,33 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { | |||
406 | 470 | ||
407 | void | 471 | void |
408 | app_sysex_event(u8 port, u8 * data, u16 count) { | 472 | app_sysex_event(u8 port, u8 * data, u16 count) { |
409 | // example - respond to UDI messages? | 473 | // ... |
410 | } | 474 | } |
411 | 475 | ||
412 | void | 476 | void |
413 | app_aftertouch_event(u8 index, u8 value) { | 477 | app_aftertouch_event(u8 index, u8 value) { |
414 | // example - send poly aftertouch to MIDI ports | 478 | // ... |
415 | // hal_send_midi(USBMIDI, POLYAFTERTOUCH | 0, index, value); | ||
416 | } | 479 | } |
417 | 480 | ||
418 | void | 481 | void |
419 | app_cable_event(u8 type, u8 value) { | 482 | app_cable_event(u8 type, u8 value) { |
420 | // example - light the Setup LED to indicate cable connections | ||
421 | if (type == MIDI_IN_CABLE) { | 483 | if (type == MIDI_IN_CABLE) { |
422 | hal_plot_led(TYPESETUP, 0, 0, value, 0); // green | 484 | if (value) { |
485 | midi_in_connected = 1; | ||
486 | } else { | ||
487 | midi_in_connected = 0; | ||
488 | } | ||
423 | } else if (type == MIDI_OUT_CABLE) { | 489 | } else if (type == MIDI_OUT_CABLE) { |
424 | hal_plot_led(TYPESETUP, 0, value, 0, 0); // red | 490 | if (value) { |
491 | midi_out_connected = 1; | ||
492 | } else { | ||
493 | midi_out_connected = 0; | ||
494 | } | ||
495 | } | ||
496 | if (midi_in_connected && midi_out_connected) { | ||
497 | hal_plot_led(TYPESETUP, 0, 0, value, 0); | ||
498 | } else { | ||
499 | hal_plot_led(TYPESETUP, 0, value, 0, 0); | ||
425 | } | 500 | } |
426 | } | 501 | } |
427 | 502 | ||
@@ -462,9 +537,27 @@ clear_pads(void) { | |||
462 | void | 537 | void |
463 | draw_poly_active_button(void) { | 538 | draw_poly_active_button(void) { |
464 | if (state.active) { | 539 | if (state.active) { |
465 | hal_plot_led(TYPEPAD, 11, 0, MAXLED, 0); | 540 | hal_plot_led(TYPEPAD, POLY_PAD_ACTIVE, 0, MAXLED, 0); |
466 | } else { | 541 | } else { |
467 | hal_plot_led(TYPEPAD, 11, MAXLED, 0, 0); | 542 | hal_plot_led(TYPEPAD, POLY_PAD_ACTIVE, MAXLED, 0, 0); |
543 | } | ||
544 | } | ||
545 | |||
546 | void | ||
547 | draw_poly_steal_button(void) { | ||
548 | switch (state.stealing) { | ||
549 | case POLY_STEAL_OFF: { | ||
550 | hal_plot_led(TYPEPAD, POLY_PAD_STEAL, MAXLED, 0, 0); | ||
551 | } break; | ||
552 | case POLY_STEAL_OLD: { | ||
553 | hal_plot_led(TYPEPAD, POLY_PAD_STEAL, 0, 24, 24); | ||
554 | } break; | ||
555 | case POLY_STEAL_HIGH: { | ||
556 | hal_plot_led(TYPEPAD, POLY_PAD_STEAL, 0, 24, 0); | ||
557 | } break; | ||
558 | case POLY_STEAL_LOW: { | ||
559 | hal_plot_led(TYPEPAD, POLY_PAD_STEAL, 0, 0, 24); | ||
560 | } break; | ||
468 | } | 561 | } |
469 | } | 562 | } |
470 | 563 | ||
@@ -473,6 +566,7 @@ draw_poly_main(void) { | |||
473 | print_number((n_voices / 10) % 10, MAXLED, MAXLED, MAXLED, 0, 0); | 566 | print_number((n_voices / 10) % 10, MAXLED, MAXLED, MAXLED, 0, 0); |
474 | print_number( n_voices % 10, MAXLED, MAXLED, MAXLED, 4, 0); | 567 | print_number( n_voices % 10, MAXLED, MAXLED, MAXLED, 4, 0); |
475 | draw_poly_active_button(); | 568 | draw_poly_active_button(); |
569 | draw_poly_steal_button(); | ||
476 | } | 570 | } |
477 | 571 | ||
478 | void | 572 | void |
@@ -547,10 +641,10 @@ draw_sel_channel(void) { | |||
547 | 641 | ||
548 | void | 642 | void |
549 | draw_poly_setup(void) { | 643 | draw_poly_setup(void) { |
550 | // TODO: ... | ||
551 | draw_min_channel(); | 644 | draw_min_channel(); |
552 | draw_max_channel(); | 645 | draw_max_channel(); |
553 | draw_sel_channel(); | 646 | draw_sel_channel(); |
647 | draw_poly_steal_button(); | ||
554 | draw_poly_active_button(); | 648 | draw_poly_active_button(); |
555 | } | 649 | } |
556 | 650 | ||
@@ -586,6 +680,7 @@ app_init(const u16 *adc_raw) { | |||
586 | // example - load button states from flash | 680 | // example - load button states from flash |
587 | state = (State){ | 681 | state = (State){ |
588 | .active = 1, | 682 | .active = 1, |
683 | .stealing = POLY_STEAL_OFF, | ||
589 | .ch_min = 0, | 684 | .ch_min = 0, |
590 | .ch_max = 7, | 685 | .ch_max = 7, |
591 | .ch_listen = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, | 686 | .ch_listen = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, |