From 0f21b338f0dc0ee59937f61d0dcd4b0b000bf564 Mon Sep 17 00:00:00 2001 From: cancel Date: Sun, 9 Dec 2018 18:33:20 +0900 Subject: [PATCH] Add basic note-off/sustain handling --- osc_out.c | 66 +++++++++++++++++++++ osc_out.h | 21 +++++++ tui_main.c | 164 +++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 215 insertions(+), 36 deletions(-) diff --git a/osc_out.c b/osc_out.c index 346aadc..81f24ca 100644 --- a/osc_out.c +++ b/osc_out.c @@ -102,3 +102,69 @@ void oosc_send_int32s(Oosc_dev* dev, char const* osc_address, I32 const* vals, } oosc_send_datagram(dev, buffer, buf_pos); } + +void susnote_list_init(Susnote_list* sl) { + sl->buffer = NULL; + sl->count = 0; + sl->capacity = 0; +} + +void susnote_list_deinit(Susnote_list* sl) { free(sl->buffer); } + +void susnote_list_clear(Susnote_list* sl) { sl->count = 0; } + +void susnote_list_add_notes(Susnote_list* sl, Susnote const* restrict notes, + Usz added_count, Usz* restrict start_removed, + Usz* restrict end_removed) { + Susnote* buffer = sl->buffer; + Usz count = sl->count; + Usz cap = sl->capacity; + Usz rem = count + added_count; + if (cap < rem) { + cap = rem < 16 ? 16 : orca_round_up_power2(rem); + buffer = realloc(buffer, cap * sizeof(Susnote)); + sl->capacity = cap; + sl->buffer = buffer; + } + *start_removed = rem; + Usz i_in = 0; + for (; i_in < added_count; ++i_in) { + Susnote this_in = notes[i_in]; + for (Usz i_old = 0; i_old < count; ++i_old) { + Susnote this_old = buffer[i_old]; + if (this_old.chan_note == this_in.chan_note) { + buffer[i_old] = this_in; + buffer[rem] = this_old; + ++rem; + goto next_in; + } + } + buffer[count] = this_in; + ++count; + next_in:; + } + sl->count = count; + *end_removed = rem; +} + +void susnote_list_advance_time(Susnote_list* sl, float delta_time, + Usz* restrict start_removed, + Usz* restrict end_removed) { + Susnote* restrict buffer = sl->buffer; + Usz count = sl->count; + *end_removed = count; + for (Usz i = 0; i < count;) { + Susnote sn = buffer[i]; + sn.remaining -= delta_time; + if (sn.remaining > 0) { + buffer[i].remaining = sn.remaining; + ++i; + } else { + buffer[i] = buffer[count - 1]; + buffer[count - 1] = sn; + --count; + } + } + *start_removed = count; + sl->count = count; +} diff --git a/osc_out.h b/osc_out.h index f5cbc15..8f0de1c 100644 --- a/osc_out.h +++ b/osc_out.h @@ -14,3 +14,24 @@ void oosc_dev_destroy(Oosc_dev* dev); void oosc_send_datagram(Oosc_dev* dev, char const* data, Usz size); void oosc_send_int32s(Oosc_dev* dev, char const* osc_address, I32 const* vals, Usz count); + +typedef struct { + float remaining; + U16 chan_note; +} Susnote; + +typedef struct { + Susnote* buffer; + Usz count; + Usz capacity; +} Susnote_list; + +void susnote_list_init(Susnote_list* sl); +void susnote_list_deinit(Susnote_list* sl); +void susnote_list_clear(Susnote_list* sl); +void susnote_list_add_notes(Susnote_list* sl, Susnote const* restrict notes, + Usz count, Usz* restrict start_removed, + Usz* restrict end_removed); +void susnote_list_advance_time(Susnote_list* sl, float delta_time, + Usz* restrict start_removed, + Usz* restrict end_removed); diff --git a/tui_main.c b/tui_main.c index af55cd8..a180138 100644 --- a/tui_main.c +++ b/tui_main.c @@ -503,6 +503,7 @@ typedef struct { Undo_history undo_hist; Oevent_list oevent_list; Oevent_list scratch_oevent_list; + Susnote_list susnote_list; Tui_cursor tui_cursor; Piano_bits piano_bits; Usz tick_num; @@ -528,6 +529,7 @@ void app_init(App_state* a) { tui_cursor_init(&a->tui_cursor); oevent_list_init(&a->oevent_list); oevent_list_init(&a->scratch_oevent_list); + susnote_list_init(&a->susnote_list); a->piano_bits = ORCA_PIANO_BITS_NONE; a->tick_num = 0; a->ruler_spacing_y = 8; @@ -552,6 +554,7 @@ void app_deinit(App_state* a) { undo_history_deinit(&a->undo_hist); oevent_list_deinit(&a->oevent_list); oevent_list_deinit(&a->scratch_oevent_list); + susnote_list_deinit(&a->susnote_list); if (a->oosc_dev) { oosc_dev_destroy(a->oosc_dev); } @@ -561,24 +564,6 @@ bool app_is_draw_dirty(App_state* a) { return a->is_draw_dirty || a->needs_remarking; } -double app_secs_to_deadline(App_state const* a) { - if (a->is_playing) { - double secs_span = 60.0 / (double)a->bpm / 4.0; - double rem = secs_span - a->accum_secs; - if (rem < 0.0) - rem = 0.0; - return rem; - } else { - return 1.0; - } -} - -void app_apply_delta_secs(App_state* a, double secs) { - if (a->is_playing) { - a->accum_secs += secs; - } -} - bool app_set_osc_udp(App_state* a, char const* dest_addr, U16 port) { if (a->oosc_dev) { oosc_dev_destroy(a->oosc_dev); @@ -596,31 +581,135 @@ void app_set_midi_mode(App_state* a, Midi_mode const* midi_mode) { a->midi_mode = midi_mode; } -void send_output_events(Oosc_dev* oosc_dev, Midi_mode const* midi_mode, - Oevent const* events, Usz count) { +void send_midi_note_offs(Oosc_dev* oosc_dev, Midi_mode const* midi_mode, + Susnote const* start, Susnote const* end) { + Midi_mode_type midi_mode_type = midi_mode->any.type; + for (; start != end; ++start) { + U16 chan_note = start->chan_note; + Usz chan = chan_note >> 8u; + Usz note = chan_note & 0xFFu; + switch (midi_mode_type) { + case Midi_mode_type_null: + break; + case Midi_mode_type_osc_bidule: { + I32 ints[3]; + ints[0] = (0x8 << 4) | (U8)chan; // status + ints[1] = (I32)note; // note number + ints[2] = 0; // velocity + oosc_send_int32s(oosc_dev, midi_mode->osc_bidule.path, ints, + ORCA_ARRAY_COUNTOF(ints)); + } break; + } + } +} + +void apply_time_to_sustained_notes(Oosc_dev* oosc_dev, + Midi_mode const* midi_mode, + double time_elapsed, + Susnote_list* susnote_list) { + Usz start_removed, end_removed; + susnote_list_advance_time(susnote_list, (float)time_elapsed, &start_removed, + &end_removed); + if (ORCA_UNLIKELY(start_removed != end_removed)) { + Susnote const* restrict susnotes_off = susnote_list->buffer; + send_midi_note_offs(oosc_dev, midi_mode, susnotes_off + start_removed, + susnotes_off + end_removed); + } +} + +void app_stop_all_sustained_notes(App_state* a) { + Susnote_list* sl = &a->susnote_list; + send_midi_note_offs(a->oosc_dev, a->midi_mode, sl->buffer, + sl->buffer + sl->count); + susnote_list_clear(sl); +} + +void send_output_events(Oosc_dev* oosc_dev, Midi_mode const* midi_mode, Usz bpm, + Susnote_list* susnote_list, Oevent const* events, + Usz count) { Midi_mode_type midi_mode_type = midi_mode->any.type; + double bar_secs = (double)bpm / 60.0 * 4.0; + + enum { Midi_on_capacity = 512 }; + typedef struct { + U8 channel; + U8 note_number; + U8 velocity; + } Midi_note_on; + Midi_note_on midi_note_ons[Midi_on_capacity]; + Susnote new_susnotes[Midi_on_capacity]; + Usz midi_note_count = 0; + for (Usz i = 0; i < count; ++i) { + if (midi_note_count == Midi_on_capacity) + break; Oevent const* e = events + i; switch ((Oevent_types)e->any.oevent_type) { case Oevent_type_midi: { - Oevent_midi const* em = (Oevent_midi const*)e; - switch (midi_mode_type) { - case Midi_mode_type_null: - break; - case Midi_mode_type_osc_bidule: { - I32 ints[3]; - ints[0] = (0x9 << 4) | em->channel; // status - ints[1] = 12 * em->octave + em->note; // note number - // ints[1] = 12 * 4 + em->note; // note number - ints[2] = em->velocity; // velocity - // ints[2] = 127; // velocity - oosc_send_int32s(oosc_dev, midi_mode->osc_bidule.path, ints, - ORCA_ARRAY_COUNTOF(ints)); - } break; - } + Oevent_midi const* em = (Oevent_midi const*)&e->midi; + Usz note_number = (Usz)(12u * em->octave + em->note); + Usz channel = em->channel; + Usz bar_div = em->bar_divisor; + midi_note_ons[midi_note_count] = + (Midi_note_on){.channel = (U8)channel, + .note_number = (U8)note_number, + .velocity = em->velocity}; + new_susnotes[midi_note_count] = (Susnote){ + .remaining = + bar_div == 0 ? 0.0f : (float)(bar_secs / (double)bar_div), + .chan_note = (U16)((channel << 8u) | note_number)}; + ++midi_note_count; } break; } } + + if (midi_note_count == 0) + return; + + Usz start_note_offs, end_note_offs; + susnote_list_add_notes(susnote_list, new_susnotes, midi_note_count, + &start_note_offs, &end_note_offs); + if (start_note_offs != end_note_offs) { + Susnote const* restrict susnotes_off = susnote_list->buffer; + send_midi_note_offs(oosc_dev, midi_mode, susnotes_off + start_note_offs, + susnotes_off + end_note_offs); + } + for (Usz i = 0; i < midi_note_count; ++i) { + Midi_note_on mno = midi_note_ons[i]; + switch (midi_mode_type) { + case Midi_mode_type_null: + break; + case Midi_mode_type_osc_bidule: { + I32 ints[3]; + ints[0] = (0x9 << 4) | mno.channel; // status + ints[1] = mno.note_number; // note number + ints[2] = mno.velocity; // velocity + oosc_send_int32s(oosc_dev, midi_mode->osc_bidule.path, ints, + ORCA_ARRAY_COUNTOF(ints)); + } break; + } + } +} + +double app_secs_to_deadline(App_state const* a) { + if (a->is_playing) { + double secs_span = 60.0 / (double)a->bpm / 4.0; + double rem = secs_span - a->accum_secs; + if (rem < 0.0) + rem = 0.0; + return rem; + } else { + return 1.0; + } +} + +void app_apply_delta_secs(App_state* a, double secs) { + if (a->is_playing) { + a->accum_secs += secs; + Oosc_dev* oosc_dev = a->oosc_dev; + Midi_mode const* midi_mode = a->midi_mode; + apply_time_to_sustained_notes(oosc_dev, midi_mode, secs, &a->susnote_list); + } } void app_do_stuff(App_state* a) { @@ -641,7 +730,8 @@ void app_do_stuff(App_state* a) { if (oosc_dev && midi_mode) { Usz count = a->oevent_list.count; if (count > 0) { - send_output_events(oosc_dev, midi_mode, a->oevent_list.buffer, count); + send_output_events(oosc_dev, midi_mode, a->bpm, &a->susnote_list, + a->oevent_list.buffer, count); } } } @@ -827,6 +917,7 @@ void app_input_cmd(App_state* a, App_input_cmd ev) { break; case App_input_cmd_toggle_play_pause: if (a->is_playing) { + app_stop_all_sustained_notes(a); a->is_playing = false; // nodelay(stdscr, FALSE); } else { @@ -1159,6 +1250,7 @@ int main(int argc, char** argv) { key = wgetch(stdscr); } quit: + app_stop_all_sustained_notes(&app_state); if (cont_win) { delwin(cont_win); }