feat: hotkeys in config

This commit is contained in:
2025-11-14 11:49:02 +01:00
parent 4ddb5241b4
commit d094a6c895
9 changed files with 189 additions and 35 deletions
+1 -1
View File
@@ -122,7 +122,7 @@ make -f Makefile.dev release-arch
- [x] Arrows (up-down: bpm / left-right: cycle) - [x] Arrows (up-down: bpm / left-right: cycle)
- [x] Save states (numkey: load / shift + numkey: save) - [x] Save states (numkey: load / shift + numkey: save)
- [ ] (clean) static functions at top of files - [ ] (clean) static functions at top of files
- [ ] Configurable key codes - [x] Configurable key codes
- [ ] Key codes as inputs - [ ] Key codes as inputs
- [ ] Mouse position and scroll as inputs - [ ] Mouse position and scroll as inputs
- [ ] Joystick as input - [ ] Joystick as input
+2
View File
@@ -169,6 +169,8 @@ When running, the following hotkeys are available:
| <kbd>0</kbd>-<kbd>9</kbd> | Load state 0 to 9 | | <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 | | <kbd>Shift</kbd> + <kbd>0</kbd>-<kbd>9</kbd> | Save state 0 to 9 |
These are configurable in the [`forge_project.cfg`](#forge_projectcfg).
### CLI arguments ### CLI arguments
```txt ```txt
+58 -1
View File
@@ -269,4 +269,61 @@ MIDI_3_2_Z=
# OTHER # OTHER
# ===== # =====
SAVE_FILE_PREFIX=forge_default_save # === SAVE FILES
# When loading/saving from last run or using save states
SAVE_FILE_PREFIX=forge_default_save
# === HOTKEYS
# You can change the default keycodes used on runtime
# Modifiers are encoded like this:
# 1000 -> shift
# 10000 -> control
# 100000 -> alt
# This means 110082 is control+alt+R
# R on (qwerty)
HOTKEY_RANDOMIZE=82
# SHIFT+R (qwerty)
HOTKEY_RESET=1082
# D (qwerty)
HOTKEY_DEMO=68
# A (qwerty)
HOTKEY_AUTORAND=65
# Left arrow
HOTKEY_AUTORAND_DOWN=263
# Right arrow
HOTKEY_AUTORAND_UP=262
# Down arrow
HOTKEY_TEMPO_DOWN=264
# Up arrow
HOTKEY_TEMPO_UP=265
# Number of load states keys
HOTKEY_LOAD_COUNT=10
# 1 to 9 then 0 keys
HOTKEY_LOAD_1=49
HOTKEY_LOAD_2=50
HOTKEY_LOAD_3=51
HOTKEY_LOAD_4=52
HOTKEY_LOAD_5=53
HOTKEY_LOAD_6=54
HOTKEY_LOAD_7=55
HOTKEY_LOAD_8=56
HOTKEY_LOAD_9=57
HOTKEY_LOAD_10=48
# Number of save states keys
HOTKEY_SAVE_COUNT=10
# 1 to 9 then 0 keys with shift
HOTKEY_SAVE_1=1049
HOTKEY_SAVE_2=1050
HOTKEY_SAVE_3=1051
HOTKEY_SAVE_4=1052
HOTKEY_SAVE_5=1053
HOTKEY_SAVE_6=1054
HOTKEY_SAVE_7=1055
HOTKEY_SAVE_8=1056
HOTKEY_SAVE_9=1057
HOTKEY_SAVE_10=1048
+11
View File
@@ -91,6 +91,17 @@ void config_file_read(ConfigFile *config, const char *path) {
file_free(&file); file_free(&file);
} }
bool config_file_has(const ConfigFile *config, const char *key) {
ConfigFileItem c_key;
const ConfigFileItem *item;
strlcpy(c_key.key, key, STR_LEN);
item = (const ConfigFileItem *)hashmap_get(config->map, &c_key);
return item != NULL && strnlen(item->value, STR_LEN) > 0;
}
const char *config_file_get_str(const ConfigFile *config, const char *key, const char *config_file_get_str(const ConfigFile *config, const char *key,
const char *default_value) { const char *default_value) {
ConfigFileItem c_key; ConfigFileItem c_key;
+2
View File
@@ -5,6 +5,8 @@
void config_file_read(ConfigFile *config, const char *path); void config_file_read(ConfigFile *config, const char *path);
bool config_file_has(const ConfigFile *config, const char *key);
const char *config_file_get_str(const ConfigFile *config, const char *key, const char *config_file_get_str(const ConfigFile *config, const char *key,
const char *default_value); const char *default_value);
+98 -31
View File
@@ -348,6 +348,68 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
strlcpy(state_config->save_file_prefix, strlcpy(state_config->save_file_prefix,
config_file_get_str(config, "SAVE_FILE_PREFIX", "forge_save"), config_file_get_str(config, "SAVE_FILE_PREFIX", "forge_save"),
STR_LEN); STR_LEN);
state_config->hotkey_randomize =
config_file_get_int(config, "HOTKEY_RANDOMIZE", 82);
state_config->hotkey_reset =
config_file_get_int(config, "HOTKEY_RESET", 1082);
state_config->hotkey_demo = config_file_get_int(config, "HOTKEY_DEMO", 68);
state_config->hotkey_autorand =
config_file_get_int(config, "HOTKEY_AUTORAND", 65);
state_config->hotkey_autorand_down =
config_file_get_int(config, "HOTKEY_AUTORAND_DOWN", 263);
state_config->hotkey_autorand_up =
config_file_get_int(config, "HOTKEY_AUTORAND_UP", 262);
state_config->hotkey_tempo_down =
config_file_get_int(config, "HOTKEY_TEMPO_DOWN", 264);
state_config->hotkey_tempo_up =
config_file_get_int(config, "HOTKEY_TEMPO_UP", 265);
if (config_file_has(config, "HOTKEY_LOAD_COUNT")) {
state_config->hotkey_load.length =
config_file_get_int(config, "HOTKEY_LOAD_COUNT", 0);
for (unsigned int i = 0; i < state_config->hotkey_load.length; i++) {
snprintf(name, STR_LEN, "HOTKEY_LOAD_%d", i + 1);
state_config->hotkey_load.values[i] =
config_file_get_int(config, name, 0);
}
} else {
state_config->hotkey_load.length = 10;
state_config->hotkey_load.values[0] = 49;
state_config->hotkey_load.values[1] = 50;
state_config->hotkey_load.values[2] = 51;
state_config->hotkey_load.values[3] = 52;
state_config->hotkey_load.values[4] = 53;
state_config->hotkey_load.values[5] = 54;
state_config->hotkey_load.values[6] = 55;
state_config->hotkey_load.values[7] = 56;
state_config->hotkey_load.values[8] = 57;
state_config->hotkey_load.values[9] = 48;
}
if (config_file_has(config, "HOTKEY_SAVE_COUNT")) {
state_config->hotkey_save.length =
config_file_get_int(config, "HOTKEY_SAVE_COUNT", 0);
for (unsigned int i = 0; i < state_config->hotkey_save.length; i++) {
snprintf(name, STR_LEN, "HOTKEY_SAVE_%d", i + 1);
state_config->hotkey_save.values[i] =
config_file_get_int(config, name, 0);
}
} else {
state_config->hotkey_save.length = 10;
state_config->hotkey_save.values[0] = 1049;
state_config->hotkey_save.values[1] = 1050;
state_config->hotkey_save.values[2] = 1051;
state_config->hotkey_save.values[3] = 1052;
state_config->hotkey_save.values[4] = 1053;
state_config->hotkey_save.values[5] = 1054;
state_config->hotkey_save.values[6] = 1055;
state_config->hotkey_save.values[7] = 1056;
state_config->hotkey_save.values[8] = 1057;
state_config->hotkey_save.values[9] = 1048;
}
} }
void state_midi_event(SharedContext *context, const StateConfig *state_config, void state_midi_event(SharedContext *context, const StateConfig *state_config,
@@ -443,51 +505,57 @@ void state_midi_event(SharedContext *context, const StateConfig *state_config,
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) { unsigned int index;
// R: randomize
log_info("[R] Randomized"); if (code == state_config->hotkey_randomize) {
log_info("[%d] Randomized", code);
randomize(context, state_config); randomize(context, state_config);
update_values(context, state_config, midi); update_values(context, state_config, midi);
} else if (code == 1082) { } else if (code == state_config->hotkey_reset) {
log_info("[SHIFT+R] Reset"); log_info("[%d] Reset", code);
reset(context); reset(context);
update_values(context, state_config, midi); update_values(context, state_config, midi);
} else if (code == 68) { } else if (code == state_config->hotkey_demo) {
// D: demo on/off log_info((context->demo ? "[%d] Demo OFF" : "[%d] Demo ON"), code);
log_info((context->demo ? "[D] Demo OFF" : "[D] Demo ON"));
context->demo = !context->demo; context->demo = !context->demo;
} else if (code == 65) { } else if (code == state_config->hotkey_autorand) {
// A: auto random on/off
log_info( log_info(
(context->auto_random ? "[A] Auto Random OFF" : "[A] Auto Random ON")); (context->auto_random ? "[%d] Auto Random OFF" : "[%d] Auto Random ON"),
code);
context->auto_random = !context->auto_random; context->auto_random = !context->auto_random;
} else if (code == 263) { } else if (code == state_config->hotkey_autorand_down) {
// LEFT ARROW
if (context->auto_random_cycle > 1) { if (context->auto_random_cycle > 1) {
context->auto_random_cycle -= 1; context->auto_random_cycle -= 1;
} }
log_info("[LEFT] Auto Random Cycle: %d", context->auto_random_cycle); log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
} else if (code == 262) { } else if (code == state_config->hotkey_autorand_up) {
// RIGHT ARROW
context->auto_random_cycle += 1; context->auto_random_cycle += 1;
log_info("[RIGHT] Auto Random Cycle: %d", context->auto_random_cycle); log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
} else if (code == 265) { } else if (code == state_config->hotkey_tempo_up) {
// UP ARROW
tempo_set(&context->tempo, context->tempo.tempo + 1); tempo_set(&context->tempo, context->tempo.tempo + 1);
log_info("[UP] Tempo: %f", context->tempo); log_info("[%d] Tempo: %f", code, context->tempo);
} else if (code == 264) { } else if (code == state_config->hotkey_tempo_down) {
// DOWN ARROW
if (context->tempo.tempo > 0) { if (context->tempo.tempo > 0) {
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("[%d] Tempo: %f", code, 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 {
index = arr_uint_index_of(state_config->hotkey_load, code);
if (index != ARRAY_NOT_FOUND) {
log_info("[%d] Loading state %d", code, index + 1);
load_from_index_file(context, state_config, index + 1);
return;
}
index = arr_uint_index_of(state_config->hotkey_save, code);
if (index != ARRAY_NOT_FOUND) {
log_info("[%d] Saving state %d", code, index + 1);
save_to_index_file(context, state_config, index + 1);
return;
}
log_info("[%d] No hotkey defined", code); log_info("[%d] No hotkey defined", code);
} }
} }
@@ -550,8 +618,7 @@ 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, bool load_state) { unsigned int base_tempo, bool load_state) {
tempo_init(&context->tempo); tempo_init(&context->tempo, base_tempo);
tempo_set(&context->tempo, base_tempo);
context->demo = demo; context->demo = demo;
context->auto_random = auto_random; context->auto_random = auto_random;
context->auto_random_cycle = auto_random_cycles; context->auto_random_cycle = auto_random_cycles;
+4 -1
View File
@@ -25,12 +25,15 @@ static void reset_tap_chain(Tempo *tempo, long t) {
memset(tempo->tap_durations, 0, sizeof(tempo->tap_durations)); memset(tempo->tap_durations, 0, sizeof(tempo->tap_durations));
} }
void tempo_init(Tempo *tempo) { void tempo_init(Tempo *tempo, float value) {
long t; long t;
t = now(); t = now();
reset_tap_chain(tempo, t); reset_tap_chain(tempo, t);
tempo->tempo = value;
tempo->beat_length = 60000.0 / value;
} }
static bool is_chain_active(const Tempo tempo, long t) { static bool is_chain_active(const Tempo tempo, long t) {
+1 -1
View File
@@ -3,7 +3,7 @@
#ifndef TEMPO_H #ifndef TEMPO_H
#define TEMPO_H #define TEMPO_H
void tempo_init(Tempo *tempo); void tempo_init(Tempo *tempo, float value);
void tempo_tap(Tempo *tempo); void tempo_tap(Tempo *tempo);
+12
View File
@@ -214,6 +214,18 @@ typedef struct StateConfig {
unsigned int tap_tempo_code; unsigned int tap_tempo_code;
char save_file_prefix[STR_LEN]; char save_file_prefix[STR_LEN];
unsigned int hotkey_randomize;
unsigned int hotkey_reset;
unsigned int hotkey_demo;
unsigned int hotkey_autorand;
unsigned int hotkey_autorand_up;
unsigned int hotkey_autorand_down;
unsigned int hotkey_tempo_up;
unsigned int hotkey_tempo_down;
UintArray hotkey_load;
UintArray hotkey_save;
} StateConfig; } StateConfig;
// timer.c // timer.c