13 Commits

Author SHA1 Message Date
klemek 1b3d6464de forge (steel) v1.1.0
Clang Lint CI / lint-no-video (push) Successful in 1m9s
Clang Lint CI / lint-video (push) Successful in 1m19s
Clang Build CI / run-video (push) Successful in 1m11s
Clang Build CI / run-no-video (push) Successful in 1m14s
Clang Build CI / build-release (push) Successful in 1m36s
2026-05-15 00:20:29 +02:00
klemek bdaf53e259 docs: add cloc 2026-05-15 00:18:06 +02:00
klemek 37a492d00d refactor: specify number of video input buffers
Clang Lint CI / lint-no-video (push) Successful in 1m5s
Clang Build CI / run-no-video (push) Successful in 1m5s
Clang Build CI / run-video (push) Successful in 1m5s
Clang Build CI / build-release (push) Successful in 2m24s
Clang Lint CI / lint-video (push) Successful in 2m22s
2026-05-15 00:09:51 +02:00
klemek 06d175c4fd docs: update DEVELOPMENT.md 2026-05-14 22:52:48 +02:00
klemek 7b9f5ca032 feat: midi/keyboard cross working codes
Clang Lint CI / lint-no-video (push) Successful in 1m17s
Clang Build CI / run-video (push) Successful in 1m17s
Clang Build CI / run-no-video (push) Successful in 1m18s
Clang Build CI / build-release (push) Successful in 1m54s
Clang Lint CI / lint-video (push) Successful in 1m22s
2026-05-14 22:52:08 +02:00
klemek 6abf050bcc refactor: not overlaping keyboard and midi
Clang Lint CI / lint-no-video (push) Successful in 1m0s
Clang Build CI / run-no-video (push) Successful in 1m0s
Clang Build CI / run-video (push) Successful in 1m1s
Clang Build CI / build-release (push) Successful in 1m56s
Clang Lint CI / lint-video (push) Successful in 1m59s
2026-05-14 22:27:39 +02:00
klemek 28b87d316a feat: auto-reconnect midi
Clang Lint CI / lint-no-video (push) Successful in 59s
Clang Build CI / run-no-video (push) Successful in 59s
Clang Build CI / run-video (push) Successful in 59s
Clang Build CI / build-release (push) Successful in 1m31s
Clang Lint CI / lint-video (push) Successful in 1m7s
2026-05-14 15:28:38 +02:00
klemek adc520bc8b refactor: no state_config pointer
Clang Build CI / run-no-video (push) Successful in 1m2s
Clang Lint CI / lint-no-video (push) Successful in 1m1s
Clang Build CI / run-video (push) Successful in 1m2s
Clang Build CI / build-release (push) Successful in 1m50s
Clang Lint CI / lint-video (push) Successful in 1m47s
2026-05-14 14:24:41 +02:00
klemek d4565fa507 feat: video auto reconnect
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
2026-05-14 14:03:41 +02:00
klemek 25b7134a43 fix: add debug capabilities for gl and egl
Clang Lint CI / lint-no-video (push) Failing after 44s
Clang Build CI / run-no-video (push) Successful in 1m7s
Clang Build CI / run-video (push) Successful in 1m7s
Clang Build CI / build-release (push) Successful in 1m26s
Clang Lint CI / lint-video (push) Successful in 1m46s
2026-05-14 00:11:49 +02:00
klemek 7da4f27e13 docs: github.com -> git.klemek.fr 2026-05-13 16:45:08 +02:00
klemek 26c90ec928 forge (steel) v1.0.2 2026-05-13 16:41:35 +02:00
klemek e982c9214d fix: PKGBUILD 2026-05-13 16:41:35 +02:00
36 changed files with 1318 additions and 1037 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ on:
- 'Makefile.am'
env:
GCC_ARGS: src/*.c src/*.h -lglfw -lGL -lm -lasound -Iinclude hashmap.c/hashmap.c log.c/src/log.c -DGLFW_INCLUDE_NONE -DGLFW_NATIVE_INCLUDE_NONE
GCC_ARGS: src/*.c src/*.h -lglfw -lGL -lm -lasound -Wno-format-truncation -Iinclude hashmap.c/hashmap.c log.c/src/log.c -DGLFW_INCLUDE_NONE -DGLFW_NATIVE_INCLUDE_NONE
GCC_ARGS_VIDEO: -DGLFW_EXPOSE_NATIVE_EGL -DVIDEO_IN
PACKAGES: "libglfw3-dev libgl-dev libasound2-dev"
PACKAGES_VIDEO: "libv4l-dev"
+1
View File
@@ -28,3 +28,4 @@ conftest.*
error.glsl
draft/
*.gch
*.out
+5 -6
View File
@@ -20,7 +20,7 @@ release-arch make arch-linux release package
# make full build
make -f Makefile.dev release-1.0.0
# push release
git push origin master --tags
git push origin main --tags
# create release from tag on github
# attach .tar.gz to the github release
make -f Makefile.dev release-arch
@@ -126,15 +126,14 @@ make -f Makefile.dev release-arch
- [x] Monitor improvements
- [ ] Ignore some values in auto random
- [x] build without video in
- [ ] Auto discover video devices
- [ ] Update README monitor/keymap
- [ ] Auto reconnect midi input
- [ ] Auto reconnect video device
- [ ] Key codes as inputs
- [x] Auto reconnect midi input
- [x] Auto reconnect video device
- [x] Key codes as inputs
- [ ] Mouse position and scroll as inputs
- [ ] Joystick as input
- [ ] Record show as text files
- [ ] Play from record text file
- [ ] Fixes
- [ ] Try to write NanoKontrol config
- [x] Investigate video device fps loss (bad unregister ?)
- [x] Investigate video device fps loss (bad unregister ?)
+2 -2
View File
@@ -1,9 +1,9 @@
AUTOMAKE_OPTIONS = foreign subdir-objects -Wall
bin_PROGRAMS = forge
forge_SOURCES = src/args.c src/arr.c src/config_file.c src/file.c src/forge.c src/main.c src/midi.c src/project.c src/rand.c src/shaders.c src/shared.c src/state.c src/string.c src/tempo.c src/timer.c src/video.c src/window.c $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/hashmap.c/hashmap.c $(top_srcdir)/log.c/src/log.c
forge_SOURCES = src/args.c src/arr.c src/config_file.c src/file.c src/forge.c src/main.c src/midi.c src/project.c src/rand.c src/shaders.c src/state.c src/string.c src/tempo.c src/timer.c src/video.c src/window.c $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/hashmap.c/hashmap.c $(top_srcdir)/log.c/src/log.c
forge_CFLAGS = -Ofast -march=native -flto -funroll-loops -fprefetch-loop-arrays -fno-exceptions -fopenmp -I$(top_srcdir)/include -DGLFW_INCLUDE_NONE -DGLFW_EXPOSE_NATIVE_EGL -DGLFW_NATIVE_INCLUDE_NONE -DLOG_USE_COLOR -DVIDEO_IN -DDATADIR=\"$(datadir)/$(PACKAGE)\"
forge_LDADD = -lm -lGL -lglfw -lasound
include_HEADERS = src/args.h src/arr.h src/config.h src/config_file.h src/constants.h src/file.h src/forge.h src/main.h src/midi.h src/project.h src/rand.h src/shaders.h src/shared.h src/state.h src/string.h src/tempo.h src/timer.h src/types.h src/video.h src/window.h $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/include/linmath.h $(top_srcdir)/include/hashmap.h $(top_srcdir)/include/log.h
include_HEADERS = src/args.h src/arr.h src/config.h src/config_file.h src/constants.h src/file.h src/forge.h src/main.h src/midi.h src/project.h src/rand.h src/shaders.h src/state.h src/string.h src/tempo.h src/timer.h src/types.h src/video.h src/window.h $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/include/linmath.h $(top_srcdir)/include/hashmap.h $(top_srcdir)/include/log.h
EXTRA_DIST = default/forge_project.cfg default/frag1.glsl default/frag10.glsl default/frag2.glsl default/frag3.glsl default/frag4.glsl default/frag5.glsl default/frag6.glsl default/frag7.glsl default/frag8.glsl default/frag9.glsl default/inc_cp437.glsl default/inc_debug.glsl default/inc_functions.glsl default/inc_fx.glsl default/inc_magic.glsl default/inc_rand.glsl default/inc_res.glsl default/inc_sentences.glsl default/inc_src.glsl default/inc_template.glsl default/inc_time.glsl default/inc_yuyv.glsl
confdir = $(prefix)/share/$(PACKAGE)
+6 -10
View File
@@ -1,6 +1,6 @@
TARGET ?= forge
INSTALL_DIR ?= $(HOME)/.local/bin
TEST_ARGS ?= --video-in=/dev/video0 --video-in=/dev/video1 --video-in=/dev/video2 --video-in=/dev/video3 --video-in=/dev/video4 --video-in=/dev/video5 --video-in=/dev/video6 --video-in=/dev/video7 --video-in=/dev/video8 --video-in=/dev/video9
RUN_ARGS ?= --help
SHELL := /bin/bash
.PHONY: build
@@ -49,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
@@ -64,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:
@@ -82,7 +78,7 @@ test-release: clean clean-release
.PHONY: release-%
release-%: clean clean-release
git pull origin master
git pull origin main
sed -i -E "s/[0-9]+\\.[0-9]+\\.[0-9]+/$*/g" configure.ac
aclocal
autoconf
@@ -95,8 +91,8 @@ release-%: clean clean-release
sha256sum build/forge-steel-$*.tar.gz | cut -d' ' -f1 | xargs -I{} sed -i -E "s/sha256sums=\\('.*'\\)/sha256sums=\\('{}'\\)/g" PKGBUILD
git add configure.ac PKGBUILD
git commit -am "forge (steel) v$*"
git tag v$* -m "forge (steel) v$*"
@echo "Push release: git push origin master --tags"
git tag v$* -m "FORGE steel-$*"
@echo "Push release: git push origin main --tags"
@echo "Rollback release: git reset HEAD~1 --hard && git tag -d v$*"
+4 -4
View File
@@ -1,12 +1,12 @@
pkgname=forge-steel
pkgver=1.0.2
pkgver=1.1.0
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://github.com/klemek/forge-steel"
source=("${pkgname}-steel-${pkgver}.tar.gz::https://git.klemek.fr/klemek/forge-steel/releases/archive/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
sha256sums=('53d47f1099556597bf4d9e09d9874e465aad77b3aaa51b3259a1fb955e774f83')
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=('794fb0f170cff19872acfba8a556e2a08c55a78e497ac0b330eca6db65343529')
srcdir=build
backup=("usr/share/${pkgname}")
+35 -20
View File
@@ -1,4 +1,4 @@
[![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/lint.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=lint.yml) [![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/build.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=build.yml)
[![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/lint.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=lint.yml) [![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/build.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=build.yml) ![LOC](https://img.shields.io/badge/cloc-3.4k-blue?style=flat-square)
<!-- omit from toc -->
# F.O.R.G.E. (Steel)
@@ -119,7 +119,7 @@ Here's a quick rundown of the process:
### From release
See [Releases](https://github.com/klemek/forge-steel/releases)
See [Releases](https://git.klemek.fr/klemek/forge-steel/releases)
```shell
tar xvzf forge-steel-x.y.z.tar.gz
@@ -132,7 +132,7 @@ make install
### From repository (PKGBUILD)
```shell
git clone --recursive https://github.com/klemek/forge-steel
git clone --recursive https://git.klemek.fr/klemek/forge-steel
cd forge
makepkg -si
```
@@ -141,7 +141,7 @@ makepkg -si
### From repository (dev version)
```shell
git clone --recursive https://github.com/klemek/forge-steel
git clone --recursive https://git.klemek.fr/klemek/forge-steel
cd forge
aclocal
autoconf
@@ -174,9 +174,9 @@ These are configurable in the [`forge_project.cfg`](#forge_projectcfg).
### CLI arguments
```txt
forge steel-dev
forge steel-VERSION
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] [-vr / -nvr] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-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] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-mr / -nmr] [-tm] [-tf]
Fusion Of Real-time Generative Effects.
@@ -196,6 +196,7 @@ 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)
-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
@@ -204,6 +205,8 @@ options:
-nls, --no-load-state do not load saved state
-ss, --save-state save state (default)
-nss, --no-save-state do not save state
-mr, --midi-reconnect auto-reconnect midi (default)
-nmr, --no-midi-reconnect do not auto-reconnect midi
-tm, --trace-midi print midi code and values
-tf, --trace-fps print fps status of subsystems
```
@@ -280,16 +283,28 @@ We will not dig down all the variables here but feel free to read either:
* [sample/forge_project.cfg](./sample/forge_project.cfg) (beginner oriented)
* [default/forge_project.cfg](./default/forge_project.cfg) (more complete)
#### Midi groups
#### Input codes
FORGE allows you to send the midi data in the form of groups.
Each input code can be either a midi event or a key code.
* 0 - 999 -> midi event
* Keyboard modifiers are encoded like this:
* 1000 -> keyboard event
* 10000 -> shift
* 100000 -> control
* 1000000 -> alt
* (This means 111082 is control+alt+R)
#### Input groups
FORGE allows you to send the midi/keyboard data in the form of groups.
Each group has the following properties:
* A number of layers with `MIDI_X_ACTIVE_COUNT` (default: `1`)
* Each midi code controlling how to change the active layer with `MIDI_X_ACTIVE_Y`
* A number of codes with `MIDI_X_COUNT`
* Each midi code controlling the active layer with `MIDI_X_Y` (as a `vec3`: `_X` / `_Y` / `_Z`)
* A number of layers with `GROUP_X_ACTIVE_COUNT` (default: `1`)
* Each midi/keyboard code controlling how to change the active layer with `GROUP_X_ACTIVE_Y`
* A number of codes with `GROUP_X_COUNT`
* Each midi/keyboard code controlling the active layer with `GROUP_X_Y` (as a `vec3`: `_X` / `_Y` / `_Z`)
#### States
@@ -297,9 +312,9 @@ FORGE allows to define a "state" to a fragment shader.
This combines several parameters:
* `SELECT_PAGE_COUNT` (default: `1`) + `SELECT_PAGE_X`: define midi codes for pages of item.
* `SELECT_ITEM_COUNT` + `SELECT_ITEM_X`: define midi codes for items per page.
* `SELECT_FRAG_X`: define midi codes to "select" a fragment shader.
* `SELECT_PAGE_COUNT` (default: `1`) + `SELECT_PAGE_X`: define midi/keyboard codes for pages of item.
* `SELECT_ITEM_COUNT` + `SELECT_ITEM_X`: define midi/keyboard codes for items per page.
* `SELECT_FRAG_X`: define midi/keyboard codes to "select" a fragment shader.
The selected fragment shader will have its state updated with either:
@@ -340,8 +355,8 @@ uniform float iTime; // the current time
uniform sampler2D iTex0; // texture 1 (0-based)
uniform sampler2D iTex9; // texture 10
uniform int iSeed1;
uniform vec3 iMidi2_3[7]; // midi group 2, layer 3, size 7
uniform vec3 iMidi3_1[2];
uniform vec3 iGroup2_3[7]; // midi group 2, layer 3, size 7
uniform vec3 iGroup3_1[2];
```
#### Working with `#include`
@@ -457,11 +472,11 @@ FORGE (steel) describe the linux version.
Verify you have the correct [requirements](#requirements) installed.
Then if you continue to have problems, you can [open an issue](https://github.com/klemek/forge-steel/issues).
Then if you continue to have problems, you can [open an issue](https://git.klemek.fr/klemek/forge-steel/issues).
### I didn't understand everything about your tutorial
Please [open an issue](https://github.com/klemek/forge-steel/issues) describing what your having trouble with,
Please [open an issue](https://git.klemek.fr/klemek/forge-steel/issues) describing what your having trouble with,
I'll try to answer and update the README.
### My nanoKontrol2 is acting strange
@@ -472,7 +487,7 @@ Use the [KORG KONTROL Editor](https://www.korg.com/us/support/download/software/
### How do I report a bug?
You're free to open a new issue in the [Issues page](https://github.com/klemek/forge-steel/issues).
You're free to open a new issue in the [Issues page](https://git.klemek.fr/klemek/forge-steel/issues).
Don't forget to add all information available to your bug (version, operating system, etc.).
+3 -1
View File
@@ -2,4 +2,6 @@
-DGLFW_INCLUDE_NONE
-DGLFW_EXPOSE_NATIVE_EGL
-DGLFW_NATIVE_INCLUDE_NONE
-DVIDEO_IN
-DVIDEO_IN
-DEGL_DEBUG
-DGL_DEBUG
+2 -1
View File
@@ -1,4 +1,4 @@
AC_INIT([forge], [steel-1.0.2], [klemek.dev@proton.me])
AC_INIT([forge], [steel-1.1.0], [klemek.dev@proton.me])
AM_INIT_AUTOMAKE
AC_PROG_CC
@@ -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])
+104 -106
View File
@@ -6,7 +6,7 @@
# (shaders, video devices, textures, midi inputs, etc.)
# Every number based constant will be "one-based" (1,2,3,etc.)
# To read more, go to
# https://github.com/klemek/forge-steel
# https://git.klemek.fr/klemek/forge-steel
# ================
@@ -24,7 +24,7 @@
UNIFORM_TIME=iTime
# Current tempo
UNIFORM_TEMPO=iTempo
# Current total beats
# Current total beats
UNIFORM_BEATS=iBeats
# --- uniform int ---
@@ -51,8 +51,6 @@ UNIFORM_STATE_PREFIX=iState
UNIFORM_ACTIVE_PREFIX=iActive
# Input X format raw integer value
UNIFORM_IN_FORMAT_PREFIX=iInputFormat
# Input X should use swap texture or not (0/1)
UNIFORM_IN_SWAP_PREFIX=iInputSwap
# --- uniform vec2 ---
@@ -66,8 +64,8 @@ UNIFORM_IN_RESOLUTION_PREFIX=iInputResolution
# --- uniform vec3 ---
# Midi group X layer Y (beware of group size)
# Injected as "iMidiX_Y[Z]"
UNIFORM_MIDI_PREFIX=iMidi
# Injected as "iGroupX_Y[Z]"
UNIFORM_GROUP_PREFIX=iGroup
# --- uniform sampler2D ---
@@ -106,9 +104,6 @@ IN_COUNT=2
# To which texture will be bound video device X
IN_1_OUT=1
IN_2_OUT=2
# To which texture will be bound swap buffers (for double-buffering)
IN_1_SWAP_OUT=3
IN_2_SWAP_OUT=4
# === FRAGMENT SHADERS
# Fragment shaders will be read from the CLI directory as "fragX.glsl"
@@ -134,10 +129,17 @@ FRAG_OUTPUT=9
FRAG_MONITOR=10
# ========
# MIDI I/O
# ========
# ==========
# I/O Inputs
# ==========
# Each code either maps to a midi event or a key code
# 0 - 999 -> midi event
# Keyboard modifiers are encoded like this:
# 1000 -> keyboard event
# 10000 -> shift
# 100000 -> control
# 1000000 -> alt
# This means 111082 is control+alt+R
# The recognized ALSA name of the midi device
MIDI_HW=hw:CARD=nanoKONTROL2
@@ -193,8 +195,8 @@ SELECT_ITEM_3=42
SELECT_ITEM_4=41
SELECT_ITEM_5=45
# === MIDI INPUT STATES
# Midi inputs will control FORGE's state as follows
# === GROUP INPUT STATES
# Inputs will control FORGE's state as follows
# X groups of Y layers sized Z
# You can manipulate only 1 layer at a time
# Every layer of every groups will be send as uniforms
@@ -202,72 +204,72 @@ SELECT_ITEM_5=45
# with the same codes for nice display
# Total number of groups
MIDI_COUNT=3
GROUP_COUNT=3
# Total number of layers of group 1
MIDI_1_ACTIVE_COUNT=2
GROUP_1_ACTIVE_COUNT=2
# Midi codes to change layer of group 1
MIDI_1_ACTIVE_1=32
MIDI_1_ACTIVE_2=64
GROUP_1_ACTIVE_1=32
GROUP_1_ACTIVE_2=64
# Size of group 1
MIDI_1_COUNT=6
GROUP_1_COUNT=6
# Every code of active layer manipulation of group 1
MIDI_1_1_X=33
MIDI_1_1_Y=49
MIDI_1_1_Z=65
MIDI_1_2_X=1
MIDI_1_2_Y=17
MIDI_1_2_Z=
MIDI_1_3_X=34
MIDI_1_3_Y=50
MIDI_1_3_Z=66
MIDI_1_4_X=2
MIDI_1_4_Y=18
MIDI_1_4_Z=
MIDI_1_5_X=35
MIDI_1_5_Y=51
MIDI_1_5_Z=67
MIDI_1_6_X=3
MIDI_1_6_Y=19
MIDI_1_6_Z=
GROUP_1_1_X=33
GROUP_1_1_Y=49
GROUP_1_1_Z=65
GROUP_1_2_X=1
GROUP_1_2_Y=17
GROUP_1_2_Z=
GROUP_1_3_X=34
GROUP_1_3_Y=50
GROUP_1_3_Z=66
GROUP_1_4_X=2
GROUP_1_4_Y=18
GROUP_1_4_Z=
GROUP_1_5_X=35
GROUP_1_5_Y=51
GROUP_1_5_Z=67
GROUP_1_6_X=3
GROUP_1_6_Y=19
GROUP_1_6_Z=
# Same for group 2
MIDI_2_ACTIVE_COUNT=3
MIDI_2_ACTIVE_1=36
MIDI_2_ACTIVE_2=68
MIDI_2_ACTIVE_3=52
MIDI_2_COUNT=7
MIDI_2_1_X=37
MIDI_2_1_Y=53
MIDI_2_1_Z=69
MIDI_2_2_X=5
MIDI_2_2_Y=21
MIDI_2_2_Z=
MIDI_2_3_X=38
MIDI_2_3_Y=54
MIDI_2_3_Z=70
MIDI_2_4_X=6
MIDI_2_4_Y=22
MIDI_2_4_Z=
MIDI_2_5_X=39
MIDI_2_5_Y=55
MIDI_2_5_Z=71
MIDI_2_6_X=7
MIDI_2_6_Y=23
MIDI_2_6_Z=
MIDI_2_7_X=4
MIDI_2_7_Y=20
MIDI_2_7_Z=59
GROUP_2_ACTIVE_COUNT=3
GROUP_2_ACTIVE_1=36
GROUP_2_ACTIVE_2=68
GROUP_2_ACTIVE_3=52
GROUP_2_COUNT=7
GROUP_2_1_X=37
GROUP_2_1_Y=53
GROUP_2_1_Z=69
GROUP_2_2_X=5
GROUP_2_2_Y=21
GROUP_2_2_Z=
GROUP_2_3_X=38
GROUP_2_3_Y=54
GROUP_2_3_Z=70
GROUP_2_4_X=6
GROUP_2_4_Y=22
GROUP_2_4_Z=
GROUP_2_5_X=39
GROUP_2_5_Y=55
GROUP_2_5_Z=71
GROUP_2_6_X=7
GROUP_2_6_Y=23
GROUP_2_6_Z=
GROUP_2_7_X=4
GROUP_2_7_Y=20
GROUP_2_7_Z=59
# Same for group 3
MIDI_3_COUNT=2
MIDI_3_1_X=48
MIDI_3_1_Y=58
MIDI_3_1_Z=
MIDI_3_2_X=0
MIDI_3_2_Y=16
MIDI_3_2_Z=
GROUP_3_COUNT=2
GROUP_3_1_X=48
GROUP_3_1_Y=58
GROUP_3_1_Z=
GROUP_3_2_X=0
GROUP_3_2_Y=16
GROUP_3_2_Z=
# =====
# OTHER
@@ -279,55 +281,51 @@ SAVE_FILE_PREFIX=forge_default_save
# === HOTKEYS
# You can change the default keycodes used on runtime
# Modifiers are encoded like this:
# 1000 -> shift
# 10000 -> control
# 100000 -> alt
# This means 110082 is control+alt+R
# R on (qwerty)
HOTKEY_RANDOMIZE=82
KEY_RANDOMIZE=1082
# SHIFT+R (qwerty)
HOTKEY_RESET=1082
KEY_RESET=11082
# D (qwerty)
HOTKEY_DEMO=68
KEY_DEMO=1068
# A (qwerty)
HOTKEY_AUTORAND=65
KEY_AUTORAND=1065
# Left arrow
HOTKEY_AUTORAND_DOWN=263
KEY_AUTORAND_DOWN=1263
# Right arrow
HOTKEY_AUTORAND_UP=262
KEY_AUTORAND_UP=1262
# Down arrow
HOTKEY_TEMPO_DOWN=264
KEY_TEMPO_DOWN=1264
# Up arrow
HOTKEY_TEMPO_UP=265
KEY_TEMPO_UP=1265
# Number of load states keys
HOTKEY_LOAD_COUNT=10
KEY_LOAD_COUNT=10
# 1 to 9 then 0 keys
HOTKEY_LOAD_1=49
HOTKEY_LOAD_2=50
HOTKEY_LOAD_3=51
HOTKEY_LOAD_4=52
HOTKEY_LOAD_5=53
HOTKEY_LOAD_6=54
HOTKEY_LOAD_7=55
HOTKEY_LOAD_8=56
HOTKEY_LOAD_9=57
HOTKEY_LOAD_10=48
KEY_LOAD_1=1049
KEY_LOAD_2=1050
KEY_LOAD_3=1051
KEY_LOAD_4=1052
KEY_LOAD_5=1053
KEY_LOAD_6=1054
KEY_LOAD_7=1055
KEY_LOAD_8=1056
KEY_LOAD_9=1057
KEY_LOAD_10=1048
# Number of save states keys
HOTKEY_SAVE_COUNT=10
KEY_SAVE_COUNT=10
# 1 to 9 then 0 keys with shift
HOTKEY_SAVE_1=1049
HOTKEY_SAVE_2=1050
HOTKEY_SAVE_3=1051
HOTKEY_SAVE_4=1052
HOTKEY_SAVE_5=1053
HOTKEY_SAVE_6=1054
HOTKEY_SAVE_7=1055
HOTKEY_SAVE_8=1056
HOTKEY_SAVE_9=1057
HOTKEY_SAVE_10=1048
KEY_SAVE_1=11049
KEY_SAVE_2=11050
KEY_SAVE_3=11051
KEY_SAVE_4=11052
KEY_SAVE_5=11053
KEY_SAVE_6=11054
KEY_SAVE_7=11055
KEY_SAVE_8=11056
KEY_SAVE_9=11057
KEY_SAVE_10=11048
+2 -2
View File
@@ -13,8 +13,8 @@ out vec4 fragColor;
subroutine uniform src_stage_sub src_stage;
uniform int iSeed3;
uniform vec3 iMidi1_1[6];
uniform vec3 iGroup1_1[6];
void main() {
fragColor = src_stage(vUV, iSeed3, iMidi1_1[0], iMidi1_1[1].xy, iMidi1_1[2], iMidi1_1[3].xy, iMidi1_1[4], iMidi1_1[5].xy);
fragColor = src_stage(vUV, iSeed3, iGroup1_1[0], iGroup1_1[1].xy, iGroup1_1[2], iGroup1_1[3].xy, iGroup1_1[4], iGroup1_1[5].xy);
}
+2 -2
View File
@@ -12,8 +12,8 @@ out vec4 fragColor;
subroutine uniform src_stage_sub src_stage;
uniform int iSeed4;
uniform vec3 iMidi1_2[6];
uniform vec3 iGroup1_2[6];
void main() {
fragColor = src_stage(vUV, iSeed4, iMidi1_2[0], iMidi1_2[1].xy, iMidi1_2[2], iMidi1_2[3].xy, iMidi1_2[4], iMidi1_2[5].xy);
fragColor = src_stage(vUV, iSeed4, iGroup1_2[0], iGroup1_2[1].xy, iGroup1_2[2], iGroup1_2[3].xy, iGroup1_2[4], iGroup1_2[5].xy);
}
+2 -2
View File
@@ -14,8 +14,8 @@ out vec4 fragColor;
uniform sampler2D iTex7;
uniform sampler2D iTex9;
uniform int iSeed5;
uniform vec3 iMidi2_1[7];
uniform vec3 iGroup2_1[7];
void main() {
fragColor = fx_stage(vUV, iTex7, iTex9, iSeed5, iMidi2_1[0], iMidi2_1[1].xy, iMidi2_1[2], iMidi2_1[3].xy, iMidi2_1[4], iMidi2_1[5].xy, iMidi2_1[6]);
fragColor = fx_stage(vUV, iTex7, iTex9, iSeed5, iGroup2_1[0], iGroup2_1[1].xy, iGroup2_1[2], iGroup2_1[3].xy, iGroup2_1[4], iGroup2_1[5].xy, iGroup2_1[6]);
}
+2 -2
View File
@@ -14,8 +14,8 @@ out vec4 fragColor;
uniform sampler2D iTex8;
uniform sampler2D iTex10;
uniform int iSeed6;
uniform vec3 iMidi2_2[7];
uniform vec3 iGroup2_2[7];
void main() {
fragColor = fx_stage(vUV, iTex8, iTex10, iSeed6, iMidi2_2[0], iMidi2_2[1].xy, iMidi2_2[2], iMidi2_2[3].xy, iMidi2_2[4], iMidi2_2[5].xy, iMidi2_2[6]);
fragColor = fx_stage(vUV, iTex8, iTex10, iSeed6, iGroup2_2[0], iGroup2_2[1].xy, iGroup2_2[2], iGroup2_2[3].xy, iGroup2_2[4], iGroup2_2[5].xy, iGroup2_2[6]);
}
+3 -3
View File
@@ -16,11 +16,11 @@ uniform int iDemo;
uniform sampler2D iTex9;
uniform sampler2D iTex10;
uniform int iSeed7;
uniform vec3 iMidi3_1[2];
uniform vec3 iGroup3_1[2];
void main() {
float mix_value = magic(iMidi3_1[1].xy, vec3(1, 0, 0), iSeed7);
bool mix_type = magic_trigger(vec3(iMidi3_1[0].x, 0, 0), iSeed7 + 10);
float mix_value = magic(iGroup3_1[1].xy, vec3(1, 0, 0), iSeed7);
bool mix_type = magic_trigger(vec3(iGroup3_1[0].x, 0, 0), iSeed7 + 10);
vec4 color_a = texture(iTex9, vUV);
vec4 color_b = texture(iTex10, vUV);
+4 -4
View File
@@ -14,16 +14,16 @@ out vec4 fragColor;
uniform sampler2D iTex11;
uniform sampler2D iTex0;
uniform int iSeed8;
uniform vec3 iMidi2_3[7];
uniform vec3 iMidi3_1[2];
uniform vec3 iGroup2_3[7];
uniform vec3 iGroup3_1[2];
uniform int iDemo;
uniform int iAutoRand;
void main() {
vec4 color = fx_stage(vUV, iTex11, iTex0, iSeed8, iMidi2_3[0], iMidi2_3[1].xy, iMidi2_3[2], iMidi2_3[3].xy, iMidi2_3[4], iMidi2_3[5].xy, iMidi2_3[6]);
vec4 color = fx_stage(vUV, iTex11, iTex0, iSeed8, iGroup2_3[0], iGroup2_3[1].xy, iGroup2_3[2], iGroup2_3[3].xy, iGroup2_3[4], iGroup2_3[5].xy, iGroup2_3[6]);
if (iDemo < 1 && iAutoRand < 1) {
color = mix(color, vec4(0), iMidi3_1[0].y);
color = mix(color, vec4(0), iGroup3_1[0].y);
}
fragColor = color;
+108 -50
View File
@@ -35,17 +35,16 @@ uniform int iState8;
uniform int iActive1;
uniform int iActive2;
uniform vec3 iMidi2_1[7];
uniform vec3 iMidi2_2[7];
uniform vec3 iMidi2_3[7];
uniform vec3 iMidi3_1[2];
uniform vec3 iGroup2_1[7];
uniform vec3 iGroup2_2[7];
uniform vec3 iGroup2_3[7];
uniform vec3 iGroup3_1[2];
vec4 debug(vec2 vUV)
{
// start
vec2 uv0 = vUV.st;
vec2 uv0 = vUV.st;
float ratio = iResolution.x / iResolution.y;
vec2 uv1 = (uv0 - .5) * vec2(ratio, 1);
@@ -92,30 +91,90 @@ vec4 debug(vec2 vUV)
if (random_mfx) {
selected_mfx = int(randTime(iSeed8 + 100) * 14);
}
float fxa_value = magic(iMidi2_1[6].xy, vec3(1, 0, 0), iSeed5);
bool fxa_invert = magic_trigger(vec3(iMidi2_1[6].z, 0, 0), iSeed5);
float fxb_value = magic(iMidi2_2[6].xy, vec3(1, 0, 0), iSeed6);
bool fxb_invert = magic_trigger(vec3(iMidi2_2[6].z, 0, 0), iSeed6);
float mfx_value = magic(iMidi2_3[6].xy, vec3(1, 0, 0), iSeed8);
bool mfx_invert = magic_trigger(vec3(iMidi2_3[6].z, 0, 0), iSeed8);
float mix_value = magic(iMidi3_1[1].xy, vec3(1, 0, 0), iSeed7);
float fxa_value = magic(iGroup2_1[6].xy, vec3(1, 0, 0), iSeed5);
bool fxa_invert = magic_trigger(vec3(iGroup2_1[6].z, 0, 0), iSeed5);
float fxb_value = magic(iGroup2_2[6].xy, vec3(1, 0, 0), iSeed6);
bool fxb_invert = magic_trigger(vec3(iGroup2_2[6].z, 0, 0), iSeed6);
float mfx_value = magic(iGroup2_3[6].xy, vec3(1, 0, 0), iSeed8);
bool mfx_invert = magic_trigger(vec3(iGroup2_3[6].z, 0, 0), iSeed8);
float mix_value = magic(iGroup3_1[1].xy, vec3(1, 0, 0), iSeed7);
mix_value = mix(mix_value, mix_value * 0.9 + 0.05, iDemo);
bool mix_type = magic_trigger(vec3(iMidi3_1[0].x, 0, 0), iSeed7 + 10);
bool mix_type = magic_trigger(vec3(iGroup3_1[0].x, 0, 0), iSeed7 + 10);
// logic
const int texts[10][5] = {
{0x46, 0x50, 0x53, 0x00, 0x00}, // FPS
{0x54, 0x45, 0x4D, 0x50, 0x4F}, // TEMPO
{0x54, 0x49, 0x4D, 0x45, 0x00}, // TIME
{0x44, 0x45, 0x4D, 0x4F, 0x00}, // DEMO
{0x4C, 0x49, 0x56, 0x45, 0x00}, // LIVE
{0x52, 0x41, 0x4E, 0x44, 0x00}, // RAND
{0x53, 0x52, 0x43, 0x00, 0x00}, // SRC
{0x46, 0x58, 0x00, 0x00, 0x00}, // FX
{0x49, 0x4E, 0x00, 0x00, 0x00}, // IN
{0x4D, 0x46, 0x58, 0x00, 0x00}, // MFX
};
{
0x46,
0x50,
0x53,
0x00,
0x00
}, // FPS
{
0x54,
0x45,
0x4D,
0x50,
0x4F
}, // TEMPO
{
0x54,
0x49,
0x4D,
0x45,
0x00
}, // TIME
{
0x44,
0x45,
0x4D,
0x4F,
0x00
}, // DEMO
{
0x4C,
0x49,
0x56,
0x45,
0x00
}, // LIVE
{
0x52,
0x41,
0x4E,
0x44,
0x00
}, // RAND
{
0x53,
0x52,
0x43,
0x00,
0x00
}, // SRC
{
0x46,
0x58,
0x00,
0x00,
0x00
}, // FX
{
0x49,
0x4E,
0x00,
0x00,
0x00
}, // IN
{
0x4D,
0x46,
0x58,
0x00,
0x00
}, // MFX
};
vec2 uv2 = uv1;
@@ -128,21 +187,20 @@ vec4 debug(vec2 vUV)
// base frame
float f =
h_rect(uv2, vec2(-5, -2), vec2(1), 0.1) +
h_rect(uv2, vec2(-2, -2), vec2(1), 0.1) +
rect(uv2, vec2(-3.5, -2), vec2(0.5, 0.1)) +
h_rect(uv2, vec2(-5, 2), vec2(1), 0.1) +
h_rect(uv2, vec2(-2, 2), vec2(1), 0.1) +
rect(uv2, vec2(-3.5, 2), vec2(0.5, 0.1)) +
h_rect(uv2, vec2(2, 0), vec2(1), 0.1) +
h_rect(uv2, vec2(5, 0), vec2(1), 0.1) +
rect(uv2, vec2(3.5, 0), vec2(0.5, 0.1)) +
rect(uv2, vec2(0.55, -2), vec2(1.5, 0.1)) +
rect(uv2, vec2(2, -1.55), vec2(0.1, 0.55)) +
rect(uv2, vec2(0.55, 2), vec2(1.5, 0.1)) +
rect(uv2, vec2(2, 1.55), vec2(0.1, 0.55)) +
rect(uv2, vec2(6.8, 0), vec2(0.75, 0.1)) +
h_circle(uv2, vec2(7.8, 0), 0.3, 0.1)
;
h_rect(uv2, vec2(-2, -2), vec2(1), 0.1) +
rect(uv2, vec2(-3.5, -2), vec2(0.5, 0.1)) +
h_rect(uv2, vec2(-5, 2), vec2(1), 0.1) +
h_rect(uv2, vec2(-2, 2), vec2(1), 0.1) +
rect(uv2, vec2(-3.5, 2), vec2(0.5, 0.1)) +
h_rect(uv2, vec2(2, 0), vec2(1), 0.1) +
h_rect(uv2, vec2(5, 0), vec2(1), 0.1) +
rect(uv2, vec2(3.5, 0), vec2(0.5, 0.1)) +
rect(uv2, vec2(0.55, -2), vec2(1.5, 0.1)) +
rect(uv2, vec2(2, -1.55), vec2(0.1, 0.55)) +
rect(uv2, vec2(0.55, 2), vec2(1.5, 0.1)) +
rect(uv2, vec2(2, 1.55), vec2(0.1, 0.55)) +
rect(uv2, vec2(6.8, 0), vec2(0.75, 0.1)) +
h_circle(uv2, vec2(7.8, 0), 0.3, 0.1);
if (iDemo < 1 && iInputFormat1 == YUYV_FOURCC) {
f += circle(uv2, vec2(-9, 2), 0.3);
@@ -183,14 +241,14 @@ vec4 debug(vec2 vUV)
// show src/fx
f += write_5(uv3 * 0.75, vec2(-11.6,-1.8), texts[6]);
f += write_5(uv3 * 0.75, vec2(-4.2,-1.8), texts[7]);
f += write_5(uv3 * 0.75, vec2(-11.6, -1.8), texts[6]);
f += write_5(uv3 * 0.75, vec2(-4.2, -1.8), texts[7]);
f += char_at(uv3 * 0.5, vec2(-11.5, 3.5), 0x41);
f += char_at(uv3 * 0.5, vec2(-11.5, -6), 0x42);
if (iDemo < 1 && (iInputFormat1 == YUYV_FOURCC || iInputFormat2 == YUYV_FOURCC)) {
f += write_5(uv3 * 0.75, vec2(-19.7,-1.8), texts[8]);
f += write_5(uv3 * 0.75, vec2(-19.7, -1.8), texts[8]);
}
f += write_5(uv3 * 0.75, vec2(10.9,2), texts[9]);
f += write_5(uv3 * 0.75, vec2(10.9, 2), texts[9]);
// show inputs / feedback
float line_a_a = rect(uv2, vec2(-7.5, 2), vec2(1.5, 0.1));
@@ -236,21 +294,21 @@ vec4 debug(vec2 vUV)
float x = 0;
x = -15;
f += write_5(uv3, vec2(x - 4.5,13), texts[1]);
f += write_int(uv3, vec2(x + 1.5,13), int(iTempo), 3);
f += write_5(uv3, vec2(x - 4.5, 13), texts[1]);
f += write_int(uv3, vec2(x + 1.5, 13), int(iTempo), 3);
v = fract(iBeats);
f += h_rect(uv3, vec2(x, 12), vec2(4, 0.5), 0.2);
f += rect(uv3, vec2(x + 4 * v - 4, 12), vec2(4 * v, 0.4));
x = 15;
if (iAutoRand > 0) {
f += write_5(uv3, vec2(x - 4.5,13), texts[5]);
f += write_int(uv3, vec2(x - 0.5,13), int(iAutoRandCycle), 5);
f += write_5(uv3, vec2(x - 4.5, 13), texts[5]);
f += write_int(uv3, vec2(x - 0.5, 13), int(iAutoRandCycle), 5);
v = fract(iBeats / iAutoRandCycle);
} else {
f += write_int(uv3, vec2(x - 0.5,13), int(iTime), 5);
f += write_int(uv3, vec2(x - 0.5, 13), int(iTime), 5);
v = fract(iTime);
f += write_5(uv3, vec2(x - 4.5,13), texts[2]);
f += write_5(uv3, vec2(x - 4.5, 13), texts[2]);
}
f += h_rect(uv3, vec2(x, 12), vec2(4, 0.5), 0.2);
f += rect(uv3, vec2(x + 4 * v - 4, 12), vec2(4 * v, 0.4));
+40 -6
View File
@@ -1,11 +1,11 @@
/**
* Loader generated by glad 2.0.8 on Sun Sep 21 11:51:45 2025
* Loader generated by glad 2.0.8 on Wed May 13 21:51:29 2026
*
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*
* Generator: C/C++
* Specification: egl
* Extensions: 10
* Extensions: 11
*
* APIs:
* - egl=1.5
@@ -19,10 +19,10 @@
* - ON_DEMAND = False
*
* Commandline:
* --api='egl=1.5' --extensions='EGL_EXT_image_dma_buf_import,EGL_EXT_image_dma_buf_import_modifiers,EGL_KHR_gl_texture_2D_image,EGL_KHR_image,EGL_KHR_image_base,EGL_KHR_platform_android,EGL_KHR_platform_gbm,EGL_KHR_platform_wayland,EGL_KHR_platform_x11,EGL_MESA_drm_image' c --header-only --loader
* --api='egl=1.5' --extensions='EGL_EXT_image_dma_buf_import,EGL_EXT_image_dma_buf_import_modifiers,EGL_KHR_debug,EGL_KHR_gl_texture_2D_image,EGL_KHR_image,EGL_KHR_image_base,EGL_KHR_platform_android,EGL_KHR_platform_gbm,EGL_KHR_platform_wayland,EGL_KHR_platform_x11,EGL_MESA_drm_image' c --header-only --loader
*
* Online:
* http://glad.sh/#api=egl%3D1.5&extensions=EGL_EXT_image_dma_buf_import%2CEGL_EXT_image_dma_buf_import_modifiers%2CEGL_KHR_gl_texture_2D_image%2CEGL_KHR_image%2CEGL_KHR_image_base%2CEGL_KHR_platform_android%2CEGL_KHR_platform_gbm%2CEGL_KHR_platform_wayland%2CEGL_KHR_platform_x11%2CEGL_MESA_drm_image&generator=c&options=HEADER_ONLY%2CLOADER
* http://glad.sh/#api=egl%3D1.5&extensions=EGL_EXT_image_dma_buf_import%2CEGL_EXT_image_dma_buf_import_modifiers%2CEGL_KHR_debug%2CEGL_KHR_gl_texture_2D_image%2CEGL_KHR_image%2CEGL_KHR_image_base%2CEGL_KHR_platform_android%2CEGL_KHR_platform_gbm%2CEGL_KHR_platform_wayland%2CEGL_KHR_platform_x11%2CEGL_MESA_drm_image&generator=c&options=HEADER_ONLY%2CLOADER
*
*/
@@ -201,6 +201,11 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2
#define EGL_CORE_NATIVE_ENGINE 0x305B
#define EGL_DEBUG_CALLBACK_KHR 0x33B8
#define EGL_DEBUG_MSG_CRITICAL_KHR 0x33B9
#define EGL_DEBUG_MSG_ERROR_KHR 0x33BA
#define EGL_DEBUG_MSG_INFO_KHR 0x33BC
#define EGL_DEBUG_MSG_WARN_KHR 0x33BB
#define EGL_DEFAULT_DISPLAY EGL_CAST(EGLNativeDisplayType,0)
#define EGL_DEPTH_SIZE 0x3025
#define EGL_DISPLAY_SCALING 10000
@@ -295,6 +300,13 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
#define EGL_NO_SURFACE EGL_CAST(EGLSurface,0)
#define EGL_NO_SYNC EGL_CAST(EGLSync,0)
#define EGL_NO_TEXTURE 0x305C
#define EGL_OBJECT_CONTEXT_KHR 0x33B2
#define EGL_OBJECT_DISPLAY_KHR 0x33B1
#define EGL_OBJECT_IMAGE_KHR 0x33B4
#define EGL_OBJECT_STREAM_KHR 0x33B6
#define EGL_OBJECT_SURFACE_KHR 0x33B3
#define EGL_OBJECT_SYNC_KHR 0x33B5
#define EGL_OBJECT_THREAD_KHR 0x33B0
#define EGL_OPENGL_API 0x30A2
#define EGL_OPENGL_BIT 0x0008
#define EGL_OPENGL_ES2_BIT 0x0004
@@ -934,6 +946,8 @@ GLAD_API_CALL int GLAD_EGL_VERSION_1_5;
GLAD_API_CALL int GLAD_EGL_EXT_image_dma_buf_import;
#define EGL_EXT_image_dma_buf_import_modifiers 1
GLAD_API_CALL int GLAD_EGL_EXT_image_dma_buf_import_modifiers;
#define EGL_KHR_debug 1
GLAD_API_CALL int GLAD_EGL_KHR_debug;
#define EGL_KHR_gl_texture_2D_image 1
GLAD_API_CALL int GLAD_EGL_KHR_gl_texture_2D_image;
#define EGL_KHR_image 1
@@ -968,6 +982,7 @@ typedef EGLSurface (GLAD_API_PTR *PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC)(EGLDisp
typedef EGLSurface (GLAD_API_PTR *PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config, void * native_window, const EGLAttrib * attrib_list);
typedef EGLSync (GLAD_API_PTR *PFNEGLCREATESYNCPROC)(EGLDisplay dpy, EGLenum type, const EGLAttrib * attrib_list);
typedef EGLSurface (GLAD_API_PTR *PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint * attrib_list);
typedef EGLint (GLAD_API_PTR *PFNEGLDEBUGMESSAGECONTROLKHRPROC)(EGLDEBUGPROCKHR callback, const EGLAttrib * attrib_list);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLDESTROYCONTEXTPROC)(EGLDisplay dpy, EGLContext ctx);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLDESTROYIMAGEPROC)(EGLDisplay dpy, EGLImage image);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLDESTROYIMAGEKHRPROC)(EGLDisplay dpy, EGLImageKHR image);
@@ -985,9 +1000,11 @@ typedef EGLDisplay (GLAD_API_PTR *PFNEGLGETPLATFORMDISPLAYPROC)(EGLenum platform
typedef __eglMustCastToProperFunctionPointerType (GLAD_API_PTR *PFNEGLGETPROCADDRESSPROC)(const char * procname);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLGETSYNCATTRIBPROC)(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib * value);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLINITIALIZEPROC)(EGLDisplay dpy, EGLint * major, EGLint * minor);
typedef EGLint (GLAD_API_PTR *PFNEGLLABELOBJECTKHRPROC)(EGLDisplay display, EGLenum objectType, EGLObjectKHR object, EGLLabelKHR label);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLMAKECURRENTPROC)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
typedef EGLenum (GLAD_API_PTR *PFNEGLQUERYAPIPROC)(void);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLQUERYCONTEXTPROC)(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint * value);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLQUERYDEBUGKHRPROC)(EGLint attribute, EGLAttrib * value);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLQUERYDMABUFFORMATSEXTPROC)(EGLDisplay dpy, EGLint max_formats, EGLint * formats, EGLint * num_formats);
typedef EGLBoolean (GLAD_API_PTR *PFNEGLQUERYDMABUFMODIFIERSEXTPROC)(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR * modifiers, EGLBoolean * external_only, EGLint * num_modifiers);
typedef const char * (GLAD_API_PTR *PFNEGLQUERYSTRINGPROC)(EGLDisplay dpy, EGLint name);
@@ -1035,6 +1052,8 @@ GLAD_API_CALL PFNEGLCREATESYNCPROC glad_eglCreateSync;
#define eglCreateSync glad_eglCreateSync
GLAD_API_CALL PFNEGLCREATEWINDOWSURFACEPROC glad_eglCreateWindowSurface;
#define eglCreateWindowSurface glad_eglCreateWindowSurface
GLAD_API_CALL PFNEGLDEBUGMESSAGECONTROLKHRPROC glad_eglDebugMessageControlKHR;
#define eglDebugMessageControlKHR glad_eglDebugMessageControlKHR
GLAD_API_CALL PFNEGLDESTROYCONTEXTPROC glad_eglDestroyContext;
#define eglDestroyContext glad_eglDestroyContext
GLAD_API_CALL PFNEGLDESTROYIMAGEPROC glad_eglDestroyImage;
@@ -1069,12 +1088,16 @@ GLAD_API_CALL PFNEGLGETSYNCATTRIBPROC glad_eglGetSyncAttrib;
#define eglGetSyncAttrib glad_eglGetSyncAttrib
GLAD_API_CALL PFNEGLINITIALIZEPROC glad_eglInitialize;
#define eglInitialize glad_eglInitialize
GLAD_API_CALL PFNEGLLABELOBJECTKHRPROC glad_eglLabelObjectKHR;
#define eglLabelObjectKHR glad_eglLabelObjectKHR
GLAD_API_CALL PFNEGLMAKECURRENTPROC glad_eglMakeCurrent;
#define eglMakeCurrent glad_eglMakeCurrent
GLAD_API_CALL PFNEGLQUERYAPIPROC glad_eglQueryAPI;
#define eglQueryAPI glad_eglQueryAPI
GLAD_API_CALL PFNEGLQUERYCONTEXTPROC glad_eglQueryContext;
#define eglQueryContext glad_eglQueryContext
GLAD_API_CALL PFNEGLQUERYDEBUGKHRPROC glad_eglQueryDebugKHR;
#define eglQueryDebugKHR glad_eglQueryDebugKHR
GLAD_API_CALL PFNEGLQUERYDMABUFFORMATSEXTPROC glad_eglQueryDmaBufFormatsEXT;
#define eglQueryDmaBufFormatsEXT glad_eglQueryDmaBufFormatsEXT
GLAD_API_CALL PFNEGLQUERYDMABUFMODIFIERSEXTPROC glad_eglQueryDmaBufModifiersEXT;
@@ -1157,6 +1180,7 @@ int GLAD_EGL_VERSION_1_4 = 0;
int GLAD_EGL_VERSION_1_5 = 0;
int GLAD_EGL_EXT_image_dma_buf_import = 0;
int GLAD_EGL_EXT_image_dma_buf_import_modifiers = 0;
int GLAD_EGL_KHR_debug = 0;
int GLAD_EGL_KHR_gl_texture_2D_image = 0;
int GLAD_EGL_KHR_image = 0;
int GLAD_EGL_KHR_image_base = 0;
@@ -1184,6 +1208,7 @@ PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC glad_eglCreatePlatformPixmapSurface = NULL
PFNEGLCREATEPLATFORMWINDOWSURFACEPROC glad_eglCreatePlatformWindowSurface = NULL;
PFNEGLCREATESYNCPROC glad_eglCreateSync = NULL;
PFNEGLCREATEWINDOWSURFACEPROC glad_eglCreateWindowSurface = NULL;
PFNEGLDEBUGMESSAGECONTROLKHRPROC glad_eglDebugMessageControlKHR = NULL;
PFNEGLDESTROYCONTEXTPROC glad_eglDestroyContext = NULL;
PFNEGLDESTROYIMAGEPROC glad_eglDestroyImage = NULL;
PFNEGLDESTROYIMAGEKHRPROC glad_eglDestroyImageKHR = NULL;
@@ -1201,9 +1226,11 @@ PFNEGLGETPLATFORMDISPLAYPROC glad_eglGetPlatformDisplay = NULL;
PFNEGLGETPROCADDRESSPROC glad_eglGetProcAddress = NULL;
PFNEGLGETSYNCATTRIBPROC glad_eglGetSyncAttrib = NULL;
PFNEGLINITIALIZEPROC glad_eglInitialize = NULL;
PFNEGLLABELOBJECTKHRPROC glad_eglLabelObjectKHR = NULL;
PFNEGLMAKECURRENTPROC glad_eglMakeCurrent = NULL;
PFNEGLQUERYAPIPROC glad_eglQueryAPI = NULL;
PFNEGLQUERYCONTEXTPROC glad_eglQueryContext = NULL;
PFNEGLQUERYDEBUGKHRPROC glad_eglQueryDebugKHR = NULL;
PFNEGLQUERYDMABUFFORMATSEXTPROC glad_eglQueryDmaBufFormatsEXT = NULL;
PFNEGLQUERYDMABUFMODIFIERSEXTPROC glad_eglQueryDmaBufModifiersEXT = NULL;
PFNEGLQUERYSTRINGPROC glad_eglQueryString = NULL;
@@ -1284,6 +1311,12 @@ static void glad_egl_load_EGL_EXT_image_dma_buf_import_modifiers( GLADuserptrloa
glad_eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC) load(userptr, "eglQueryDmaBufFormatsEXT");
glad_eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC) load(userptr, "eglQueryDmaBufModifiersEXT");
}
static void glad_egl_load_EGL_KHR_debug( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_EGL_KHR_debug) return;
glad_eglDebugMessageControlKHR = (PFNEGLDEBUGMESSAGECONTROLKHRPROC) load(userptr, "eglDebugMessageControlKHR");
glad_eglLabelObjectKHR = (PFNEGLLABELOBJECTKHRPROC) load(userptr, "eglLabelObjectKHR");
glad_eglQueryDebugKHR = (PFNEGLQUERYDEBUGKHRPROC) load(userptr, "eglQueryDebugKHR");
}
static void glad_egl_load_EGL_KHR_image( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_EGL_KHR_image) return;
glad_eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) load(userptr, "eglCreateImageKHR");
@@ -1338,6 +1371,7 @@ static int glad_egl_find_extensions_egl(EGLDisplay display) {
GLAD_EGL_EXT_image_dma_buf_import = glad_egl_has_extension(extensions, "EGL_EXT_image_dma_buf_import");
GLAD_EGL_EXT_image_dma_buf_import_modifiers = glad_egl_has_extension(extensions, "EGL_EXT_image_dma_buf_import_modifiers");
GLAD_EGL_KHR_debug = glad_egl_has_extension(extensions, "EGL_KHR_debug");
GLAD_EGL_KHR_gl_texture_2D_image = glad_egl_has_extension(extensions, "EGL_KHR_gl_texture_2D_image");
GLAD_EGL_KHR_image = glad_egl_has_extension(extensions, "EGL_KHR_image");
GLAD_EGL_KHR_image_base = glad_egl_has_extension(extensions, "EGL_KHR_image_base");
@@ -1409,6 +1443,7 @@ int gladLoadEGLUserPtr(EGLDisplay display, GLADuserptrloadfunc load, void* userp
if (!glad_egl_find_extensions_egl(display)) return 0;
glad_egl_load_EGL_EXT_image_dma_buf_import_modifiers(load, userptr);
glad_egl_load_EGL_KHR_debug(load, userptr);
glad_egl_load_EGL_KHR_image(load, userptr);
glad_egl_load_EGL_KHR_image_base(load, userptr);
glad_egl_load_EGL_MESA_drm_image(load, userptr);
@@ -1421,7 +1456,7 @@ int gladLoadEGL(EGLDisplay display, GLADloadfunc load) {
return gladLoadEGLUserPtr(display, glad_egl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load);
}
#ifdef GLAD_EGL
@@ -1572,4 +1607,3 @@ void gladLoaderUnloadEGL(void) {
#endif
#endif /* GLAD_EGL_IMPLEMENTATION */
+30 -7
View File
@@ -1,11 +1,11 @@
/**
* Loader generated by glad 2.0.8 on Sun Sep 21 13:34:57 2025
* Loader generated by glad 2.0.8 on Wed May 13 21:57:23 2026
*
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*
* Generator: C/C++
* Specification: gl
* Extensions: 3
* Extensions: 4
*
* APIs:
* - gl:compatibility=4.6
@@ -19,10 +19,10 @@
* - ON_DEMAND = False
*
* Commandline:
* --api='gl:compatibility=4.6' --extensions='GL_ARB_direct_state_access,GL_EXT_EGL_image_storage,GL_EXT_direct_state_access' c --header-only --loader
* --api='gl:compatibility=4.6' --extensions='GL_ARB_direct_state_access,GL_EXT_EGL_image_storage,GL_EXT_direct_state_access,GL_KHR_debug' c --header-only --loader
*
* Online:
* http://glad.sh/#api=gl%3Acompatibility%3D4.6&extensions=GL_ARB_direct_state_access%2CGL_EXT_EGL_image_storage%2CGL_EXT_direct_state_access&generator=c&options=HEADER_ONLY%2CLOADER
* http://glad.sh/#api=gl%3Acompatibility%3D4.6&extensions=GL_ARB_direct_state_access%2CGL_EXT_EGL_image_storage%2CGL_EXT_direct_state_access%2CGL_KHR_debug&generator=c&options=HEADER_ONLY%2CLOADER
*
*/
@@ -2409,6 +2409,8 @@ GLAD_API_CALL int GLAD_GL_ARB_direct_state_access;
GLAD_API_CALL int GLAD_GL_EXT_EGL_image_storage;
#define GL_EXT_direct_state_access 1
GLAD_API_CALL int GLAD_GL_EXT_direct_state_access;
#define GL_KHR_debug 1
GLAD_API_CALL int GLAD_GL_KHR_debug;
typedef void (GLAD_API_PTR *PFNGLACCUMPROC)(GLenum op, GLfloat value);
@@ -6396,6 +6398,7 @@ int GLAD_GL_VERSION_4_6 = 0;
int GLAD_GL_ARB_direct_state_access = 0;
int GLAD_GL_EXT_EGL_image_storage = 0;
int GLAD_GL_EXT_direct_state_access = 0;
int GLAD_GL_KHR_debug = 0;
@@ -9178,6 +9181,20 @@ static void glad_gl_load_GL_EXT_direct_state_access( GLADuserptrloadfunc load, v
glad_glVertexArrayVertexBindingDivisorEXT = (PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) load(userptr, "glVertexArrayVertexBindingDivisorEXT");
glad_glVertexArrayVertexOffsetEXT = (PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) load(userptr, "glVertexArrayVertexOffsetEXT");
}
static void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GL_KHR_debug) return;
glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load(userptr, "glDebugMessageCallback");
glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load(userptr, "glDebugMessageControl");
glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load(userptr, "glDebugMessageInsert");
glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load(userptr, "glGetDebugMessageLog");
glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC) load(userptr, "glGetObjectLabel");
glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) load(userptr, "glGetObjectPtrLabel");
glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv");
glad_glObjectLabel = (PFNGLOBJECTLABELPROC) load(userptr, "glObjectLabel");
glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) load(userptr, "glObjectPtrLabel");
glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load(userptr, "glPopDebugGroup");
glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load(userptr, "glPushDebugGroup");
}
@@ -9276,6 +9293,7 @@ static int glad_gl_find_extensions_gl(void) {
GLAD_GL_ARB_direct_state_access = glad_gl_has_extension(exts, exts_i, "GL_ARB_direct_state_access");
GLAD_GL_EXT_EGL_image_storage = glad_gl_has_extension(exts, exts_i, "GL_EXT_EGL_image_storage");
GLAD_GL_EXT_direct_state_access = glad_gl_has_extension(exts, exts_i, "GL_EXT_direct_state_access");
GLAD_GL_KHR_debug = glad_gl_has_extension(exts, exts_i, "GL_KHR_debug");
glad_gl_free_extensions(exts_i);
@@ -9360,6 +9378,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {
glad_gl_load_GL_ARB_direct_state_access(load, userptr);
glad_gl_load_GL_EXT_EGL_image_storage(load, userptr);
glad_gl_load_GL_EXT_direct_state_access(load, userptr);
glad_gl_load_GL_KHR_debug(load, userptr);
@@ -9373,7 +9392,7 @@ int gladLoadGL( GLADloadfunc load) {
#ifdef GLAD_GL
@@ -9482,7 +9501,9 @@ static void* glad_gl_dlopen_handle(void) {
"libGL-1.so",
#endif
"libGL.so.1",
"libGL.so"
"libGL.so",
"libEGL.so.1",
"libEGL.so"
};
#endif
@@ -9505,6 +9526,9 @@ static struct _glad_gl_userptr glad_gl_build_userptr(void *handle) {
#else
userptr.gl_get_proc_address_ptr =
(GLADglprocaddrfunc) glad_dlsym_handle(handle, "glXGetProcAddressARB");
if (!userptr.gl_get_proc_address_ptr)
userptr.gl_get_proc_address_ptr =
(GLADglprocaddrfunc) glad_dlsym_handle(handle, "eglGetProcAddress");
#endif
return userptr;
@@ -9547,4 +9571,3 @@ void gladLoaderUnloadGL(void) {
#endif
#endif /* GLAD_GL_IMPLEMENTATION */
+79 -71
View File
@@ -6,7 +6,7 @@
# (shaders, video devices, textures, midi inputs, etc.)
# Every number based constant will be "one-based" (1,2,3,etc.)
# To read more, go to
# https://github.com/klemek/forge-steel
# https://git.klemek.fr/klemek/forge-steel
# ================
@@ -24,7 +24,7 @@
UNIFORM_TIME=iTime
# Current tempo
UNIFORM_TEMPO=iTempo
# Current total beats
# Current total beats
UNIFORM_BEATS=iBeats
# --- uniform int ---
@@ -44,8 +44,8 @@ UNIFORM_RESOLUTION=iResolution
# --- uniform vec3 ---
# Midi group X layer Y (beware of group size)
# Injected as "iMidiX_Y[Z]"
UNIFORM_MIDI_PREFIX=iMidi
# Injected as "iGroupX_Y[Z]"
UNIFORM_GROUP_PREFIX=iGroup
# --- uniform sampler2D ---
@@ -79,9 +79,17 @@ FRAG_OUTPUT=2
FRAG_MONITOR=2
# ========
# MIDI I/O
# ========
# ==========
# I/O Inputs
# ==========
# Each code either maps to a midi event or a key code
# 0 - 999 -> midi event
# Keyboard modifiers are encoded like this:
# 1000 -> keyboard event
# 10000 -> shift
# 100000 -> control
# 1000000 -> alt
# This means 111082 is control+alt+R
# The recognized ALSA name of the midi device
@@ -114,8 +122,8 @@ FADER_16=23
# Midi code for tap tempo
TAP_TEMPO=46
# === MIDI INPUT STATES
# Midi inputs will control FORGE's state as follows
# === GROUP INPUT STATES
# Inputs will control FORGE's state as follows
# X groups of Y layers sized Z
# You can manipulate only 1 layer at a time
# Every layer of every groups will be send as uniforms
@@ -123,68 +131,68 @@ TAP_TEMPO=46
# with the same codes for nice display
# Total number of groups
MIDI_COUNT=1
GROUP_COUNT=1
# Size of group 1
MIDI_1_COUNT=20
GROUP_1_COUNT=20
# Every code of active layer manipulation of group 1
MIDI_1_1_X=32
MIDI_1_1_Y=48
MIDI_1_1_Z=64
MIDI_1_2_X=0
MIDI_1_2_Y=16
MIDI_1_2_Z=
MIDI_1_3_X=33
MIDI_1_3_Y=49
MIDI_1_3_Z=65
MIDI_1_4_X=1
MIDI_1_4_Y=17
MIDI_1_4_Z=
MIDI_1_5_X=34
MIDI_1_5_Y=50
MIDI_1_5_Z=66
MIDI_1_6_X=2
MIDI_1_6_Y=18
MIDI_1_6_Z=
MIDI_1_7_X=35
MIDI_1_7_Y=51
MIDI_1_7_Z=67
MIDI_1_8_X=3
MIDI_1_8_Y=19
MIDI_1_8_Z=
MIDI_1_9_X=36
MIDI_1_9_Y=52
MIDI_1_9_Z=68
MIDI_1_10_X=4
MIDI_1_10_Y=20
MIDI_1_10_Z=
MIDI_1_11_X=37
MIDI_1_11_Y=53
MIDI_1_11_Z=69
MIDI_1_12_X=5
MIDI_1_12_Y=21
MIDI_1_12_Z=
MIDI_1_13_X=38
MIDI_1_13_Y=54
MIDI_1_13_Z=70
MIDI_1_14_X=6
MIDI_1_14_Y=22
MIDI_1_14_Z=
MIDI_1_15_X=39
MIDI_1_15_Y=55
MIDI_1_15_Z=71
MIDI_1_16_X=7
MIDI_1_16_Y=23
MIDI_1_16_Z=
MIDI_1_17_X=58
MIDI_1_17_Y=59
MIDI_1_17_Z=
MIDI_1_18_X=60
MIDI_1_18_Y=61
MIDI_1_18_Z=62
MIDI_1_19_X=43
MIDI_1_19_Y=44
MIDI_1_19_Z=42
MIDI_1_20_X=41
MIDI_1_20_Y=45
MIDI_1_20_Z=
GROUP_1_1_X=32
GROUP_1_1_Y=48
GROUP_1_1_Z=64
GROUP_1_2_X=0
GROUP_1_2_Y=16
GROUP_1_2_Z=
GROUP_1_3_X=33
GROUP_1_3_Y=49
GROUP_1_3_Z=65
GROUP_1_4_X=1
GROUP_1_4_Y=17
GROUP_1_4_Z=
GROUP_1_5_X=34
GROUP_1_5_Y=50
GROUP_1_5_Z=66
GROUP_1_6_X=2
GROUP_1_6_Y=18
GROUP_1_6_Z=
GROUP_1_7_X=35
GROUP_1_7_Y=51
GROUP_1_7_Z=67
GROUP_1_8_X=3
GROUP_1_8_Y=19
GROUP_1_8_Z=
GROUP_1_9_X=36
GROUP_1_9_Y=52
GROUP_1_9_Z=68
GROUP_1_10_X=4
GROUP_1_10_Y=20
GROUP_1_10_Z=
GROUP_1_11_X=37
GROUP_1_11_Y=53
GROUP_1_11_Z=69
GROUP_1_12_X=5
GROUP_1_12_Y=21
GROUP_1_12_Z=
GROUP_1_13_X=38
GROUP_1_13_Y=54
GROUP_1_13_Z=70
GROUP_1_14_X=6
GROUP_1_14_Y=22
GROUP_1_14_Z=
GROUP_1_15_X=39
GROUP_1_15_Y=55
GROUP_1_15_Z=71
GROUP_1_16_X=7
GROUP_1_16_Y=23
GROUP_1_16_Z=
GROUP_1_17_X=58
GROUP_1_17_Y=59
GROUP_1_17_Z=
GROUP_1_18_X=60
GROUP_1_18_Y=61
GROUP_1_18_Z=62
GROUP_1_19_X=43
GROUP_1_19_Y=44
GROUP_1_19_Z=42
GROUP_1_20_X=41
GROUP_1_20_Y=45
GROUP_1_20_Z=
+29 -29
View File
@@ -12,31 +12,31 @@ uniform int iFPS; // output window frames per seconds
uniform int iDemo; // 0/1 if demo mode
uniform int iSeed1; // a random seed assigned at start
uniform vec2 iResolution; // output window resolution in pixels
uniform vec3 iMidi1_1[20]; // all midi inputs defined
uniform vec3 iGroup1_1[20]; // all midi inputs defined
uniform sampler2D iTex0; // available texture (this code output, so feedback)
void main() {
// all available buttons and faders
vec3 b1 = iMidi1_1[0];
vec2 f1 = iMidi1_1[1].xy;
vec3 b2 = iMidi1_1[2];
vec2 f2 = iMidi1_1[3].xy;
vec3 b3 = iMidi1_1[4];
vec2 f3 = iMidi1_1[5].xy;
vec3 b4 = iMidi1_1[6];
vec2 f4 = iMidi1_1[7].xy;
vec3 b5 = iMidi1_1[8];
vec2 f5 = iMidi1_1[9].xy;
vec3 b6 = iMidi1_1[10];
vec2 f6 = iMidi1_1[11].xy;
vec3 b7 = iMidi1_1[12];
vec2 f7 = iMidi1_1[13].xy;
vec3 b8 = iMidi1_1[14];
vec2 f8 = iMidi1_1[15].xy;
vec3 b9 = iMidi1_1[16];
vec3 b10 = iMidi1_1[17];
vec3 b11 = iMidi1_1[18];
vec3 b12 = iMidi1_1[19];
vec3 b1 = iGroup1_1[0];
vec2 f1 = iGroup1_1[1].xy;
vec3 b2 = iGroup1_1[2];
vec2 f2 = iGroup1_1[3].xy;
vec3 b3 = iGroup1_1[4];
vec2 f3 = iGroup1_1[5].xy;
vec3 b4 = iGroup1_1[6];
vec2 f4 = iGroup1_1[7].xy;
vec3 b5 = iGroup1_1[8];
vec2 f5 = iGroup1_1[9].xy;
vec3 b6 = iGroup1_1[10];
vec2 f6 = iGroup1_1[11].xy;
vec3 b7 = iGroup1_1[12];
vec2 f7 = iGroup1_1[13].xy;
vec3 b8 = iGroup1_1[14];
vec2 f8 = iGroup1_1[15].xy;
vec3 b9 = iGroup1_1[16];
vec3 b10 = iGroup1_1[17];
vec3 b11 = iGroup1_1[18];
vec3 b12 = iGroup1_1[19];
// center UV and scale it to ratio
vec2 uv0 = vUV.st;
@@ -47,19 +47,19 @@ void main() {
float circle_dist = 0.1 + 0.9 * f1.x;
float circle_size = 0.1 + 0.9 * f2.x;
vec2 circle_pos = circle_dist * vec2(
sin(iBeats * 2 * PI),
cos(iBeats * 2 * PI)
);
sin(iBeats * 2 * PI),
cos(iBeats * 2 * PI)
);
float circle = 1 - step(circle_size, length(uv1 - circle_pos));
// fader 3, 4 and 5 controls the color
vec3 color = vec3(
f3.x,
f4.x,
f5.x
);
f3.x,
f4.x,
f5.x
);
vec3 out_color = mix(color, 1 - color, circle);
// fader 8 controls the feedback value
fragColor = mix(vec4(out_color, 1), texture(iTex0, vUV.st), f8.x);
}
}
+43 -33
View File
@@ -32,11 +32,13 @@ static void print_help(int status_code) {
#ifdef VIDEO_IN
"[-vi=FILE] "
"[-vs=SIZE] "
"[-vb=COUNT] "
"[-vr / -nvr] "
#endif /* VIDEO_IN */
"[-is=SIZE] "
"[-ls / -nls] "
"[-ss / -nss] "
"[-mr / -nmr] "
"[-tm] "
"[-tf] "
"\n\n"
@@ -62,6 +64,8 @@ static void print_help(int status_code) {
#ifdef VIDEO_IN
" -vi, --video-in path to video capture device (multiple "
"allowed)\n"
" -vb, --video-buffers number of video buffers to use (default: "
"2)\n"
" -vs, --video-size video capture desired height (default: "
"internal texture height)\n"
" -vr, --video-reconnect auto-reconnect video (default)\n"
@@ -72,6 +76,8 @@ static void print_help(int status_code) {
" -nls, --no-load-state do not load saved state\n"
" -ss, --save-state save state (default)\n"
" -nss, --no-save-state do not save state\n"
" -mr, --midi-reconnect auto-reconnect midi (default)\n"
" -nmr, --no-midi-reconnect do not auto-reconnect midi\n"
" -tm, --trace-midi print midi code and values\n"
" -tf, --trace-fps print fps status of subsystems\n");
exit(status_code);
@@ -131,12 +137,16 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->demo = false;
params->auto_random = false;
params->auto_random_cycle = 4;
#ifdef VIDEO_IN
params->video_in.length = 0;
params->video_buffers = 2;
params->video_size = 0;
params->video_reconnect = true;
#endif /* VIDEO_IN */
params->internal_size = 720;
params->load_state = true;
params->save_state = true;
params->midi_reconnect = true;
params->trace_midi = false;
params->trace_fps = false;
@@ -180,38 +190,6 @@ void args_parse(Parameters *params, int argc, char **argv) {
if (params->auto_random_cycle == 0) {
invalid_value(arg, value);
}
} else if (is_arg(arg, "-vi") || is_arg(arg, "--video-in")) {
#ifdef VIDEO_IN
if (params->video_in.length == MAX_VIDEO) {
log_error("maximum video input reached");
exit(EXIT_FAILURE);
}
strlcpy(params->video_in.values[params->video_in.length++], value,
STR_LEN);
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-vs") || is_arg(arg, "--video-size")) {
#ifdef VIDEO_IN
params->video_size = parse_uint(arg, value);
if (params->video_size == 0) {
invalid_value(arg, value);
}
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-vr") || is_arg(arg, "--video-reconnect")) {
#ifdef VIDEO_IN
params->video_reconnect = true;
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-nvr") || is_arg(arg, "--no-video-reconnect")) {
#ifdef VIDEO_IN
params->video_reconnect = false;
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-is") || is_arg(arg, "--internal-size")) {
params->internal_size = parse_uint(arg, value);
if (params->internal_size == 0) {
@@ -225,12 +203,43 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->save_state = true;
} else if (is_arg(arg, "-nss") || is_arg(arg, "--no-save-state")) {
params->save_state = false;
} else if (is_arg(arg, "-mr") || is_arg(arg, "--midi-reconnect")) {
params->midi_reconnect = true;
} else if (is_arg(arg, "-nmr") || is_arg(arg, "--no-midi-reconnect")) {
params->midi_reconnect = false;
} else if (is_arg(arg, "-tm") || is_arg(arg, "--trace-midi")) {
params->trace_midi = true;
} else if (is_arg(arg, "-tf") || is_arg(arg, "--trace-fps")) {
params->trace_fps = true;
} else {
#ifdef VIDEO_IN
if (is_arg(arg, "-vi") || is_arg(arg, "--video-in")) {
if (params->video_in.length == MAX_VIDEO) {
log_error("maximum video input reached");
exit(EXIT_FAILURE);
}
strlcpy(params->video_in.values[params->video_in.length++], value,
STR_LEN);
} else if (is_arg(arg, "-vb") || is_arg(arg, "--video-buffers")) {
params->video_buffers = parse_uint(arg, value);
if (params->video_buffers == 0) {
invalid_value(arg, value);
}
} else if (is_arg(arg, "-vs") || is_arg(arg, "--video-size")) {
params->video_size = parse_uint(arg, value);
if (params->video_size == 0) {
invalid_value(arg, value);
}
} else if (is_arg(arg, "-vr") || is_arg(arg, "--video-reconnect")) {
params->video_reconnect = true;
} else if (is_arg(arg, "-nvr") || is_arg(arg, "--no-video-reconnect")) {
params->video_reconnect = false;
} else {
invalid_arg(arg);
}
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
}
}
@@ -239,8 +248,9 @@ void args_parse(Parameters *params, int argc, char **argv) {
log_error("monitor screen cannot be the same as output screen");
exit(EXIT_FAILURE);
}
#ifdef VIDEO_IN
if (params->video_size == 0) {
params->video_size = params->internal_size;
}
#endif /* VIDEO_IN */
}
+152 -104
View File
@@ -1,5 +1,6 @@
#include <log.h>
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -14,7 +15,6 @@
#include "midi.h"
#include "project.h"
#include "shaders.h"
#include "shared.h"
#include "state.h"
#include "tempo.h"
#include "timer.h"
@@ -22,15 +22,18 @@
#include "window.h"
static Parameters init_params;
static SharedContext *context;
static Context context;
static ShaderProgram program;
static Window *window_output;
static Window *window_monitor;
static Timer timer;
static MidiDevice midi;
static bool trace_midi;
static Project project;
#ifdef VIDEO_IN
static VideoCaptureArray video_captures;
#endif /* VIDEO_IN */
static void compute_fps() {
double fps;
char title[STR_LEN];
@@ -52,103 +55,108 @@ static void compute_fps() {
window_update_title(window_monitor, title);
}
context->fps = (unsigned int)round(fps);
context.fps = (unsigned int)round(fps);
}
}
static void init_context() {
context = shared_init_context("/" PACKAGE "_context");
context.stop = false;
context->stop = false;
state_init(context, &project.state_config, init_params.demo,
state_init(&context, project.state_config, init_params.demo,
init_params.auto_random, init_params.auto_random_cycle,
init_params.base_tempo, init_params.load_state);
memset(context->input_resolutions, 0, sizeof(context->input_resolutions));
#ifdef VIDEO_IN
memset(context.input_resolutions, 0, sizeof(context.input_resolutions));
memset(context.input_formats, 0, sizeof(context.input_formats));
memset(context.input_fps, 0, sizeof(context.input_fps));
memset(context.input_index, 0, sizeof(context.input_index));
#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],
init_params.video_size);
video_init(&video_captures.values[i], init_params.video_in.values[i],
init_params.video_size, init_params.video_buffers);
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, init_params.video_buffers);
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,66 @@ 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 void *background_reconnect_midi(__attribute__((unused)) void *args) {
log_info("background midi reconnect started");
while (!context.stop) {
sleep(1);
if (!midi.connected) {
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"));
if (midi.connected) {
start_midi_background_listen();
}
}
}
pthread_exit(NULL);
}
static void init_midi() {
pthread_t thread;
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"));
if (midi.connected) {
start_midi_background_listen();
}
if (init_params.midi_reconnect) {
pthread_create(&thread, NULL, background_reconnect_midi, NULL);
pthread_detach(thread);
}
}
static bool init(const Parameters *params) {
@@ -196,38 +257,24 @@ 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"));
init_midi();
if (midi.error) {
context->demo = true;
} else {
trace_midi = params->trace_midi;
if (!midi_background_listen(&midi, context, midi_callback)) {
return false;
}
}
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 +284,20 @@ 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,
init_params.video_buffers);
#endif /* VIDEO_IN */
if (program.error) {
context->stop = true;
context.stop = true;
window_terminate();
exit(EXIT_FAILURE);
}
@@ -273,10 +321,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 +332,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 +357,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,7 +381,7 @@ static void shutdown() {
free_video_captures();
#endif /* VIDEO_IN */
free_context();
midi_close(&midi);
project_free(&project);
+39 -26
View File
@@ -1,6 +1,6 @@
#include <alsa/asoundlib.h>
#include <log.h>
#include <stdlib.h>
#include <pthread.h>
#include "types.h"
@@ -13,14 +13,17 @@ void midi_open(MidiDevice *device, const char *name) {
snd_rawmidi_open(&device->input, &device->output, name, SND_RAWMIDI_NONBLOCK);
device->error = device->input == NULL || device->output == NULL;
device->connected = device->input != NULL && device->output != NULL;
log_info("(%s) MIDI open", name);
if (device->connected) {
log_info("(%s) MIDI open", name);
} else {
log_warn("(%s) MIDI open failed", name);
}
}
void midi_write(const MidiDevice *device, unsigned char code,
unsigned char value) {
if (device->error) {
void midi_write(MidiDevice device, unsigned char code, unsigned char value) {
if (!device.connected) {
return;
}
@@ -30,35 +33,45 @@ void midi_write(const MidiDevice *device, unsigned char code,
buffer[1] = code;
buffer[2] = value;
snd_rawmidi_write(device->output, buffer, 3);
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_close(MidiDevice *device) {
if (device->connected) {
snd_rawmidi_close(device->input);
snd_rawmidi_close(device->output);
device->connected = false;
}
}
void *midi_background_listen(void *args) {
MidiBackgroundReadArgs *process_args = (MidiBackgroundReadArgs *)args;
MidiDevice *device = process_args->device;
Context *context = process_args->context;
int bytes_read;
snd_rawmidi_info_t *info;
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) {
snd_rawmidi_info_malloc(&info);
while (!context->stop && snd_rawmidi_info(device->output, info) == 0) {
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);
snd_rawmidi_info_free(info);
if (context->stop) {
log_info("(%s) background acquisition stopped by main thread",
device->name);
} else {
log_info("(%s) background acquisition stopped after error", device->name);
midi_close(device);
}
pthread_exit(NULL);
}
+3 -6
View File
@@ -4,11 +4,8 @@
#define MIDI_H
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_write(MidiDevice device, unsigned char code, unsigned char value);
void *midi_background_listen(void *args);
void midi_close(MidiDevice *device);
#endif /* MIDI_H */
+158 -85
View File
@@ -26,7 +26,7 @@
static const GLuint unused_uniform = (GLuint)-1;
bool check_glerror_ro(const char *context) {
static bool check_glerror_ro(const char *context) {
unsigned int code;
code = glGetError();
@@ -39,7 +39,7 @@ bool check_glerror_ro(const char *context) {
return false;
}
bool check_glerror(ShaderProgram *program, const char *context) {
static bool check_glerror(ShaderProgram *program, const char *context) {
if (check_glerror_ro(context)) {
program->error = true;
return true;
@@ -47,7 +47,20 @@ bool check_glerror(ShaderProgram *program, const char *context) {
return false;
}
bool check_eglerror_ro(const char *context) {
#ifdef GL_DEBUG
static void gl_debug_callback(GLenum source, GLenum type, GLuint id,
GLenum severity,
__attribute__((unused)) GLsizei length,
const GLchar *message,
__attribute__((unused)) const void *userParam) {
log_debug("GL Debug: source=%x, type=%x, id=%d, severity=%x, message=%s",
source, type, id, severity, message ? message : "(null)");
}
#endif /* GL_DEBUG */
static bool check_eglerror_ro(const char *context) {
#ifdef VIDEO_IN
unsigned int code;
@@ -61,7 +74,7 @@ bool check_eglerror_ro(const char *context) {
return false;
}
bool check_eglerror(ShaderProgram *program, const char *context) {
static bool check_eglerror(ShaderProgram *program, const char *context) {
if (check_eglerror_ro(context)) {
program->error = true;
return true;
@@ -69,9 +82,29 @@ bool check_eglerror(ShaderProgram *program, const char *context) {
return false;
}
#ifdef VIDEO_IN
#ifdef EGL_DEBUG
static void egl_debug_callback(EGLenum error, const char *command,
EGLint messageType, EGLLabelKHR threadLabel,
EGLLabelKHR objectLabel, const char *message) {
log_debug("EGL Debug: error=%04x, command=%s, type=%d, "
"thread=%p, object=%p, message=%s",
error, command ? command : "(null)", messageType, threadLabel,
objectLabel, message ? message : "(null)");
}
#endif /* EGL_DEBUG */
#endif /* VIDEO_IN */
static bool init_gl(ShaderProgram *program) {
gladLoadGL(glfwGetProcAddress);
#ifdef GL_DEBUG
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(gl_debug_callback, NULL);
#endif /* GL_DEBUG */
#ifdef VIDEO_IN
program->egl_display = glfwGetEGLDisplay();
if (program->egl_display == EGL_NO_DISPLAY) {
@@ -81,14 +114,27 @@ static bool init_gl(ShaderProgram *program) {
}
gladLoadEGL(program->egl_display, glfwGetProcAddress);
#ifdef EGL_DEBUG
{
PFNEGLDEBUGMESSAGECONTROLKHRPROC debug_ctrl =
(PFNEGLDEBUGMESSAGECONTROLKHRPROC)glfwGetProcAddress(
"eglDebugMessageControlKHR");
if (debug_ctrl != NULL) {
const EGLAttrib attrib_list[] = {EGL_NONE};
debug_ctrl(egl_debug_callback, attrib_list);
}
}
#endif /* EGL_DEBUG */
#endif /* VIDEO_IN */
return !check_glerror(program, "init_gl") &&
!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;
@@ -145,18 +191,20 @@ static void rebind_textures(const ShaderProgram *program) {
#ifdef VIDEO_IN
static bool link_input_to_texture(ShaderProgram *program, VideoCapture *input,
unsigned int input_index,
unsigned int texture_index, bool swap,
bool reload) {
unsigned int sub_index, bool reload) {
unsigned int texture_index =
program->tex_count + input_index * program->sub_video_count + sub_index;
if (reload) {
glDeleteTextures(1, program->textures + texture_index);
if (check_glerror(program, "link_input_to_texture/glDeleteTextures")) {
return false;
}
}
glGenTextures(1, program->textures + texture_index);
if (check_glerror(program, "link_input_to_texture/glGenTextures")) {
return false;
}
glGenTextures(1, program->textures + texture_index);
if (check_glerror(program, "link_input_to_texture/glGenTextures")) {
return false;
}
EGLImageKHR dma_image;
@@ -171,7 +219,7 @@ static bool link_input_to_texture(ShaderProgram *program, VideoCapture *input,
EGL_LINUX_DRM_FOURCC_EXT,
input->pixelformat,
EGL_DMA_BUF_PLANE0_FD_EXT,
swap ? input->exp_fd_swap : input->exp_fd,
input->exp_fd[sub_index],
EGL_DMA_BUF_PLANE0_OFFSET_EXT,
0,
EGL_DMA_BUF_PLANE0_PITCH_EXT,
@@ -184,11 +232,8 @@ static bool link_input_to_texture(ShaderProgram *program, VideoCapture *input,
return false;
}
if (swap) {
program->dma_images_swap[input_index] = dma_image;
} else {
program->dma_images[input_index] = dma_image;
}
program->dma_images[input_index * program->sub_video_count + sub_index] =
dma_image;
glActiveTexture(GL_TEXTURE0 + texture_index);
if (check_glerror(program, "link_input_to_texture/glActiveTexture")) {
@@ -213,33 +258,35 @@ static bool link_input_to_texture(ShaderProgram *program, VideoCapture *input,
return false;
}
log_info("Texture %d linked to %s", texture_index, input->name);
log_info("Texture %d linked to %s[%d]", texture_index, input->name,
sub_index);
return true;
}
static bool init_input(ShaderProgram *program, const ConfigFile *config,
VideoCaptureArray *inputs, unsigned int i, bool reload) {
VideoCaptureArray *inputs, unsigned int input_index,
bool reload) {
unsigned int tex_i;
unsigned int sub_index;
char name[STR_LEN];
if (i < inputs->length && !inputs->values[i].error) {
snprintf(name, STR_LEN, "IN_%d_OUT", i + 1);
if (input_index < inputs->length && input_index < program->in_count &&
!inputs->values[input_index].error) {
snprintf(name, STR_LEN, "IN_%d_OUT", input_index + 1);
tex_i = config_file_get_int(config, name, 0);
if (!link_input_to_texture(program, &inputs->values[i], i, tex_i, false,
reload)) {
return false;
}
if (inputs->values[i].with_swap) {
snprintf(name, STR_LEN, "IN_%d_SWAP_OUT", i + 1);
tex_i = config_file_get_int(config, name, 0);
if (!link_input_to_texture(program, &inputs->values[i], i, tex_i, true,
reload)) {
for (sub_index = 0; sub_index < inputs->values[input_index].buf_count;
sub_index++) {
if (!link_input_to_texture(program, &inputs->values[input_index],
input_index, sub_index, reload)) {
return false;
}
}
program->input_tex_map[input_index] = tex_i;
program->tex_map[tex_i] =
program->tex_count + input_index * program->sub_video_count;
} else {
log_warn("Cannot link input %d", i + 1);
log_warn("Cannot link input %d", input_index + 1);
}
return true;
}
@@ -489,13 +536,6 @@ static bool init_single_program(ShaderProgram *program, unsigned int i,
glGetUniformLocation(program->programs[i], name);
}
prefix = config_file_get_str(config, "UNIFORM_IN_SWAP_PREFIX", "iInputSwap");
for (unsigned int j = 0; j < program->in_count; j++) {
snprintf(name, STR_LEN, "%s%d", prefix, j + 1);
program->iinswap_locations[i * program->in_count + j] =
glGetUniformLocation(program->programs[i], name);
}
prefix = config_file_get_str(config, "UNIFORM_SEED_PREFIX", "iSeed");
for (unsigned int j = 0; j < program->frag_count; j++) {
snprintf(name, STR_LEN, "%s%d", prefix, j + 1);
@@ -519,23 +559,24 @@ static bool init_single_program(ShaderProgram *program, unsigned int i,
if (program->midi_lengths.length == 0) {
index1 = 0;
for (unsigned int j = 0; j < state_config->midi_active_counts.length; j++) {
for (unsigned int k = 0; k < state_config->midi_active_counts.values[j];
for (unsigned int j = 0; j < state_config->group_active_counts.length;
j++) {
for (unsigned int k = 0; k < state_config->group_active_counts.values[j];
k++) {
program->midi_lengths.values[index1++] =
state_config->midi_counts.values[j];
state_config->group_counts.values[j];
}
}
program->midi_lengths.length = index1;
}
prefix = config_file_get_str(config, "UNIFORM_MIDI_PREFIX", "iMidi");
prefix = config_file_get_str(config, "UNIFORM_GROUP_PREFIX", "iGroup");
index2 = 0;
for (unsigned int j = 0; j < state_config->midi_active_counts.length; j++) {
for (unsigned int k = 0; k < state_config->midi_active_counts.values[j];
for (unsigned int j = 0; j < state_config->group_active_counts.length; j++) {
for (unsigned int k = 0; k < state_config->group_active_counts.values[j];
k++) {
snprintf(name, STR_LEN, "%s%d_%d", prefix, j + 1, k + 1);
program->imidi_locations[i * program->midi_lengths.length + index2++] =
program->igroup_locations[i * program->midi_lengths.length + index2++] =
glGetUniformLocation(program->programs[i], name);
}
}
@@ -592,8 +633,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]) {
@@ -604,8 +644,7 @@ static void update_viewport(ShaderProgram *program,
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, context->tex_resolution[0],
context->tex_resolution[1], 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
check_glerror_ro(
"update_viewport/glTexImage2D"); // TODO dont remap input textures
check_glerror_ro("update_viewport/glTexImage2D");
}
program->last_resolution[0] = context->resolution[0];
program->last_resolution[1] = context->resolution[1];
@@ -638,7 +677,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;
@@ -681,17 +720,19 @@ 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);
write_uniform_1i(program->iinswap_locations[i * program->in_count + j],
context->inputs.values[j].swap ? 1 : 0);
context->input_fps[j]);
}
#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],
@@ -706,7 +747,7 @@ static void use_program(const ShaderProgram *program, int i, bool output,
offset = 0;
for (unsigned int j = 0; j < program->midi_lengths.length; j++) {
write_uniform_multi_3f(
program->imidi_locations[i * program->midi_lengths.length + j],
program->igroup_locations[i * program->midi_lengths.length + j],
program->midi_lengths.values[j], context->values + offset);
offset += program->midi_lengths.values[j];
}
@@ -733,7 +774,7 @@ static void use_program(const ShaderProgram *program, int i, bool output,
// set GL_TEXTURE(X) to uniform sampler2D texX
for (unsigned int j = 0; j < program->tex_count; j++) {
write_uniform_1i(program->textures_locations[i * program->tex_count + j],
j);
program->tex_map[j]);
}
// draw output
@@ -741,12 +782,17 @@ 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) {
unsigned int i;
if (!rebind) {
program->error = false;
program->last_resolution[0] = context->resolution[0];
program->last_resolution[1] = context->resolution[1];
program->tex_count = config_file_get_int(&project->config, "TEX_COUNT", 9);
memset(program->tex_map, 0, sizeof(program->tex_map));
for (i = 0; i < program->tex_count; i++) {
program->tex_map[i] = i;
}
program->frag_count = project->frag_count;
program->frag_output_index =
config_file_get_int(&project->config, "FRAG_OUTPUT", 1) - 1;
@@ -756,12 +802,12 @@ void shaders_init(ShaderProgram *program, const Project *project,
config_file_get_int(&project->config, "SUB_TYPE_COUNT", 0);
program->in_count = config_file_get_int(&project->config, "IN_COUNT", 0);
program->sub_variant_count = project->state_config.state_max;
program->active_count = project->state_config.midi_active_counts.length;
program->active_count = project->state_config.group_active_counts.length;
program->midi_lengths.length = 0;
#ifdef VIDEO_IN
memset(program->dma_images, 0, sizeof(program->dma_images));
memset(program->dma_images_swap, 0, sizeof(program->dma_images_swap));
memset(program->input_tex_map, 0, sizeof(program->input_tex_map));
#endif /* VIDEO_IN */
if (!init_gl(program)) {
@@ -794,23 +840,55 @@ void shaders_init(ShaderProgram *program, const Project *project,
}
}
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_relink_input(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs, unsigned int input_index) {
shaders_free_input(program, input_index);
init_input(program, &project->config, inputs, input_index, true);
}
void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs) {
#ifdef VIDEO_IN
for (unsigned int i = 0; i < program->in_count; i++) {
VideoCaptureArray *inputs, unsigned int buffer_count) {
unsigned int i;
program->sub_video_count = buffer_count;
for (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) {
unsigned int sub_index;
for (sub_index = 0; sub_index < program->sub_video_count; sub_index++) {
if (program->dma_images[input_index * program->sub_video_count +
sub_index] != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(
program->egl_display,
program
->dma_images[input_index * program->sub_video_count + sub_index]);
program->dma_images[input_index * program->sub_video_count + sub_index] =
EGL_NO_IMAGE_KHR;
}
}
check_eglerror_ro("shaders_free_input/eglDestroyImageKHR");
}
static void update_inputs_tex_map(ShaderProgram *program,
const Context *context) {
unsigned int i;
unsigned int tex_i;
for (i = 0; i < program->in_count; i++) {
if (context->input_formats[i] != 0) {
tex_i = program->input_tex_map[i];
program->tex_map[tex_i] = program->tex_count +
i * program->sub_video_count +
context->input_index[i];
}
}
}
#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,
@@ -821,8 +899,12 @@ 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) {
#ifdef VIDEO_IN
update_inputs_tex_map(program, context);
#endif /* VIDEO_IN */
if (!output_only) {
glBindVertexArray(program->vertex_array[0]);
@@ -859,6 +941,12 @@ void shaders_free(const ShaderProgram *program) {
glDeleteTextures(program->tex_count, program->textures);
check_glerror_ro("shaders_free/glDeleteTextures");
#ifdef VIDEO_IN
glDeleteTextures(program->in_count * program->sub_video_count,
program->textures + program->tex_count);
check_glerror_ro("shaders_free/glDeleteTextures");
#endif
glDeleteBuffers(1, &program->vertex_buffer);
check_glerror_ro("shaders_free/glDeleteBuffers");
}
@@ -867,18 +955,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 */
}
+9 -5
View File
@@ -4,24 +4,28 @@
#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);
void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs);
VideoCaptureArray *inputs, unsigned int buffer_count);
void shaders_free_input(ShaderProgram *program, unsigned int input_index);
#endif /* VIDEO_IN */
void shaders_update(ShaderProgram *program, const File *fragment_shader,
unsigned int i, const Project *project);
void shaders_compute(ShaderProgram *program, const SharedContext *context,
void shaders_compute(ShaderProgram *program, const Context *context,
bool monitor, bool output_only);
void shaders_free(const ShaderProgram *program);
void shaders_free_window(const ShaderProgram *program, bool secondary);
void shaders_free_input(ShaderProgram *program, unsigned int input_index);
#endif /* SHADERS_H */
-35
View File
@@ -1,35 +0,0 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "types.h"
#include "shared.h"
static void *open_shared(const char *key, size_t size, int *fd) {
*fd = shm_open(key, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(*fd, size);
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
}
static void close_shared(void *shared, size_t size, int fd) {
munmap(shared, size);
close(fd);
}
SharedContext *shared_init_context(const char *key) {
int shared_fd;
SharedContext *shared;
shared = open_shared(key, sizeof(SharedContext), &shared_fd);
shared->fd = shared_fd;
return shared;
}
void shared_close_context(SharedContext *shared) {
close_shared(shared, sizeof(SharedContext), shared->fd);
}
-9
View File
@@ -1,9 +0,0 @@
#include "types.h"
#ifndef SHARED_H
#define SHARED_H
SharedContext *shared_init_context(const char *key);
void shared_close_context(SharedContext *shared);
#endif /* SHARED_H */
+284 -261
View File
@@ -1,6 +1,6 @@
#include <log.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
@@ -13,97 +13,93 @@
#include "state.h"
#include "tempo.h"
static void safe_midi_write(const MidiDevice *midi, unsigned int code,
static void safe_midi_write(MidiDevice midi, unsigned int code,
unsigned char value) {
if (code != UNSET_MIDI_CODE) {
if (code != UNSET_MIDI_CODE && code < 1000) {
midi_write(midi, code, value);
}
}
static void update_page(const SharedContext *context,
const StateConfig *state_config,
const MidiDevice *midi) {
static void update_page(const Context *context, StateConfig state_config,
MidiDevice midi) {
unsigned int page_item_min;
unsigned int page_item_max;
// SHOW PAGE
for (unsigned int i = 0; i < state_config->select_page_codes.length; i++) {
safe_midi_write(midi, state_config->select_page_codes.values[i],
for (unsigned int i = 0; i < state_config.select_page_codes.length; i++) {
safe_midi_write(midi, state_config.select_page_codes.values[i],
i == context->page ? MIDI_MAX : 0);
}
// SHOW PAGE ITEM
page_item_min = state_config->select_item_codes.length * context->page;
page_item_max = page_item_min + state_config->select_item_codes.length;
page_item_min = state_config.select_item_codes.length * context->page;
page_item_max = page_item_min + state_config.select_item_codes.length;
if (context->state.values[context->selected] >= page_item_min &&
context->state.values[context->selected] < page_item_max) {
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) {
safe_midi_write(midi, state_config->select_item_codes.values[i],
for (unsigned int i = 0; i < state_config.select_item_codes.length; i++) {
safe_midi_write(midi, state_config.select_item_codes.values[i],
i == context->state.values[context->selected] -
page_item_min
? MIDI_MAX
: 0);
}
} else {
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) {
safe_midi_write(midi, state_config->select_item_codes.values[i], 0);
for (unsigned int i = 0; i < state_config.select_item_codes.length; i++) {
safe_midi_write(midi, state_config.select_item_codes.values[i], 0);
}
}
}
static void update_active(const SharedContext *context,
const StateConfig *state_config,
const MidiDevice *midi) {
static void update_active(const Context *context, StateConfig state_config,
MidiDevice midi, bool beat_active) {
unsigned int k;
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
for (unsigned int j = 0; j < state_config->midi_active_counts.values[i];
for (unsigned int i = 0; i < state_config.group_active_counts.length; i++) {
for (unsigned int j = 0; j < state_config.group_active_counts.values[i];
j++) {
k = state_config->midi_active_offsets.values[i] + j;
safe_midi_write(midi, state_config->midi_active_codes.values[k],
context->active[i] == j ? MIDI_MAX : 0);
k = state_config.group_active_offsets.values[i] + j;
safe_midi_write(midi, state_config.group_active_codes.values[k],
context->active[i] == j && beat_active ? MIDI_MAX : 0);
}
}
}
static void update_values(const SharedContext *context,
const StateConfig *state_config,
const MidiDevice *midi) {
static void update_values(const Context *context, StateConfig state_config,
MidiDevice midi) {
unsigned int j;
unsigned int k;
unsigned int part;
for (unsigned int i = 0; i < state_config->midi_codes.length; i++) {
for (unsigned int i = 0; i < state_config.codes.length; i++) {
j = i / 3;
part = arr_uint_remap_index(state_config->midi_offsets, &j);
k = state_config->values_offsets.values[part] +
context->active[part] * state_config->midi_counts.values[part] + j;
safe_midi_write(midi, state_config->midi_codes.values[i],
part = arr_uint_remap_index(state_config.group_offsets, &j);
k = state_config.values_offsets.values[part] +
context->active[part] * state_config.group_counts.values[part] + j;
safe_midi_write(midi, state_config.codes.values[i],
context->values[k][i % 3] * MIDI_MAX);
}
}
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, StateConfig state_config) {
unsigned int j;
unsigned int l;
unsigned int part;
for (unsigned int i = 0; i < state_config->midi_codes.length; i++) {
for (unsigned int i = 0; i < state_config.codes.length; i++) {
j = i / 3;
part = arr_uint_remap_index(state_config->midi_offsets, &j);
for (unsigned int k = 0; k < state_config->midi_active_counts.values[part];
part = arr_uint_remap_index(state_config.group_offsets, &j);
for (unsigned int k = 0; k < state_config.group_active_counts.values[part];
k++) {
l = state_config->values_offsets.values[part] +
k * state_config->midi_counts.values[part] + j;
l = state_config.values_offsets.values[part] +
k * state_config.group_counts.values[part] + j;
if (arr_uint_index_of(state_config->fader_codes,
state_config->midi_codes.values[i]) !=
ARRAY_NOT_FOUND) {
if (arr_uint_index_of(state_config.fader_codes,
state_config.codes.values[i]) != ARRAY_NOT_FOUND) {
context->values[l][i % 3] = (float)rand_uint(MIDI_MAX + 1) / MIDI_MAX;
} else {
context->values[l][i % 3] = rand_uint(2) == 1 ? 1 : 0;
@@ -112,12 +108,11 @@ static void randomize(SharedContext *context, const StateConfig *state_config) {
}
for (unsigned int i = 0; i < context->state.length; i++) {
context->state.values[i] = rand_uint(state_config->state_max);
context->state.values[i] = rand_uint(state_config.state_max);
}
}
static void load_from_file(SharedContext *context,
const StateConfig *state_config,
static void load_from_file(Context *context, StateConfig state_config,
const char *state_file) {
ConfigFile saved_state;
char key[STR_LEN];
@@ -141,12 +136,12 @@ static void load_from_file(SharedContext *context,
context->state.values[i] = config_file_get_int(&saved_state, key, 0);
}
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
for (unsigned int i = 0; i < state_config.group_active_counts.length; i++) {
snprintf(key, STR_LEN, "active_%d", i);
context->active[i] = config_file_get_int(&saved_state, key, 0);
}
for (unsigned int i = 0; i < state_config->value_count; i++) {
for (unsigned int i = 0; i < state_config.value_count; i++) {
snprintf(key, STR_LEN, "value_%d_x", i);
context->values[i][0] =
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
@@ -161,28 +156,25 @@ static void load_from_file(SharedContext *context,
config_file_free(&saved_state);
}
static void load_from_default_file(SharedContext *context,
const StateConfig *state_config) {
static void load_from_default_file(Context *context, StateConfig state_config) {
char state_file[STR_LEN];
snprintf(state_file, STR_LEN, "%s.txt", state_config->save_file_prefix);
snprintf(state_file, STR_LEN, "%s.txt", state_config.save_file_prefix);
load_from_file(context, state_config, state_file);
}
static void load_from_index_file(SharedContext *context,
const StateConfig *state_config,
static void load_from_index_file(Context *context, StateConfig state_config,
unsigned int index) {
char state_file[STR_LEN];
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config->save_file_prefix,
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config.save_file_prefix,
index);
load_from_file(context, state_config, state_file);
}
static void save_to_file(const SharedContext *context,
const StateConfig *state_config,
static void save_to_file(const Context *context, StateConfig state_config,
const char *state_file) {
StringArray lines;
@@ -203,12 +195,12 @@ static void save_to_file(const SharedContext *context,
context->state.values[i]);
}
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
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]);
}
for (unsigned int i = 0; i < state_config->value_count; i++) {
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));
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_y=%d", i,
@@ -220,21 +212,20 @@ static void save_to_file(const SharedContext *context,
file_write(state_file, &lines);
}
static void save_to_default_file(const SharedContext *context,
const StateConfig *state_config) {
static void save_to_default_file(const Context *context,
StateConfig state_config) {
char state_file[STR_LEN];
snprintf(state_file, STR_LEN, "%s.txt", state_config->save_file_prefix);
snprintf(state_file, STR_LEN, "%s.txt", state_config.save_file_prefix);
save_to_file(context, state_config, state_file);
}
static void save_to_index_file(const SharedContext *context,
const StateConfig *state_config,
static void save_to_index_file(const Context *context, StateConfig state_config,
unsigned int index) {
char state_file[STR_LEN];
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config->save_file_prefix,
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config.save_file_prefix,
index);
save_to_file(context, state_config, state_file);
@@ -275,62 +266,62 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
config_file_get_int(config, name, UNSET_MIDI_CODE);
}
state_config->midi_active_counts.length = state_config->midi_counts.length =
state_config->midi_active_offsets.length =
state_config->midi_offsets.length =
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, "MIDI_COUNT", 0);
config_file_get_int(config, "GROUP_COUNT", 0);
count = 0;
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
snprintf(name, STR_LEN, "MIDI_%d_ACTIVE_COUNT", i + 1);
state_config->midi_active_counts.values[i] =
for (unsigned int i = 0; i < state_config->group_active_counts.length; i++) {
snprintf(name, STR_LEN, "GROUP_%d_ACTIVE_COUNT", i + 1);
state_config->group_active_counts.values[i] =
config_file_get_int(config, name, 1);
state_config->midi_active_offsets.values[i] = count;
count += state_config->midi_active_counts.values[i];
state_config->group_active_offsets.values[i] = count;
count += state_config->group_active_counts.values[i];
}
state_config->midi_active_codes.length = count;
state_config->group_active_codes.length = count;
for (unsigned int i = 0; i < state_config->midi_active_counts.length; i++) {
for (unsigned int j = 0; j < state_config->midi_active_counts.values[i];
for (unsigned int i = 0; i < state_config->group_active_counts.length; i++) {
for (unsigned int j = 0; j < state_config->group_active_counts.values[i];
j++) {
snprintf(name, STR_LEN, "MIDI_%d_ACTIVE_%d", i + 1, j + 1);
state_config->midi_active_codes
.values[state_config->midi_active_offsets.values[i] + j] =
snprintf(name, STR_LEN, "GROUP_%d_ACTIVE_%d", i + 1, j + 1);
state_config->group_active_codes
.values[state_config->group_active_offsets.values[i] + j] =
config_file_get_int(config, name, UNSET_MIDI_CODE);
}
}
count = 0;
offset = 0;
for (unsigned int i = 0; i < state_config->midi_counts.length; i++) {
snprintf(name, STR_LEN, "MIDI_%d_COUNT", i + 1);
state_config->midi_counts.values[i] = config_file_get_int(config, name, 0);
state_config->midi_offsets.values[i] = count;
for (unsigned int i = 0; i < state_config->group_counts.length; i++) {
snprintf(name, STR_LEN, "GROUP_%d_COUNT", i + 1);
state_config->group_counts.values[i] = config_file_get_int(config, name, 0);
state_config->group_offsets.values[i] = count;
state_config->values_offsets.values[i] = offset;
offset += state_config->midi_counts.values[i] *
state_config->midi_active_counts.values[i];
count += state_config->midi_counts.values[i];
offset += state_config->group_counts.values[i] *
state_config->group_active_counts.values[i];
count += state_config->group_counts.values[i];
}
state_config->value_count = offset;
state_config->midi_codes.length = count * 3;
state_config->codes.length = count * 3;
for (unsigned int i = 0; i < state_config->midi_counts.length; i++) {
offset = state_config->midi_offsets.values[i];
for (unsigned int j = 0; j < state_config->midi_counts.values[i]; j++) {
snprintf(name, STR_LEN, "MIDI_%d_%d_X", i + 1, j + 1);
state_config->midi_codes.values[(offset + j) * 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);
snprintf(name, STR_LEN, "MIDI_%d_%d_Y", i + 1, j + 1);
state_config->midi_codes.values[(offset + j) * 3 + 1] =
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, "MIDI_%d_%d_Z", i + 1, j + 1);
state_config->midi_codes.values[(offset + j) * 3 + 2] =
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);
}
}
@@ -351,244 +342,276 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
config_file_get_str(config, "SAVE_FILE_PREFIX", "forge_save"),
STR_LEN);
state_config->hotkey_randomize =
config_file_get_int(config, "HOTKEY_RANDOMIZE", 82);
state_config->hotkey_reset =
config_file_get_int(config, "HOTKEY_RESET", 1082);
state_config->hotkey_demo = config_file_get_int(config, "HOTKEY_DEMO", 68);
state_config->hotkey_autorand =
config_file_get_int(config, "HOTKEY_AUTORAND", 65);
state_config->hotkey_autorand_down =
config_file_get_int(config, "HOTKEY_AUTORAND_DOWN", 263);
state_config->hotkey_autorand_up =
config_file_get_int(config, "HOTKEY_AUTORAND_UP", 262);
state_config->hotkey_tempo_down =
config_file_get_int(config, "HOTKEY_TEMPO_DOWN", 264);
state_config->hotkey_tempo_up =
config_file_get_int(config, "HOTKEY_TEMPO_UP", 265);
state_config->key_randomize =
config_file_get_int(config, "KEY_RANDOMIZE", 1082);
state_config->key_reset = config_file_get_int(config, "KEY_RESET", 11082);
state_config->key_demo = config_file_get_int(config, "KEY_DEMO", 1068);
state_config->key_autorand =
config_file_get_int(config, "KEY_AUTORAND", 1065);
state_config->key_autorand_down =
config_file_get_int(config, "KEY_AUTORAND_DOWN", 1263);
state_config->key_autorand_up =
config_file_get_int(config, "KEY_AUTORAND_UP", 1262);
state_config->key_tempo_down =
config_file_get_int(config, "KEY_TEMPO_DOWN", 1264);
state_config->key_tempo_up =
config_file_get_int(config, "KEY_TEMPO_UP", 1265);
if (config_file_has(config, "HOTKEY_LOAD_COUNT")) {
state_config->hotkey_load.length =
config_file_get_int(config, "HOTKEY_LOAD_COUNT", 0);
if (config_file_has(config, "KEY_LOAD_COUNT")) {
state_config->key_load.length =
config_file_get_int(config, "KEY_LOAD_COUNT", 0);
for (unsigned int i = 0; i < state_config->hotkey_load.length; i++) {
snprintf(name, STR_LEN, "HOTKEY_LOAD_%d", i + 1);
state_config->hotkey_load.values[i] =
config_file_get_int(config, name, 0);
for (unsigned int i = 0; i < state_config->key_load.length; i++) {
snprintf(name, STR_LEN, "KEY_LOAD_%d", i + 1);
state_config->key_load.values[i] = config_file_get_int(config, name, 0);
}
} else {
state_config->hotkey_load.length = 10;
state_config->hotkey_load.values[0] = 49;
state_config->hotkey_load.values[1] = 50;
state_config->hotkey_load.values[2] = 51;
state_config->hotkey_load.values[3] = 52;
state_config->hotkey_load.values[4] = 53;
state_config->hotkey_load.values[5] = 54;
state_config->hotkey_load.values[6] = 55;
state_config->hotkey_load.values[7] = 56;
state_config->hotkey_load.values[8] = 57;
state_config->hotkey_load.values[9] = 48;
state_config->key_load.length = 10;
state_config->key_load.values[0] = 1049;
state_config->key_load.values[1] = 1050;
state_config->key_load.values[2] = 1051;
state_config->key_load.values[3] = 1052;
state_config->key_load.values[4] = 1053;
state_config->key_load.values[5] = 1054;
state_config->key_load.values[6] = 1055;
state_config->key_load.values[7] = 1056;
state_config->key_load.values[8] = 1057;
state_config->key_load.values[9] = 1048;
}
if (config_file_has(config, "HOTKEY_SAVE_COUNT")) {
state_config->hotkey_save.length =
config_file_get_int(config, "HOTKEY_SAVE_COUNT", 0);
if (config_file_has(config, "KEY_SAVE_COUNT")) {
state_config->key_save.length =
config_file_get_int(config, "KEY_SAVE_COUNT", 0);
for (unsigned int i = 0; i < state_config->hotkey_save.length; i++) {
snprintf(name, STR_LEN, "HOTKEY_SAVE_%d", i + 1);
state_config->hotkey_save.values[i] =
config_file_get_int(config, name, 0);
for (unsigned int i = 0; i < state_config->key_save.length; i++) {
snprintf(name, STR_LEN, "KEY_SAVE_%d", i + 1);
state_config->key_save.values[i] = config_file_get_int(config, name, 0);
}
} else {
state_config->hotkey_save.length = 10;
state_config->hotkey_save.values[0] = 1049;
state_config->hotkey_save.values[1] = 1050;
state_config->hotkey_save.values[2] = 1051;
state_config->hotkey_save.values[3] = 1052;
state_config->hotkey_save.values[4] = 1053;
state_config->hotkey_save.values[5] = 1054;
state_config->hotkey_save.values[6] = 1055;
state_config->hotkey_save.values[7] = 1056;
state_config->hotkey_save.values[8] = 1057;
state_config->hotkey_save.values[9] = 1048;
state_config->key_save.length = 10;
state_config->key_save.values[0] = 11049;
state_config->key_save.values[1] = 11050;
state_config->key_save.values[2] = 11051;
state_config->key_save.values[3] = 11052;
state_config->key_save.values[4] = 11053;
state_config->key_save.values[5] = 11054;
state_config->key_save.values[6] = 11055;
state_config->key_save.values[7] = 11056;
state_config->key_save.values[8] = 11057;
state_config->key_save.values[9] = 11048;
}
}
void state_midi_event(SharedContext *context, const StateConfig *state_config,
const MidiDevice *midi, unsigned char code,
unsigned char value, bool trace_midi) {
static bool compute_event(Context *context, StateConfig state_config,
MidiDevice midi, unsigned int code,
unsigned int value) {
unsigned int i;
unsigned int j;
unsigned int k;
unsigned int part;
bool found;
found = false;
// PAGE CHANGE
i = arr_uint_index_of(state_config->select_page_codes, code);
i = arr_uint_index_of(state_config.select_page_codes, code);
if (i != ARRAY_NOT_FOUND) {
found = true;
if (value > 0) {
context->page = i;
update_page(context, state_config, midi);
}
return true;
}
// TARGET CHANGE
i = arr_uint_index_of(state_config->select_frag_codes, code);
i = arr_uint_index_of(state_config.select_frag_codes, code);
if (i != ARRAY_NOT_FOUND) {
found = true;
if (value > 0) {
context->selected = i;
update_page(context, state_config, midi);
}
return true;
}
// ITEM CHANGE
i = arr_uint_index_of(state_config->select_item_codes, code);
i = arr_uint_index_of(state_config.select_item_codes, code);
if (i != ARRAY_NOT_FOUND) {
found = true;
if (value > 0) {
context->state.values[context->selected] =
context->page * state_config->select_item_codes.length + i;
context->page * state_config.select_item_codes.length + i;
update_page(context, state_config, midi);
}
return true;
}
// ACTIVE CHANGE
i = arr_uint_index_of(state_config->midi_active_codes, code);
i = arr_uint_index_of(state_config.group_active_codes, code);
if (i != ARRAY_NOT_FOUND) {
found = true;
if (value > 0) {
part = arr_uint_remap_index(state_config->midi_active_offsets, &i);
part = arr_uint_remap_index(state_config.group_active_offsets, &i);
context->active[part] = i;
update_active(context, state_config, midi);
update_active(context, state_config, midi, true);
update_values(context, state_config, midi);
}
return true;
}
// VALUE CHANGE
i = arr_uint_index_of(state_config->midi_codes, code);
i = arr_uint_index_of(state_config.codes, code);
if (i != ARRAY_NOT_FOUND) {
found = true;
j = i / 3;
part = arr_uint_remap_index(state_config->midi_offsets, &j);
k = state_config->values_offsets.values[part] +
context->active[part] * state_config->midi_counts.values[part] + j;
part = arr_uint_remap_index(state_config.group_offsets, &j);
k = state_config.values_offsets.values[part] +
context->active[part] * state_config.group_counts.values[part] + j;
if (arr_uint_index_of(state_config->fader_codes, code) != ARRAY_NOT_FOUND) {
if (arr_uint_index_of(state_config.fader_codes, code) != ARRAY_NOT_FOUND) {
context->values[k][i % 3] = (float)value / MIDI_MAX;
} else if (value > 0) {
if (context->values[k][i % 3] > 0.5) {
context->values[k][i % 3] = 0;
midi_write(midi, code, 0);
safe_midi_write(midi, code, 0);
} else {
context->values[k][i % 3] = 1;
midi_write(midi, code, MIDI_MAX);
safe_midi_write(midi, code, MIDI_MAX);
}
}
return true;
}
if (code == state_config->tap_tempo_code) {
found = true;
midi_write(midi, code, value);
// TAP TEMPO
if (code == state_config.tap_tempo_code) {
safe_midi_write(midi, code, value);
if (value > 0) {
tempo_tap(&context->tempo);
}
return true;
}
if (!found) {
if (trace_midi) {
log_trace("unknown midi: %d %d", code, value);
// OTHER KEYS
if (code == state_config.key_randomize) {
if (value > 0) {
log_info("[%d] Randomized", code);
randomize(context, state_config);
update_values(context, state_config, midi);
}
midi_write(midi, code, value);
return true;
}
if (code == state_config.key_reset) {
if (value > 0) {
log_info("[%d] Reset", code);
reset(context);
update_values(context, state_config, midi);
}
return true;
}
if (code == state_config.key_demo) {
if (value > 0) {
log_info((context->demo ? "[%d] Demo OFF" : "[%d] Demo ON"), code);
context->demo = !context->demo;
}
return true;
}
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;
}
return true;
}
if (code == state_config.key_autorand_down) {
if (value > 0) {
if (context->auto_random_cycle > 1) {
context->auto_random_cycle -= 1;
}
log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
}
return true;
}
if (code == state_config.key_autorand_up) {
if (value > 0) {
context->auto_random_cycle += 1;
log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
}
return true;
}
if (code == state_config.key_tempo_up) {
if (value > 0) {
tempo_set(&context->tempo, context->tempo.tempo + 1);
log_info("[%d] Tempo: %f", code, context->tempo);
}
return true;
}
if (code == state_config.key_tempo_down) {
if (value > 0) {
if (context->tempo.tempo > 0) {
tempo_set(&context->tempo, context->tempo.tempo - 1);
}
log_info("[%d] Tempo: %f", code, context->tempo);
}
return true;
}
// LOAD STATE
i = arr_uint_index_of(state_config.key_load, code);
if (i != ARRAY_NOT_FOUND) {
if (value > 0) {
log_info("[%d] Loading state %d", code, i + 1);
load_from_index_file(context, state_config, i + 1);
}
return true;
}
// SAVE STATE
i = arr_uint_index_of(state_config.key_save, code);
if (i != ARRAY_NOT_FOUND) {
if (value > 0) {
log_info("[%d] Saving state %d", code, i + 1);
save_to_index_file(context, state_config, i + 1);
}
return true;
}
return false;
}
void state_midi_event(Context *context, StateConfig state_config,
MidiDevice midi, unsigned char code, unsigned char value,
bool trace_midi) {
if (!compute_event(context, state_config, midi, code, value)) {
log_warn("unknown midi: %d %d", code, value);
safe_midi_write(midi, code, value);
} else if (trace_midi) {
log_trace("midi: %d %d", code, value);
}
}
void state_key_event(SharedContext *context, const StateConfig *state_config,
unsigned int code, const MidiDevice *midi) {
unsigned int index;
if (code == state_config->hotkey_randomize) {
log_info("[%d] Randomized", code);
randomize(context, state_config);
update_values(context, state_config, midi);
} else if (code == state_config->hotkey_reset) {
log_info("[%d] Reset", code);
reset(context);
update_values(context, state_config, midi);
} else if (code == state_config->hotkey_demo) {
log_info((context->demo ? "[%d] Demo OFF" : "[%d] Demo ON"), code);
context->demo = !context->demo;
} else if (code == state_config->hotkey_autorand) {
log_info(
(context->auto_random ? "[%d] Auto Random OFF" : "[%d] Auto Random ON"),
code);
context->auto_random = !context->auto_random;
} else if (code == state_config->hotkey_autorand_down) {
if (context->auto_random_cycle > 1) {
context->auto_random_cycle -= 1;
}
log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
} else if (code == state_config->hotkey_autorand_up) {
context->auto_random_cycle += 1;
log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
} else if (code == state_config->hotkey_tempo_up) {
tempo_set(&context->tempo, context->tempo.tempo + 1);
log_info("[%d] Tempo: %f", code, context->tempo);
} else if (code == state_config->hotkey_tempo_down) {
if (context->tempo.tempo > 0) {
tempo_set(&context->tempo, context->tempo.tempo - 1);
}
log_info("[%d] Tempo: %f", code, context->tempo);
} else {
index = arr_uint_index_of(state_config->hotkey_load, code);
if (index != ARRAY_NOT_FOUND) {
log_info("[%d] Loading state %d", code, index + 1);
load_from_index_file(context, state_config, index + 1);
return;
}
index = arr_uint_index_of(state_config->hotkey_save, code);
if (index != ARRAY_NOT_FOUND) {
log_info("[%d] Saving state %d", code, index + 1);
save_to_index_file(context, state_config, index + 1);
return;
}
log_info("[%d] No hotkey defined", code);
void state_key_event(Context *context, StateConfig state_config,
unsigned int code, MidiDevice midi) {
if (!compute_event(context, state_config, midi, code, MIDI_MAX)) {
log_warn("[%d] No hotkey defined", code);
}
}
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);
if (!midi->error) {
update_page(context, state_config, midi);
update_active(context, state_config, midi);
update_values(context, state_config, midi);
}
log_info("(state) background writing started");
last_active = false;
last_change = false;
@@ -596,12 +619,12 @@ bool state_background_write(SharedContext *context,
while (!context->stop) {
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,
beat_active ? MIDI_MAX : 0);
if (midi->connected && beat_active != last_active) {
update_values(context, state_config, *midi);
update_page(context, state_config, *midi);
update_active(context, state_config, *midi, beat_active);
safe_midi_write(midi,
state_config->select_frag_codes.values[context->selected],
safe_midi_write(*midi, state_config.tap_tempo_code,
beat_active ? MIDI_MAX : 0);
}
@@ -613,25 +636,25 @@ bool state_background_write(SharedContext *context,
if (context->auto_random && change && !last_change) {
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, 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;
context->auto_random = auto_random;
context->auto_random_cycle = auto_random_cycles;
context->state.length = state_config->select_frag_codes.length;
context->state.length = state_config.select_frag_codes.length;
memset(context->state.values, 0, sizeof(context->state.values));
if (auto_random) {
@@ -655,6 +678,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, StateConfig state_config) {
save_to_default_file(context, state_config);
}
+8 -10
View File
@@ -5,21 +5,19 @@
void state_parse_config(StateConfig *state_config, const ConfigFile *config);
void state_midi_event(SharedContext *context, const StateConfig *state_config,
const MidiDevice *midi, unsigned char code,
void state_midi_event(Context *context, StateConfig state_config,
MidiDevice midi, unsigned char code,
unsigned char value, bool trace_midi);
void state_key_event(SharedContext *context, const StateConfig *state_config,
unsigned int code, const MidiDevice *midi);
void state_key_event(Context *context, StateConfig state_config,
unsigned int code, 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, 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, StateConfig state_config);
#endif /* STATE_H */
+88 -61
View File
@@ -45,12 +45,16 @@ typedef struct Parameters {
bool demo;
bool auto_random;
unsigned int auto_random_cycle;
#ifdef VIDEO_IN
StringArray video_in;
unsigned int video_buffers;
unsigned int video_size;
unsigned int internal_size;
bool video_reconnect;
#endif /* VIDEO_IN */
unsigned int internal_size;
bool load_state;
bool save_state;
bool midi_reconnect;
bool trace_midi;
bool trace_fps;
} Parameters;
@@ -84,6 +88,7 @@ typedef struct ShaderProgram {
GLuint vertex_array[2];
unsigned int tex_count;
unsigned int tex_map[ARRAY_SIZE];
GLuint textures[ARRAY_SIZE];
unsigned int frag_count;
@@ -104,7 +109,6 @@ typedef struct ShaderProgram {
GLuint iinres_locations[ARRAY_SIZE];
GLuint iinfmt_locations[ARRAY_SIZE];
GLuint iinfps_locations[ARRAY_SIZE];
GLuint iinswap_locations[ARRAY_SIZE];
GLuint idemo_locations[ARRAY_SIZE];
GLuint iautorand_locations[ARRAY_SIZE];
GLuint iautorandcycle_locations[ARRAY_SIZE];
@@ -115,7 +119,7 @@ typedef struct ShaderProgram {
GLuint iactive_locations[ARRAY_SIZE];
UintArray midi_lengths;
GLuint imidi_locations[ARRAY_SIZE];
GLuint igroup_locations[ARRAY_SIZE];
GLuint vpos_locations[ARRAY_SIZE];
@@ -129,40 +133,13 @@ typedef struct ShaderProgram {
unsigned int in_count;
#ifdef VIDEO_IN
unsigned int sub_video_count;
unsigned int input_tex_map[MAX_VIDEO];
EGLDisplay egl_display;
EGLImageKHR dma_images[MAX_VIDEO];
EGLImageKHR dma_images_swap[MAX_VIDEO];
#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 +159,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];
unsigned int input_index[MAX_VIDEO];
#endif /* VIDEO_IN */
double time;
Tempo tempo;
double tempo_total;
@@ -200,9 +182,57 @@ 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;
unsigned int buf_count;
unsigned int buf_index;
int fd;
int exp_fd[ARRAY_SIZE];
unsigned int width;
unsigned int height;
unsigned int pixelformat;
unsigned int bytesperline;
struct v4l2_buffer buf[ARRAY_SIZE];
} 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 {
_Atomic bool connected;
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
@@ -212,12 +242,12 @@ typedef struct StateConfig {
UintArray select_page_codes;
UintArray select_item_codes;
UintArray select_frag_codes;
UintArray midi_active_counts;
UintArray midi_active_offsets;
UintArray midi_active_codes;
UintArray midi_counts;
UintArray midi_offsets;
UintArray midi_codes;
UintArray group_active_counts;
UintArray group_active_offsets;
UintArray group_active_codes;
UintArray group_counts;
UintArray group_offsets;
UintArray codes;
UintArray fader_codes;
UintArray values_offsets;
@@ -227,19 +257,25 @@ typedef struct StateConfig {
char save_file_prefix[STR_LEN];
unsigned int hotkey_randomize;
unsigned int hotkey_reset;
unsigned int hotkey_demo;
unsigned int hotkey_autorand;
unsigned int hotkey_autorand_up;
unsigned int hotkey_autorand_down;
unsigned int hotkey_tempo_up;
unsigned int hotkey_tempo_down;
unsigned int key_randomize;
unsigned int key_reset;
unsigned int key_demo;
unsigned int key_autorand;
unsigned int key_autorand_up;
unsigned int key_autorand_down;
unsigned int key_tempo_up;
unsigned int key_tempo_down;
UintArray hotkey_load;
UintArray hotkey_save;
UintArray key_load;
UintArray key_save;
} StateConfig;
typedef struct StateBackgroundWriteArgs {
Context *context;
StateConfig state_config;
MidiDevice *midi;
} StateBackgroundWriteArgs;
// timer.c
typedef struct Timer {
@@ -260,15 +296,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 {
+53 -62
View File
@@ -4,8 +4,8 @@
#include <fcntl.h>
#include <linux/videodev2.h>
#include <log.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -192,14 +192,15 @@ static bool set_format(VideoCapture *video_capture) {
return true;
}
static bool request_buffers(VideoCapture *video_capture) {
static bool request_buffers(VideoCapture *video_capture,
unsigned int buffer_count) {
struct v4l2_requestbuffers reqbuf;
memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.type = buf_type;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 2;
reqbuf.count = buffer_count;
if (ioctl(video_capture->fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
ioctl_error(video_capture, "VIDIOC_REQBUFS",
@@ -209,7 +210,7 @@ static bool request_buffers(VideoCapture *video_capture) {
log_info("(%s) V4L2 Buffer Count: %d", video_capture->name, reqbuf.count);
video_capture->with_swap = reqbuf.count > 1;
video_capture->buf_count = reqbuf.count;
return true;
}
@@ -239,16 +240,15 @@ static bool export_buffer(VideoCapture *video_capture, int *fd,
}
static bool export_buffers(VideoCapture *video_capture) {
bool result;
unsigned int i;
result = export_buffer(video_capture, &video_capture->exp_fd, 0);
if (result && video_capture->with_swap) {
result =
result && export_buffer(video_capture, &video_capture->exp_fd_swap, 1);
for (i = 0; i < video_capture->buf_count; i++) {
if (!export_buffer(video_capture, &video_capture->exp_fd[i], i)) {
return false;
}
}
return result;
return true;
}
static bool open_stream(VideoCapture *video_capture) {
@@ -274,10 +274,10 @@ static void create_image_buffer(const VideoCapture *video_capture,
}
static void create_image_buffers(VideoCapture *video_capture) {
create_image_buffer(video_capture, &video_capture->buf, 0);
unsigned int i;
if (video_capture->with_swap) {
create_image_buffer(video_capture, &video_capture->buf_swap, 1);
for (i = 0; i < video_capture->buf_count; i++) {
create_image_buffer(video_capture, &video_capture->buf[i], i);
}
}
@@ -286,31 +286,26 @@ static void close_stream(const VideoCapture *video_capture) {
}
static unsigned int read_video(VideoCapture *video_capture) {
unsigned int result;
struct v4l2_capability cap;
result = 0;
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 ((video_capture->swap || !video_capture->with_swap) &&
ioctl(video_capture->fd, VIDIOC_DQBUF, &video_capture->buf) != -1) {
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf);
result = 1;
} else if (!video_capture->swap && video_capture->with_swap &&
ioctl(video_capture->fd, VIDIOC_DQBUF, &video_capture->buf_swap) !=
-1) {
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf_swap);
result = 2;
} else if (ioctl(video_capture->fd, VIDIOC_QUERYCAP, &cap) == -1) {
if (ioctl(video_capture->fd, VIDIOC_QUERYCAP, &cap) == -1) {
video_capture->error = true;
}
return result;
return false;
}
void video_init(VideoCapture *video_capture, const char *name,
unsigned int preferred_height) {
unsigned int preferred_height, unsigned int buffer_count) {
open_device(video_capture, name);
if (video_capture->error) {
@@ -329,7 +324,7 @@ void video_init(VideoCapture *video_capture, const char *name,
return;
}
if (!request_buffers(video_capture)) {
if (!request_buffers(video_capture, buffer_count)) {
return;
}
@@ -344,60 +339,56 @@ 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;
bool 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) {
video_result = read_video(video_capture);
if (video_result > 0 && timer_inc(&timer)) {
result = read_video(video_capture);
if (result && 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;
if (result) {
context->input_index[input_index] = video_capture->buf_index;
}
}
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) {
unsigned int i;
close_stream(video_capture);
if (video_capture->exp_fd != -1) {
close(video_capture->exp_fd);
}
if (video_capture->exp_fd_swap != -1) {
close(video_capture->exp_fd);
for (i = 0; i < video_capture->buf_count; i++) {
close(video_capture->exp_fd[i]);
}
if (video_capture->fd != -1) {
close(video_capture->fd);
}
+7 -4
View File
@@ -3,12 +3,15 @@
#ifndef VIDEO_H
#define VIDEO_H
void video_init(VideoCapture *video_capture, const char *name,
unsigned int preferred_height);
#ifdef VIDEO_IN
bool video_background_read(VideoCapture *video_capture, SharedContext *context,
int input_index, bool trace_fps);
void video_init(VideoCapture *video_capture, const char *name,
unsigned int preferred_height, unsigned int buffer_count);
void *video_background_read(void *args);
void video_free(const VideoCapture *video_capture);
#endif /* VIDEO_IN */
#endif /* VIDEO_H */
+9 -6
View File
@@ -59,6 +59,9 @@ create_window(GLFWmonitor *monitor, const char *title, Window *shared_context,
// Context related hints
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
#ifdef GL_DEBUG
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
#endif
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// create fullscreen window in selected monitor
@@ -120,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;
@@ -154,15 +157,15 @@ unsigned int window_read_key(int key, int action, int mods) {
key == GLFW_KEY_RIGHT_ALT) {
return 0;
}
result = key;
result = 1000 + key;
if ((mods & GLFW_MOD_SHIFT) > 0) {
result += 1000;
}
if ((mods & GLFW_MOD_CONTROL) > 0) {
result += 10000;
}
if ((mods & GLFW_MOD_ALT) > 0) {
if ((mods & GLFW_MOD_CONTROL) > 0) {
result += 100000;
}
if ((mods & GLFW_MOD_ALT) > 0) {
result += 1000000;
}
return result;
}
+1 -1
View File
@@ -15,7 +15,7 @@ void window_update_title(Window *window, const char *title);
double window_get_time();
void window_use(Window *window, SharedContext *context);
void window_use(Window *window, Context *context);
void window_refresh(Window *window);