feat: auto-reconnect midi
Clang Lint CI / lint-no-video (push) Successful in 59s
Clang Build CI / run-no-video (push) Successful in 59s
Clang Build CI / run-video (push) Successful in 59s
Clang Build CI / build-release (push) Successful in 1m31s
Clang Lint CI / lint-video (push) Successful in 1m7s
Clang Lint CI / lint-no-video (push) Successful in 59s
Clang Build CI / run-no-video (push) Successful in 59s
Clang Build CI / run-video (push) Successful in 59s
Clang Build CI / build-release (push) Successful in 1m31s
Clang Lint CI / lint-video (push) Successful in 1m7s
This commit is contained in:
@@ -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
|
||||
```
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
+34
-9
@@ -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();
|
||||
|
||||
+30
-8
@@ -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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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 */
|
||||
|
||||
+19
-25
@@ -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;
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user