feat: video auto reconnect
Clang Lint CI / lint-no-video (push) Successful in 56s
Clang Build CI / run-no-video (push) Successful in 59s
Clang Build CI / run-video (push) Successful in 1m15s
Clang Build CI / build-release (push) Successful in 2m23s
Clang Lint CI / lint-video (push) Successful in 2m14s

This commit is contained in:
2026-05-14 13:16:55 +02:00
parent 25b7134a43
commit d4565fa507
18 changed files with 305 additions and 328 deletions
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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)
+3 -8
View File
@@ -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:
+1
View File
@@ -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])
+121 -99
View File
@@ -1,5 +1,6 @@
#include <log.h>
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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();
+10 -19
View File
@@ -1,6 +1,6 @@
#include <alsa/asoundlib.h>
#include <log.h>
#include <stdlib.h>
#include <pthread.h>
#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);
}
+1 -4
View File
@@ -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 */
+29 -29
View File
@@ -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 */
}
+8 -4
View File
@@ -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 */
-35
View File
@@ -1,35 +0,0 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#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);
}
-9
View File
@@ -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 */
+33 -41
View File
@@ -1,6 +1,6 @@
#include <log.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#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);
}
+6 -8
View File
@@ -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 */
+65 -41
View File
@@ -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 {
+18 -24
View File
@@ -4,8 +4,8 @@
#include <fcntl.h>
#include <linux/videodev2.h>
#include <log.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -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);
+5 -2
View File
@@ -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 */
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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);