From d4565fa507d9b2e5f2ab31bea5ed24ebfc132558 Mon Sep 17 00:00:00 2001 From: klemek Date: Thu, 14 May 2026 13:16:55 +0200 Subject: [PATCH] feat: video auto reconnect --- .github/workflows/build.yml | 2 +- Makefile.am | 4 +- Makefile.dev | 11 +- configure.ac | 1 + src/forge.c | 220 ++++++++++++++++++++---------------- src/midi.c | 29 ++--- src/midi.h | 5 +- src/shaders.c | 58 +++++----- src/shaders.h | 12 +- src/shared.c | 35 ------ src/shared.h | 9 -- src/state.c | 74 ++++++------ src/state.h | 14 +-- src/types.h | 106 ++++++++++------- src/video.c | 42 +++---- src/video.h | 7 +- src/window.c | 2 +- src/window.h | 2 +- 18 files changed, 305 insertions(+), 328 deletions(-) delete mode 100644 src/shared.c delete mode 100644 src/shared.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c0355f..a6c2cd2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ on: - 'Makefile.am' env: - GCC_ARGS: src/*.c src/*.h -lglfw -lGL -lm -lasound -Iinclude hashmap.c/hashmap.c log.c/src/log.c -DGLFW_INCLUDE_NONE -DGLFW_NATIVE_INCLUDE_NONE + GCC_ARGS: src/*.c src/*.h -lglfw -lGL -lm -lasound -Wno-format-truncation -Iinclude hashmap.c/hashmap.c log.c/src/log.c -DGLFW_INCLUDE_NONE -DGLFW_NATIVE_INCLUDE_NONE GCC_ARGS_VIDEO: -DGLFW_EXPOSE_NATIVE_EGL -DVIDEO_IN PACKAGES: "libglfw3-dev libgl-dev libasound2-dev" PACKAGES_VIDEO: "libv4l-dev" diff --git a/Makefile.am b/Makefile.am index 59e4e3c..a3b343d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,9 +1,9 @@ AUTOMAKE_OPTIONS = foreign subdir-objects -Wall bin_PROGRAMS = forge -forge_SOURCES = src/args.c src/arr.c src/config_file.c src/file.c src/forge.c src/main.c src/midi.c src/project.c src/rand.c src/shaders.c src/shared.c src/state.c src/string.c src/tempo.c src/timer.c src/video.c src/window.c $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/hashmap.c/hashmap.c $(top_srcdir)/log.c/src/log.c +forge_SOURCES = src/args.c src/arr.c src/config_file.c src/file.c src/forge.c src/main.c src/midi.c src/project.c src/rand.c src/shaders.c src/state.c src/string.c src/tempo.c src/timer.c src/video.c src/window.c $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/hashmap.c/hashmap.c $(top_srcdir)/log.c/src/log.c forge_CFLAGS = -Ofast -march=native -flto -funroll-loops -fprefetch-loop-arrays -fno-exceptions -fopenmp -I$(top_srcdir)/include -DGLFW_INCLUDE_NONE -DGLFW_EXPOSE_NATIVE_EGL -DGLFW_NATIVE_INCLUDE_NONE -DLOG_USE_COLOR -DVIDEO_IN -DDATADIR=\"$(datadir)/$(PACKAGE)\" forge_LDADD = -lm -lGL -lglfw -lasound -include_HEADERS = src/args.h src/arr.h src/config.h src/config_file.h src/constants.h src/file.h src/forge.h src/main.h src/midi.h src/project.h src/rand.h src/shaders.h src/shared.h src/state.h src/string.h src/tempo.h src/timer.h src/types.h src/video.h src/window.h $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/include/linmath.h $(top_srcdir)/include/hashmap.h $(top_srcdir)/include/log.h +include_HEADERS = src/args.h src/arr.h src/config.h src/config_file.h src/constants.h src/file.h src/forge.h src/main.h src/midi.h src/project.h src/rand.h src/shaders.h src/state.h src/string.h src/tempo.h src/timer.h src/types.h src/video.h src/window.h $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/include/linmath.h $(top_srcdir)/include/hashmap.h $(top_srcdir)/include/log.h EXTRA_DIST = default/forge_project.cfg default/frag1.glsl default/frag10.glsl default/frag2.glsl default/frag3.glsl default/frag4.glsl default/frag5.glsl default/frag6.glsl default/frag7.glsl default/frag8.glsl default/frag9.glsl default/inc_cp437.glsl default/inc_debug.glsl default/inc_functions.glsl default/inc_fx.glsl default/inc_magic.glsl default/inc_rand.glsl default/inc_res.glsl default/inc_sentences.glsl default/inc_src.glsl default/inc_template.glsl default/inc_time.glsl default/inc_yuyv.glsl confdir = $(prefix)/share/$(PACKAGE) diff --git a/Makefile.dev b/Makefile.dev index 58a7d3d..d902d33 100644 --- a/Makefile.dev +++ b/Makefile.dev @@ -1,6 +1,6 @@ TARGET ?= forge INSTALL_DIR ?= $(HOME)/.local/bin -TEST_ARGS ?= --video-in=/dev/video0 --video-in=/dev/video1 --video-in=/dev/video2 --video-in=/dev/video3 --video-in=/dev/video4 --video-in=/dev/video5 --video-in=/dev/video6 --video-in=/dev/video7 --video-in=/dev/video8 --video-in=/dev/video9 +RUN_ARGS ?= --help SHELL := /bin/bash .PHONY: build @@ -22,7 +22,6 @@ build: -DGLFW_NATIVE_INCLUDE_NONE \ -DLOG_USE_COLOR \ -DVIDEO_IN \ - -DEGL_DEBUG \ -o build/$(TARGET) \ -g -Og @@ -50,11 +49,7 @@ format: .PHONY: run run: build - ./build/$(TARGET) $(TEST_ARGS) --monitor-only --internal-size=480 --video-size=240 --hot-reload - -.PHONY: demo -demo: build - ./build/$(TARGET) $(TEST_ARGS) --demo + ./build/$(TARGET) $(RUN_ARGS) .PHONY: sample sample: build @@ -65,7 +60,7 @@ valgrind: build valgrind \ --show-realloc-size-zero=no \ --undef-value-errors=no \ - ./build/$(TARGET) $(TEST_ARGS) + ./build/$(TARGET) $(RUN_ARGS) .PHONY: clean-release clean-release: diff --git a/configure.ac b/configure.ac index 3ff52c6..7cecb8e 100644 --- a/configure.ac +++ b/configure.ac @@ -22,6 +22,7 @@ AC_CHECK_HEADERS([math.h]) AC_CHECK_HEADERS([string.h]) AC_CHECK_HEADERS([time.h]) AC_CHECK_HEADERS([unistd.h]) +AC_CHECK_HEADERS([pthread.h]) AC_CHECK_HEADERS([linux/videodev2.h]) diff --git a/src/forge.c b/src/forge.c index 594a939..bfb5f1a 100644 --- a/src/forge.c +++ b/src/forge.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -14,7 +15,6 @@ #include "midi.h" #include "project.h" #include "shaders.h" -#include "shared.h" #include "state.h" #include "tempo.h" #include "timer.h" @@ -22,15 +22,18 @@ #include "window.h" static Parameters init_params; -static SharedContext *context; +static Context context; static ShaderProgram program; static Window *window_output; static Window *window_monitor; static Timer timer; static MidiDevice midi; -static bool trace_midi; static Project project; +#ifdef VIDEO_IN +static VideoCaptureArray video_captures; +#endif /* VIDEO_IN */ + static void compute_fps() { double fps; char title[STR_LEN]; @@ -52,103 +55,108 @@ static void compute_fps() { window_update_title(window_monitor, title); } - context->fps = (unsigned int)round(fps); + context.fps = (unsigned int)round(fps); } } static void init_context() { - context = shared_init_context("/" PACKAGE "_context"); + context.stop = false; - context->stop = false; - - state_init(context, &project.state_config, init_params.demo, + state_init(&context, &project.state_config, init_params.demo, init_params.auto_random, init_params.auto_random_cycle, init_params.base_tempo, init_params.load_state); - memset(context->input_resolutions, 0, sizeof(context->input_resolutions)); +#ifdef VIDEO_IN + memset(context.input_resolutions, 0, sizeof(context.input_resolutions)); + memset(context.input_formats, 0, sizeof(context.input_formats)); + memset(context.input_fps, 0, sizeof(context.input_fps)); + memset(context.input_swap, 0, sizeof(context.input_swap)); +#endif /* VIDEO_IN */ } -static void free_context() { shared_close_context(context); } - static void reload_shader(unsigned int i) { shaders_update(&program, &project.fragment_shaders[i][0], i, &project); } #ifdef VIDEO_IN + static void init_inputs() { - context->inputs.length = init_params.video_in.length; + video_captures.length = init_params.video_in.length; for (unsigned int i = 0; i < init_params.video_in.length; i++) { - video_init(&context->inputs.values[i], init_params.video_in.values[i], + video_init(&video_captures.values[i], init_params.video_in.values[i], init_params.video_size); - if (!context->inputs.values[i].error) { - context->input_resolutions[i][0] = context->inputs.values[i].width; - context->input_resolutions[i][1] = context->inputs.values[i].height; - context->inputs.values[i].needs_reload = false; - context->inputs.values[i].disconnected = false; + if (!video_captures.values[i].error) { + context.input_resolutions[i][0] = video_captures.values[i].width; + context.input_resolutions[i][1] = video_captures.values[i].height; + context.input_formats[i] = video_captures.values[i].pixelformat; + video_captures.values[i].needs_reload = false; + video_captures.values[i].disconnected = false; } } } -static bool reconnect_video_captures() { - for (unsigned int i = 0; i < init_params.video_in.length; i++) { - if (context->inputs.values[i].disconnected) { - video_init(&context->inputs.values[i], init_params.video_in.values[i], - init_params.video_size); +static void start_video_background_read(VideoCapture *video_capture, + Context *context, int input_index, + bool trace_fps) { + pthread_t thread; + struct VideoBackgroundReadArgs *process_args = + (struct VideoBackgroundReadArgs *)malloc( + sizeof(struct VideoBackgroundReadArgs)); + process_args->capture = video_capture; + process_args->context = context; + process_args->input_index = input_index; + process_args->trace_fps = trace_fps; + pthread_create(&thread, NULL, video_background_read, (void *)process_args); + pthread_detach(thread); +} - if (!context->inputs.values[i].error) { - context->input_resolutions[i][0] = context->inputs.values[i].width; - context->input_resolutions[i][1] = context->inputs.values[i].height; +static void * +background_reconnect_video_captures(__attribute__((unused)) void *args) { + log_info("background video capture reconnect started"); + while (!context.stop) { + sleep(1); + for (unsigned int i = 0; i < init_params.video_in.length; i++) { + if (video_captures.values[i].disconnected) { + video_free(&video_captures.values[i]); + video_init(&video_captures.values[i], init_params.video_in.values[i], + init_params.video_size); - if (!video_background_read(&context->inputs.values[i], context, i, - init_params.trace_fps)) { - return false; + if (!video_captures.values[i].error) { + context.input_resolutions[i][0] = video_captures.values[i].width; + context.input_resolutions[i][1] = video_captures.values[i].height; + context.input_formats[i] = video_captures.values[i].pixelformat; + video_captures.values[i].needs_reload = true; + video_captures.values[i].disconnected = false; + start_video_background_read(&video_captures.values[i], &context, i, + init_params.trace_fps); } - - context->inputs.values[i].needs_reload = true; - context->inputs.values[i].disconnected = false; } } } - return true; + pthread_exit(NULL); } -static bool start_video_captures() { - pid_t pid; - for (unsigned int i = 0; i < context->inputs.length; i++) { - if (!context->inputs.values[i].error && - !video_background_read(&context->inputs.values[i], context, i, - init_params.trace_fps)) { - return false; +static void start_video_captures() { + pthread_t thread; + for (unsigned int i = 0; i < video_captures.length; i++) { + if (!video_captures.values[i].error) { + start_video_background_read(&video_captures.values[i], &context, i, + init_params.trace_fps); } } - if (!init_params.video_reconnect) { - return true; + if (init_params.video_reconnect) { + pthread_create(&thread, NULL, background_reconnect_video_captures, NULL); + pthread_detach(thread); } - pid = fork(); - if (pid < 0) { - log_error("Could not create subprocess"); - return false; - } - if (pid == 0) { - return true; - } - log_info("background reconnect acquisition started (pid: %d)", pid); - while (!context->stop) { - sleep(1); - if (!reconnect_video_captures()) { - return false; - } - } - exit(EXIT_SUCCESS); } static void free_video_captures() { - for (unsigned int i = 0; i < context->inputs.length; i++) { + for (unsigned int i = 0; i < video_captures.length; i++) { shaders_free_input(&program, i); - video_free(&context->inputs.values[i]); + video_free(&video_captures.values[i]); } } @@ -157,7 +165,7 @@ static void free_video_captures() { static void error_callback(int error, const char *description) { log_error("[GLFW] %d: %s", error, description); window_terminate(); - context->stop = true; + context.stop = true; exit(EXIT_FAILURE); } @@ -173,13 +181,37 @@ 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, - trace_midi); + state_midi_event(&context, &project.state_config, &midi, code, value, + init_params.trace_midi); +} + +static void start_state_background_write() { + pthread_t thread; + struct StateBackgroundWriteArgs *process_args = + (struct StateBackgroundWriteArgs *)malloc( + sizeof(struct StateBackgroundWriteArgs)); + process_args->context = &context; + process_args->state_config = project.state_config; + process_args->midi = &midi; + pthread_create(&thread, NULL, state_background_write, process_args); + pthread_detach(thread); +} + +static void start_midi_background_listen() { + pthread_t thread; + struct MidiBackgroundListenArgs *process_args = + (struct MidiBackgroundListenArgs *)malloc( + sizeof(struct MidiBackgroundListenArgs)); + process_args->device = &midi; + process_args->context = &context; + process_args->event_callback = midi_callback; + pthread_create(&thread, NULL, midi_background_listen, process_args); + pthread_detach(thread); } static bool init(const Parameters *params) { @@ -196,38 +228,30 @@ static bool init(const Parameters *params) { #ifdef VIDEO_IN init_inputs(); - if (!start_video_captures()) { - return false; - } + start_video_captures(); #endif /* VIDEO_IN */ midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw")); if (midi.error) { - context->demo = true; + context.demo = true; } else { - trace_midi = params->trace_midi; - - if (!midi_background_listen(&midi, context, midi_callback)) { - return false; - } + start_midi_background_listen(); } - if (!state_background_write(context, &project.state_config, &midi)) { - return false; - } + start_state_background_write(); window_startup(error_callback); - context->tex_resolution[1] = params->internal_size; + context.tex_resolution[1] = params->internal_size; if (params->output) { window_output = window_init(PACKAGE " " VERSION, params->output_screen, params->windowed, NULL, key_callback); - window_use(window_output, context); + window_use(window_output, &context); - shaders_init(&program, &project, context, false); + shaders_init(&program, &project, &context, false); } else { window_output = NULL; } @@ -237,19 +261,19 @@ static bool init(const Parameters *params) { window_init(PACKAGE " " VERSION " (monitor)", params->monitor_screen, params->windowed, window_output, key_callback); - window_use(window_monitor, context); + window_use(window_monitor, &context); - shaders_init(&program, &project, context, window_output != NULL); + shaders_init(&program, &project, &context, window_output != NULL); } else { window_monitor = NULL; } #ifdef VIDEO_IN - shaders_link_inputs(&program, &project, &context->inputs); + shaders_link_inputs(&program, &project, &video_captures); #endif /* VIDEO_IN */ if (program.error) { - context->stop = true; + context.stop = true; window_terminate(); exit(EXIT_FAILURE); } @@ -273,10 +297,10 @@ static bool loop() { #ifdef VIDEO_IN if (init_params.video_reconnect) { - for (unsigned int i = 0; i < context->inputs.length; i++) { - if (context->inputs.values[i].needs_reload) { - shaders_relink_input(&program, &project, &context->inputs, i); - context->inputs.values[i].needs_reload = false; + for (unsigned int i = 0; i < video_captures.length; i++) { + if (video_captures.values[i].needs_reload) { + shaders_relink_input(&program, &project, &video_captures, i); + video_captures.values[i].needs_reload = false; } } } @@ -284,21 +308,21 @@ static bool loop() { compute_fps(); - context->time = window_get_time(); - context->tempo_total = (float)tempo_total(&context->tempo); + context.time = window_get_time(); + context.tempo_total = (float)tempo_total(&context.tempo); if (window_output != NULL) { - window_use(window_output, context); + window_use(window_output, &context); - shaders_compute(&program, context, false, false); + shaders_compute(&program, &context, false, false); window_refresh(window_output); } if (window_monitor != NULL) { - window_use(window_monitor, context); + window_use(window_monitor, &context); - shaders_compute(&program, context, true, window_output != NULL); + shaders_compute(&program, &context, true, window_output != NULL); window_refresh(window_monitor); } @@ -309,22 +333,22 @@ static bool loop() { } static void shutdown() { - context->stop = true; + context.stop = true; if (init_params.save_state) { - state_save(context, &project.state_config); + state_save(&context, &project.state_config); } shaders_free(&program); if (window_output != NULL) { - window_use(window_output, context); + window_use(window_output, &context); shaders_free_window(&program, false); } if (window_monitor != NULL) { - window_use(window_monitor, context); + window_use(window_monitor, &context); shaders_free_window(&program, init_params.output); } @@ -333,8 +357,6 @@ static void shutdown() { free_video_captures(); #endif /* VIDEO_IN */ - free_context(); - project_free(&project); window_terminate(); diff --git a/src/midi.c b/src/midi.c index 7a49a2c..e25c8e0 100644 --- a/src/midi.c +++ b/src/midi.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include "types.h" @@ -33,32 +33,23 @@ void midi_write(const MidiDevice *device, unsigned char code, snd_rawmidi_write(device->output, buffer, 3); } -bool midi_background_listen(const MidiDevice *device, - const SharedContext *context, - void (*event_callback)(unsigned char code, - unsigned char value)) { - pid_t pid; +void *midi_background_listen(void *args) { + MidiBackgroundReadArgs *process_args = (MidiBackgroundReadArgs *)args; + MidiDevice *device = process_args->device; + Context *context = process_args->context; + int bytes_read; unsigned char buffer[3]; - pid = fork(); - if (pid < 0) { - log_error("Could not create subprocess"); - return false; - } - if (pid == 0) { - return true; - } - log_info("(%s) background acquisition started (pid: %d)", device->name, pid); + log_info("(%s) background acquisition started", device->name); while (!context->stop) { bytes_read = snd_rawmidi_read(device->input, buffer, 3); if (bytes_read == 3) { - event_callback(buffer[1], buffer[2]); + process_args->event_callback(buffer[1], buffer[2]); } } - log_info("(%s) background acquisition stopped by main thread (pid: %d)", - device->name, pid); - exit(EXIT_SUCCESS); + log_info("(%s) background acquisition stopped by main thread", device->name); + pthread_exit(NULL); } diff --git a/src/midi.h b/src/midi.h index fde79d5..16422ca 100644 --- a/src/midi.h +++ b/src/midi.h @@ -6,9 +6,6 @@ void midi_open(MidiDevice *device, const char *name); void midi_write(const MidiDevice *device, unsigned char code, unsigned char value); -bool midi_background_listen(const MidiDevice *device, - const SharedContext *context, - void (*event_callback)(unsigned char code, - unsigned char value)); +void *midi_background_listen(void *args); #endif /* MIDI_H */ diff --git a/src/shaders.c b/src/shaders.c index 3f444ed..6a50f81 100644 --- a/src/shaders.c +++ b/src/shaders.c @@ -134,8 +134,7 @@ static bool init_gl(ShaderProgram *program) { !check_eglerror(program, "init_gl"); } -static bool init_textures(ShaderProgram *program, - const SharedContext *context) { +static bool init_textures(ShaderProgram *program, const Context *context) { glGenTextures(program->tex_count, program->textures); if (check_glerror(program, "init_textures/glGenTextures")) { return false; @@ -639,8 +638,7 @@ static bool init_programs(ShaderProgram *program, const ConfigFile *config, return true; } -static void update_viewport(ShaderProgram *program, - const SharedContext *context) { +static void update_viewport(ShaderProgram *program, const Context *context) { // viewport changed if (context->resolution[0] != program->last_resolution[0] || context->resolution[1] != program->last_resolution[1]) { @@ -685,7 +683,7 @@ static void write_uniform_multi_3f(GLuint location, unsigned int count, } static void use_program(const ShaderProgram *program, int i, bool output, - const SharedContext *context) { + const Context *context) { unsigned int k; unsigned int offset; unsigned int subcount; @@ -728,17 +726,21 @@ static void use_program(const ShaderProgram *program, int i, bool output, context->active[j] + 1); } +#ifdef VIDEO_IN + for (unsigned int j = 0; j < program->in_count; j++) { write_uniform_2f(program->iinres_locations[i * program->in_count + j], &context->input_resolutions[j]); write_uniform_1i(program->iinfmt_locations[i * program->in_count + j], - context->inputs.values[j].pixelformat); + context->input_formats[j]); write_uniform_1i(program->iinfps_locations[i * program->in_count + j], - context->inputs.values[j].fps); + context->input_fps[j]); write_uniform_1i(program->iinswap_locations[i * program->in_count + j], - context->inputs.values[j].swap ? 1 : 0); + context->input_swap[j] ? 1 : 0); } +#endif /* VIDEO_IN */ + // set seeds uniforms for (unsigned int j = 0; j < program->frag_count; j++) { write_uniform_1i(program->iseed_locations[i * program->frag_count + j], @@ -788,7 +790,7 @@ static void use_program(const ShaderProgram *program, int i, bool output, } void shaders_init(ShaderProgram *program, const Project *project, - const SharedContext *context, bool rebind) { + const Context *context, bool rebind) { if (!rebind) { program->error = false; program->last_resolution[0] = context->resolution[0]; @@ -841,23 +843,36 @@ void shaders_init(ShaderProgram *program, const Project *project, } } +#ifdef VIDEO_IN + void shaders_relink_input(ShaderProgram *program, const Project *project, VideoCaptureArray *inputs, unsigned int i) { -#ifdef VIDEO_IN // shaders_free_input(program, i); init_input(program, &project->config, inputs, i, true); -#endif /* VIDEO_IN */ } void shaders_link_inputs(ShaderProgram *program, const Project *project, VideoCaptureArray *inputs) { -#ifdef VIDEO_IN for (unsigned int i = 0; i < program->in_count; i++) { init_input(program, &project->config, inputs, i, false); } -#endif /* VIDEO_IN */ } +void shaders_free_input(ShaderProgram *program, unsigned int input_index) { + if (program->dma_images[input_index] != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(program->egl_display, program->dma_images[input_index]); + program->dma_images[input_index] = EGL_NO_IMAGE_KHR; + } + if (program->dma_images_swap[input_index] != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(program->egl_display, + program->dma_images_swap[input_index]); + program->dma_images_swap[input_index] = EGL_NO_IMAGE_KHR; + } + check_eglerror_ro("shaders_free_input/eglDestroyImageKHR"); +} + +#endif /* VIDEO_IN */ + void shaders_update(ShaderProgram *program, const File *fragment_shader, unsigned int i, const Project *project) { if (compile_shader(program->fragment_shaders[i], fragment_shader->path, @@ -868,7 +883,7 @@ void shaders_update(ShaderProgram *program, const File *fragment_shader, } } -void shaders_compute(ShaderProgram *program, const SharedContext *context, +void shaders_compute(ShaderProgram *program, const Context *context, bool monitor, bool output_only) { if (!output_only) { glBindVertexArray(program->vertex_array[0]); @@ -914,18 +929,3 @@ void shaders_free_window(const ShaderProgram *program, bool secondary) { glDeleteVertexArrays(1, &program->vertex_array[secondary ? 1 : 0]); check_glerror_ro("shaders_free_window/glDeleteVertexArrays"); } - -void shaders_free_input(ShaderProgram *program, unsigned int input_index) { -#ifdef VIDEO_IN - if (program->dma_images[input_index] != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(program->egl_display, program->dma_images[input_index]); - program->dma_images[input_index] = EGL_NO_IMAGE_KHR; - } - if (program->dma_images_swap[input_index] != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(program->egl_display, - program->dma_images_swap[input_index]); - program->dma_images_swap[input_index] = EGL_NO_IMAGE_KHR; - } - check_eglerror_ro("shaders_free_input/eglDestroyImageKHR"); -#endif /* VIDEO_IN */ -} diff --git a/src/shaders.h b/src/shaders.h index 347a938..a983fe6 100644 --- a/src/shaders.h +++ b/src/shaders.h @@ -4,7 +4,9 @@ #define SHADERS_H void shaders_init(ShaderProgram *program, const Project *project, - const SharedContext *context, bool rebind); + const Context *context, bool rebind); + +#ifdef VIDEO_IN void shaders_relink_input(ShaderProgram *program, const Project *project, VideoCaptureArray *inputs, unsigned int i); @@ -12,16 +14,18 @@ void shaders_relink_input(ShaderProgram *program, const Project *project, void shaders_link_inputs(ShaderProgram *program, const Project *project, VideoCaptureArray *inputs); +void shaders_free_input(ShaderProgram *program, unsigned int input_index); + +#endif /* VIDEO_IN */ + void shaders_update(ShaderProgram *program, const File *fragment_shader, unsigned int i, const Project *project); -void shaders_compute(ShaderProgram *program, const SharedContext *context, +void shaders_compute(ShaderProgram *program, const Context *context, bool monitor, bool output_only); void shaders_free(const ShaderProgram *program); void shaders_free_window(const ShaderProgram *program, bool secondary); -void shaders_free_input(ShaderProgram *program, unsigned int input_index); - #endif /* SHADERS_H */ diff --git a/src/shared.c b/src/shared.c deleted file mode 100644 index 8512d56..0000000 --- a/src/shared.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include -#include - -#include "types.h" - -#include "shared.h" - -static void *open_shared(const char *key, size_t size, int *fd) { - *fd = shm_open(key, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); - ftruncate(*fd, size); - - return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0); -} - -static void close_shared(void *shared, size_t size, int fd) { - munmap(shared, size); - close(fd); -} - -SharedContext *shared_init_context(const char *key) { - int shared_fd; - SharedContext *shared; - - shared = open_shared(key, sizeof(SharedContext), &shared_fd); - - shared->fd = shared_fd; - - return shared; -} - -void shared_close_context(SharedContext *shared) { - close_shared(shared, sizeof(SharedContext), shared->fd); -} diff --git a/src/shared.h b/src/shared.h deleted file mode 100644 index f7b353c..0000000 --- a/src/shared.h +++ /dev/null @@ -1,9 +0,0 @@ -#include "types.h" - -#ifndef SHARED_H -#define SHARED_H - -SharedContext *shared_init_context(const char *key); -void shared_close_context(SharedContext *shared); - -#endif /* SHARED_H */ diff --git a/src/state.c b/src/state.c index 75e36a5..c733e6c 100644 --- a/src/state.c +++ b/src/state.c @@ -1,6 +1,6 @@ #include +#include #include -#include #include "types.h" @@ -20,8 +20,7 @@ static void safe_midi_write(const MidiDevice *midi, unsigned int code, } } -static void update_page(const SharedContext *context, - const StateConfig *state_config, +static void update_page(const Context *context, const StateConfig *state_config, const MidiDevice *midi) { unsigned int page_item_min; unsigned int page_item_max; @@ -51,7 +50,7 @@ static void update_page(const SharedContext *context, } } -static void update_active(const SharedContext *context, +static void update_active(const Context *context, const StateConfig *state_config, const MidiDevice *midi) { unsigned int k; @@ -66,7 +65,7 @@ static void update_active(const SharedContext *context, } } -static void update_values(const SharedContext *context, +static void update_values(const Context *context, const StateConfig *state_config, const MidiDevice *midi) { unsigned int j; @@ -83,12 +82,12 @@ static void update_values(const SharedContext *context, } } -static void reset(SharedContext *context) { +static void reset(Context *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) { +static void randomize(Context *context, const StateConfig *state_config) { unsigned int j; unsigned int l; unsigned int part; @@ -116,8 +115,7 @@ static void randomize(SharedContext *context, const StateConfig *state_config) { } } -static void load_from_file(SharedContext *context, - const StateConfig *state_config, +static void load_from_file(Context *context, const StateConfig *state_config, const char *state_file) { ConfigFile saved_state; char key[STR_LEN]; @@ -161,7 +159,7 @@ static void load_from_file(SharedContext *context, config_file_free(&saved_state); } -static void load_from_default_file(SharedContext *context, +static void load_from_default_file(Context *context, const StateConfig *state_config) { char state_file[STR_LEN]; @@ -170,7 +168,7 @@ static void load_from_default_file(SharedContext *context, load_from_file(context, state_config, state_file); } -static void load_from_index_file(SharedContext *context, +static void load_from_index_file(Context *context, const StateConfig *state_config, unsigned int index) { char state_file[STR_LEN]; @@ -181,7 +179,7 @@ static void load_from_index_file(SharedContext *context, load_from_file(context, state_config, state_file); } -static void save_to_file(const SharedContext *context, +static void save_to_file(const Context *context, const StateConfig *state_config, const char *state_file) { StringArray lines; @@ -220,7 +218,7 @@ static void save_to_file(const SharedContext *context, file_write(state_file, &lines); } -static void save_to_default_file(const SharedContext *context, +static void save_to_default_file(const Context *context, const StateConfig *state_config) { char state_file[STR_LEN]; @@ -229,7 +227,7 @@ static void save_to_default_file(const SharedContext *context, save_to_file(context, state_config, state_file); } -static void save_to_index_file(const SharedContext *context, +static void save_to_index_file(const Context *context, const StateConfig *state_config, unsigned int index) { char state_file[STR_LEN]; @@ -414,7 +412,7 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) { } } -void state_midi_event(SharedContext *context, const StateConfig *state_config, +void state_midi_event(Context *context, const StateConfig *state_config, const MidiDevice *midi, unsigned char code, unsigned char value, bool trace_midi) { unsigned int i; @@ -508,7 +506,7 @@ void state_midi_event(SharedContext *context, const StateConfig *state_config, } } -void state_key_event(SharedContext *context, const StateConfig *state_config, +void state_key_event(Context *context, const StateConfig *state_config, unsigned int code, const MidiDevice *midi) { unsigned int index; @@ -565,29 +563,23 @@ void state_key_event(SharedContext *context, const StateConfig *state_config, } } -bool state_background_write(SharedContext *context, - const StateConfig *state_config, - const MidiDevice *midi) { - pid_t pid; +void *state_background_write(void *args) { + StateBackgroundWriteArgs *process_args = (StateBackgroundWriteArgs *)args; + Context *context = process_args->context; + StateConfig state_config = process_args->state_config; + MidiDevice *midi = process_args->midi; + bool beat_active; bool last_active; bool change; bool last_change; - pid = fork(); - if (pid < 0) { - log_error("Could not create subprocess"); - return false; - } - if (pid == 0) { - return true; - } - log_info("(state) background writing started (pid: %d)", pid); + 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); + update_page(context, &state_config, midi); + update_active(context, &state_config, midi); + update_values(context, &state_config, midi); } last_active = false; @@ -597,11 +589,11 @@ bool state_background_write(SharedContext *context, 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, + safe_midi_write(midi, state_config.tap_tempo_code, beat_active ? MIDI_MAX : 0); safe_midi_write(midi, - state_config->select_frag_codes.values[context->selected], + state_config.select_frag_codes.values[context->selected], beat_active ? MIDI_MAX : 0); } @@ -611,20 +603,20 @@ bool state_background_write(SharedContext *context, (double)context->auto_random_cycle) < 0.5; if (context->auto_random && change && !last_change) { - randomize(context, state_config); + randomize(context, &state_config); - update_values(context, state_config, midi); + update_values(context, &state_config, midi); } last_change = change; } - log_info("(state) background writing stopped by main thread (pid: %d)", pid); - exit(EXIT_SUCCESS); + log_info("(state) background writing stopped by main thread"); + pthread_exit(NULL); } -void state_init(SharedContext *context, const StateConfig *state_config, - bool demo, bool auto_random, unsigned int auto_random_cycles, +void state_init(Context *context, const StateConfig *state_config, bool demo, + bool auto_random, unsigned int auto_random_cycles, unsigned int base_tempo, bool load_state) { tempo_init(&context->tempo, base_tempo); context->demo = demo; @@ -655,6 +647,6 @@ void state_init(SharedContext *context, const StateConfig *state_config, } } -void state_save(const SharedContext *context, const StateConfig *state_config) { +void state_save(const Context *context, const StateConfig *state_config) { save_to_default_file(context, state_config); } diff --git a/src/state.h b/src/state.h index 3cc4fc9..4346837 100644 --- a/src/state.h +++ b/src/state.h @@ -5,21 +5,19 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config); -void state_midi_event(SharedContext *context, const StateConfig *state_config, +void state_midi_event(Context *context, const StateConfig *state_config, const MidiDevice *midi, unsigned char code, unsigned char value, bool trace_midi); -void state_key_event(SharedContext *context, const StateConfig *state_config, +void state_key_event(Context *context, const StateConfig *state_config, unsigned int code, const MidiDevice *midi); -bool state_background_write(SharedContext *context, - const StateConfig *state_config, - const MidiDevice *midi); +void *state_background_write(void *args); -void state_init(SharedContext *context, const StateConfig *state_config, - bool demo, bool auto_random, unsigned int auto_random_cycles, +void state_init(Context *context, const StateConfig *state_config, bool demo, + bool auto_random, unsigned int auto_random_cycles, unsigned int base_tempo, bool load_state); -void state_save(const SharedContext *context, const StateConfig *state_config); +void state_save(const Context *context, const StateConfig *state_config); #endif /* STATE_H */ diff --git a/src/types.h b/src/types.h index 5a2d9cd..ee311ac 100644 --- a/src/types.h +++ b/src/types.h @@ -135,34 +135,6 @@ typedef struct ShaderProgram { #endif /* VIDEO_IN */ } ShaderProgram; -// video.c - -typedef struct VideoCapture { - char name[STR_LEN]; - bool error; - bool disconnected; - bool needs_reload; - bool with_swap; - bool swap; - unsigned int fps; - int fd; - int exp_fd; - int exp_fd_swap; - unsigned int width; - unsigned int height; - unsigned int pixelformat; - unsigned int bytesperline; -#ifdef VIDEO_IN - struct v4l2_buffer buf; - struct v4l2_buffer buf_swap; -#endif /* VIDEO_IN */ -} VideoCapture; - -typedef struct VideoCaptureArray { - VideoCapture values[MAX_VIDEO]; - unsigned int length; -} VideoCaptureArray; - // window.c typedef GLFWwindow Window; @@ -182,11 +154,16 @@ typedef struct Tempo { // context.c -typedef struct SharedContext { +typedef struct Context { int fd; vec2 resolution; vec2 tex_resolution; +#ifdef VIDEO_IN vec2 input_resolutions[MAX_VIDEO]; + unsigned int input_formats[MAX_VIDEO]; + unsigned int input_fps[MAX_VIDEO]; + bool input_swap[MAX_VIDEO]; +#endif /* VIDEO_IN */ double time; Tempo tempo; double tempo_total; @@ -200,9 +177,59 @@ typedef struct SharedContext { unsigned int auto_random_cycle; unsigned int seeds[MAX_FRAG]; unsigned int fps; - VideoCaptureArray inputs; - bool stop; -} SharedContext; + _Atomic bool stop; +} Context; + +// video.c + +#ifdef VIDEO_IN + +typedef struct VideoCapture { + char name[STR_LEN]; + _Atomic bool error; + _Atomic bool disconnected; + _Atomic bool needs_reload; + bool with_swap; + _Atomic bool swap; + int fd; + int exp_fd; + int exp_fd_swap; + unsigned int width; + unsigned int height; + unsigned int pixelformat; + unsigned int bytesperline; + struct v4l2_buffer buf; + struct v4l2_buffer buf_swap; +} VideoCapture; + +typedef struct VideoCaptureArray { + VideoCapture values[MAX_VIDEO]; + unsigned int length; +} VideoCaptureArray; + +typedef struct VideoBackgroundReadArgs { + VideoCapture *capture; + Context *context; + int input_index; + bool trace_fps; +} VideoBackgroundReadArgs; + +#endif /* VIDEO_IN */ + +// midi.c + +typedef struct MidiDevice { + bool error; + char name[STR_LEN]; + snd_rawmidi_t *input; + snd_rawmidi_t *output; +} MidiDevice; + +typedef struct MidiBackgroundListenArgs { + MidiDevice *device; + Context *context; + void (*event_callback)(unsigned char code, unsigned char value); +} MidiBackgroundReadArgs; // state.c @@ -240,6 +267,12 @@ typedef struct StateConfig { UintArray hotkey_save; } StateConfig; +typedef struct StateBackgroundWriteArgs { + Context *context; + StateConfig state_config; + MidiDevice *midi; +} StateBackgroundWriteArgs; + // timer.c typedef struct Timer { @@ -260,15 +293,6 @@ typedef struct ConfigFileItem { char value[STR_LEN]; } ConfigFileItem; -// midi.c - -typedef struct MidiDevice { - bool error; - char name[STR_LEN]; - snd_rawmidi_t *input; - snd_rawmidi_t *output; -} MidiDevice; - // project.c typedef struct Project { diff --git a/src/video.c b/src/video.c index 458442c..2b22053 100644 --- a/src/video.c +++ b/src/video.c @@ -4,8 +4,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -344,23 +344,17 @@ void video_init(VideoCapture *video_capture, const char *name, create_image_buffers(video_capture); } -bool video_background_read(VideoCapture *video_capture, SharedContext *context, - int input_index, bool trace_fps) { - pid_t pid; +void *video_background_read(void *args) { + VideoBackgroundReadArgs *process_args = (VideoBackgroundReadArgs *)args; + VideoCapture *video_capture = process_args->capture; + Context *context = process_args->context; + int input_index = process_args->input_index; + bool trace_fps = process_args->trace_fps; Timer timer; double fps; unsigned int video_result; - pid = fork(); - if (pid < 0) { - log_error("Could not create subprocess"); - return false; - } - if (pid == 0) { - return true; - } - log_info("(%s) background acquisition started (pid: %d)", video_capture->name, - pid); + log_info("(%s) background acquisition started", video_capture->name); timer_init(&timer, 30); while (!context->stop && !video_capture->error) { @@ -368,26 +362,26 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context, if (video_result > 0 && timer_inc(&timer)) { fps = timer_reset(&timer); - context->inputs.values[input_index].fps = (unsigned int)round(fps); + context->input_fps[input_index] = (unsigned int)round(fps); if (trace_fps) { log_trace("(%s) %.2ffps", video_capture->name, fps); } } if (video_result > 0) { - video_capture->swap = video_result == 2; + context->input_swap[input_index] = video_capture->swap = + video_result == 2; } } if (context->stop) { - log_info("(%s) background acquisition stopped by main thread (pid: %d)", - video_capture->name, pid); + log_info("(%s) background acquisition stopped by main thread", + video_capture->name); } else { - log_info("(%s) background acquisition stopped after error (pid: %d)", - video_capture->name, pid); + log_info("(%s) background acquisition stopped after error", + video_capture->name); video_capture->disconnected = true; - video_capture->pixelformat = 0; - video_free(video_capture); + context->input_formats[input_index] = 0; } - exit(context->stop ? EXIT_SUCCESS : EXIT_FAILURE); + pthread_exit(NULL); } void video_free(const VideoCapture *video_capture) { @@ -396,7 +390,7 @@ void video_free(const VideoCapture *video_capture) { close(video_capture->exp_fd); } if (video_capture->exp_fd_swap != -1) { - close(video_capture->exp_fd); + close(video_capture->exp_fd_swap); } if (video_capture->fd != -1) { close(video_capture->fd); diff --git a/src/video.h b/src/video.h index 9a01fa9..b9e6bbd 100644 --- a/src/video.h +++ b/src/video.h @@ -3,12 +3,15 @@ #ifndef VIDEO_H #define VIDEO_H +#ifdef VIDEO_IN + void video_init(VideoCapture *video_capture, const char *name, unsigned int preferred_height); -bool video_background_read(VideoCapture *video_capture, SharedContext *context, - int input_index, bool trace_fps); +void *video_background_read(void *args); void video_free(const VideoCapture *video_capture); +#endif /* VIDEO_IN */ + #endif /* VIDEO_H */ diff --git a/src/window.c b/src/window.c index 671a44f..e8b1ed2 100644 --- a/src/window.c +++ b/src/window.c @@ -123,7 +123,7 @@ void window_events() { glfwPollEvents(); } double window_get_time() { return glfwGetTime(); } -void window_use(Window *window, SharedContext *context) { +void window_use(Window *window, Context *context) { int width; int height; diff --git a/src/window.h b/src/window.h index 6803614..7579111 100644 --- a/src/window.h +++ b/src/window.h @@ -15,7 +15,7 @@ void window_update_title(Window *window, const char *title); double window_get_time(); -void window_use(Window *window, SharedContext *context); +void window_use(Window *window, Context *context); void window_refresh(Window *window);