Browse Source

Add basic note-off/sustain handling

master
cancel 6 years ago
parent
commit
0f21b338f0
  1. 66
      osc_out.c
  2. 21
      osc_out.h
  3. 164
      tui_main.c

66
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;
}

21
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);

164
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);
}

Loading…
Cancel
Save