summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-02-09 17:23:20 +0100
committerBad Diode <bd@badd10de.dev>2023-02-09 17:23:20 +0100
commit7ec0c7fa97a8d6ee5d51f0e040e331387c6b8b64 (patch)
tree3c0edb0bf0e1ea9b1ed1a6b2b8cf9a438b53f33d
parentca67fe00a89b834328f2b48c35e0a534ee91a075 (diff)
downloadlaunchpad-polymaker-7ec0c7fa97a8d6ee5d51f0e040e331387c6b8b64.tar.gz
launchpad-polymaker-7ec0c7fa97a8d6ee5d51f0e040e331387c6b8b64.zip
Add voice stealing
-rw-r--r--src/app.c131
1 files changed, 113 insertions, 18 deletions
diff --git a/src/app.c b/src/app.c
index 4f52d89..aeb5c84 100644
--- a/src/app.c
+++ b/src/app.c
@@ -119,6 +119,7 @@ u16 font_numbers[10] = {
119// Enums and structs. 119// Enums and structs.
120typedef enum Button { 120typedef 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
172typedef enum Mode { 173typedef 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
178typedef 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
177typedef struct Voice { 185typedef struct Voice {
178 u8 active; 186 u8 active;
179 u8 channel; 187 u8 channel;
@@ -186,10 +194,11 @@ typedef struct Voice {
186typedef struct State { 194typedef 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;
198Voice voices[16] = {0}; 207Voice voices[16] = {0};
199State state = {0}; 208State state = {0};
200u32 note_counter = 0; 209u32 note_counter = 0;
210u8 midi_in_connected = 0;
211u8 midi_out_connected = 0;
201 212
202// store ADC frame pointer 213// store ADC frame pointer
203static const u16 *g_ADC = 0; 214static const u16 *g_ADC = 0;
@@ -225,6 +236,15 @@ poly_active_toggle(void) {
225} 236}
226 237
227void 238void
239poly_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
247void
228select_ch_min(u8 target) { 248select_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
407void 471void
408app_sysex_event(u8 port, u8 * data, u16 count) { 472app_sysex_event(u8 port, u8 * data, u16 count) {
409 // example - respond to UDI messages? 473 // ...
410} 474}
411 475
412void 476void
413app_aftertouch_event(u8 index, u8 value) { 477app_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
418void 481void
419app_cable_event(u8 type, u8 value) { 482app_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) {
462void 537void
463draw_poly_active_button(void) { 538draw_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
546void
547draw_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
478void 572void
@@ -547,10 +641,10 @@ draw_sel_channel(void) {
547 641
548void 642void
549draw_poly_setup(void) { 643draw_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},