
8 changed files with 3921 additions and 3678 deletions
File diff suppressed because it is too large
@ -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); |
||||
|
|
@ -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); |
||||
|
} |
@ -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); |
File diff suppressed because it is too large
@ -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); |
File diff suppressed because it is too large
Loading…
Reference in new issue