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
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:
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user