2 Commits
Author SHA1 Message Date
klemek 2a7d1f6df4 docs: update README
Clang Lint CI / lint-no-video (push) Successful in 50s
Clang Build CI / run-no-video (push) Successful in 58s
Clang Build CI / run-video (push) Successful in 59s
Clang Build CI / build-release (push) Successful in 1m29s
Clang Lint CI / lint-video (push) Successful in 1m5s
2026-08-29 12:00:56 +02:00
klemek 80add2ec67 feat(state): auto-save 2026-08-29 12:00:50 +02:00
8 changed files with 58 additions and 22 deletions
+12 -12
View File
@@ -133,7 +133,7 @@ make install
```shell ```shell
git clone --recursive https://git.klemek.fr/klemek/forge-steel git clone --recursive https://git.klemek.fr/klemek/forge-steel
cd forge cd forge-steel
makepkg -si makepkg -si
``` ```
@@ -142,11 +142,11 @@ makepkg -si
```shell ```shell
git clone --recursive https://git.klemek.fr/klemek/forge-steel git clone --recursive https://git.klemek.fr/klemek/forge-steel
cd forge cd forge-steel
aclocal aclocal
autoconf autoconf
automake --add-missing automake --add-missing
./configure ./configure --prefix=$HOME/.local
make make
make install make install
``` ```
@@ -174,9 +174,7 @@ These are configurable in the [`forge_project.cfg`](#forge_projectcfg).
### CLI arguments ### CLI arguments
```txt ```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] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-mr / -nmr] [-tm] [-tf] [--x11] [--wayland] [--cocoa] [--win32] [-as / -nas] [-oma=MAJOR] [-omi=MINOR]
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] [-vb=COUNT] [-vr / -nvr] [-vf=FOURCC] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-mr / -nmr] [-tm] [-tf]
Fusion Of Real-time Generative Effects. Fusion Of Real-time Generative Effects.
@@ -195,21 +193,23 @@ options:
-ar, --auto-random randomize state every cycle (4 beats) -ar, --auto-random randomize state every cycle (4 beats)
-nar, --no-auto-random do not randomize state (default) -nar, --no-auto-random do not randomize state (default)
-arc, --auto-random-cycle auto random cycle length (default: 4) -arc, --auto-random-cycle auto random cycle length (default: 4)
-vi, --video-in path to video capture device (multiple allowed)
-vb, --video-buffers number of video buffers to use (default: 1)
-vs, --video-size video capture desired height (default: internal texture height)
-vr, --video-reconnect auto-reconnect video (default)
-nvr, --no-video-reconnect do not auto-reconnect video
-vf, --video-fourcc video codec fourcc (default: YUYV)
-is, --internal-size internal texture height (default: 720) -is, --internal-size internal texture height (default: 720)
-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)
-nss, --no-save-state do not save state -nss, --no-save-state do not save state
-as, --auto-save continuously save state (every ~10ms)
-nas, --no-auto-save do not continuously save state (default)
-mr, --midi-reconnect auto-reconnect midi (default) -mr, --midi-reconnect auto-reconnect midi (default)
-nmr, --no-midi-reconnect do not auto-reconnect midi -nmr, --no-midi-reconnect do not auto-reconnect midi
-tm, --trace-midi print midi code and values -tm, --trace-midi print midi code and values
-tf, --trace-fps print fps status of subsystems -tf, --trace-fps print fps status of subsystems
--x11 force X11 GLFW backend
--wayland force Wayland GLFW backend
--cocoa force Cocoa GLFW backend
--win32 force Win32 GLFW backend
-oma, --opengl-major-version OpenGL major version (default: 4)
-omi, --opengl-minor-version OpenGL minor version (default: 6)
``` ```
## Default Project ## Default Project
+8
View File
@@ -49,6 +49,7 @@ static void print_help(int status_code) {
"[--wayland] " "[--wayland] "
"[--cocoa] " "[--cocoa] "
"[--win32] " "[--win32] "
"[-as / -nas] "
"[-oma=MAJOR] " "[-oma=MAJOR] "
"[-omi=MINOR] " "[-omi=MINOR] "
"\n\n" "\n\n"
@@ -87,6 +88,8 @@ static void print_help(int status_code) {
" -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"
" -nss, --no-save-state do not save state\n" " -nss, --no-save-state do not save state\n"
" -as, --auto-save continuously save state (every ~10ms)\n"
" -nas, --no-auto-save do not continuously save state (default)\n"
" -mr, --midi-reconnect auto-reconnect midi (default)\n" " -mr, --midi-reconnect auto-reconnect midi (default)\n"
" -nmr, --no-midi-reconnect do not auto-reconnect midi\n" " -nmr, --no-midi-reconnect do not auto-reconnect midi\n"
" -tm, --trace-midi print midi code and values\n" " -tm, --trace-midi print midi code and values\n"
@@ -168,6 +171,7 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->internal_size = 720; params->internal_size = 720;
params->load_state = true; params->load_state = true;
params->save_state = true; params->save_state = true;
params->auto_save = false;
params->midi_reconnect = true; params->midi_reconnect = true;
params->trace_midi = false; params->trace_midi = false;
params->trace_fps = false; params->trace_fps = false;
@@ -234,6 +238,10 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->save_state = true; params->save_state = true;
} else if (is_arg(arg, "-nss") || is_arg(arg, "--no-save-state")) { } else if (is_arg(arg, "-nss") || is_arg(arg, "--no-save-state")) {
params->save_state = false; params->save_state = false;
} else if (is_arg(arg, "-as") || is_arg(arg, "--auto-save")) {
params->auto_save = true;
} else if (is_arg(arg, "-nas") || is_arg(arg, "--no-auto-save")) {
params->auto_save = false;
} else if (is_arg(arg, "-mr") || is_arg(arg, "--midi-reconnect")) { } else if (is_arg(arg, "-mr") || is_arg(arg, "--midi-reconnect")) {
params->midi_reconnect = true; params->midi_reconnect = true;
} else if (is_arg(arg, "-nmr") || is_arg(arg, "--no-midi-reconnect")) { } else if (is_arg(arg, "-nmr") || is_arg(arg, "--no-midi-reconnect")) {
+7
View File
@@ -87,6 +87,12 @@
#define MAX_TAP_VALUES 10 #define MAX_TAP_VALUES 10
#endif #endif
/* STATE */
#ifndef STATE_AUTO_SAVE_SECONDS
#define STATE_AUTO_SAVE_SECONDS 0.1
#endif
/* OPENGL */ /* OPENGL */
#ifndef OPENGL_MAJOR_VERSION #ifndef OPENGL_MAJOR_VERSION
@@ -98,6 +104,7 @@
#endif #endif
/* GLFW CONSTANTS */ /* GLFW CONSTANTS */
#ifndef GLFW_BACKENDS #ifndef GLFW_BACKENDS
#define GLFW_BACKEND_ANY 0 #define GLFW_BACKEND_ANY 0
#define GLFW_BACKEND_X11 1 #define GLFW_BACKEND_X11 1
+4 -2
View File
@@ -109,10 +109,12 @@ void file_dump(const char *path, const char *content) {
fclose(file_pointer); fclose(file_pointer);
} }
void file_write(const char *path, const StringArray *lines) { void file_write(const char *path, const StringArray *lines, const bool log) {
FILE *file_pointer; FILE *file_pointer;
log_info("Writing %s...", path); if (log) {
log_info("Writing %s...", path);
}
// open file // open file
file_pointer = fopen(path, "w"); file_pointer = fopen(path, "w");
+1 -1
View File
@@ -9,7 +9,7 @@ bool file_should_update(const File *file);
bool file_update(File *file); bool file_update(File *file);
void file_write(const char *path, const StringArray *lines); void file_write(const char *path, const StringArray *lines, const bool log);
void file_dump(const char *path, const char *content); void file_dump(const char *path, const char *content);
+1
View File
@@ -207,6 +207,7 @@ static void start_state_background_write() {
process_args->context = &context; process_args->context = &context;
process_args->state_config = project.state_config; process_args->state_config = project.state_config;
process_args->midi = &midi; process_args->midi = &midi;
process_args->auto_save = init_params.auto_save;
if (pthread_create(&thread, NULL, state_background_write, process_args) == if (pthread_create(&thread, NULL, state_background_write, process_args) ==
0) { 0) {
pthread_detach(thread); pthread_detach(thread);
+23 -7
View File
@@ -12,6 +12,7 @@
#include "rand.h" #include "rand.h"
#include "state.h" #include "state.h"
#include "tempo.h" #include "tempo.h"
#include "timer.h"
static void safe_midi_write(MidiDevice midi, unsigned int code, static void safe_midi_write(MidiDevice midi, unsigned int code,
unsigned char value) { unsigned char value) {
@@ -179,10 +180,12 @@ static void load_from_index_file(Context *context, StateConfig state_config,
} }
static void save_to_file(const Context *context, StateConfig state_config, static void save_to_file(const Context *context, StateConfig state_config,
const char *state_file) { const char *state_file, const bool log) {
StringArray lines; StringArray lines;
log_info("Saving state to '%s'...", state_file); if (log) {
log_info("Saving state to '%s'...", state_file);
}
lines.length = 0; lines.length = 0;
@@ -278,16 +281,16 @@ static void save_to_file(const Context *context, StateConfig state_config,
} }
} }
file_write(state_file, &lines); file_write(state_file, &lines, log);
} }
static void save_to_default_file(const Context *context, static void save_to_default_file(const Context *context,
StateConfig state_config) { StateConfig state_config, const bool log) {
char state_file[STR_LEN]; char state_file[STR_LEN];
snprintf(state_file, STR_LEN, "%s.txt", state_config.save_file_prefix); snprintf(state_file, STR_LEN, "%s.txt", state_config.save_file_prefix);
save_to_file(context, state_config, state_file); save_to_file(context, state_config, state_file, log);
} }
static void save_to_index_file(const Context *context, StateConfig state_config, static void save_to_index_file(const Context *context, StateConfig state_config,
@@ -297,7 +300,7 @@ static void save_to_index_file(const Context *context, StateConfig state_config,
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config.save_file_prefix, snprintf(state_file, STR_LEN, "%s.%d.txt", state_config.save_file_prefix,
index); index);
save_to_file(context, state_config, state_file); save_to_file(context, state_config, state_file, true);
} }
void state_parse_config(StateConfig *state_config, const ConfigFile *config) { void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
@@ -756,16 +759,20 @@ void *state_background_write(void *args) {
Context *context = process_args->context; Context *context = process_args->context;
StateConfig state_config = process_args->state_config; StateConfig state_config = process_args->state_config;
MidiDevice *midi = process_args->midi; MidiDevice *midi = process_args->midi;
bool auto_save = process_args->auto_save;
bool beat_active; bool beat_active;
bool last_active; bool last_active;
bool change; bool change;
bool last_change; bool last_change;
bool do_auto_save;
bool last_auto_save;
log_info("(state) background writing started"); log_info("(state) background writing started");
last_active = false; last_active = false;
last_change = false; last_change = false;
last_auto_save = false;
while (!context->stop) { while (!context->stop) {
beat_active = tempo_progress(&context->tempo, 1.0) < 0.5; beat_active = tempo_progress(&context->tempo, 1.0) < 0.5;
@@ -791,6 +798,15 @@ void *state_background_write(void *args) {
} }
last_change = change; last_change = change;
do_auto_save = fmod(context->time, STATE_AUTO_SAVE_SECONDS) <
STATE_AUTO_SAVE_SECONDS * 0.5;
if (auto_save && do_auto_save && !last_auto_save) {
save_to_default_file(context, state_config, false);
}
last_auto_save = do_auto_save;
} }
log_info("(state) background writing stopped by main thread"); log_info("(state) background writing stopped by main thread");
@@ -831,5 +847,5 @@ void state_init(Context *context, StateConfig state_config, bool demo,
} }
void state_save(const Context *context, StateConfig state_config) { void state_save(const Context *context, StateConfig state_config) {
save_to_default_file(context, state_config); save_to_default_file(context, state_config, true);
} }
+2
View File
@@ -55,6 +55,7 @@ typedef struct Parameters {
unsigned int internal_size; unsigned int internal_size;
bool load_state; bool load_state;
bool save_state; bool save_state;
bool auto_save;
bool midi_reconnect; bool midi_reconnect;
bool trace_midi; bool trace_midi;
bool trace_fps; bool trace_fps;
@@ -279,6 +280,7 @@ typedef struct StateBackgroundWriteArgs {
Context *context; Context *context;
StateConfig state_config; StateConfig state_config;
MidiDevice *midi; MidiDevice *midi;
bool auto_save;
} StateBackgroundWriteArgs; } StateBackgroundWriteArgs;
// timer.c // timer.c