
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