feat: load/save state from number hotkeys
This commit is contained in:
+1
-1
@@ -24,6 +24,6 @@ pkg
|
|||||||
forge-*
|
forge-*
|
||||||
confdeps.*
|
confdeps.*
|
||||||
conftest.*
|
conftest.*
|
||||||
forge_saved_state.txt
|
*.txt
|
||||||
error.glsl
|
error.glsl
|
||||||
draft/
|
draft/
|
||||||
+2
-1
@@ -120,7 +120,8 @@ make -f Makefile.dev release-arch
|
|||||||
- [ ] Extra features
|
- [ ] Extra features
|
||||||
- [x] `--auto-random-cycle=4`
|
- [x] `--auto-random-cycle=4`
|
||||||
- [x] Arrows (up-down: bpm / left-right: cycle)
|
- [x] Arrows (up-down: bpm / left-right: cycle)
|
||||||
- [ ] Save states (numkey: load / shift + numkey: save)
|
- [x] Save states (numkey: load / shift + numkey: save)
|
||||||
|
- [ ] (clean) static functions at top of files
|
||||||
- [ ] Configurable key codes
|
- [ ] Configurable key codes
|
||||||
- [ ] Key codes as inputs
|
- [ ] Key codes as inputs
|
||||||
- [ ] Mouse position and scroll as inputs
|
- [ ] Mouse position and scroll as inputs
|
||||||
|
|||||||
@@ -166,11 +166,13 @@ When running, the following hotkeys are available:
|
|||||||
| <kbd>A</kbd> | Auto Random mode On/Off |
|
| <kbd>A</kbd> | Auto Random mode On/Off |
|
||||||
| <kbd>←</kbd> / <kbd>→</kbd> | Auto Random Cycle -/+ 1 |
|
| <kbd>←</kbd> / <kbd>→</kbd> | Auto Random Cycle -/+ 1 |
|
||||||
| <kbd>↑</kbd> / <kbd>↓</kbd> | BPM +/- 1 |
|
| <kbd>↑</kbd> / <kbd>↓</kbd> | BPM +/- 1 |
|
||||||
|
| <kbd>0</kbd>-<kbd>9</kbd> | Load state 0 to 9 |
|
||||||
|
| <kbd>Shift</kbd> + <kbd>0</kbd>-<kbd>9</kbd> | Save state 0 to 9 |
|
||||||
|
|
||||||
### CLI arguments
|
### CLI arguments
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-arc=CYCLES] [-v=FILE] [-vs=SIZE] [-is=SIZE] [-sf=STATE_PATH] [-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] [-v=FILE] [-vs=SIZE] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-tm] [-tf]
|
||||||
|
|
||||||
Fusion Of Real-time Generative Effects.
|
Fusion Of Real-time Generative Effects.
|
||||||
|
|
||||||
@@ -192,7 +194,6 @@ options:
|
|||||||
-v, --video-in path to video capture device (multiple allowed)
|
-v, --video-in path to video capture device (multiple allowed)
|
||||||
-vs, --video-size video capture desired height (default: internal texture height)
|
-vs, --video-size video capture desired height (default: internal texture height)
|
||||||
-is, --internal-size internal texture height (default: 720)
|
-is, --internal-size internal texture height (default: 720)
|
||||||
-sf, --state-file saved state file (default: forge_saved_state.txt)
|
|
||||||
-ls, --load-state load saved state (default)
|
-ls, --load-state load saved state (default)
|
||||||
-nls, --no-load-state do not load saved state
|
-nls, --no-load-state do not load saved state
|
||||||
-ss, --save-state save state (default)
|
-ss, --save-state save state (default)
|
||||||
|
|||||||
@@ -264,3 +264,9 @@ MIDI_3_1_Z=
|
|||||||
MIDI_3_2_X=0
|
MIDI_3_2_X=0
|
||||||
MIDI_3_2_Y=16
|
MIDI_3_2_Y=16
|
||||||
MIDI_3_2_Z=
|
MIDI_3_2_Z=
|
||||||
|
|
||||||
|
# =====
|
||||||
|
# OTHER
|
||||||
|
# =====
|
||||||
|
|
||||||
|
SAVE_FILE_PREFIX=forge_default_save
|
||||||
@@ -33,7 +33,6 @@ static void print_help(int status_code) {
|
|||||||
"[-v=FILE] "
|
"[-v=FILE] "
|
||||||
"[-vs=SIZE] "
|
"[-vs=SIZE] "
|
||||||
"[-is=SIZE] "
|
"[-is=SIZE] "
|
||||||
"[-sf=STATE_PATH] "
|
|
||||||
"[-ls / -nls] "
|
"[-ls / -nls] "
|
||||||
"[-ss / -nss] "
|
"[-ss / -nss] "
|
||||||
"[-tm] "
|
"[-tm] "
|
||||||
@@ -63,8 +62,6 @@ static void print_help(int status_code) {
|
|||||||
" -vs, --video-size video capture desired height (default: "
|
" -vs, --video-size video capture desired height (default: "
|
||||||
"internal texture height)\n"
|
"internal texture height)\n"
|
||||||
" -is, --internal-size internal texture height (default: 720)\n"
|
" -is, --internal-size internal texture height (default: 720)\n"
|
||||||
" -sf, --state-file saved state file (default: "
|
|
||||||
"forge_saved_state.txt)\n"
|
|
||||||
" -ls, --load-state load saved state (default)\n"
|
" -ls, --load-state load saved state (default)\n"
|
||||||
" -nls, --no-load-state do not load saved state\n"
|
" -nls, --no-load-state do not load saved state\n"
|
||||||
" -ss, --save-state save state (default)\n"
|
" -ss, --save-state save state (default)\n"
|
||||||
@@ -131,7 +128,6 @@ void args_parse(Parameters *params, int argc, char **argv) {
|
|||||||
params->video_in.length = 0;
|
params->video_in.length = 0;
|
||||||
params->video_size = 0;
|
params->video_size = 0;
|
||||||
params->internal_size = 720;
|
params->internal_size = 720;
|
||||||
strlcpy(params->state_file, "forge_saved_state.txt", STR_LEN);
|
|
||||||
params->load_state = true;
|
params->load_state = true;
|
||||||
params->save_state = true;
|
params->save_state = true;
|
||||||
params->trace_midi = false;
|
params->trace_midi = false;
|
||||||
@@ -194,8 +190,6 @@ void args_parse(Parameters *params, int argc, char **argv) {
|
|||||||
if (params->internal_size == 0) {
|
if (params->internal_size == 0) {
|
||||||
invalid_value(arg, value);
|
invalid_value(arg, value);
|
||||||
}
|
}
|
||||||
} else if (is_arg(arg, "-sf") || is_arg(arg, "--state-file")) {
|
|
||||||
strlcpy(params->state_file, value, STR_LEN);
|
|
||||||
} else if (is_arg(arg, "-ls") || is_arg(arg, "--load-state")) {
|
} else if (is_arg(arg, "-ls") || is_arg(arg, "--load-state")) {
|
||||||
params->load_state = true;
|
params->load_state = true;
|
||||||
} else if (is_arg(arg, "-nls") || is_arg(arg, "--no-load-state")) {
|
} else if (is_arg(arg, "-nls") || is_arg(arg, "--no-load-state")) {
|
||||||
|
|||||||
@@ -75,9 +75,12 @@ void config_file_read(ConfigFile *config, const char *path) {
|
|||||||
file_read(&file, path);
|
file_read(&file, path);
|
||||||
|
|
||||||
if (file.error) {
|
if (file.error) {
|
||||||
|
config->error = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config->error = false;
|
||||||
|
|
||||||
line = strtok_r(file.content, "\n", &rest);
|
line = strtok_r(file.content, "\n", &rest);
|
||||||
|
|
||||||
while (line != NULL) {
|
while (line != NULL) {
|
||||||
|
|||||||
+2
-3
@@ -59,8 +59,7 @@ static void compute_fps(bool trace_fps) {
|
|||||||
|
|
||||||
static void init_context(const Parameters *params, unsigned int in_count) {
|
static void init_context(const Parameters *params, unsigned int in_count) {
|
||||||
state_init(context, &project.state_config, params->demo, params->auto_random,
|
state_init(context, &project.state_config, params->demo, params->auto_random,
|
||||||
params->auto_random_cycle, params->base_tempo, params->state_file,
|
params->auto_random_cycle, params->base_tempo, params->load_state);
|
||||||
params->load_state);
|
|
||||||
|
|
||||||
context->monitor = params->monitor;
|
context->monitor = params->monitor;
|
||||||
|
|
||||||
@@ -247,7 +246,7 @@ void forge_run(const Parameters *params) {
|
|||||||
context->stop = true;
|
context->stop = true;
|
||||||
|
|
||||||
if (params->save_state) {
|
if (params->save_state) {
|
||||||
state_save(context, &project.state_config, params->state_file);
|
state_save(context, &project.state_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
shaders_free(&program);
|
shaders_free(&program);
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ void midi_open(MidiDevice *device, const char *name) {
|
|||||||
|
|
||||||
void midi_write(const MidiDevice *device, unsigned char code,
|
void midi_write(const MidiDevice *device, unsigned char code,
|
||||||
unsigned char value) {
|
unsigned char value) {
|
||||||
|
if (device->error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char buffer[3];
|
unsigned char buffer[3];
|
||||||
|
|
||||||
buffer[0] = 0xB0;
|
buffer[0] = 0xB0;
|
||||||
|
|||||||
+239
-181
@@ -12,6 +12,232 @@
|
|||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "tempo.h"
|
#include "tempo.h"
|
||||||
|
|
||||||
|
static void safe_midi_write(const MidiDevice *midi, unsigned int code,
|
||||||
|
unsigned char value) {
|
||||||
|
if (code != UNSET_MIDI_CODE) {
|
||||||
|
midi_write(midi, code, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_page(const SharedContext *context,
|
||||||
|
const StateConfig *state_config,
|
||||||
|
const MidiDevice *midi) {
|
||||||
|
unsigned int page_item_min;
|
||||||
|
unsigned int page_item_max;
|
||||||
|
// SHOW PAGE
|
||||||
|
for (unsigned int i = 0; i < state_config->select_page_codes.length; i++) {
|
||||||
|
safe_midi_write(midi, state_config->select_page_codes.values[i],
|
||||||
|
i == context->page ? MIDI_MAX : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHOW PAGE ITEM
|
||||||
|
page_item_min = state_config->select_item_codes.length * context->page;
|
||||||
|
page_item_max = page_item_min + state_config->select_item_codes.length;
|
||||||
|
|
||||||
|
if (context->state.values[context->selected] >= page_item_min &&
|
||||||
|
context->state.values[context->selected] < page_item_max) {
|
||||||
|
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) {
|
||||||
|
safe_midi_write(midi, state_config->select_item_codes.values[i],
|
||||||
|
i == context->state.values[context->selected] -
|
||||||
|
page_item_min
|
||||||
|
? MIDI_MAX
|
||||||
|
: 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) {
|
||||||
|
safe_midi_write(midi, state_config->select_item_codes.values[i], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_active(const SharedContext *context,
|
||||||
|
const StateConfig *state_config,
|
||||||
|
const MidiDevice *midi) {
|
||||||
|
unsigned int k;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
|
||||||
|
for (unsigned int j = 0; j < state_config->midi_active_counts.values[i];
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_values(const SharedContext *context,
|
||||||
|
const StateConfig *state_config,
|
||||||
|
const MidiDevice *midi) {
|
||||||
|
unsigned int j;
|
||||||
|
unsigned int k;
|
||||||
|
unsigned int part;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < state_config->midi_codes.length; i++) {
|
||||||
|
j = i / 3;
|
||||||
|
part = arr_uint_remap_index(state_config->midi_offsets, &j);
|
||||||
|
k = state_config->values_offsets.values[part] +
|
||||||
|
context->active[part] * state_config->midi_counts.values[part] + j;
|
||||||
|
safe_midi_write(midi, state_config->midi_codes.values[i],
|
||||||
|
context->values[k][i % 3] * MIDI_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset(SharedContext *context) {
|
||||||
|
memset(context->values, 0, sizeof(context->values));
|
||||||
|
memset(context->state.values, 0, sizeof(context->state.values));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void randomize(SharedContext *context, const StateConfig *state_config) {
|
||||||
|
unsigned int j;
|
||||||
|
unsigned int l;
|
||||||
|
unsigned int part;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < state_config->midi_codes.length; i++) {
|
||||||
|
j = i / 3;
|
||||||
|
part = arr_uint_remap_index(state_config->midi_offsets, &j);
|
||||||
|
for (unsigned int k = 0; k < state_config->midi_active_counts.values[part];
|
||||||
|
k++) {
|
||||||
|
l = state_config->values_offsets.values[part] +
|
||||||
|
k * state_config->midi_counts.values[part] + j;
|
||||||
|
|
||||||
|
if (arr_uint_index_of(state_config->fader_codes,
|
||||||
|
state_config->midi_codes.values[i]) !=
|
||||||
|
ARRAY_NOT_FOUND) {
|
||||||
|
context->values[l][i % 3] = (float)rand_uint(MIDI_MAX + 1) / MIDI_MAX;
|
||||||
|
} else {
|
||||||
|
context->values[l][i % 3] = rand_uint(2) == 1 ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < context->state.length; i++) {
|
||||||
|
context->state.values[i] = rand_uint(state_config->state_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_from_file(SharedContext *context,
|
||||||
|
const StateConfig *state_config, char *state_file) {
|
||||||
|
ConfigFile saved_state;
|
||||||
|
char key[STR_LEN];
|
||||||
|
|
||||||
|
config_file_read(&saved_state, state_file);
|
||||||
|
|
||||||
|
if (saved_state.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempo_set(&context->tempo,
|
||||||
|
config_file_get_int(&saved_state, "tempo", context->tempo.tempo));
|
||||||
|
context->page = config_file_get_int(&saved_state, "page", 0);
|
||||||
|
context->selected = config_file_get_int(&saved_state, "selected", 0);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < context->state.length; i++) {
|
||||||
|
snprintf(key, STR_LEN, "seed_%d", i);
|
||||||
|
context->seeds[i] =
|
||||||
|
config_file_get_int(&saved_state, key, context->seeds[i]);
|
||||||
|
snprintf(key, STR_LEN, "state_%d", i);
|
||||||
|
context->state.values[i] = config_file_get_int(&saved_state, key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
|
||||||
|
snprintf(key, STR_LEN, "active_%d", i);
|
||||||
|
context->active[i] = config_file_get_int(&saved_state, key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < state_config->value_count; i++) {
|
||||||
|
snprintf(key, STR_LEN, "value_%d_x", i);
|
||||||
|
context->values[i][0] =
|
||||||
|
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
|
||||||
|
snprintf(key, STR_LEN, "value_%d_y", i);
|
||||||
|
context->values[i][1] =
|
||||||
|
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
|
||||||
|
snprintf(key, STR_LEN, "value_%d_z", i);
|
||||||
|
context->values[i][2] =
|
||||||
|
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_file_free(&saved_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_from_default_file(SharedContext *context,
|
||||||
|
const StateConfig *state_config) {
|
||||||
|
char state_file[STR_LEN];
|
||||||
|
|
||||||
|
snprintf(state_file, STR_LEN, "%s.txt", state_config->save_file_prefix);
|
||||||
|
|
||||||
|
load_from_file(context, state_config, state_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_from_index_file(SharedContext *context,
|
||||||
|
const StateConfig *state_config,
|
||||||
|
unsigned int index) {
|
||||||
|
char state_file[STR_LEN];
|
||||||
|
|
||||||
|
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config->save_file_prefix,
|
||||||
|
index);
|
||||||
|
|
||||||
|
load_from_file(context, state_config, state_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void save_to_file(const SharedContext *context,
|
||||||
|
const StateConfig *state_config,
|
||||||
|
const char *state_file) {
|
||||||
|
StringArray lines;
|
||||||
|
|
||||||
|
log_info("Saving state to '%s'...", state_file);
|
||||||
|
|
||||||
|
lines.length = 0;
|
||||||
|
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "tempo=%d",
|
||||||
|
(unsigned int)context->tempo.tempo);
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "page=%d", context->page);
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "selected=%d",
|
||||||
|
context->selected);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < context->state.length; i++) {
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "seed_%d=%d", i,
|
||||||
|
context->seeds[i]);
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "state_%d=%d", i,
|
||||||
|
context->state.values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "active_%d=%d", i,
|
||||||
|
context->active[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < state_config->value_count; i++) {
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_x=%d", i,
|
||||||
|
(unsigned int)(context->values[i][0] * MIDI_MAX));
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_y=%d", i,
|
||||||
|
(unsigned int)(context->values[i][1] * MIDI_MAX));
|
||||||
|
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_z=%d", i,
|
||||||
|
(unsigned int)(context->values[i][2] * MIDI_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
file_write(state_file, &lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void save_to_default_file(const SharedContext *context,
|
||||||
|
const StateConfig *state_config) {
|
||||||
|
char state_file[STR_LEN];
|
||||||
|
|
||||||
|
snprintf(state_file, STR_LEN, "%s.txt", state_config->save_file_prefix);
|
||||||
|
|
||||||
|
save_to_file(context, state_config, state_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void save_to_index_file(const SharedContext *context,
|
||||||
|
const StateConfig *state_config,
|
||||||
|
unsigned int index) {
|
||||||
|
char state_file[STR_LEN];
|
||||||
|
|
||||||
|
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config->save_file_prefix,
|
||||||
|
index);
|
||||||
|
|
||||||
|
save_to_file(context, state_config, state_file);
|
||||||
|
}
|
||||||
|
|
||||||
void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||||
unsigned int offset;
|
unsigned int offset;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
@@ -118,76 +344,10 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
|||||||
|
|
||||||
state_config->tap_tempo_code =
|
state_config->tap_tempo_code =
|
||||||
config_file_get_int(config, "TAP_TEMPO", UNSET_MIDI_CODE);
|
config_file_get_int(config, "TAP_TEMPO", UNSET_MIDI_CODE);
|
||||||
}
|
|
||||||
|
|
||||||
static void safe_midi_write(const MidiDevice *midi, unsigned int code,
|
strlcpy(state_config->save_file_prefix,
|
||||||
unsigned char value) {
|
config_file_get_str(config, "SAVE_FILE_PREFIX", "forge_save"),
|
||||||
if (code != UNSET_MIDI_CODE) {
|
STR_LEN);
|
||||||
midi_write(midi, code, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_page(const SharedContext *context,
|
|
||||||
const StateConfig *state_config,
|
|
||||||
const MidiDevice *midi) {
|
|
||||||
unsigned int page_item_min;
|
|
||||||
unsigned int page_item_max;
|
|
||||||
// SHOW PAGE
|
|
||||||
for (unsigned int i = 0; i < state_config->select_page_codes.length; i++) {
|
|
||||||
safe_midi_write(midi, state_config->select_page_codes.values[i],
|
|
||||||
i == context->page ? MIDI_MAX : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHOW PAGE ITEM
|
|
||||||
page_item_min = state_config->select_item_codes.length * context->page;
|
|
||||||
page_item_max = page_item_min + state_config->select_item_codes.length;
|
|
||||||
|
|
||||||
if (context->state.values[context->selected] >= page_item_min &&
|
|
||||||
context->state.values[context->selected] < page_item_max) {
|
|
||||||
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) {
|
|
||||||
safe_midi_write(midi, state_config->select_item_codes.values[i],
|
|
||||||
i == context->state.values[context->selected] -
|
|
||||||
page_item_min
|
|
||||||
? MIDI_MAX
|
|
||||||
: 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) {
|
|
||||||
safe_midi_write(midi, state_config->select_item_codes.values[i], 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_active(const SharedContext *context,
|
|
||||||
const StateConfig *state_config,
|
|
||||||
const MidiDevice *midi) {
|
|
||||||
unsigned int k;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
|
|
||||||
for (unsigned int j = 0; j < state_config->midi_active_counts.values[i];
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_values(const SharedContext *context,
|
|
||||||
const StateConfig *state_config,
|
|
||||||
const MidiDevice *midi) {
|
|
||||||
unsigned int j;
|
|
||||||
unsigned int k;
|
|
||||||
unsigned int part;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < state_config->midi_codes.length; i++) {
|
|
||||||
j = i / 3;
|
|
||||||
part = arr_uint_remap_index(state_config->midi_offsets, &j);
|
|
||||||
k = state_config->values_offsets.values[part] +
|
|
||||||
context->active[part] * state_config->midi_counts.values[part] + j;
|
|
||||||
safe_midi_write(midi, state_config->midi_codes.values[i],
|
|
||||||
context->values[k][i % 3] * MIDI_MAX);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void state_midi_event(SharedContext *context, const StateConfig *state_config,
|
void state_midi_event(SharedContext *context, const StateConfig *state_config,
|
||||||
@@ -281,39 +441,6 @@ void state_midi_event(SharedContext *context, const StateConfig *state_config,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset(SharedContext *context) {
|
|
||||||
memset(context->values, 0, sizeof(context->values));
|
|
||||||
memset(context->state.values, 0, sizeof(context->state.values));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void randomize(SharedContext *context, const StateConfig *state_config) {
|
|
||||||
unsigned int j;
|
|
||||||
unsigned int l;
|
|
||||||
unsigned int part;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < state_config->midi_codes.length; i++) {
|
|
||||||
j = i / 3;
|
|
||||||
part = arr_uint_remap_index(state_config->midi_offsets, &j);
|
|
||||||
for (unsigned int k = 0; k < state_config->midi_active_counts.values[part];
|
|
||||||
k++) {
|
|
||||||
l = state_config->values_offsets.values[part] +
|
|
||||||
k * state_config->midi_counts.values[part] + j;
|
|
||||||
|
|
||||||
if (arr_uint_index_of(state_config->fader_codes,
|
|
||||||
state_config->midi_codes.values[i]) !=
|
|
||||||
ARRAY_NOT_FOUND) {
|
|
||||||
context->values[l][i % 3] = (float)rand_uint(MIDI_MAX + 1) / MIDI_MAX;
|
|
||||||
} else {
|
|
||||||
context->values[l][i % 3] = rand_uint(2) == 1 ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < context->state.length; i++) {
|
|
||||||
context->state.values[i] = rand_uint(state_config->state_max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_key_event(SharedContext *context, const StateConfig *state_config,
|
void state_key_event(SharedContext *context, const StateConfig *state_config,
|
||||||
unsigned int code, const MidiDevice *midi) {
|
unsigned int code, const MidiDevice *midi) {
|
||||||
if (code == 82) {
|
if (code == 82) {
|
||||||
@@ -354,6 +481,12 @@ void state_key_event(SharedContext *context, const StateConfig *state_config,
|
|||||||
tempo_set(&context->tempo, context->tempo.tempo - 1);
|
tempo_set(&context->tempo, context->tempo.tempo - 1);
|
||||||
}
|
}
|
||||||
log_info("[DOWN] Tempo: %f", context->tempo);
|
log_info("[DOWN] Tempo: %f", context->tempo);
|
||||||
|
} else if (code >= 48 && code <= 57) {
|
||||||
|
log_info("[%d] Loading state %d", code - 48, code - 48);
|
||||||
|
load_from_index_file(context, state_config, code - 48);
|
||||||
|
} else if (code >= 1048 && code <= 1057) {
|
||||||
|
log_info("[%d] Saving state %d", code - 1048, code - 1048);
|
||||||
|
save_to_index_file(context, state_config, code - 1048);
|
||||||
} else {
|
} else {
|
||||||
log_info("[%d] No hotkey defined", code);
|
log_info("[%d] No hotkey defined", code);
|
||||||
}
|
}
|
||||||
@@ -414,50 +547,9 @@ bool state_background_write(SharedContext *context,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void state_load(SharedContext *context, const StateConfig *state_config,
|
|
||||||
const char *state_file) {
|
|
||||||
ConfigFile saved_state;
|
|
||||||
char key[STR_LEN];
|
|
||||||
|
|
||||||
config_file_read(&saved_state, state_file);
|
|
||||||
|
|
||||||
tempo_set(&context->tempo,
|
|
||||||
config_file_get_int(&saved_state, "tempo", context->tempo.tempo));
|
|
||||||
context->page = config_file_get_int(&saved_state, "page", 0);
|
|
||||||
context->selected = config_file_get_int(&saved_state, "selected", 0);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < context->state.length; i++) {
|
|
||||||
snprintf(key, STR_LEN, "seed_%d", i);
|
|
||||||
context->seeds[i] =
|
|
||||||
config_file_get_int(&saved_state, key, context->seeds[i]);
|
|
||||||
snprintf(key, STR_LEN, "state_%d", i);
|
|
||||||
context->state.values[i] = config_file_get_int(&saved_state, key, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
|
|
||||||
snprintf(key, STR_LEN, "active_%d", i);
|
|
||||||
context->active[i] = config_file_get_int(&saved_state, key, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < state_config->value_count; i++) {
|
|
||||||
snprintf(key, STR_LEN, "value_%d_x", i);
|
|
||||||
context->values[i][0] =
|
|
||||||
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
|
|
||||||
snprintf(key, STR_LEN, "value_%d_y", i);
|
|
||||||
context->values[i][1] =
|
|
||||||
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
|
|
||||||
snprintf(key, STR_LEN, "value_%d_z", i);
|
|
||||||
context->values[i][2] =
|
|
||||||
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
config_file_free(&saved_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_init(SharedContext *context, const StateConfig *state_config,
|
void state_init(SharedContext *context, const StateConfig *state_config,
|
||||||
bool demo, bool auto_random, unsigned int auto_random_cycles,
|
bool demo, bool auto_random, unsigned int auto_random_cycles,
|
||||||
unsigned int base_tempo, const char *state_file,
|
unsigned int base_tempo, bool load_state) {
|
||||||
bool load_state) {
|
|
||||||
tempo_init(&context->tempo);
|
tempo_init(&context->tempo);
|
||||||
tempo_set(&context->tempo, base_tempo);
|
tempo_set(&context->tempo, base_tempo);
|
||||||
context->demo = demo;
|
context->demo = demo;
|
||||||
@@ -484,44 +576,10 @@ void state_init(SharedContext *context, const StateConfig *state_config,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (load_state) {
|
if (load_state) {
|
||||||
state_load(context, state_config, state_file);
|
load_from_default_file(context, state_config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void state_save(const SharedContext *context, const StateConfig *state_config,
|
void state_save(const SharedContext *context, const StateConfig *state_config) {
|
||||||
const char *state_file) {
|
save_to_default_file(context, state_config);
|
||||||
StringArray lines;
|
|
||||||
|
|
||||||
log_info("Saving state to '%s'...", state_file);
|
|
||||||
|
|
||||||
lines.length = 0;
|
|
||||||
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "tempo=%d",
|
|
||||||
(unsigned int)context->tempo.tempo);
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "page=%d", context->page);
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "selected=%d",
|
|
||||||
context->selected);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < context->state.length; i++) {
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "seed_%d=%d", i,
|
|
||||||
context->seeds[i]);
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "state_%d=%d", i,
|
|
||||||
context->state.values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "active_%d=%d", i,
|
|
||||||
context->active[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < state_config->value_count; i++) {
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_x=%d", i,
|
|
||||||
(unsigned int)(context->values[i][0] * MIDI_MAX));
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_y=%d", i,
|
|
||||||
(unsigned int)(context->values[i][1] * MIDI_MAX));
|
|
||||||
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_z=%d", i,
|
|
||||||
(unsigned int)(context->values[i][2] * MIDI_MAX));
|
|
||||||
}
|
|
||||||
|
|
||||||
file_write(state_file, &lines);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -18,10 +18,8 @@ bool state_background_write(SharedContext *context,
|
|||||||
|
|
||||||
void state_init(SharedContext *context, const StateConfig *state_config,
|
void state_init(SharedContext *context, const StateConfig *state_config,
|
||||||
bool demo, bool auto_random, unsigned int auto_random_cycles,
|
bool demo, bool auto_random, unsigned int auto_random_cycles,
|
||||||
unsigned int base_tempo, const char *state_file,
|
unsigned int base_tempo, bool load_state);
|
||||||
bool load_state);
|
|
||||||
|
|
||||||
void state_save(const SharedContext *context, const StateConfig *state_config,
|
void state_save(const SharedContext *context, const StateConfig *state_config);
|
||||||
const char *state_file);
|
|
||||||
|
|
||||||
#endif /* STATE_H */
|
#endif /* STATE_H */
|
||||||
+3
-1
@@ -47,7 +47,6 @@ typedef struct Parameters {
|
|||||||
StringArray video_in;
|
StringArray video_in;
|
||||||
unsigned int video_size;
|
unsigned int video_size;
|
||||||
unsigned int internal_size;
|
unsigned int internal_size;
|
||||||
char state_file[STR_LEN];
|
|
||||||
bool load_state;
|
bool load_state;
|
||||||
bool save_state;
|
bool save_state;
|
||||||
bool trace_midi;
|
bool trace_midi;
|
||||||
@@ -213,6 +212,8 @@ typedef struct StateConfig {
|
|||||||
unsigned int value_count;
|
unsigned int value_count;
|
||||||
|
|
||||||
unsigned int tap_tempo_code;
|
unsigned int tap_tempo_code;
|
||||||
|
|
||||||
|
char save_file_prefix[STR_LEN];
|
||||||
} StateConfig;
|
} StateConfig;
|
||||||
|
|
||||||
// timer.c
|
// timer.c
|
||||||
@@ -227,6 +228,7 @@ typedef struct Timer {
|
|||||||
|
|
||||||
typedef struct ConfigFile {
|
typedef struct ConfigFile {
|
||||||
struct hashmap *map;
|
struct hashmap *map;
|
||||||
|
bool error;
|
||||||
} ConfigFile;
|
} ConfigFile;
|
||||||
|
|
||||||
typedef struct ConfigFileItem {
|
typedef struct ConfigFileItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user