Browse Source

refactor: untangle tui_main.c into modules tui, ged, midi

trying to create clear and interfaces and separation of concern
master
heck 2 years ago
parent
commit
1c0f30e5c4
  1. 105
      src/field.h
  2. 1772
      src/ged.c
  3. 148
      src/ged.h
  4. 233
      src/midi.c
  5. 78
      src/midi.h
  6. 1330
      src/tui.c
  7. 54
      src/tui.h
  8. 3879
      src/tui_main.c

105
src/field.h

@ -49,3 +49,108 @@ typedef struct Mbuf_reusable {
void mbuf_reusable_init(Mbuf_reusable *mbr);
void mbuf_reusable_ensure_size(Mbuf_reusable *mbr, Usz height, Usz width);
void mbuf_reusable_deinit(Mbuf_reusable *mbr);
// ------------------------------------------------------------
// FIELD-UNDO
// ------------------------------------------------------------
typedef struct Undo_node {
Field field;
Usz tick_num;
struct Undo_node *prev, *next;
} Undo_node;
typedef struct {
Undo_node *first, *last;
Usz count, limit;
} Undo_history;
static void undo_history_init(Undo_history *hist, Usz limit)
{
*hist = (Undo_history){ 0 };
hist->limit = limit;
}
static void undo_history_deinit(Undo_history *hist)
{
Undo_node *a = hist->first;
while (a) {
Undo_node *b = a->next;
field_deinit(&a->field);
free(a);
a = b;
}
}
staticni bool undo_history_push(Undo_history *hist, Field *field, Usz tick_num)
{
if (hist->limit == 0)
return false;
Undo_node *new_node;
if (hist->count == hist->limit) {
new_node = hist->first;
if (new_node == hist->last) {
hist->first = NULL;
hist->last = NULL;
} else {
hist->first = new_node->next;
hist->first->prev = NULL;
}
} else {
new_node = malloc(sizeof(Undo_node));
if (!new_node)
return false;
++hist->count;
field_init(&new_node->field);
}
field_copy(field, &new_node->field);
new_node->tick_num = tick_num;
if (hist->last) {
hist->last->next = new_node;
new_node->prev = hist->last;
} else {
hist->first = new_node;
hist->last = new_node;
new_node->prev = NULL;
}
new_node->next = NULL;
hist->last = new_node;
return true;
}
staticni void undo_history_apply(Undo_history *hist, Field *out_field, Usz *out_tick_num)
{
Undo_node *last = hist->last;
if (!last)
return;
field_copy(&last->field, out_field);
*out_tick_num = last->tick_num;
}
static Usz undo_history_count(Undo_history *hist)
{
return hist->count;
}
staticni void undo_history_pop(Undo_history *hist, Field *out_field, Usz *out_tick_num)
{
Undo_node *last = hist->last;
if (!last)
return;
field_copy(&last->field, out_field);
*out_tick_num = last->tick_num;
if (hist->first == last) {
hist->first = NULL;
hist->last = NULL;
} else {
Undo_node *new_last = last->prev;
new_last->next = NULL;
hist->last = new_last;
}
field_deinit(&last->field);
free(last);
--hist->count;
}

1772
src/ged.c

File diff suppressed because it is too large

148
src/ged.h

@ -0,0 +1,148 @@
#pragma once
#include "field.h"
#include "vmio.h"
#include "term_util.h"
#include "midi.h"
#include "osc_out.h"
typedef enum
{
Ged_input_mode_normal = 0,
Ged_input_mode_append,
Ged_input_mode_selresize,
Ged_input_mode_slide,
} Ged_input_mode;
typedef struct {
Usz y;
Usz x;
Usz h;
Usz w;
} Ged_cursor;
typedef struct {
Field field;
Field scratch_field;
Field clipboard_field;
Mbuf_reusable mbuf_r;
Undo_history undo_hist;
Oevent_list oevent_list;
Oevent_list scratch_oevent_list;
Susnote_list susnote_list;
Ged_cursor ged_cursor;
Usz tick_num;
Usz ruler_spacing_y;
Usz ruler_spacing_x;
Ged_input_mode input_mode;
Usz bpm;
U64 clock;
double accum_secs;
double time_to_next_note_off;
Oosc_dev *oosc_dev;
Midi_mode midi_mode;
Usz activity_counter;
Usz random_seed;
Usz drag_start_y;
Usz drag_start_x;
int win_h;
int win_w;
int softmargin_y;
int softmargin_x;
int grid_h;
int grid_scroll_y;
int grid_scroll_x; // not sure if i like this being int
U8 midi_bclock_sixths; // 0..5, holds 6th of the quarter note step
bool needs_remarking : 1;
bool is_draw_dirty : 1;
bool is_playing : 1;
bool midi_bclock : 1;
bool draw_event_list : 1;
bool is_mouse_down : 1;
bool is_mouse_dragging : 1;
bool is_hud_visible : 1;
} Ged;
void ged_init(Ged *a, Usz undo_limit, Usz init_bpm, Usz init_seed);
void ged_make_cursor_visible(Ged *a);
void ged_send_osc_bpm(Ged *a, I32 bpm);
void ged_set_playing(Ged *a, bool playing);
void ged_do_stuff(Ged *a);
bool ged_is_draw_dirty(Ged *a);
void ged_draw(Ged *a, WINDOW *win, char const *filename, bool use_fancy_dots, bool use_fancy_rulers);
double ged_secs_to_deadline(Ged const *a);
ORCA_OK_IF_UNUSED void ged_mouse_event(Ged *a, Usz vis_y, Usz vis_x, mmask_t mouse_bstate);
typedef enum
{
Ged_dir_up,
Ged_dir_down,
Ged_dir_left,
Ged_dir_right,
} Ged_dir;
void ged_dir_input(Ged *a, Ged_dir dir, int step_length);
void ged_input_character(Ged *a, char c);
typedef enum
{
Ged_input_cmd_undo,
Ged_input_cmd_toggle_append_mode,
Ged_input_cmd_toggle_selresize_mode,
Ged_input_cmd_toggle_slide_mode,
Ged_input_cmd_step_forward,
Ged_input_cmd_toggle_show_event_list,
Ged_input_cmd_toggle_play_pause,
Ged_input_cmd_cut,
Ged_input_cmd_copy,
Ged_input_cmd_paste,
Ged_input_cmd_escape,
} Ged_input_cmd;
void ged_set_window_size(Ged *a, int win_h, int win_w, int softmargin_y, int softmargin_x);
void ged_resize_grid(
Field *field,
Mbuf_reusable *mbr,
Usz new_height,
Usz new_width,
Usz tick_num,
Field *scratch_field,
Undo_history *undo_hist,
Ged_cursor *ged_cursor);
void ged_cursor_confine(Ged_cursor *tc, Usz height, Usz width);
void ged_update_internal_geometry(Ged *a);
void ged_input_cmd(Ged *a, Ged_input_cmd ev);
void ged_adjust_rulers_relative(Ged *a, Isz delta_y, Isz delta_x);
void ged_resize_grid_relative(Ged *a, Isz delta_y, Isz delta_x);
void ged_clear_osc_udp(Ged *a);
bool ged_set_osc_udp(Ged *a, char const *dest_addr, char const *dest_port);
bool ged_is_using_osc_udp(Ged *a);
void ged_adjust_bpm(Ged *a, Isz delta_bpm);
void ged_modify_selection_size(Ged *a, int delta_y, int delta_x);
bool ged_slide_selection(Ged *a, int delta_y, int delta_x);
void ged_stop_all_sustained_notes(Ged *a);
void ged_deinit(Ged *a);

233
src/midi.c

@ -0,0 +1,233 @@
#include "base.h"
#include "osc_out.h"
#include "oso.h"
#include "midi.h"
#include "sokol_time.h"
PmError portmidi_init_if_necessary(void)
{
// if (portmidi_is_initialized)
// return 0;
PmError e = Pm_Initialize();
if (e)
return e;
// portmidi_is_initialized = true;
return 0;
}
void midi_mode_init_null(Midi_mode *mm)
{
mm->any.type = Midi_mode_type_null;
}
void midi_mode_init_osc_bidule(Midi_mode *mm, char const *path)
{
mm->osc_bidule.type = Midi_mode_type_osc_bidule;
mm->osc_bidule.path = path;
}
#ifdef FEAT_PORTMIDI
enum
{
Portmidi_artificial_latency = 1,
};
struct {
U64 clock_base;
bool did_init;
} portmidi_global_data;
PmTimestamp portmidi_timestamp_now(void)
{
if (!portmidi_global_data.did_init) {
portmidi_global_data.did_init = true;
portmidi_global_data.clock_base = stm_now();
}
return (PmTimestamp)(stm_ms(stm_since(portmidi_global_data.clock_base)));
}
PmTimestamp portmidi_timeproc(void *time_info)
{
(void)time_info;
return portmidi_timestamp_now();
}
PmError midi_mode_init_portmidi(Midi_mode *mm, PmDeviceID dev_id)
{
PmError e = portmidi_init_if_necessary();
if (e)
goto fail;
e = Pm_OpenOutput(
&mm->portmidi.stream,
dev_id,
NULL,
128,
portmidi_timeproc,
NULL,
Portmidi_artificial_latency);
if (e)
goto fail;
mm->portmidi.type = Midi_mode_type_portmidi;
mm->portmidi.device_id = dev_id;
return pmNoError;
fail:
midi_mode_init_null(mm);
return e;
}
// Returns true on success. todo currently output only
bool portmidi_find_device_id_by_name(
char const *name,
Usz namelen,
PmError *out_pmerror,
PmDeviceID *out_id)
{
*out_pmerror = portmidi_init_if_necessary();
if (*out_pmerror)
return false;
int num = Pm_CountDevices();
for (int i = 0; i < num; ++i) {
PmDeviceInfo const *info = Pm_GetDeviceInfo(i);
if (!info || !info->output)
continue;
Usz len = strlen(info->name);
if (len != namelen)
continue;
if (strncmp(name, info->name, namelen) == 0) {
*out_id = i;
return true;
}
}
return false;
}
bool portmidi_find_name_of_device_id(PmDeviceID id, PmError *out_pmerror, oso **out_name)
{
*out_pmerror = portmidi_init_if_necessary();
if (*out_pmerror)
return false;
int num = Pm_CountDevices();
if (id < 0 || id >= num)
return false;
PmDeviceInfo const *info = Pm_GetDeviceInfo(id);
if (!info || !info->output)
return false;
osoput(out_name, info->name);
return true;
}
#endif
void midi_mode_deinit(Midi_mode *mm)
{
switch (mm->any.type) {
case Midi_mode_type_null:
case Midi_mode_type_osc_bidule:
break;
#ifdef FEAT_PORTMIDI
case Midi_mode_type_portmidi:
// Because PortMidi seems to work correctly ony more platforms when using
// its timing stuff, we are using it. And because we are using it, and
// because it may be buffering events for sending 'later', we might have
// pending outgoing MIDI events. We'll need to wait until they finish being
// before calling Pm_Close, otherwise users could have problems like MIDI
// notes being stuck on. This is slow and blocking, but not much we can do
// about it right now.
//
// TODO use nansleep on platforms that support it.
for (U64 start = stm_now();
stm_ms(stm_since(start)) <= (double)Portmidi_artificial_latency;)
sleep(0);
Pm_Close(mm->portmidi.stream);
break;
#endif
}
}
void send_midi_3bytes(Oosc_dev *oosc_dev, Midi_mode const *midi_mode, int status, int byte1, int byte2)
{
switch (midi_mode->any.type) {
case Midi_mode_type_null:
break;
case Midi_mode_type_osc_bidule: {
if (!oosc_dev)
break;
oosc_send_int32s(oosc_dev, midi_mode->osc_bidule.path, (int[]){ status, byte1, byte2 }, 3);
break;
}
#ifdef FEAT_PORTMIDI
case Midi_mode_type_portmidi: {
// timestamp is totally fake, to prevent problems with some MIDI systems
// getting angry if there's no timestamping info.
//
// Eventually, we will want to create real timestamps based on a real orca
// clock, instead of ad-hoc at the last moment like this. When we do that,
// we'll need to thread the timestamping/timing info through the function
// calls, instead of creating it at the last moment here. (This timestamp
// is actually 'useless', because it doesn't convey any additional
// information. But if we don't provide it, at least to PortMidi, some
// people's MIDI setups may malfunction and have terrible timing problems.)
PmTimestamp pm_timestamp = portmidi_timestamp_now();
PmError pme = Pm_WriteShort(
midi_mode->portmidi.stream,
pm_timestamp,
Pm_Message(status, byte1, byte2));
(void)pme;
break;
}
#endif
}
}
void send_midi_chan_msg(
Oosc_dev *oosc_dev,
Midi_mode const *midi_mode,
int type /*0..15*/,
int chan /*0.. 15*/,
int byte1 /*0..127*/,
int byte2 /*0..127*/)
{
send_midi_3bytes(oosc_dev, midi_mode, type << 4 | chan, byte1, byte2);
}
void send_midi_note_offs(
Oosc_dev *oosc_dev,
Midi_mode *midi_mode,
Susnote const *start,
Susnote const *end)
{
for (; start != end; ++start) {
#if 0
float under = start->remaining;
if (under < 0.0) {
fprintf(stderr, "cutoff slop: %f\n", under);
}
#endif
U16 chan_note = start->chan_note;
send_midi_chan_msg(oosc_dev, midi_mode, 0x8, chan_note >> 8, chan_note & 0xFF, 0);
}
}
void send_control_message(Oosc_dev *oosc_dev, char const *osc_address)
{
if (!oosc_dev)
return;
oosc_send_int32s(oosc_dev, osc_address, NULL, 0);
}
void send_num_message(Oosc_dev *oosc_dev, char const *osc_address, I32 num)
{
if (!oosc_dev)
return;
I32 nums[1];
nums[0] = num;
oosc_send_int32s(oosc_dev, osc_address, nums, ORCA_ARRAY_COUNTOF(nums));
}
void send_midi_byte(Oosc_dev *oosc_dev, Midi_mode const *midi_mode, int x)
{
// PortMidi wants 0 and 0 for the unused bytes. Likewise, Bidule's
// MIDI-via-OSC won't accept the message unless there are at least all 3
// bytes, with the second 2 set to zero.
send_midi_3bytes(oosc_dev, midi_mode, x, 0, 0);
}

78
src/midi.h

@ -0,0 +1,78 @@
#pragma once
#include "oso.h"
#include "osc_out.h"
#ifdef FEAT_PORTMIDI
#include <portmidi.h>
#endif
typedef enum
{
Midi_mode_type_null,
Midi_mode_type_osc_bidule,
#ifdef FEAT_PORTMIDI
Midi_mode_type_portmidi,
#endif
} Midi_mode_type;
typedef struct {
Midi_mode_type type;
} Midi_mode_any;
typedef struct {
Midi_mode_type type;
char const *path;
} Midi_mode_osc_bidule;
#ifdef FEAT_PORTMIDI
typedef struct {
Midi_mode_type type;
PmDeviceID device_id;
PortMidiStream *stream;
} Midi_mode_portmidi;
// Not sure whether it's OK to call Pm_Terminate() without having a successful
// call to Pm_Initialize() -- let's just treat it with tweezers.
//bool portmidi_is_initialized = false;
#endif
typedef union {
Midi_mode_any any;
Midi_mode_osc_bidule osc_bidule;
#ifdef FEAT_PORTMIDI
Midi_mode_portmidi portmidi;
#endif
} Midi_mode;
void midi_mode_deinit(Midi_mode *mm);
void midi_mode_init_osc_bidule(Midi_mode *mm, char const *path);
void send_midi_note_offs(
Oosc_dev *oosc_dev,
Midi_mode *midi_mode,
Susnote const *start,
Susnote const *end);
void send_midi_chan_msg(
Oosc_dev *oosc_dev,
Midi_mode const *midi_mode,
int type /*0..15*/,
int chan /*0.. 15*/,
int byte1 /*0..127*/,
int byte2 /*0..127*/);
void midi_mode_init_null(Midi_mode *mm);
void send_num_message(Oosc_dev *oosc_dev, char const *osc_address, I32 num);
void send_midi_byte(Oosc_dev *oosc_dev, Midi_mode const *midi_mode, int x);
void send_control_message(Oosc_dev *oosc_dev, char const *osc_address);
PmError portmidi_init_if_necessary(void);
bool portmidi_find_device_id_by_name(char const *name, Usz namelen, PmError *out_pmerror, PmDeviceID *out_id);
PmError midi_mode_init_portmidi(Midi_mode *mm, PmDeviceID dev_id);
bool portmidi_find_name_of_device_id(PmDeviceID id, PmError *out_pmerror, oso **out_name);

1330
src/tui.c

File diff suppressed because it is too large

54
src/tui.h

@ -0,0 +1,54 @@
#pragma once
#include "base.h"
#include "oso.h"
#include "ged.h"
#include "term_util.h"
typedef struct {
Ged *ged;
oso *file_name;
oso *osc_address;
oso *osc_port;
oso *osc_midi_bidule_path;
int undo_history_limit;
int softmargin_y;
int softmargin_x;
int hardmargin_y;
int hardmargin_x;
U32 prefs_touched;
bool use_gui_cboard; // not bitfields due to taking address of
bool strict_timing;
bool osc_output_enabled;
bool fancy_grid_dots;
bool fancy_grid_rulers;
} Tui;
void tui_init(Tui *tui, Ged *ged);
void push_main_menu(void);
void push_controls_msg(void);
void push_opers_guide_msg(void);
void push_open_form(char const *initial);
void tui_load_conf(Tui *tui);
bool tui_suggest_nice_grid_size(Tui *tui, int win_h, int win_w, Usz *out_grid_h, Usz *out_grid_w);
void tui_restart_osc_udp_if_enabled(Tui *tui);
void tui_adjust_term_size(Tui *tui, WINDOW **cont_window);
void tui_try_save(Tui *tui);
typedef enum
{
Tui_menus_nothing = 0,
Tui_menus_quit,
Tui_menus_consumed_input,
} Tui_menus_result;
Tui_menus_result tui_drive_menus(Tui *tui, int key);

3879
src/tui_main.c

File diff suppressed because it is too large
Loading…
Cancel
Save