Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7db7f4b89e | |||
| e5b2d2306f | |||
| 54b166d33f | |||
| 344029f195 | |||
| 4837ab2786 | |||
| e38f57af46 | |||
| e667c6b869 | |||
| 0b344eb52a | |||
| fddb11dbe3 | |||
| 591bfbe0aa | |||
| b01314778d | |||
| cbdf4c768d | |||
| 9787f468e8 | |||
| 2a8bd612b3 | |||
| ba44237661 | |||
| efb13bb9ea | |||
| d89de9d6dd | |||
| 6ff0246f42 | |||
| c45b5ef405 | |||
| 12942267d2 | |||
| 15fb29b673 | |||
| 39e1fcc709 | |||
| 3ae3b23c2a | |||
| 61c88aae2a | |||
| fcb2baf84a | |||
| 8e7be2258f | |||
| dcb334ff81 | |||
| aab770ba3d | |||
| 6a7c9b3aab | |||
| 3b6d4d642d | |||
| 988cf799b4 | |||
| a92cca33ad | |||
| fd9f94d48a | |||
| 859dfc4307 | |||
| a85a75f93d | |||
| 7e1328aab0 | |||
| 7cce5babc2 | |||
| 9969230cd9 | |||
| fb77d8e893 | |||
| 0fd78bf4cc | |||
| 20e3d39963 | |||
| aa8d6c85c0 | |||
| b9fd36debd | |||
| 7e8eb187d8 | |||
| bd61cb0a2d | |||
| fe1fc9864c | |||
| 233b75e854 | |||
| 910c122c8d | |||
| fbd73ebf4c | |||
| 43ce38f2f4 | |||
| ca3e523f93 | |||
| c68bebfd7c | |||
| 2843e5e863 |
+2
-1
@@ -68,7 +68,8 @@ valgrind: build/$(TARGET)
|
||||
.PHONY: full-clean
|
||||
full-clean:
|
||||
git clean -f -x
|
||||
rm -rf **/**/.deps
|
||||
rm -rf */*/.deps
|
||||
rm -rf */.deps
|
||||
|
||||
.PHONY: test-release
|
||||
test-release: clean full-clean
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
pkgname=forge-steel
|
||||
pkgver=1.1.1
|
||||
pkgver=1.1.3
|
||||
pkgrel=1
|
||||
pkgdesc="Fusion Of Real Time Generative Effects"
|
||||
arch=('i686' 'pentium4' 'x86_64' 'arm' 'armv7h' 'armv6h' 'aarch64' 'riscv64')
|
||||
depends=('glfw>=1:3', 'v4l-utils>=1.32', 'alsa-lib>=1.2', 'libglvnd>=1.7')
|
||||
url="https://git.klemek.fr/klemek/forge-steel"
|
||||
source=("${pkgname}-steel-${pkgver}.tar.gz::https://git.klemek.fr/klemek/forge-steel/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
sha256sums=('7b518ac91de1ecacb34da3f52a06ae82b26a287512cbf43ba5dd7d39e35b8d8f')
|
||||
sha256sums=('536370c14aaac35729b29270cecca01a6c966bcae306fa2ec20f8a514a3ed8bd')
|
||||
srcdir=build
|
||||
backup=("usr/share/${pkgname}")
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[](https://git.klemek.fr/klemek/forge-steel/actions?workflow=lint.yml) [](https://git.klemek.fr/klemek/forge-steel/actions?workflow=build.yml) 
|
||||
[](https://git.klemek.fr/klemek/forge-steel/actions?workflow=lint.yml) [](https://git.klemek.fr/klemek/forge-steel/actions?workflow=build.yml) 
|
||||
|
||||
<!-- omit from toc -->
|
||||
# F.O.R.G.E. (Steel)
|
||||
@@ -174,9 +174,9 @@ These are configurable in the [`forge_project.cfg`](#forge_projectcfg).
|
||||
### CLI arguments
|
||||
|
||||
```txt
|
||||
forge steel-VERSION
|
||||
forge steel-dev
|
||||
|
||||
usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-arc=CYCLES] [-vi=FILE] [-vs=SIZE] [-vb=COUNT] [-vr / -nvr] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-mr / -nmr] [-tm] [-tf]
|
||||
usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-arc=CYCLES] [-vi=FILE] [-vs=SIZE] [-vb=COUNT] [-vr / -nvr] [-vf=FOURCC] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-mr / -nmr] [-tm] [-tf]
|
||||
|
||||
Fusion Of Real-time Generative Effects.
|
||||
|
||||
@@ -196,10 +196,11 @@ options:
|
||||
-nar, --no-auto-random do not randomize state (default)
|
||||
-arc, --auto-random-cycle auto random cycle length (default: 4)
|
||||
-vi, --video-in path to video capture device (multiple allowed)
|
||||
-vb, --video-buffers number of video buffers to use (default: 2)
|
||||
-vb, --video-buffers number of video buffers to use (default: 1)
|
||||
-vs, --video-size video capture desired height (default: internal texture height)
|
||||
-vr, --video-reconnect auto-reconnect video (default)
|
||||
-nvr, --no-video-reconnect do not auto-reconnect video
|
||||
-vf, --video-fourcc video codec fourcc (default: YUYV)
|
||||
-is, --internal-size internal texture height (default: 720)
|
||||
-ls, --load-state load saved state (default)
|
||||
-nls, --no-load-state do not load saved state
|
||||
@@ -499,7 +500,7 @@ You can check your device real FPS on [V4L2 UCP](https://github.com/HedgeHawk/v4
|
||||
|
||||
### My video feed got strange lines
|
||||
|
||||
You need to decode the [V4L2 YUYV format](https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-yuyv.html).
|
||||
You may need to decode the [V4L2 YUYV format](https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-yuyv.html).
|
||||
|
||||
The code is available in the default project [default/inc_yuyv.glsl](./default/inc_yuyv.glsl)
|
||||
|
||||
|
||||
+1
-2
@@ -1,4 +1,4 @@
|
||||
AC_INIT([forge], [steel-1.1.1], [klemek.dev@proton.me])
|
||||
AC_INIT([forge], [steel-1.1.3], [klemek.dev@proton.me])
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_PROG_CC
|
||||
|
||||
@@ -11,7 +11,6 @@ AC_CHECK_HEADERS([stdlib.h])
|
||||
AC_CHECK_HEADERS([sys/ioctl.h])
|
||||
AC_CHECK_HEADERS([sys/mman.h])
|
||||
AC_CHECK_HEADERS([sys/stat.h])
|
||||
AC_CHECK_HEADERS([sys/time.h])
|
||||
AC_CHECK_HEADERS([sys/types.h])
|
||||
AC_CHECK_HEADERS([sys/wait.h])
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ GROUP_3_1_Y=58
|
||||
GROUP_3_1_Z=
|
||||
GROUP_3_2_X=0
|
||||
GROUP_3_2_Y=16
|
||||
GROUP_3_2_Z=
|
||||
GROUP_3_2_Z=11089
|
||||
|
||||
# =====
|
||||
# OTHER
|
||||
|
||||
+4
-1
@@ -15,9 +15,12 @@ uniform sampler2D iTex1;
|
||||
uniform sampler2D iTex3;
|
||||
uniform int iInputFormat1;
|
||||
uniform vec2 iInputResolution1;
|
||||
uniform vec3 iGroup3_1[2];
|
||||
|
||||
void main() {
|
||||
if (iInputFormat1 == YUYV_FOURCC) {
|
||||
if (iGroup3_1[1].z > 0) {
|
||||
fragColor = texture(iTex1, vUV * vec2(1, -1));
|
||||
} else if (iInputFormat1 == YUYV_FOURCC) {
|
||||
fragColor = yuyvTex(iTex1, vUV, int(iInputResolution1.x));
|
||||
} else {
|
||||
fragColor = texture(iTex0, vUV);
|
||||
|
||||
+4
-1
@@ -15,9 +15,12 @@ uniform sampler2D iTex2;
|
||||
uniform sampler2D iTex4;
|
||||
uniform int iInputFormat2;
|
||||
uniform vec2 iInputResolution2;
|
||||
uniform vec3 iGroup3_1[2];
|
||||
|
||||
void main() {
|
||||
if (iInputFormat2 == YUYV_FOURCC) {
|
||||
if (iGroup3_1[1].z > 0) {
|
||||
fragColor = texture(iTex2, vUV * vec2(1, -1));
|
||||
} else if (iInputFormat2 == YUYV_FOURCC) {
|
||||
fragColor = yuyvTex(iTex2, vUV, int(iInputResolution2.x));
|
||||
} else {
|
||||
fragColor = texture(iTex0, vUV);
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
uniform int iDemo;
|
||||
|
||||
uniform sampler2D iTex0;
|
||||
uniform sampler2D iTex5;
|
||||
uniform sampler2D iTex6;
|
||||
uniform sampler2D iTex3;
|
||||
uniform sampler2D iTex4;
|
||||
|
||||
subroutine vec4 src_stage_sub(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3);
|
||||
|
||||
@@ -298,7 +298,7 @@ subroutine ( src_stage_sub ) vec4 src_6(vec2 vUV, int seed, vec3 b1, vec2 f1, ve
|
||||
return src_2(vUV, seed, b1, f1, b2, f2, b3, f3);
|
||||
}
|
||||
|
||||
return src_thru(vUV, iTex5, seed, b1, f1, b2, f2, b3, f3);
|
||||
return src_thru(vUV, iTex3, seed, b1, f1, b2, f2, b3, f3);
|
||||
}
|
||||
|
||||
#include inc_cp437.glsl
|
||||
@@ -462,7 +462,7 @@ subroutine ( src_stage_sub ) vec4 src_11(vec2 vUV, int seed, vec3 b1, vec2 f1, v
|
||||
return src_3(vUV, seed, b1, f1, b2, f2, b3, f3);
|
||||
}
|
||||
|
||||
return src_thru(vUV, iTex6, seed, b1, f1, b2, f2, b3, f3);
|
||||
return src_thru(vUV, iTex4, seed, b1, f1, b2, f2, b3, f3);
|
||||
}
|
||||
|
||||
// SRC 12 : Scales
|
||||
|
||||
+23
-7
@@ -3,11 +3,27 @@
|
||||
|
||||
const int YUYV_FOURCC = 1448695129;
|
||||
|
||||
const mat3x3 yuyv_to_rgb = {{1,1,1},{0,-0.39465,2.03211},{1.13983,-0.5806,0}};
|
||||
// https://en.wikipedia.org/wiki/Y%E2%80%B2UV
|
||||
const mat3x3 yuyv_to_rgb_bt709 = {
|
||||
{
|
||||
1,
|
||||
1,
|
||||
1
|
||||
},
|
||||
{
|
||||
0,
|
||||
-0.21482,
|
||||
2.12798
|
||||
},
|
||||
{
|
||||
1.28033,
|
||||
-0.38059,
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
vec4 yuyvTex(sampler2D tex, vec2 vUV, int base_width) {
|
||||
float w = base_width - 1;
|
||||
|
||||
int x = int(vUV.x * w);
|
||||
|
||||
int xU = x - x % 2;
|
||||
@@ -17,12 +33,12 @@ vec4 yuyvTex(sampler2D tex, vec2 vUV, int base_width) {
|
||||
vec4 tV = texture(tex, vec2(xV / w, 1 - vUV.y));
|
||||
|
||||
vec3 yuv = vec3(
|
||||
x % 2 == 0 ? tU.x : tV.x,
|
||||
tU.y - 0.5,
|
||||
tV.y - 0.5
|
||||
);
|
||||
x % 2 == 0 ? tU.x : tV.x,
|
||||
tU.y - 0.5,
|
||||
tV.y - 0.5
|
||||
);
|
||||
|
||||
return vec4(yuyv_to_rgb * yuv, 1.0);
|
||||
return vec4(yuyv_to_rgb_bt709 * yuv, 1.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+20
-3
@@ -34,6 +34,7 @@ static void print_help(int status_code) {
|
||||
"[-vs=SIZE] "
|
||||
"[-vb=COUNT] "
|
||||
"[-vr / -nvr] "
|
||||
"[-vf=FOURCC] "
|
||||
#endif /* VIDEO_IN */
|
||||
"[-is=SIZE] "
|
||||
"[-ls / -nls] "
|
||||
@@ -65,11 +66,12 @@ static void print_help(int status_code) {
|
||||
" -vi, --video-in path to video capture device (multiple "
|
||||
"allowed)\n"
|
||||
" -vb, --video-buffers number of video buffers to use (default: "
|
||||
"2)\n"
|
||||
"1)\n"
|
||||
" -vs, --video-size video capture desired height (default: "
|
||||
"internal texture height)\n"
|
||||
" -vr, --video-reconnect auto-reconnect video (default)\n"
|
||||
" -nvr, --no-video-reconnect do not auto-reconnect video\n"
|
||||
" -vf, --video-fourcc video codec fourcc (default: YUYV)\n"
|
||||
#endif /* VIDEO_IN */
|
||||
" -is, --internal-size internal texture height (default: 720)\n"
|
||||
" -ls, --load-state load saved state (default)\n"
|
||||
@@ -114,7 +116,7 @@ static unsigned int parse_uint(const char *arg, const char *value) {
|
||||
|
||||
tmp_value = (unsigned long long)atoll(value);
|
||||
|
||||
if (tmp_value >= UINT_MAX) {
|
||||
if (tmp_value > UINT_MAX) {
|
||||
invalid_value(arg, value);
|
||||
}
|
||||
|
||||
@@ -139,9 +141,10 @@ void args_parse(Parameters *params, int argc, char **argv) {
|
||||
params->auto_random_cycle = 4;
|
||||
#ifdef VIDEO_IN
|
||||
params->video_in.length = 0;
|
||||
params->video_buffers = 2;
|
||||
params->video_buffers = 1;
|
||||
params->video_size = 0;
|
||||
params->video_reconnect = true;
|
||||
strlcpy(params->video_fourcc, "YUYV", 5);
|
||||
#endif /* VIDEO_IN */
|
||||
params->internal_size = 720;
|
||||
params->load_state = true;
|
||||
@@ -159,8 +162,14 @@ void args_parse(Parameters *params, int argc, char **argv) {
|
||||
puts(PACKAGE " " VERSION);
|
||||
exit(EXIT_SUCCESS);
|
||||
} else if (is_arg(arg, "-p") || is_arg(arg, "--project")) {
|
||||
if (strlen(value) == 0) {
|
||||
invalid_value(arg, value);
|
||||
}
|
||||
strlcpy(params->project_path, value, STR_LEN);
|
||||
} else if (is_arg(arg, "-c") || is_arg(arg, "--config")) {
|
||||
if (strlen(value) == 0) {
|
||||
invalid_value(arg, value);
|
||||
}
|
||||
strlcpy(params->config_file, value, STR_LEN);
|
||||
} else if (is_arg(arg, "-hr") || is_arg(arg, "--hot-reload")) {
|
||||
params->hot_reload = true;
|
||||
@@ -218,6 +227,9 @@ void args_parse(Parameters *params, int argc, char **argv) {
|
||||
log_error("maximum video input reached");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (strlen(value) == 0) {
|
||||
invalid_value(arg, value);
|
||||
}
|
||||
strlcpy(params->video_in.values[params->video_in.length++], value,
|
||||
STR_LEN);
|
||||
} else if (is_arg(arg, "-vb") || is_arg(arg, "--video-buffers")) {
|
||||
@@ -234,6 +246,11 @@ void args_parse(Parameters *params, int argc, char **argv) {
|
||||
params->video_reconnect = true;
|
||||
} else if (is_arg(arg, "-nvr") || is_arg(arg, "--no-video-reconnect")) {
|
||||
params->video_reconnect = false;
|
||||
} else if (is_arg(arg, "-vf") || is_arg(arg, "--video-fourcc")) {
|
||||
if (strlen(value) == 0) {
|
||||
invalid_value(arg, value);
|
||||
}
|
||||
strlcpy(params->video_fourcc, value, 5);
|
||||
} else {
|
||||
invalid_arg(arg);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ unsigned int arr_uint_index_of(UintArray array, unsigned int value) {
|
||||
}
|
||||
|
||||
unsigned int arr_uint_remap_index(UintArray offsets, unsigned int *index) {
|
||||
if (offsets.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (unsigned int i = offsets.length - 1; i > 0; i--) {
|
||||
if (*index >= offsets.values[i]) {
|
||||
*index -= offsets.values[i];
|
||||
|
||||
+9
-3
@@ -68,8 +68,7 @@ void config_file_read(ConfigFile *config, const char *path) {
|
||||
char *line;
|
||||
char *rest;
|
||||
|
||||
config->map = hashmap_new(sizeof(ConfigFileItem), 0, 0, 0, item_hash,
|
||||
item_compare, NULL, NULL);
|
||||
config->map = NULL;
|
||||
|
||||
file_read(&file, path);
|
||||
|
||||
@@ -78,6 +77,9 @@ void config_file_read(ConfigFile *config, const char *path) {
|
||||
return;
|
||||
}
|
||||
|
||||
config->map = hashmap_new(sizeof(ConfigFileItem), 0, 0, 0, item_hash,
|
||||
item_compare, NULL, NULL);
|
||||
|
||||
config->error = false;
|
||||
|
||||
line = strtok_r(file.content, "\n", &rest);
|
||||
@@ -138,4 +140,8 @@ unsigned int config_file_get_int(const ConfigFile *config, const char *key,
|
||||
return (unsigned int)atoi(item->value);
|
||||
}
|
||||
|
||||
void config_file_free(const ConfigFile *config) { hashmap_free(config->map); }
|
||||
void config_file_free(const ConfigFile *config) {
|
||||
if (config->map != NULL) {
|
||||
hashmap_free(config->map);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
|
||||
static char *vertex_shader_text =
|
||||
static const char *vertex_shader_text =
|
||||
"#version 460\n"
|
||||
"const mat4 mvp = "
|
||||
"{{2.,0.,0.,0.},{0.,2.,0.,0.},{0.,0.,2.,0.},{-1.,-1.,1.,1.}};\n"
|
||||
@@ -19,4 +19,4 @@ static const Vertex vertices[6] = {{{0.0f, 0.0f}}, {{0.0f, 1.0f}},
|
||||
{{1.0f, 1.0f}}, {{0.0f, 0.0f}},
|
||||
{{1.0f, 1.0f}}, {{1.0f, 0.0f}}};
|
||||
|
||||
#endif /* CONSTANTS_H */
|
||||
#endif /* CONSTANTS_H */
|
||||
|
||||
+17
-3
@@ -26,6 +26,7 @@ bool file_should_update(const File *file) {
|
||||
|
||||
bool file_update(File *file) {
|
||||
long length;
|
||||
size_t read_length;
|
||||
FILE *file_pointer;
|
||||
|
||||
// free remaining data
|
||||
@@ -47,6 +48,12 @@ bool file_update(File *file) {
|
||||
// read file length
|
||||
fseek(file_pointer, 0, SEEK_END);
|
||||
length = ftell(file_pointer);
|
||||
if (length == -1L) {
|
||||
file->error = true;
|
||||
fclose(file_pointer);
|
||||
log_error("Cannot get file length for '%s'", file->path);
|
||||
return false;
|
||||
}
|
||||
// init buffer
|
||||
fseek(file_pointer, 0, SEEK_SET);
|
||||
file->content = malloc(length + 1);
|
||||
@@ -57,7 +64,13 @@ bool file_update(File *file) {
|
||||
return false;
|
||||
}
|
||||
// read file
|
||||
fread(file->content, sizeof(char), length, file_pointer);
|
||||
read_length = fread(file->content, sizeof(char), length, file_pointer);
|
||||
if (read_length != (unsigned)length) {
|
||||
file->error = true;
|
||||
fclose(file_pointer);
|
||||
log_error("Cannot read complete file '%s'", file->path);
|
||||
return false;
|
||||
}
|
||||
// close file
|
||||
fclose(file_pointer);
|
||||
// append null byte
|
||||
@@ -117,8 +130,9 @@ void file_write(const char *path, const StringArray *lines) {
|
||||
fclose(file_pointer);
|
||||
}
|
||||
|
||||
void file_free(const File *file) {
|
||||
if (!file->error) {
|
||||
void file_free(File *file) {
|
||||
if (!file->error && file->content != NULL) {
|
||||
free(file->content);
|
||||
file->content = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,6 +13,6 @@ void file_write(const char *path, const StringArray *lines);
|
||||
|
||||
void file_dump(const char *path, const char *content);
|
||||
|
||||
void file_free(const File *file);
|
||||
void file_free(File *file);
|
||||
|
||||
#endif /* FILE_H */
|
||||
|
||||
+52
-25
@@ -85,7 +85,8 @@ static void init_inputs() {
|
||||
|
||||
for (unsigned int i = 0; i < init_params.video_in.length; i++) {
|
||||
video_init(&video_captures.values[i], init_params.video_in.values[i],
|
||||
init_params.video_size, init_params.video_buffers);
|
||||
init_params.video_size, init_params.video_buffers,
|
||||
init_params.video_fourcc, true);
|
||||
|
||||
if (!video_captures.values[i].error) {
|
||||
context.input_resolutions[i][0] = video_captures.values[i].width;
|
||||
@@ -101,15 +102,19 @@ 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));
|
||||
VideoBackgroundReadArgs *process_args =
|
||||
(VideoBackgroundReadArgs *)malloc(sizeof(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 (pthread_create(&thread, NULL, video_background_read,
|
||||
(void *)process_args) == 0) {
|
||||
pthread_detach(thread);
|
||||
} else {
|
||||
log_error("background video acquisition failed to start");
|
||||
free(process_args);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
@@ -121,7 +126,8 @@ background_reconnect_video_captures(__attribute__((unused)) void *args) {
|
||||
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, init_params.video_buffers);
|
||||
init_params.video_size, init_params.video_buffers,
|
||||
init_params.video_fourcc, false);
|
||||
|
||||
if (!video_captures.values[i].error) {
|
||||
context.input_resolutions[i][0] = video_captures.values[i].width;
|
||||
@@ -147,8 +153,12 @@ static void start_video_captures() {
|
||||
}
|
||||
}
|
||||
if (init_params.video_reconnect) {
|
||||
pthread_create(&thread, NULL, background_reconnect_video_captures, NULL);
|
||||
pthread_detach(thread);
|
||||
if (pthread_create(&thread, NULL, background_reconnect_video_captures,
|
||||
NULL) == 0) {
|
||||
pthread_detach(thread);
|
||||
} else {
|
||||
log_info("background video capture reconnect failed to start");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,26 +202,34 @@ static void midi_callback(unsigned char code, unsigned char value) {
|
||||
|
||||
static void start_state_background_write() {
|
||||
pthread_t thread;
|
||||
struct StateBackgroundWriteArgs *process_args =
|
||||
(struct StateBackgroundWriteArgs *)malloc(
|
||||
sizeof(struct StateBackgroundWriteArgs));
|
||||
StateBackgroundWriteArgs *process_args =
|
||||
(StateBackgroundWriteArgs *)malloc(sizeof(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);
|
||||
if (pthread_create(&thread, NULL, state_background_write, process_args) ==
|
||||
0) {
|
||||
pthread_detach(thread);
|
||||
} else {
|
||||
log_error("background writing failed to start");
|
||||
free(process_args);
|
||||
}
|
||||
}
|
||||
|
||||
static void start_midi_background_listen() {
|
||||
pthread_t thread;
|
||||
struct MidiBackgroundListenArgs *process_args =
|
||||
(struct MidiBackgroundListenArgs *)malloc(
|
||||
sizeof(struct MidiBackgroundListenArgs));
|
||||
MidiBackgroundListenArgs *process_args =
|
||||
(MidiBackgroundListenArgs *)malloc(sizeof(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);
|
||||
if (pthread_create(&thread, NULL, midi_background_listen, process_args) ==
|
||||
0) {
|
||||
pthread_detach(thread);
|
||||
} else {
|
||||
log_error("background midi acquisition failed to start");
|
||||
free(process_args);
|
||||
}
|
||||
}
|
||||
|
||||
static void *background_reconnect_midi(__attribute__((unused)) void *args) {
|
||||
@@ -219,7 +237,8 @@ static void *background_reconnect_midi(__attribute__((unused)) void *args) {
|
||||
while (!context.stop) {
|
||||
sleep(1);
|
||||
if (!midi.connected) {
|
||||
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"));
|
||||
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"),
|
||||
false);
|
||||
if (midi.connected) {
|
||||
start_midi_background_listen();
|
||||
}
|
||||
@@ -231,15 +250,18 @@ static void *background_reconnect_midi(__attribute__((unused)) void *args) {
|
||||
static void init_midi() {
|
||||
pthread_t thread;
|
||||
|
||||
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"));
|
||||
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"), true);
|
||||
|
||||
if (midi.connected) {
|
||||
start_midi_background_listen();
|
||||
}
|
||||
|
||||
if (init_params.midi_reconnect) {
|
||||
pthread_create(&thread, NULL, background_reconnect_midi, NULL);
|
||||
pthread_detach(thread);
|
||||
if (pthread_create(&thread, NULL, background_reconnect_midi, NULL) == 0) {
|
||||
pthread_detach(thread);
|
||||
} else {
|
||||
log_error("background midi reconnect failed to start");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +288,7 @@ static bool init(const Parameters *params) {
|
||||
|
||||
window_startup(error_callback);
|
||||
|
||||
context.tex_resolution[0] = params->internal_size;
|
||||
context.tex_resolution[1] = params->internal_size;
|
||||
|
||||
if (params->output) {
|
||||
@@ -292,6 +315,10 @@ static bool init(const Parameters *params) {
|
||||
}
|
||||
|
||||
#ifdef VIDEO_IN
|
||||
if (params->output && params->monitor) {
|
||||
window_use(window_output, &context);
|
||||
}
|
||||
|
||||
shaders_link_inputs(&program, &project, &video_captures,
|
||||
init_params.video_buffers);
|
||||
#endif /* VIDEO_IN */
|
||||
@@ -363,8 +390,6 @@ static void shutdown() {
|
||||
state_save(&context, project.state_config);
|
||||
}
|
||||
|
||||
shaders_free(&program);
|
||||
|
||||
if (window_output != NULL) {
|
||||
window_use(window_output, &context);
|
||||
|
||||
@@ -377,6 +402,8 @@ static void shutdown() {
|
||||
shaders_free_window(&program, init_params.output);
|
||||
}
|
||||
|
||||
shaders_free(&program);
|
||||
|
||||
#ifdef VIDEO_IN
|
||||
free_video_captures();
|
||||
#endif /* VIDEO_IN */
|
||||
|
||||
+28
-10
@@ -6,19 +6,30 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
void midi_open(MidiDevice *device, const char *name) {
|
||||
void snd_no_log(__attribute__((unused)) int prio,
|
||||
__attribute__((unused)) int interface,
|
||||
__attribute__((unused)) const char *file,
|
||||
__attribute__((unused)) int line,
|
||||
__attribute__((unused)) const char *function,
|
||||
__attribute__((unused)) int errcode,
|
||||
__attribute__((unused)) const char *fmt,
|
||||
__attribute__((unused)) va_list arg) {}
|
||||
|
||||
void midi_open(MidiDevice *device, const char *name, bool log_error) {
|
||||
strlcpy(device->name, name, STR_LEN);
|
||||
device->connected = false;
|
||||
device->input = NULL;
|
||||
device->output = NULL;
|
||||
|
||||
snd_rawmidi_open(&device->input, &device->output, name, SND_RAWMIDI_NONBLOCK);
|
||||
|
||||
device->connected = device->input != NULL && device->output != NULL;
|
||||
|
||||
if (device->connected) {
|
||||
log_info("(%s) MIDI open", name);
|
||||
} else {
|
||||
log_warn("(%s) MIDI open failed", name);
|
||||
device->connected = snd_rawmidi_open(&device->input, &device->output, name,
|
||||
SND_RAWMIDI_SYNC) == 0 &&
|
||||
device->input != NULL && device->output != NULL;
|
||||
if (log_error) {
|
||||
if (device->connected) {
|
||||
log_info("(%s) MIDI open", name);
|
||||
} else {
|
||||
log_warn("(%s) MIDI open failed", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +56,7 @@ void midi_close(MidiDevice *device) {
|
||||
}
|
||||
|
||||
void *midi_background_listen(void *args) {
|
||||
MidiBackgroundReadArgs *process_args = (MidiBackgroundReadArgs *)args;
|
||||
MidiBackgroundListenArgs *process_args = (MidiBackgroundListenArgs *)args;
|
||||
MidiDevice *device = process_args->device;
|
||||
Context *context = process_args->context;
|
||||
|
||||
@@ -57,6 +68,12 @@ void *midi_background_listen(void *args) {
|
||||
|
||||
snd_rawmidi_info_malloc(&info);
|
||||
|
||||
if (info == NULL) {
|
||||
log_error("(%s) failed to allocate MIDI info", device->name);
|
||||
free(process_args);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
while (!context->stop && snd_rawmidi_info(device->output, info) == 0) {
|
||||
bytes_read = snd_rawmidi_read(device->input, buffer, 3);
|
||||
if (bytes_read == 3) {
|
||||
@@ -73,5 +90,6 @@ void *midi_background_listen(void *args) {
|
||||
log_info("(%s) background acquisition stopped after error", device->name);
|
||||
midi_close(device);
|
||||
}
|
||||
free(process_args);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
#ifndef MIDI_H
|
||||
#define MIDI_H
|
||||
|
||||
void midi_open(MidiDevice *device, const char *name);
|
||||
void midi_open(MidiDevice *device, const char *name, bool log_error);
|
||||
void midi_write(MidiDevice device, unsigned char code, unsigned char value);
|
||||
void *midi_background_listen(void *args);
|
||||
void midi_close(MidiDevice *device);
|
||||
|
||||
+10
-1
@@ -48,6 +48,8 @@ static bool parse_fragment_shader_file(Project *project, unsigned int i) {
|
||||
include_end - project->fragment_shaders[i][0].content,
|
||||
tmp_file.content);
|
||||
|
||||
free(project->fragment_shaders[i][0].content);
|
||||
|
||||
project->fragment_shaders[i][0].content = new_content;
|
||||
|
||||
file_free(&tmp_file);
|
||||
@@ -102,6 +104,12 @@ void project_init(Project *project, const char *project_path,
|
||||
|
||||
project->sub_counts.length = project->frag_count;
|
||||
|
||||
for (unsigned int i = 0; i < project->frag_count; i++) {
|
||||
for (unsigned int j = 0; j < MAX_SUB_FILE + 1; j++) {
|
||||
project->fragment_shaders[i][j].content = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < project->frag_count; i++) {
|
||||
project->sub_counts.values[i] = 0;
|
||||
if (!read_fragment_shader_file(project, frag_prefix, i)) {
|
||||
@@ -134,9 +142,10 @@ void project_reload(Project *project, void (*reload_callback)(unsigned int)) {
|
||||
}
|
||||
}
|
||||
|
||||
void project_free(const Project *project) {
|
||||
void project_free(Project *project) {
|
||||
for (unsigned int i = 0; i < project->frag_count; i++) {
|
||||
file_free(&project->fragment_shaders[i][0]);
|
||||
// other sub files contents are freed at parse_fragment_shader_file
|
||||
}
|
||||
|
||||
config_file_free(&project->config);
|
||||
|
||||
+1
-1
@@ -8,6 +8,6 @@ void project_init(Project *project, const char *project_path,
|
||||
|
||||
void project_reload(Project *project, void (*reload_callback)(unsigned int));
|
||||
|
||||
void project_free(const Project *project);
|
||||
void project_free(Project *project);
|
||||
|
||||
#endif /* PROJECT_H */
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ static unsigned long long mcg_state = 0xcafef00dd15ea5e5u; // Must be odd
|
||||
static unsigned long long const multiplier = 6364136223846793005u;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Permuted_congruential_generator
|
||||
static unsigned long rand(void) {
|
||||
static unsigned long fast_rand(void) {
|
||||
unsigned long long x = mcg_state;
|
||||
unsigned count = (unsigned)(x >> 61);
|
||||
|
||||
@@ -15,9 +15,9 @@ static unsigned long rand(void) {
|
||||
|
||||
void rand_set_seed(unsigned long long seed) {
|
||||
mcg_state = 2 * seed + 1;
|
||||
(void)rand();
|
||||
(void)fast_rand();
|
||||
}
|
||||
|
||||
unsigned int rand_uint(const unsigned int max) {
|
||||
return max == 0 ? 0 : (unsigned int)(rand() % max);
|
||||
return max == 0 ? 0 : (unsigned int)(fast_rand() % max);
|
||||
}
|
||||
|
||||
+33
-18
@@ -24,7 +24,7 @@
|
||||
#include <GLFW/glfw3native.h>
|
||||
#endif /* VIDEO_IN */
|
||||
|
||||
static const GLuint unused_uniform = (GLuint)-1;
|
||||
static const GLint UNUSED_UNIFORM = -1;
|
||||
|
||||
static bool check_glerror_ro(const char *context) {
|
||||
unsigned int code;
|
||||
@@ -66,7 +66,7 @@ static bool check_eglerror_ro(const char *context) {
|
||||
|
||||
code = eglGetError();
|
||||
|
||||
if (code > 0 && code != EGL_SUCCESS) {
|
||||
if (code != EGL_SUCCESS) {
|
||||
log_warn("EGL Error: %04x (%s)", code, context);
|
||||
return true;
|
||||
}
|
||||
@@ -245,12 +245,6 @@ static bool link_input_to_texture(ShaderProgram *program, VideoCapture *input,
|
||||
return false;
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, input->width, input->height, 0, GL_RGB,
|
||||
GL_UNSIGNED_BYTE, 0);
|
||||
if (check_glerror(program, "link_input_to_texture/glTexImage2D")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_EGL_image_storage.txt
|
||||
glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, dma_image, NULL);
|
||||
if (check_eglerror(program,
|
||||
@@ -411,7 +405,7 @@ static bool compile_shader(GLuint shader_id, const char *name,
|
||||
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &status_params);
|
||||
check_glerror_ro("compile_shader/glGetShaderiv");
|
||||
|
||||
glGetShaderInfoLog(shader_id, 1024, NULL, (GLchar *)&log);
|
||||
glGetShaderInfoLog(shader_id, STR_LEN, NULL, (GLchar *)log);
|
||||
check_glerror_ro("compile_shader/glGetShaderInfoLog");
|
||||
|
||||
if (status_params == GL_FALSE) {
|
||||
@@ -462,6 +456,8 @@ static bool init_single_program(ShaderProgram *program, unsigned int i,
|
||||
unsigned int index2;
|
||||
char name[STR_LEN];
|
||||
const char *prefix;
|
||||
GLint link_status;
|
||||
char link_log[STR_LEN];
|
||||
|
||||
program->programs[i] = glCreateProgram();
|
||||
if (check_glerror(program, "init_single_program/glCreateProgram")) {
|
||||
@@ -479,6 +475,19 @@ static bool init_single_program(ShaderProgram *program, unsigned int i,
|
||||
return false;
|
||||
}
|
||||
|
||||
glGetProgramiv(program->programs[i], GL_LINK_STATUS, &link_status);
|
||||
if (check_glerror(program, "init_single_program/glGetProgramiv")) {
|
||||
return false;
|
||||
}
|
||||
if (link_status != GL_TRUE) {
|
||||
glGetProgramInfoLog(program->programs[i], STR_LEN, NULL, link_log);
|
||||
if (check_glerror(program, "init_single_program/glGetProgramInfoLog")) {
|
||||
return false;
|
||||
}
|
||||
log_error("Program %d link error: %s", i + 1, link_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create uniforms pointers
|
||||
program->itime_locations[i] = glGetUniformLocation(
|
||||
program->programs[i],
|
||||
@@ -651,27 +660,27 @@ static void update_viewport(ShaderProgram *program, const Context *context) {
|
||||
}
|
||||
}
|
||||
|
||||
static void write_uniform_1f(GLuint location, float value) {
|
||||
if (location != unused_uniform) {
|
||||
static void write_uniform_1f(GLint location, float value) {
|
||||
if (location != UNUSED_UNIFORM) {
|
||||
glUniform1f(location, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_uniform_1i(GLuint location, unsigned int value) {
|
||||
if (location != unused_uniform) {
|
||||
static void write_uniform_1i(GLint location, unsigned int value) {
|
||||
if (location != UNUSED_UNIFORM) {
|
||||
glUniform1i(location, (const GLint)value);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_uniform_2f(GLuint location, const vec2 *value) {
|
||||
if (location != unused_uniform) {
|
||||
static void write_uniform_2f(GLint location, const vec2 *value) {
|
||||
if (location != UNUSED_UNIFORM) {
|
||||
glUniform2fv(location, 1, (const GLfloat *)value);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_uniform_multi_3f(GLuint location, unsigned int count,
|
||||
static void write_uniform_multi_3f(GLint location, unsigned int count,
|
||||
const vec3 *value) {
|
||||
if (location != unused_uniform) {
|
||||
if (location != UNUSED_UNIFORM) {
|
||||
glUniform3fv(location, count, (const GLfloat *)value);
|
||||
}
|
||||
}
|
||||
@@ -759,7 +768,7 @@ static void use_program(const ShaderProgram *program, int i, bool output,
|
||||
if (program->sub_locations[i * program->sub_variant_count *
|
||||
program->sub_type_count +
|
||||
j * program->sub_variant_count + k] !=
|
||||
unused_uniform) {
|
||||
UNUSED_UNIFORM) {
|
||||
subroutines[subcount++] =
|
||||
program->sub_locations[i * program->sub_variant_count *
|
||||
program->sub_type_count +
|
||||
@@ -935,6 +944,12 @@ void shaders_free(const ShaderProgram *program) {
|
||||
}
|
||||
check_glerror_ro("shaders_free/glDeleteProgram");
|
||||
|
||||
glDeleteShader(program->vertex_shader);
|
||||
for (unsigned int i = 0; i < program->frag_count; i++) {
|
||||
glDeleteShader(program->fragment_shaders[i]);
|
||||
}
|
||||
check_glerror_ro("shaders_free/glDeleteShader");
|
||||
|
||||
glDeleteFramebuffers(program->frag_count, program->frame_buffers);
|
||||
check_glerror_ro("shaders_free/glDeleteFramebuffers");
|
||||
|
||||
|
||||
+95
-27
@@ -184,29 +184,65 @@ static void save_to_file(const Context *context, StateConfig state_config,
|
||||
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "tempo=%d",
|
||||
(unsigned int)context->tempo.tempo);
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "page=%d", context->page);
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "selected=%d",
|
||||
context->selected);
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < context->state.length; i++) {
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "seed_%d=%d", i,
|
||||
context->seeds[i]);
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "state_%d=%d", i,
|
||||
context->state.values[i]);
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < state_config.group_active_counts.length; i++) {
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "active_%d=%d", i,
|
||||
context->active[i]);
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < state_config.value_count; i++) {
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_x=%d", i,
|
||||
(unsigned int)(context->values[i][0] * MIDI_MAX));
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_y=%d", i,
|
||||
(unsigned int)(context->values[i][1] * MIDI_MAX));
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_z=%d", i,
|
||||
(unsigned int)(context->values[i][2] * MIDI_MAX));
|
||||
if (lines.length > ARRAY_SIZE) {
|
||||
log_error("Too many values to save");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
file_write(state_file, &lines);
|
||||
@@ -234,10 +270,14 @@ static void save_to_index_file(const Context *context, StateConfig state_config,
|
||||
void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||
unsigned int offset;
|
||||
unsigned int count;
|
||||
unsigned int length;
|
||||
char name[STR_LEN];
|
||||
|
||||
state_config->select_page_codes.length =
|
||||
config_file_get_int(config, "SELECT_PAGE_COUNT", 0);
|
||||
length = config_file_get_int(config, "SELECT_PAGE_COUNT", 0);
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->select_page_codes.length = length;
|
||||
|
||||
for (unsigned int i = 0; i < state_config->select_page_codes.length; i++) {
|
||||
snprintf(name, STR_LEN, "SELECT_PAGE_%d", i + 1);
|
||||
@@ -245,8 +285,11 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
}
|
||||
|
||||
state_config->select_item_codes.length =
|
||||
config_file_get_int(config, "SELECT_ITEM_COUNT", 0);
|
||||
length = config_file_get_int(config, "SELECT_ITEM_COUNT", 0);
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->select_item_codes.length = length;
|
||||
|
||||
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) {
|
||||
snprintf(name, STR_LEN, "SELECT_ITEM_%d", i + 1);
|
||||
@@ -257,8 +300,11 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||
state_config->state_max = state_config->select_page_codes.length *
|
||||
state_config->select_item_codes.length;
|
||||
|
||||
state_config->select_frag_codes.length =
|
||||
config_file_get_int(config, "FRAG_COUNT", 1);
|
||||
length = config_file_get_int(config, "FRAG_COUNT", 1);
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->select_frag_codes.length = length;
|
||||
|
||||
for (unsigned int i = 0; i < state_config->select_frag_codes.length; i++) {
|
||||
snprintf(name, STR_LEN, "SELECT_FRAG_%d", i + 1);
|
||||
@@ -266,11 +312,14 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
}
|
||||
|
||||
length = config_file_get_int(config, "GROUP_COUNT", 0);
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->group_active_counts.length = state_config->group_counts.length =
|
||||
state_config->group_active_offsets.length =
|
||||
state_config->group_offsets.length =
|
||||
state_config->values_offsets.length =
|
||||
config_file_get_int(config, "GROUP_COUNT", 0);
|
||||
state_config->values_offsets.length = length;
|
||||
|
||||
count = 0;
|
||||
for (unsigned int i = 0; i < state_config->group_active_counts.length; i++) {
|
||||
@@ -307,27 +356,40 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||
|
||||
state_config->value_count = offset;
|
||||
|
||||
length = count * 3;
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->codes.length = count * 3;
|
||||
|
||||
for (unsigned int i = 0; i < state_config->group_counts.length; i++) {
|
||||
offset = state_config->group_offsets.values[i];
|
||||
for (unsigned int j = 0; j < state_config->group_counts.values[i]; j++) {
|
||||
snprintf(name, STR_LEN, "GROUP_%d_%d_X", i + 1, j + 1);
|
||||
state_config->codes.values[(offset + j) * 3] =
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
if ((offset + j) * 3 < ARRAY_SIZE) {
|
||||
snprintf(name, STR_LEN, "GROUP_%d_%d_X", i + 1, j + 1);
|
||||
state_config->codes.values[(offset + j) * 3] =
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
}
|
||||
|
||||
snprintf(name, STR_LEN, "GROUP_%d_%d_Y", i + 1, j + 1);
|
||||
state_config->codes.values[(offset + j) * 3 + 1] =
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
if ((offset + j) * 3 + 1 < ARRAY_SIZE) {
|
||||
snprintf(name, STR_LEN, "GROUP_%d_%d_Y", i + 1, j + 1);
|
||||
state_config->codes.values[(offset + j) * 3 + 1] =
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
}
|
||||
|
||||
snprintf(name, STR_LEN, "GROUP_%d_%d_Z", i + 1, j + 1);
|
||||
state_config->codes.values[(offset + j) * 3 + 2] =
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
if ((offset + j) * 3 + 2 < ARRAY_SIZE) {
|
||||
snprintf(name, STR_LEN, "GROUP_%d_%d_Z", i + 1, j + 1);
|
||||
state_config->codes.values[(offset + j) * 3 + 2] =
|
||||
config_file_get_int(config, name, UNSET_MIDI_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state_config->fader_codes.length =
|
||||
config_file_get_int(config, "FADER_COUNT", 0);
|
||||
length = config_file_get_int(config, "FADER_COUNT", 0);
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->fader_codes.length = length;
|
||||
|
||||
for (unsigned int i = 0; i < state_config->fader_codes.length; i++) {
|
||||
snprintf(name, STR_LEN, "FADER_%d", i + 1);
|
||||
@@ -358,8 +420,11 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||
config_file_get_int(config, "KEY_TEMPO_UP", 1265);
|
||||
|
||||
if (config_file_has(config, "KEY_LOAD_COUNT")) {
|
||||
state_config->key_load.length =
|
||||
config_file_get_int(config, "KEY_LOAD_COUNT", 0);
|
||||
length = config_file_get_int(config, "KEY_LOAD_COUNT", 0);
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->key_load.length = length;
|
||||
|
||||
for (unsigned int i = 0; i < state_config->key_load.length; i++) {
|
||||
snprintf(name, STR_LEN, "KEY_LOAD_%d", i + 1);
|
||||
@@ -380,8 +445,11 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
|
||||
}
|
||||
|
||||
if (config_file_has(config, "KEY_SAVE_COUNT")) {
|
||||
state_config->key_save.length =
|
||||
config_file_get_int(config, "KEY_SAVE_COUNT", 0);
|
||||
length = config_file_get_int(config, "KEY_SAVE_COUNT", 0);
|
||||
if (length > ARRAY_SIZE) {
|
||||
length = ARRAY_SIZE;
|
||||
}
|
||||
state_config->key_save.length = length;
|
||||
|
||||
for (unsigned int i = 0; i < state_config->key_save.length; i++) {
|
||||
snprintf(name, STR_LEN, "KEY_SAVE_%d", i + 1);
|
||||
@@ -513,10 +581,9 @@ static bool compute_event(Context *context, StateConfig state_config,
|
||||
|
||||
if (code == state_config.key_autorand) {
|
||||
if (value > 0) {
|
||||
log_info((context->auto_random ? "[%d] Auto Random OFF"
|
||||
: "[%d] Auto Random ON"),
|
||||
code);
|
||||
context->auto_random = !context->auto_random;
|
||||
log_info("[%d] Auto Random %s", code,
|
||||
context->auto_random ? "ON" : "OFF");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -552,7 +619,7 @@ static bool compute_event(Context *context, StateConfig state_config,
|
||||
if (context->tempo.tempo > 0) {
|
||||
tempo_set(&context->tempo, context->tempo.tempo - 1);
|
||||
}
|
||||
log_info("[%d] Tempo: %f", code, context->tempo);
|
||||
log_info("[%d] Tempo: %f", code, context->tempo.tempo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -643,6 +710,7 @@ void *state_background_write(void *args) {
|
||||
}
|
||||
|
||||
log_info("(state) background writing stopped by main thread");
|
||||
free(process_args);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -15,7 +15,7 @@ unsigned int string_trim(char *str) {
|
||||
start = 0;
|
||||
end = strnlen(str, STR_LEN) - 1;
|
||||
|
||||
if (end == 0) {
|
||||
if (end <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,11 +62,19 @@ char *string_replace_at(const char *src, unsigned int from, unsigned int to,
|
||||
unsigned long rpl_len;
|
||||
char *dst;
|
||||
|
||||
if (to < from) {
|
||||
return "";
|
||||
}
|
||||
|
||||
src_len = strnlen(src, STR_LEN * STR_LEN);
|
||||
rpl_len = strnlen(rpl, STR_LEN * STR_LEN);
|
||||
|
||||
dst = malloc(src_len - (to - from) + rpl_len + 1);
|
||||
|
||||
if (dst == NULL) {
|
||||
return "";
|
||||
}
|
||||
|
||||
strlcpy(dst, src, from + 1);
|
||||
strlcpy(dst + from, rpl, rpl_len + 1);
|
||||
strlcpy(dst + from + rpl_len, src + to, src_len - to + 1);
|
||||
|
||||
+11
-11
@@ -1,18 +1,18 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "tempo.h"
|
||||
|
||||
static long now() {
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
return now.tv_sec * 1000 + now.tv_usec / 1000;
|
||||
static long now_ms() {
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
|
||||
return 0;
|
||||
}
|
||||
return 1000 * ts.tv_sec + ts.tv_nsec / 1e6;
|
||||
}
|
||||
|
||||
static void reset_tap_chain(Tempo *tempo, long t) {
|
||||
@@ -93,7 +93,7 @@ static void add_tap_to_chain(Tempo *tempo, long t) {
|
||||
void tempo_init(Tempo *tempo, float value) {
|
||||
long t;
|
||||
|
||||
t = now();
|
||||
t = now_ms();
|
||||
|
||||
reset_tap_chain(tempo, t);
|
||||
|
||||
@@ -105,7 +105,7 @@ void tempo_set(Tempo *tempo, float value) {
|
||||
long t;
|
||||
long progress;
|
||||
|
||||
t = now();
|
||||
t = now_ms();
|
||||
|
||||
progress = (t - tempo->last_reset) % tempo->beat_length;
|
||||
|
||||
@@ -118,7 +118,7 @@ void tempo_set(Tempo *tempo, float value) {
|
||||
void tempo_tap(Tempo *tempo) {
|
||||
long t;
|
||||
|
||||
t = now();
|
||||
t = now_ms();
|
||||
|
||||
if (!is_chain_active(*tempo, t)) {
|
||||
reset_tap_chain(tempo, t);
|
||||
@@ -130,7 +130,7 @@ void tempo_tap(Tempo *tempo) {
|
||||
double tempo_total(const Tempo *tempo) {
|
||||
long t;
|
||||
|
||||
t = now();
|
||||
t = now_ms();
|
||||
|
||||
return (double)(t - tempo->last_reset) / (double)tempo->beat_length;
|
||||
}
|
||||
|
||||
+11
-8
@@ -1,4 +1,6 @@
|
||||
#include <sys/time.h>
|
||||
#include <bits/time.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
@@ -7,24 +9,25 @@
|
||||
void timer_init(Timer *timer, const unsigned int target) {
|
||||
timer->counter = 0;
|
||||
timer->target = target;
|
||||
|
||||
gettimeofday(&timer->start, NULL);
|
||||
clock_gettime(CLOCK_REALTIME, &timer->start);
|
||||
}
|
||||
|
||||
bool timer_inc(Timer *timer) {
|
||||
timer->counter += 1;
|
||||
return timer->counter >= timer->target;
|
||||
return timer->counter == UINT_MAX || timer->counter >= timer->target;
|
||||
}
|
||||
|
||||
double timer_reset(Timer *timer) {
|
||||
struct timeval stop;
|
||||
struct timespec stop;
|
||||
double secs;
|
||||
double per_secs;
|
||||
|
||||
gettimeofday(&stop, NULL);
|
||||
if (clock_gettime(CLOCK_REALTIME, &stop) != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
secs = (double)(stop.tv_usec - timer->start.tv_usec) / 1000000 +
|
||||
(double)(stop.tv_sec - timer->start.tv_sec);
|
||||
secs = (double)(stop.tv_sec - timer->start.tv_sec) +
|
||||
(double)(stop.tv_nsec - timer->start.tv_nsec) / 1e9;
|
||||
per_secs = (double)timer->counter / secs;
|
||||
|
||||
timer->start = stop;
|
||||
|
||||
+24
-23
@@ -50,6 +50,7 @@ typedef struct Parameters {
|
||||
unsigned int video_buffers;
|
||||
unsigned int video_size;
|
||||
bool video_reconnect;
|
||||
char video_fourcc[5];
|
||||
#endif /* VIDEO_IN */
|
||||
unsigned int internal_size;
|
||||
bool load_state;
|
||||
@@ -100,34 +101,34 @@ typedef struct ShaderProgram {
|
||||
GLuint frame_buffers[ARRAY_SIZE];
|
||||
GLuint fragment_shaders[ARRAY_SIZE];
|
||||
|
||||
GLuint itime_locations[ARRAY_SIZE];
|
||||
GLuint itempo_locations[ARRAY_SIZE];
|
||||
GLuint ibeats_locations[ARRAY_SIZE];
|
||||
GLuint ifps_locations[ARRAY_SIZE];
|
||||
GLuint ires_locations[ARRAY_SIZE];
|
||||
GLuint itexres_locations[ARRAY_SIZE];
|
||||
GLuint iinres_locations[ARRAY_SIZE];
|
||||
GLuint iinfmt_locations[ARRAY_SIZE];
|
||||
GLuint iinfps_locations[ARRAY_SIZE];
|
||||
GLuint idemo_locations[ARRAY_SIZE];
|
||||
GLuint iautorand_locations[ARRAY_SIZE];
|
||||
GLuint iautorandcycle_locations[ARRAY_SIZE];
|
||||
GLuint iseed_locations[ARRAY_SIZE];
|
||||
GLuint istate_locations[ARRAY_SIZE];
|
||||
GLuint ipage_locations[ARRAY_SIZE];
|
||||
GLuint iselected_locations[ARRAY_SIZE];
|
||||
GLuint iactive_locations[ARRAY_SIZE];
|
||||
GLint itime_locations[ARRAY_SIZE];
|
||||
GLint itempo_locations[ARRAY_SIZE];
|
||||
GLint ibeats_locations[ARRAY_SIZE];
|
||||
GLint ifps_locations[ARRAY_SIZE];
|
||||
GLint ires_locations[ARRAY_SIZE];
|
||||
GLint itexres_locations[ARRAY_SIZE];
|
||||
GLint iinres_locations[ARRAY_SIZE];
|
||||
GLint iinfmt_locations[ARRAY_SIZE];
|
||||
GLint iinfps_locations[ARRAY_SIZE];
|
||||
GLint idemo_locations[ARRAY_SIZE];
|
||||
GLint iautorand_locations[ARRAY_SIZE];
|
||||
GLint iautorandcycle_locations[ARRAY_SIZE];
|
||||
GLint iseed_locations[ARRAY_SIZE];
|
||||
GLint istate_locations[ARRAY_SIZE];
|
||||
GLint ipage_locations[ARRAY_SIZE];
|
||||
GLint iselected_locations[ARRAY_SIZE];
|
||||
GLint iactive_locations[ARRAY_SIZE];
|
||||
|
||||
UintArray midi_lengths;
|
||||
GLuint igroup_locations[ARRAY_SIZE];
|
||||
GLint igroup_locations[ARRAY_SIZE];
|
||||
|
||||
GLuint vpos_locations[ARRAY_SIZE];
|
||||
GLint vpos_locations[ARRAY_SIZE];
|
||||
|
||||
GLuint textures_locations[ARRAY_SIZE];
|
||||
GLint textures_locations[ARRAY_SIZE];
|
||||
|
||||
unsigned int sub_type_count;
|
||||
unsigned int sub_variant_count;
|
||||
GLuint sub_locations[ARRAY_SIZE];
|
||||
GLint sub_locations[ARRAY_SIZE];
|
||||
|
||||
unsigned int active_count;
|
||||
|
||||
@@ -232,7 +233,7 @@ typedef struct MidiBackgroundListenArgs {
|
||||
MidiDevice *device;
|
||||
Context *context;
|
||||
void (*event_callback)(unsigned char code, unsigned char value);
|
||||
} MidiBackgroundReadArgs;
|
||||
} MidiBackgroundListenArgs;
|
||||
|
||||
// state.c
|
||||
|
||||
@@ -279,7 +280,7 @@ typedef struct StateBackgroundWriteArgs {
|
||||
// timer.c
|
||||
|
||||
typedef struct Timer {
|
||||
struct timeval start;
|
||||
struct timespec start;
|
||||
unsigned int counter;
|
||||
unsigned int target;
|
||||
} Timer;
|
||||
|
||||
+81
-25
@@ -16,7 +16,6 @@
|
||||
#include "timer.h"
|
||||
#include "video.h"
|
||||
|
||||
static const unsigned int pixel_format = V4L2_PIX_FMT_YUYV;
|
||||
static const enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
static void ioctl_error(VideoCapture *video_capture, const char *operation,
|
||||
@@ -70,14 +69,31 @@ static void ioctl_error(VideoCapture *video_capture, const char *operation,
|
||||
video_capture->error = true;
|
||||
}
|
||||
|
||||
static void open_device(VideoCapture *video_capture, const char *name) {
|
||||
static void fourcc_to_string(unsigned int fourcc, char *str) {
|
||||
str[0] = (char)(fourcc & 0xFF);
|
||||
str[1] = (char)((fourcc >> 8) & 0xFF);
|
||||
str[2] = (char)((fourcc >> 16) & 0xFF);
|
||||
str[3] = (char)((fourcc >> 24) & 0xFF);
|
||||
str[4] = '\0';
|
||||
}
|
||||
|
||||
static int string_to_fourcc(const char *str) {
|
||||
return (unsigned int)str[0] | ((unsigned int)str[1] << 8) |
|
||||
((unsigned int)str[2] << 16) | ((unsigned int)str[3] << 24);
|
||||
}
|
||||
|
||||
static void open_device(VideoCapture *video_capture, const char *name,
|
||||
bool log_error) {
|
||||
strlcpy(video_capture->name, name, STR_LEN);
|
||||
video_capture->error = false;
|
||||
video_capture->fd = -1;
|
||||
video_capture->buf_count = 0;
|
||||
|
||||
video_capture->fd = open(name, O_RDWR | O_NONBLOCK);
|
||||
if (video_capture->fd == -1) {
|
||||
log_warn("(%s) Cannot open device", name);
|
||||
if (log_error) {
|
||||
log_warn("(%s) Cannot open device", name);
|
||||
}
|
||||
video_capture->error = true;
|
||||
}
|
||||
}
|
||||
@@ -108,7 +124,8 @@ static bool check_caps(VideoCapture *video_capture) {
|
||||
}
|
||||
|
||||
static bool get_available_sizes(VideoCapture *video_capture,
|
||||
unsigned int preferred_height) {
|
||||
unsigned int preferred_height,
|
||||
unsigned int pixel_format) {
|
||||
struct v4l2_frmsizeenum fmt_enum;
|
||||
unsigned int index;
|
||||
bool found = false;
|
||||
@@ -161,8 +178,9 @@ static bool get_available_sizes(VideoCapture *video_capture,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool set_format(VideoCapture *video_capture) {
|
||||
static bool set_format(VideoCapture *video_capture, unsigned int pixel_format) {
|
||||
struct v4l2_format fmt;
|
||||
char fourcc[STR_LEN];
|
||||
|
||||
memset(&fmt, 0, sizeof(fmt));
|
||||
|
||||
@@ -183,9 +201,9 @@ static bool set_format(VideoCapture *video_capture) {
|
||||
video_capture->pixelformat = fmt.fmt.pix.pixelformat;
|
||||
video_capture->bytesperline = fmt.fmt.pix.bytesperline;
|
||||
|
||||
log_info("(%s) Format fourcc: %c%c%c%c", video_capture->name,
|
||||
fmt.fmt.pix.pixelformat, fmt.fmt.pix.pixelformat >> 8,
|
||||
fmt.fmt.pix.pixelformat >> 16, fmt.fmt.pix.pixelformat >> 24);
|
||||
fourcc_to_string(fmt.fmt.pix.pixelformat, fourcc);
|
||||
|
||||
log_info("(%s) Format fourcc: %s", video_capture->name, fourcc);
|
||||
log_info("(%s) Resolution: %dx%d", video_capture->name, fmt.fmt.pix.width,
|
||||
fmt.fmt.pix.height);
|
||||
|
||||
@@ -211,6 +229,9 @@ static bool request_buffers(VideoCapture *video_capture,
|
||||
log_info("(%s) V4L2 Buffer Count: %d", video_capture->name, reqbuf.count);
|
||||
|
||||
video_capture->buf_count = reqbuf.count;
|
||||
for (unsigned int i = 0; i < reqbuf.count; i++) {
|
||||
video_capture->exp_fd[i] = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -262,7 +283,7 @@ static bool open_stream(VideoCapture *video_capture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void create_image_buffer(const VideoCapture *video_capture,
|
||||
static bool create_image_buffer(VideoCapture *video_capture,
|
||||
struct v4l2_buffer *buf, unsigned int index) {
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
|
||||
@@ -270,15 +291,24 @@ static void create_image_buffer(const VideoCapture *video_capture,
|
||||
buf->memory = V4L2_MEMORY_MMAP;
|
||||
buf->index = index;
|
||||
|
||||
ioctl(video_capture->fd, VIDIOC_QBUF, buf);
|
||||
if (ioctl(video_capture->fd, VIDIOC_QBUF, buf) == -1) {
|
||||
ioctl_error(video_capture, "VIDIOC_QBUF", "Could not enqueue buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void create_image_buffers(VideoCapture *video_capture) {
|
||||
static bool create_image_buffers(VideoCapture *video_capture) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < video_capture->buf_count; i++) {
|
||||
create_image_buffer(video_capture, &video_capture->buf[i], i);
|
||||
if (!create_image_buffer(video_capture, &video_capture->buf[i], i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void close_stream(const VideoCapture *video_capture) {
|
||||
@@ -290,11 +320,15 @@ static unsigned int read_video(VideoCapture *video_capture) {
|
||||
|
||||
if (ioctl(video_capture->fd, VIDIOC_DQBUF,
|
||||
&video_capture->buf[video_capture->buf_index]) != -1) {
|
||||
ioctl(video_capture->fd, VIDIOC_QBUF,
|
||||
&video_capture->buf[video_capture->buf_index]);
|
||||
video_capture->buf_index =
|
||||
(video_capture->buf_index + 1) % video_capture->buf_count;
|
||||
return true;
|
||||
if (ioctl(video_capture->fd, VIDIOC_QBUF,
|
||||
&video_capture->buf[video_capture->buf_index]) == -1) {
|
||||
video_capture->error = true;
|
||||
return false;
|
||||
} else {
|
||||
video_capture->buf_index =
|
||||
(video_capture->buf_index + 1) % video_capture->buf_count;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ioctl(video_capture->fd, VIDIOC_QUERYCAP, &cap) == -1) {
|
||||
@@ -305,38 +339,56 @@ static unsigned int read_video(VideoCapture *video_capture) {
|
||||
}
|
||||
|
||||
void video_init(VideoCapture *video_capture, const char *name,
|
||||
unsigned int preferred_height, unsigned int buffer_count) {
|
||||
open_device(video_capture, name);
|
||||
unsigned int preferred_height, unsigned int buffer_count,
|
||||
const char *video_fourcc, bool log_error) {
|
||||
open_device(video_capture, name, log_error);
|
||||
|
||||
if (video_capture->error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!check_caps(video_capture)) {
|
||||
video_capture->error = true;
|
||||
video_free(video_capture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!get_available_sizes(video_capture, preferred_height)) {
|
||||
if (!get_available_sizes(video_capture, preferred_height,
|
||||
string_to_fourcc(video_fourcc))) {
|
||||
video_capture->error = true;
|
||||
video_free(video_capture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!set_format(video_capture)) {
|
||||
if (!set_format(video_capture, string_to_fourcc(video_fourcc))) {
|
||||
video_capture->error = true;
|
||||
video_free(video_capture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request_buffers(video_capture, buffer_count)) {
|
||||
video_capture->error = true;
|
||||
video_free(video_capture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!export_buffers(video_capture)) {
|
||||
video_capture->error = true;
|
||||
video_free(video_capture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!open_stream(video_capture)) {
|
||||
video_capture->error = true;
|
||||
video_free(video_capture);
|
||||
return;
|
||||
}
|
||||
|
||||
create_image_buffers(video_capture);
|
||||
if (!create_image_buffers(video_capture)) {
|
||||
video_capture->error = true;
|
||||
video_free(video_capture);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void *video_background_read(void *args) {
|
||||
@@ -376,20 +428,24 @@ void *video_background_read(void *args) {
|
||||
video_capture->name);
|
||||
video_capture->disconnected = true;
|
||||
context->input_formats[input_index] = 0;
|
||||
context->input_resolutions[input_index][0] = 0;
|
||||
context->input_resolutions[input_index][1] = 0;
|
||||
}
|
||||
free(process_args);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void video_free(const VideoCapture *video_capture) {
|
||||
unsigned int i;
|
||||
|
||||
close_stream(video_capture);
|
||||
|
||||
for (i = 0; i < video_capture->buf_count; i++) {
|
||||
close(video_capture->exp_fd[i]);
|
||||
if (video_capture->exp_fd[i] != -1) {
|
||||
close(video_capture->exp_fd[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (video_capture->fd != -1) {
|
||||
close_stream(video_capture);
|
||||
close(video_capture->fd);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@
|
||||
#ifdef VIDEO_IN
|
||||
|
||||
void video_init(VideoCapture *video_capture, const char *name,
|
||||
unsigned int preferred_height, unsigned int buffer_count);
|
||||
unsigned int preferred_height, unsigned int buffer_count,
|
||||
const char *video_fourcc, bool log_error);
|
||||
|
||||
void *video_background_read(void *args);
|
||||
|
||||
|
||||
+4
-3
@@ -131,9 +131,10 @@ void window_use(Window *window, Context *context) {
|
||||
glfwGetFramebufferSize(window, &width, &height);
|
||||
context->resolution[0] = width;
|
||||
context->resolution[1] = height;
|
||||
context->tex_resolution[0] =
|
||||
(int)(context->tex_resolution[1] * context->resolution[0] /
|
||||
context->resolution[1]);
|
||||
if (height > 0) {
|
||||
context->tex_resolution[0] =
|
||||
(int)(context->tex_resolution[1] * width / height);
|
||||
}
|
||||
}
|
||||
|
||||
void window_close(Window *window) {
|
||||
|
||||
Reference in New Issue
Block a user