From 7ec0c7fa97a8d6ee5d51f0e040e331387c6b8b64 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Thu, 9 Feb 2023 17:23:20 +0100 Subject: Add voice stealing --- src/app.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 18 deletions(-) (limited to 'src/app.c') 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] = { // Enums and structs. typedef enum Button { POLY_PAD_ACTIVE = 11, + POLY_PAD_STEAL = 11 + 7, POLY_PAD_MIN_CH_0 = 11 + 10 * 7 + 0, POLY_PAD_MIN_CH_1 = 11 + 10 * 7 + 1, POLY_PAD_MIN_CH_2 = 11 + 10 * 7 + 2, @@ -170,10 +171,17 @@ typedef enum Button { } Button; typedef enum Mode { - MOD_POLY_MAIN = 0, + MOD_POLY_MAIN = 0, MOD_POLY_SETUP = 1, } Mode; +typedef enum Stealing { + POLY_STEAL_OFF = 0, + POLY_STEAL_OLD = 1, + POLY_STEAL_HIGH = 2, + POLY_STEAL_LOW = 3, +} Stealing; + typedef struct Voice { u8 active; u8 channel; @@ -186,10 +194,11 @@ typedef struct Voice { typedef struct State { // Polyphony. u8 active; + u8 stealing; u8 ch_min; u8 ch_max; u8 ch_listen[16]; - Mode mode; + u8 mode; } State; // Globals. @@ -198,6 +207,8 @@ u16 n_voices = 0; Voice voices[16] = {0}; State state = {0}; u32 note_counter = 0; +u8 midi_in_connected = 0; +u8 midi_out_connected = 0; // store ADC frame pointer static const u16 *g_ADC = 0; @@ -224,6 +235,15 @@ poly_active_toggle(void) { state.active = !state.active; } +void +poly_steal_cycle(void) { + poly_stop_all(); + state.stealing += 1; + if (state.stealing > POLY_STEAL_LOW) { + state.stealing = POLY_STEAL_OFF; + } +} + void select_ch_min(u8 target) { if (target <= state.ch_max) { @@ -250,8 +270,12 @@ app_surface_event(u8 type, u8 index, u8 value) { case TYPEPAD: { switch (state.mode) { case MOD_POLY_MAIN: { - if (value && index == POLY_PAD_ACTIVE) { - poly_active_toggle(); + if (value) { + switch (index) { + case POLY_PAD_ACTIVE: { poly_active_toggle(); } break; + case POLY_PAD_STEAL: { poly_steal_cycle(); } break; + default: break; + } } } break; case MOD_POLY_SETUP: { @@ -310,6 +334,7 @@ app_surface_event(u8 type, u8 index, u8 value) { case POLY_PAD_SEL_CH_F: { select_ch_sel(15); } break; // Handle active toggle. case POLY_PAD_ACTIVE: { poly_active_toggle(); } break; + case POLY_PAD_STEAL: { poly_steal_cycle(); } break; default: break; } } @@ -346,9 +371,51 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { return; } if ((status & 0xF0) == NOTEON) { - // TODO: Only do this if voice stealing is disabled. if (n_voices == (state.ch_max - state.ch_min + 1)) { - return; + u8 selected = 0; + switch (state.stealing) { + case POLY_STEAL_OFF: { + return; + } break; + case POLY_STEAL_OLD: { + u32 age = -1; + for (u8 i = state.ch_min; i <= state.ch_max; i++) { + Voice *voice = &voices[i]; + if (voice->age < age) { + age = voice->age; + selected = i; + } + } + } break; + case POLY_STEAL_HIGH: { + u8 pitch = 0; + for (u8 i = state.ch_min; i <= state.ch_max; i++) { + Voice *voice = &voices[i]; + if (voice->d1 >= pitch) { + pitch = voice->d1; + selected = i; + } + } + } break; + case POLY_STEAL_LOW: { + u8 pitch = -1; + for (u8 i = state.ch_min; i <= state.ch_max; i++) { + Voice *voice = &voices[i]; + if (voice->d1 < pitch) { + pitch = voice->d1; + selected = i; + } + } + } break; + } + + Voice *voice = &voices[selected]; + voice->active = 0; + hal_send_midi(voice->port, + NOTEOFF | voice->channel, + voice->d1, + voice->d2); + n_voices--; } // Find if the note was already pressed. @@ -384,9 +451,6 @@ app_midi_event(u8 port, u8 status, u8 d1, u8 d2) { } } - // TODO: if there are no free slots choose the oldest active one. - // TODO: - Send noteoff message for the previous active channel. - // TODO: Round robin scheduling? n_voices++; } else if ((status & 0xF0) == NOTEOFF) { 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) { void app_sysex_event(u8 port, u8 * data, u16 count) { - // example - respond to UDI messages? + // ... } void app_aftertouch_event(u8 index, u8 value) { - // example - send poly aftertouch to MIDI ports - // hal_send_midi(USBMIDI, POLYAFTERTOUCH | 0, index, value); + // ... } void app_cable_event(u8 type, u8 value) { - // example - light the Setup LED to indicate cable connections if (type == MIDI_IN_CABLE) { - hal_plot_led(TYPESETUP, 0, 0, value, 0); // green + if (value) { + midi_in_connected = 1; + } else { + midi_in_connected = 0; + } } else if (type == MIDI_OUT_CABLE) { - hal_plot_led(TYPESETUP, 0, value, 0, 0); // red + if (value) { + midi_out_connected = 1; + } else { + midi_out_connected = 0; + } + } + if (midi_in_connected && midi_out_connected) { + hal_plot_led(TYPESETUP, 0, 0, value, 0); + } else { + hal_plot_led(TYPESETUP, 0, value, 0, 0); } } @@ -462,9 +537,27 @@ clear_pads(void) { void draw_poly_active_button(void) { if (state.active) { - hal_plot_led(TYPEPAD, 11, 0, MAXLED, 0); + hal_plot_led(TYPEPAD, POLY_PAD_ACTIVE, 0, MAXLED, 0); } else { - hal_plot_led(TYPEPAD, 11, MAXLED, 0, 0); + hal_plot_led(TYPEPAD, POLY_PAD_ACTIVE, MAXLED, 0, 0); + } +} + +void +draw_poly_steal_button(void) { + switch (state.stealing) { + case POLY_STEAL_OFF: { + hal_plot_led(TYPEPAD, POLY_PAD_STEAL, MAXLED, 0, 0); + } break; + case POLY_STEAL_OLD: { + hal_plot_led(TYPEPAD, POLY_PAD_STEAL, 0, 24, 24); + } break; + case POLY_STEAL_HIGH: { + hal_plot_led(TYPEPAD, POLY_PAD_STEAL, 0, 24, 0); + } break; + case POLY_STEAL_LOW: { + hal_plot_led(TYPEPAD, POLY_PAD_STEAL, 0, 0, 24); + } break; } } @@ -473,6 +566,7 @@ draw_poly_main(void) { print_number((n_voices / 10) % 10, MAXLED, MAXLED, MAXLED, 0, 0); print_number( n_voices % 10, MAXLED, MAXLED, MAXLED, 4, 0); draw_poly_active_button(); + draw_poly_steal_button(); } void @@ -547,10 +641,10 @@ draw_sel_channel(void) { void draw_poly_setup(void) { - // TODO: ... draw_min_channel(); draw_max_channel(); draw_sel_channel(); + draw_poly_steal_button(); draw_poly_active_button(); } @@ -586,6 +680,7 @@ app_init(const u16 *adc_raw) { // example - load button states from flash state = (State){ .active = 1, + .stealing = POLY_STEAL_OFF, .ch_min = 0, .ch_max = 7, .ch_listen = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, -- cgit v1.2.1