diff --git a/README.md b/README.md index 10133e7..0ce7841 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ These are configurable in the [`forge_project.cfg`](#forge_projectcfg). ```txt forge steel-dev -usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-arc=CYCLES] [-vi=FILE] [-vs=SIZE] [-vr / -nvr] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-tm] [-tf] +usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-arc=CYCLES] [-vi=FILE] [-vs=SIZE] [-vr / -nvr] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-mr / -nmr] [-tm] [-tf] Fusion Of Real-time Generative Effects. @@ -204,6 +204,8 @@ options: -nls, --no-load-state do not load saved state -ss, --save-state save state (default) -nss, --no-save-state do not save state + -mr, --midi-reconnect auto-reconnect midi (default) + -nmr, --no-midi-reconnect do not auto-reconnect midi -tm, --trace-midi print midi code and values -tf, --trace-fps print fps status of subsystems ``` diff --git a/src/args.c b/src/args.c index bad6ba1..08a7a2f 100644 --- a/src/args.c +++ b/src/args.c @@ -37,6 +37,7 @@ static void print_help(int status_code) { "[-is=SIZE] " "[-ls / -nls] " "[-ss / -nss] " + "[-mr / -nmr] " "[-tm] " "[-tf] " "\n\n" @@ -72,6 +73,8 @@ static void print_help(int status_code) { " -nls, --no-load-state do not load saved state\n" " -ss, --save-state save state (default)\n" " -nss, --no-save-state do not save state\n" + " -mr, --midi-reconnect auto-reconnect midi (default)\n" + " -nmr, --no-midi-reconnect do not auto-reconnect midi\n" " -tm, --trace-midi print midi code and values\n" " -tf, --trace-fps print fps status of subsystems\n"); exit(status_code); @@ -137,6 +140,7 @@ void args_parse(Parameters *params, int argc, char **argv) { params->internal_size = 720; params->load_state = true; params->save_state = true; + params->midi_reconnect = true; params->trace_midi = false; params->trace_fps = false; @@ -225,6 +229,10 @@ void args_parse(Parameters *params, int argc, char **argv) { params->save_state = true; } else if (is_arg(arg, "-nss") || is_arg(arg, "--no-save-state")) { params->save_state = false; + } else if (is_arg(arg, "-mr") || is_arg(arg, "--midi-reconnect")) { + params->midi_reconnect = true; + } else if (is_arg(arg, "-nmr") || is_arg(arg, "--no-midi-reconnect")) { + params->midi_reconnect = false; } else if (is_arg(arg, "-tm") || is_arg(arg, "--trace-midi")) { params->trace_midi = true; } else if (is_arg(arg, "-tf") || is_arg(arg, "--trace-fps")) { diff --git a/src/forge.c b/src/forge.c index 7789270..c07e96b 100644 --- a/src/forge.c +++ b/src/forge.c @@ -181,12 +181,12 @@ static void key_callback(Window *window, int key, log_info("[ESC] Closing..."); window_close(window); } else if (event > 0) { - state_key_event(&context, project.state_config, event, &midi); + state_key_event(&context, project.state_config, event, midi); } } static void midi_callback(unsigned char code, unsigned char value) { - state_midi_event(&context, project.state_config, &midi, code, value, + state_midi_event(&context, project.state_config, midi, code, value, init_params.trace_midi); } @@ -214,6 +214,35 @@ static void start_midi_background_listen() { pthread_detach(thread); } +static void *background_reconnect_midi(__attribute__((unused)) void *args) { + log_info("background midi reconnect started"); + while (!context.stop) { + sleep(1); + if (!midi.connected) { + midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw")); + if (midi.connected) { + start_midi_background_listen(); + } + } + } + pthread_exit(NULL); +} + +static void init_midi() { + pthread_t thread; + + midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw")); + + if (midi.connected) { + start_midi_background_listen(); + } + + if (init_params.midi_reconnect) { + pthread_create(&thread, NULL, background_reconnect_midi, NULL); + pthread_detach(thread); + } +} + static bool init(const Parameters *params) { init_params = *params; @@ -231,13 +260,7 @@ static bool init(const Parameters *params) { start_video_captures(); #endif /* VIDEO_IN */ - midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw")); - - if (midi.error) { - context.demo = true; - } else { - start_midi_background_listen(); - } + init_midi(); start_state_background_write(); @@ -357,6 +380,8 @@ static void shutdown() { free_video_captures(); #endif /* VIDEO_IN */ + midi_close(&midi); + project_free(&project); window_terminate(); diff --git a/src/midi.c b/src/midi.c index e25c8e0..e8958f7 100644 --- a/src/midi.c +++ b/src/midi.c @@ -13,14 +13,17 @@ void midi_open(MidiDevice *device, const char *name) { snd_rawmidi_open(&device->input, &device->output, name, SND_RAWMIDI_NONBLOCK); - device->error = device->input == NULL || device->output == NULL; + device->connected = device->input != NULL && device->output != NULL; - log_info("(%s) MIDI open", name); + if (device->connected) { + log_info("(%s) MIDI open", name); + } else { + log_warn("(%s) MIDI open failed", name); + } } -void midi_write(const MidiDevice *device, unsigned char code, - unsigned char value) { - if (device->error) { +void midi_write(MidiDevice device, unsigned char code, unsigned char value) { + if (!device.connected) { return; } @@ -30,7 +33,15 @@ void midi_write(const MidiDevice *device, unsigned char code, buffer[1] = code; buffer[2] = value; - snd_rawmidi_write(device->output, buffer, 3); + snd_rawmidi_write(device.output, buffer, 3); +} + +void midi_close(MidiDevice *device) { + if (device->connected) { + snd_rawmidi_close(device->input); + snd_rawmidi_close(device->output); + device->connected = false; + } } void *midi_background_listen(void *args) { @@ -39,17 +50,28 @@ void *midi_background_listen(void *args) { Context *context = process_args->context; int bytes_read; + snd_rawmidi_info_t *info; unsigned char buffer[3]; log_info("(%s) background acquisition started", device->name); - while (!context->stop) { + snd_rawmidi_info_malloc(&info); + + while (!context->stop && snd_rawmidi_info(device->output, info) == 0) { bytes_read = snd_rawmidi_read(device->input, buffer, 3); if (bytes_read == 3) { process_args->event_callback(buffer[1], buffer[2]); } } - log_info("(%s) background acquisition stopped by main thread", device->name); + snd_rawmidi_info_free(info); + + if (context->stop) { + log_info("(%s) background acquisition stopped by main thread", + device->name); + } else { + log_info("(%s) background acquisition stopped after error", device->name); + midi_close(device); + } pthread_exit(NULL); } diff --git a/src/midi.h b/src/midi.h index 16422ca..717fb97 100644 --- a/src/midi.h +++ b/src/midi.h @@ -4,8 +4,8 @@ #define MIDI_H void midi_open(MidiDevice *device, const char *name); -void midi_write(const MidiDevice *device, unsigned char code, - unsigned char value); +void midi_write(MidiDevice device, unsigned char code, unsigned char value); void *midi_background_listen(void *args); +void midi_close(MidiDevice *device); #endif /* MIDI_H */ diff --git a/src/state.c b/src/state.c index 3c69b09..d55b138 100644 --- a/src/state.c +++ b/src/state.c @@ -13,7 +13,7 @@ #include "state.h" #include "tempo.h" -static void safe_midi_write(const MidiDevice *midi, unsigned int code, +static void safe_midi_write(MidiDevice midi, unsigned int code, unsigned char value) { if (code != UNSET_MIDI_CODE) { midi_write(midi, code, value); @@ -21,7 +21,7 @@ static void safe_midi_write(const MidiDevice *midi, unsigned int code, } static void update_page(const Context *context, StateConfig state_config, - const MidiDevice *midi) { + MidiDevice midi) { unsigned int page_item_min; unsigned int page_item_max; // SHOW PAGE @@ -51,7 +51,7 @@ static void update_page(const Context *context, StateConfig state_config, } static void update_active(const Context *context, StateConfig state_config, - const MidiDevice *midi) { + MidiDevice midi, bool beat_active) { unsigned int k; for (unsigned int i = 0; i < state_config.midi_active_counts.length; i++) { @@ -59,13 +59,13 @@ static void update_active(const Context *context, StateConfig state_config, j++) { k = state_config.midi_active_offsets.values[i] + j; safe_midi_write(midi, state_config.midi_active_codes.values[k], - context->active[i] == j ? MIDI_MAX : 0); + context->active[i] == j && beat_active ? MIDI_MAX : 0); } } } static void update_values(const Context *context, StateConfig state_config, - const MidiDevice *midi) { + MidiDevice midi) { unsigned int j; unsigned int k; unsigned int part; @@ -407,8 +407,8 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) { } void state_midi_event(Context *context, StateConfig state_config, - const MidiDevice *midi, unsigned char code, - unsigned char value, bool trace_midi) { + MidiDevice midi, unsigned char code, unsigned char value, + bool trace_midi) { unsigned int i; unsigned int j; unsigned int k; @@ -455,7 +455,7 @@ void state_midi_event(Context *context, StateConfig state_config, if (value > 0) { part = arr_uint_remap_index(state_config.midi_active_offsets, &i); context->active[part] = i; - update_active(context, state_config, midi); + update_active(context, state_config, midi, true); update_values(context, state_config, midi); } } @@ -474,17 +474,17 @@ void state_midi_event(Context *context, StateConfig state_config, } else if (value > 0) { if (context->values[k][i % 3] > 0.5) { context->values[k][i % 3] = 0; - midi_write(midi, code, 0); + safe_midi_write(midi, code, 0); } else { context->values[k][i % 3] = 1; - midi_write(midi, code, MIDI_MAX); + safe_midi_write(midi, code, MIDI_MAX); } } } if (code == state_config.tap_tempo_code) { found = true; - midi_write(midi, code, value); + safe_midi_write(midi, code, value); if (value > 0) { tempo_tap(&context->tempo); } @@ -494,14 +494,14 @@ void state_midi_event(Context *context, StateConfig state_config, if (trace_midi) { log_trace("unknown midi: %d %d", code, value); } - midi_write(midi, code, value); + safe_midi_write(midi, code, value); } else if (trace_midi) { log_trace("midi: %d %d", code, value); } } void state_key_event(Context *context, StateConfig state_config, - unsigned int code, const MidiDevice *midi) { + unsigned int code, MidiDevice midi) { unsigned int index; if (code == state_config.hotkey_randomize) { @@ -570,24 +570,18 @@ void *state_background_write(void *args) { log_info("(state) background writing started"); - if (!midi->error) { - update_page(context, state_config, midi); - update_active(context, state_config, midi); - update_values(context, state_config, midi); - } - last_active = false; last_change = false; while (!context->stop) { beat_active = tempo_progress(&context->tempo, 1.0) < 0.5; - if (!midi->error && beat_active != last_active) { - safe_midi_write(midi, state_config.tap_tempo_code, - beat_active ? MIDI_MAX : 0); + if (midi->connected && beat_active != last_active) { + update_values(context, state_config, *midi); + update_page(context, state_config, *midi); + update_active(context, state_config, *midi, beat_active); - safe_midi_write(midi, - state_config.select_frag_codes.values[context->selected], + safe_midi_write(*midi, state_config.tap_tempo_code, beat_active ? MIDI_MAX : 0); } @@ -599,7 +593,7 @@ void *state_background_write(void *args) { if (context->auto_random && change && !last_change) { randomize(context, state_config); - update_values(context, state_config, midi); + update_values(context, state_config, *midi); } last_change = change; diff --git a/src/state.h b/src/state.h index f350b64..ef84d5b 100644 --- a/src/state.h +++ b/src/state.h @@ -6,11 +6,11 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config); void state_midi_event(Context *context, StateConfig state_config, - const MidiDevice *midi, unsigned char code, + MidiDevice midi, unsigned char code, unsigned char value, bool trace_midi); void state_key_event(Context *context, StateConfig state_config, - unsigned int code, const MidiDevice *midi); + unsigned int code, MidiDevice midi); void *state_background_write(void *args); diff --git a/src/types.h b/src/types.h index ee311ac..cad3f97 100644 --- a/src/types.h +++ b/src/types.h @@ -51,6 +51,7 @@ typedef struct Parameters { bool video_reconnect; bool load_state; bool save_state; + bool midi_reconnect; bool trace_midi; bool trace_fps; } Parameters; @@ -219,7 +220,7 @@ typedef struct VideoBackgroundReadArgs { // midi.c typedef struct MidiDevice { - bool error; + _Atomic bool connected; char name[STR_LEN]; snd_rawmidi_t *input; snd_rawmidi_t *output;