31 Commits

Author SHA1 Message Date
klemek 97f768f65e fix(video): alternate read for double buffering 2025-11-24 19:11:59 +01:00
klemek c66a5c166e feat: double buffered video input 2025-11-24 18:52:44 +01:00
klemek aa42e9d2aa docs: README update 2025-11-24 00:28:59 +01:00
klemek 3dceb044aa build: check build no video 2025-11-24 00:28:30 +01:00
klemek d19f5d2d81 fix(video): slightly faster video 2025-11-23 19:11:08 +01:00
klemek 1bf0bfc558 fix(build): remove video args if no video 2025-11-23 19:10:57 +01:00
klemek 06544ee23e refactor: more maintainability 2025-11-23 15:55:43 +01:00
klemek 1f5f502905 ci(sonar): add properties file 2025-11-23 15:39:17 +01:00
klemek 7120bc0207 refactor: clean unused variables 2025-11-23 15:36:21 +01:00
klemek c7ae4191d3 fix(video): better fps for interlaced 2025-11-23 15:04:21 +01:00
klemek 85349598c3 fix(default): debug screen fix 2025-11-23 15:04:10 +01:00
klemek 2f83bbb21e style: identifiers separation 2025-11-23 13:06:40 +01:00
klemek 6b4630f255 feat(video): slightly faster video acquisition with O_NONBLOCK 2025-11-23 12:58:49 +01:00
klemek 01266e7823 feat(video): can build without video 2025-11-23 00:45:54 +01:00
klemek d9074c366e fix(args): vi for video-in 2025-11-23 00:27:05 +01:00
klemek 81c7471226 docs: update todo 2025-11-17 14:07:01 +01:00
klemek 31800e3392 fix: better mix value 2025-11-17 14:06:46 +01:00
klemek 69e34b499c refactor: clearer monitor screen 2025-11-17 14:01:34 +01:00
klemek 8a14ad52bf feat: replace src 4 with circuit 2025-11-17 12:14:00 +01:00
klemek 0fe3b067cb refactor: debug improvements 2025-11-17 12:13:49 +01:00
klemek e1219c316c docs: update tiodo list 2025-11-17 12:13:09 +01:00
klemek 2692bb0f9b refactor: move static function on top of files 2025-11-14 11:52:47 +01:00
klemek d094a6c895 feat: hotkeys in config 2025-11-14 11:49:02 +01:00
klemek 4ddb5241b4 feat: load/save state from number hotkeys 2025-11-14 11:02:21 +01:00
klemek f04fe1f5c1 refactor: staticify state local functions 2025-11-14 10:34:43 +01:00
klemek f0c5ecab16 feat: arrow keys to control bpm/cycle 2025-11-14 10:20:55 +01:00
klemek 7739ac8254 refactor: state read event struct 2025-11-14 09:37:29 +01:00
klemek c229b9bc68 feat: --auto-random-cycle 2025-11-14 09:03:23 +01:00
klemek 2ab0947550 fix: don't black out screen in demo 2025-11-13 19:32:48 +01:00
klemek 116ccb0e84 docs: update schema 2025-11-13 09:06:20 +01:00
klemek ce156e4acf docs: add shields.io badges 2025-11-13 00:14:27 +01:00
44 changed files with 1428 additions and 795 deletions
+12 -1
View File
@@ -16,7 +16,18 @@ jobs:
- name: install libs - name: install libs
run: sudo apt install -y libglfw3-dev libgl-dev libv4l-dev libasound2-dev libbsd-dev run: sudo apt install -y libglfw3-dev libgl-dev libv4l-dev libasound2-dev libbsd-dev
- name: gcc - name: gcc
run: mkdir -p build && gcc -v -Wall -Wextra -Werror -Wno-format-truncation src/*.c src/*.h -lglfw -lGL -lm -lasound -lbsd -Iinclude hashmap.c/hashmap.c log.c/src/log.c -DGLFW_INCLUDE_NONE -DGLFW_EXPOSE_NATIVE_EGL -DGLFW_NATIVE_INCLUDE_NONE run: mkdir -p build && gcc -v -Wall -Wextra -Werror -Wno-format-truncation src/*.c src/*.h -lglfw -lGL -lm -lasound -lbsd -Iinclude hashmap.c/hashmap.c log.c/src/log.c -DGLFW_INCLUDE_NONE -DGLFW_EXPOSE_NATIVE_EGL -DGLFW_NATIVE_INCLUDE_NONE -DVIDEO_IN
build-no-video:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- name: install libs
run: sudo apt install -y libglfw3-dev libgl-dev libasound2-dev libbsd-dev
- name: gcc
run: mkdir -p build && gcc -v -Werror src/*.c src/*.h -lglfw -lGL -lm -lasound -lbsd -Iinclude hashmap.c/hashmap.c log.c/src/log.c -DGLFW_INCLUDE_NONE -DGLFW_EXPOSE_NATIVE_EGL -DGLFW_NATIVE_INCLUDE_NONE
build-release: build-release:
needs: lint needs: lint
+1 -1
View File
@@ -24,6 +24,6 @@ pkg
forge-* forge-*
confdeps.* confdeps.*
conftest.* conftest.*
forge_saved_state.txt *.txt
error.glsl error.glsl
draft/ draft/
+15
View File
@@ -0,0 +1,15 @@
# Path to sources
sonar.sources=src
#sonar.exclusions=
#sonar.inclusions=
# Path to tests
#sonar.tests=
#sonar.test.exclusions=
#sonar.test.inclusions=
# Source encoding
sonar.sourceEncoding=UTF-8
# Exclusions for copy-paste detection
#sonar.cpd.exclusions=
+17 -5
View File
@@ -117,12 +117,24 @@ make -f Makefile.dev release-arch
- [x] Printable PDF of default scr/fx - [x] Printable PDF of default scr/fx
- [x] Add NanoKontrol setup file - [x] Add NanoKontrol setup file
- [x] Find and fix opengl errors 0500 ? - [x] Find and fix opengl errors 0500 ?
- [ ] Improvements - [ ] Extra features
- [ ] Record show as text files - [x] `--auto-random-cycle=4`
- [ ] Play from record text file - [x] Arrows (up-down: bpm / left-right: cycle)
- [x] Save states (numkey: load / shift + numkey: save)
- [x] (clean) static functions at top of files
- [x] Configurable key codes
- [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 - [ ] Key codes as inputs
- [ ] Mouse position and scroll as inputs - [ ] Mouse position and scroll as inputs
- [ ] Joystick as input
- [ ] Record show as text files
- [ ] Play from record text file
- [ ] Fixes - [ ] Fixes
- [ ] Try to write NanoKontrol config - [ ] Try to write NanoKontrol config
- [ ] Investigate video device fps loss (bad unregister ?) - [x] Investigate video device fps loss (bad unregister ?)
- explore libv4l directly [github](https://github.com/philips/libv4l) (with `-lv4l2`)
+3 -3
View File
@@ -1,10 +1,10 @@
AUTOMAKE_OPTIONS = foreign subdir-objects -Wall AUTOMAKE_OPTIONS = foreign subdir-objects -Wall
bin_PROGRAMS = forge 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/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_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 -DDATADIR=\"$(datadir)/$(PACKAGE)\" 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 -lbsd forge_LDADD = -lm -lGL -lglfw -lasound -lbsd
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/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
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_yuv.glsl 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) confdir = $(prefix)/share/$(PACKAGE)
conf_DATA = 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_yuv.glsl conf_DATA = 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
+1
View File
@@ -21,6 +21,7 @@ build:
-DGLFW_EXPOSE_NATIVE_EGL \ -DGLFW_EXPOSE_NATIVE_EGL \
-DGLFW_NATIVE_INCLUDE_NONE \ -DGLFW_NATIVE_INCLUDE_NONE \
-DLOG_USE_COLOR \ -DLOG_USE_COLOR \
-DVIDEO_IN \
-o build/$(TARGET) \ -o build/$(TARGET) \
-g -Og -g -Og
+49 -33
View File
@@ -1,5 +1,4 @@
[![CI](https://github.com/klemek/forge-steel/actions/workflows/ci.yml/badge.svg)](https://github.com/klemek/forge-steel/actions/workflows/ci.yml) [![GitHub Release](https://img.shields.io/github/v/release/klemek/forge-steel?style=flat-square)](https://github.com/klemek/forge-steel/releases) [![GitHub Release Date](https://img.shields.io/github/release-date/klemek/forge-steel?style=flat-square)](https://github.com/klemek/forge-steel/releases) [![GitHub last commit](https://img.shields.io/github/last-commit/klemek/forge?style=flat-square)](https://github.com/klemek/forge-steel/commits/master/) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/klemek/forge-steel/ci.yml?style=flat-square)](https://github.com/klemek/forge-steel/actions/workflows/ci.yml) [![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/klemek_forge-steel?server=https%3A%2F%2Fsonarcloud.io&style=flat-square)](https://sonarcloud.io/summary/new_code?id=klemek_forge-steel) ![LOC](https://img.shields.io/badge/LOC-3.8k-blue?style=flat-square)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=klemek_forge-steel&metric=alert_status&token=b68ccdc07773027cc51403bc5712d9364d64febf)](https://sonarcloud.io/summary/new_code?id=klemek_forge-steel)
<!-- omit from toc --> <!-- omit from toc -->
# F.O.R.G.E. (Steel) # F.O.R.G.E. (Steel)
@@ -83,6 +82,7 @@ Here are some pointers if you want to customize your FORGE experience:
- [My nanoKontrol2 is acting strange](#my-nanokontrol2-is-acting-strange) - [My nanoKontrol2 is acting strange](#my-nanokontrol2-is-acting-strange)
- [How do I report a bug?](#how-do-i-report-a-bug) - [How do I report a bug?](#how-do-i-report-a-bug)
- [Help I got low FPS on my video device](#help-i-got-low-fps-on-my-video-device) - [Help I got low FPS on my video device](#help-i-got-low-fps-on-my-video-device)
- [My video feed got strange lines](#my-video-feed-got-strange-lines)
- [How do I change the default project built-in sentences?](#how-do-i-change-the-default-project-built-in-sentences) - [How do I change the default project built-in sentences?](#how-do-i-change-the-default-project-built-in-sentences)
## What is FORGE ? ## What is FORGE ?
@@ -158,43 +158,51 @@ make install
When running, the following hotkeys are available: When running, the following hotkeys are available:
* <kbd>Esc</kbd>: Exit window | Hotkey | Function |
* <kbd>R</kbd>: Randomize internal values | ------ | -------- |
* <kbd>0</kbd>: Reset internal values to 0 | <kbd>Esc</kbd> | Exit FORGE |
* <kbd>D</kbd>: Demo mode On/Off | <kbd>R</kbd> | Randomize internal values |
* <kbd>A</kbd>: Auto Random mode On/Off | <kbd>Shift</kbd> + <kbd>R</kbd> | Reset internal values to 0 |
| <kbd>D</kbd> | Demo mode On/Off |
| <kbd>A</kbd> | Auto Random mode On/Off |
| <kbd>&larr;</kbd> / <kbd>&rarr;</kbd> | Auto Random Cycle -/+ 1 |
| <kbd>&uarr;</kbd> / <kbd>&darr;</kbd> | BPM +/- 1 |
| <kbd>0</kbd>-<kbd>9</kbd> | Load state 0 to 9 |
| <kbd>Shift</kbd> + <kbd>0</kbd>-<kbd>9</kbd> | Save state 0 to 9 |
These are configurable in the [`forge_project.cfg`](#forge_projectcfg).
### CLI arguments ### CLI arguments
```txt ```txt
usage: forge [-h] [-v] [-p=PROJECT_PATH] [-c=CFG_FILE] [-hr] [-s=SCREEN] [-m=SCREEN] [-mo] [-w] [-t=TEMPO] [-d] [-ar / -nar] [-v=FILE] [-vs=SIZE] [-is=SIZE] [-sf=STATE_PATH] [-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] [-is=SIZE] [-ls / -nls] [-ss / -nss] [-tm] [-tf]
Fusion Of Real-time Generative Effects. Fusion Of Real-time Generative Effects.
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
-v, --version print version -v, --version print version
-p, --project forge project directory (default: /usr/share/forge/default) -p, --project forge project directory (default: /usr/share/forge/default)
-c, --config config file name (default: forge_project.cfg) -c, --config config file name (default: forge_project.cfg)
-hr, --hot-reload hot reload of shaders scripts -hr, --hot-reload hot reload of shaders scripts
-s, --screen output screen number (default: primary) -s, --screen output screen number (default: primary)
-m, --monitor monitor screen number (default: none) -m, --monitor monitor screen number (default: none)
-mo, --monitor-only no output screen -mo, --monitor-only no output screen
-w, --windowed not fullscreen -w, --windowed not fullscreen
-t, --tempo base tempo (default: 60) -t, --tempo base tempo (default: 60)
-d, --demo demonstration mode (assume --no-save-state, --no-load-state, --auto-random) -d, --demo demonstration mode (assume --no-save-state, --no-load-state, --auto-random)
-ar, --auto-random randomize state every 4 beats -ar, --auto-random randomize state every cycle (4 beats)
-nar, --no-auto-random do not randomize state (default) -nar, --no-auto-random do not randomize state (default)
-v, --video-in path to video capture device (multiple allowed) -arc, --auto-random-cycle auto random cycle length (default: 4)
-vs, --video-size video capture desired height (default: internal texture height) -vi, --video-in path to video capture device (multiple allowed)
-is, --internal-size internal texture height (default: 720) -vs, --video-size video capture desired height (default: internal texture height)
-sf, --state-file saved state file (default: forge_saved_state.txt) -is, --internal-size internal texture height (default: 720)
-ls, --load-state load saved state (default) -ls, --load-state load saved state (default)
-nls, --no-load-state do not load saved state -nls, --no-load-state do not load saved state
-ss, --save-state save state (default) -ss, --save-state save state (default)
-nss, --no-save-state do not save state -nss, --no-save-state do not save state
-tm, --trace-midi print midi code and values -tm, --trace-midi print midi code and values
-tf, --trace-fps print fps status of subsystems -tf, --trace-fps print fps status of subsystems
``` ```
## Default Project ## Default Project
@@ -233,7 +241,7 @@ Working with pages and items, you can use the following predefined sources and e
| **1** | **0** | Feedback + Thru | _Hue_ | _Saturation_ | _Light_ | Thru | _Hue_ | _Saturation_ | _Light_ | | **1** | **0** | Feedback + Thru | _Hue_ | _Saturation_ | _Light_ | Thru | _Hue_ | _Saturation_ | _Light_ |
| | **1** | Lines | _Thick. / Dezoom_ | _Rotation_ | _Distortion_ | Feedback + Shift | _Zoom / Dezoom_ | _X Shift_ | _Y Shift_ | | | **1** | Lines | _Thick. / Dezoom_ | _Rotation_ | _Distortion_ | Feedback + Shift | _Zoom / Dezoom_ | _X Shift_ | _Y Shift_ |
| | **2** | Dots | _Zoom_ | _Rotation_ | _Lens_ | Shift | _Zoom / Dezoom_ | _X Shift_ | _Y Shift_ | | | **2** | Dots | _Zoom_ | _Rotation_ | _Lens_ | Shift | _Zoom / Dezoom_ | _X Shift_ | _Y Shift_ |
| | **3** | Waves | _Spacing_ | _Thickness_ | _Vert. Scroll (R)_ | Colorize | _Black Color_ | _White Color_ | _Shift_ | | | **3** | Circuit | _Zoom_ | _H. connect_ | _V. connect_ | Colorize | _Black Color_ | _White Color_ | _Shift_ |
| | **4** | Noise | _Zoom_ | _Voronoi dist._ | _Details_ | Quantize | _Pixel Size_ | _Bit Depth_ | _Blur_ | | | **4** | Noise | _Zoom_ | _Voronoi dist._ | _Details_ | Quantize | _Pixel Size_ | _Bit Depth_ | _Blur_ |
| **2** | **5** | Video In 1 + Thru | _Hue_ | _Saturation_ | _Light_ | Dithering | _Pixel Size_ | _Bit Depth_ | _Blur_ | | **2** | **5** | Video In 1 + Thru | _Hue_ | _Saturation_ | _Light_ | Dithering | _Pixel Size_ | _Bit Depth_ | _Blur_ |
| | **6** | CP437 | _Zoom_ | _Charset_ | _Char. Delta_ | TV | _Lens_ | _Horz. Noise_ | _Dezoom_ | | | **6** | CP437 | _Zoom_ | _Charset_ | _Char. Delta_ | TV | _Lens_ | _Horz. Noise_ | _Dezoom_ |
@@ -467,7 +475,15 @@ Don't forget to add all information available to your bug (version, operating sy
### Help I got low FPS on my video device ### Help I got low FPS on my video device
There's already an [open issue](https://github.com/klemek/forge-steel/issues/1) on this subject. Unfortunately, V4L2 is very slow compared to driver-specific decoding.
You can check your device real FPS on [V4L2 UCP](https://github.com/HedgeHawk/v4l2ucp) or [GTK UVC Viewer](https://github.com/jaswdr/guvcview).
### My video feed got strange lines
You need to decode the [V4L2 YUYV format](https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-yuyv.html).
The code is available in the default project [default/inc_yuyv.glsl](./default/inc_yuyv.glsl)
### How do I change the default project built-in sentences? ### How do I change the default project built-in sentences?
+2 -1
View File
@@ -1,4 +1,5 @@
-Iinclude -Iinclude
-DGLFW_INCLUDE_NONE -DGLFW_INCLUDE_NONE
-DGLFW_EXPOSE_NATIVE_EGL -DGLFW_EXPOSE_NATIVE_EGL
-DGLFW_NATIVE_INCLUDE_NONE -DGLFW_NATIVE_INCLUDE_NONE
-DVIDEO_IN
+78 -9
View File
@@ -37,6 +37,8 @@ UNIFORM_IN_FPS_PREFIX=iInputFPS
UNIFORM_DEMO=iDemo UNIFORM_DEMO=iDemo
# 0/1 if auto random # 0/1 if auto random
UNIFORM_AUTORAND=iAutoRand UNIFORM_AUTORAND=iAutoRand
# auto random cycle length
UNIFORM_AUTORANDCYCLE=iAutoRandCycle
# Current page # Current page
UNIFORM_PAGE=iPage UNIFORM_PAGE=iPage
# Current selected shader # Current selected shader
@@ -49,6 +51,8 @@ UNIFORM_STATE_PREFIX=iState
UNIFORM_ACTIVE_PREFIX=iActive UNIFORM_ACTIVE_PREFIX=iActive
# Input X format raw integer value # Input X format raw integer value
UNIFORM_IN_FORMAT_PREFIX=iInputFormat UNIFORM_IN_FORMAT_PREFIX=iInputFormat
# Input X should use swap texture or not (0/1)
UNIFORM_IN_SWAP_PREFIX=iInputSwap
# --- uniform vec2 --- # --- uniform vec2 ---
@@ -91,7 +95,7 @@ SUB_2_PREFIX=fx_
# Total number of internal textures # Total number of internal textures
TEX_COUNT=10 TEX_COUNT=12
# === VIDEO DEVICES # === VIDEO DEVICES
# Video devices will be read from CLI arguments # Video devices will be read from CLI arguments
@@ -102,10 +106,12 @@ IN_COUNT=2
# To which texture will be bound video device X # To which texture will be bound video device X
IN_1_OUT=1 IN_1_OUT=1
IN_2_OUT=2 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
# Fragment shaders will be read from the CLI directory as "fragX.glsl" # Fragment shaders will be read from the CLI directory as "fragX.glsl"
# Special shader "frag0.glsl" will be prepend to each one
# Prefix of fragment shaders to detect # Prefix of fragment shaders to detect
FRAG_FILE_PREFIX=frag FRAG_FILE_PREFIX=frag
@@ -114,13 +120,13 @@ FRAG_FILE_PREFIX=frag
FRAG_COUNT=10 FRAG_COUNT=10
# To which texture will the shader fragX.glsl will render to # To which texture will the shader fragX.glsl will render to
FRAG_1_OUT=3 FRAG_1_OUT=5
FRAG_2_OUT=4 FRAG_2_OUT=6
FRAG_3_OUT=5 FRAG_3_OUT=7
FRAG_4_OUT=6 FRAG_4_OUT=8
FRAG_5_OUT=7 FRAG_5_OUT=9
FRAG_6_OUT=8 FRAG_6_OUT=10
FRAG_7_OUT=9 FRAG_7_OUT=11
FRAG_8_OUT=0 FRAG_8_OUT=0
# Which fragment shader renders to output window # Which fragment shader renders to output window
FRAG_OUTPUT=9 FRAG_OUTPUT=9
@@ -262,3 +268,66 @@ MIDI_3_1_Z=
MIDI_3_2_X=0 MIDI_3_2_X=0
MIDI_3_2_Y=16 MIDI_3_2_Y=16
MIDI_3_2_Z= MIDI_3_2_Z=
# =====
# OTHER
# =====
# === SAVE FILES
# When loading/saving from last run or using save states
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
# SHIFT+R (qwerty)
HOTKEY_RESET=1082
# D (qwerty)
HOTKEY_DEMO=68
# A (qwerty)
HOTKEY_AUTORAND=65
# Left arrow
HOTKEY_AUTORAND_DOWN=263
# Right arrow
HOTKEY_AUTORAND_UP=262
# Down arrow
HOTKEY_TEMPO_DOWN=264
# Up arrow
HOTKEY_TEMPO_UP=265
# Number of load states keys
HOTKEY_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
# Number of save states keys
HOTKEY_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
+10 -4
View File
@@ -2,22 +2,28 @@
// VIDEO 1 // VIDEO 1
// ----------- // -----------
// IN: 1 (RAW IN A) // IN: 1+3 (RAW IN A)
// OUT: 3 (IN A) // OUT: 5 (IN A)
in vec2 vUV; in vec2 vUV;
out vec4 fragColor; out vec4 fragColor;
#include inc_yuv.glsl #include inc_yuyv.glsl
uniform sampler2D iTex0; uniform sampler2D iTex0;
uniform sampler2D iTex1; uniform sampler2D iTex1;
uniform sampler2D iTex3;
uniform int iInputFormat1; uniform int iInputFormat1;
uniform int iInputSwap1;
uniform vec2 iInputResolution1; uniform vec2 iInputResolution1;
void main() { void main() {
if (iInputFormat1 == YUYV_FOURCC) { if (iInputFormat1 == YUYV_FOURCC) {
fragColor = yuyvTex(iTex1, vUV, int(iInputResolution1.x)); if (iInputSwap1 > 0) {
fragColor = yuyvTex(iTex3, vUV, int(iInputResolution1.x));
} else {
fragColor = yuyvTex(iTex1, vUV, int(iInputResolution1.x));
}
} else { } else {
fragColor = texture(iTex0, vUV); fragColor = texture(iTex0, vUV);
} }
+75 -41
View File
@@ -9,15 +9,14 @@ out vec4 fragColor;
#include inc_debug.glsl #include inc_debug.glsl
uniform sampler2D iTex0; uniform sampler2D iTex0;
uniform sampler2D iTex1;
uniform sampler2D iTex2;
uniform sampler2D iTex3;
uniform sampler2D iTex4;
uniform sampler2D iTex5; uniform sampler2D iTex5;
uniform sampler2D iTex6; uniform sampler2D iTex6;
uniform sampler2D iTex7; uniform sampler2D iTex7;
uniform sampler2D iTex8; uniform sampler2D iTex8;
uniform sampler2D iTex9; uniform sampler2D iTex9;
uniform sampler2D iTex10;
uniform sampler2D iTex11;
uniform int iFPS;
uniform int iInputFPS1; uniform int iInputFPS1;
uniform int iInputFPS2; uniform int iInputFPS2;
@@ -26,7 +25,7 @@ float s(vec2 uv, float x0, float y0) {
step(-y0 - 1, -uv.y); step(-y0 - 1, -uv.y);
} }
const int texts[10][5] = { const int texts[12][5] = {
{0x49, 0x4E, 0x20, 0x41, 0x00}, // IN A {0x49, 0x4E, 0x20, 0x41, 0x00}, // IN A
{0x49, 0x4E, 0x20, 0x42, 0x00}, // IN B {0x49, 0x4E, 0x20, 0x42, 0x00}, // IN B
{0x53, 0x52, 0x43, 0x20, 0x41}, // SRC A {0x53, 0x52, 0x43, 0x20, 0x41}, // SRC A
@@ -37,6 +36,8 @@ const int texts[10][5] = {
{0x4D, 0x46, 0x58, 0x00, 0x00}, // MFX {0x4D, 0x46, 0x58, 0x00, 0x00}, // MFX
{0x46, 0x50, 0x53, 0x00, 0x00}, // FPS {0x46, 0x50, 0x53, 0x00, 0x00}, // FPS
{0x4F, 0x46, 0x46, 0x00, 0x00}, // OFF {0x4F, 0x46, 0x46, 0x00, 0x00}, // OFF
{0x44, 0x45, 0x4D, 0x4F, 0x00}, // DEMO
{0x4C, 0x49, 0x56, 0x45, 0x00}, // LIVE
}; };
void main() { void main() {
@@ -49,56 +50,89 @@ void main() {
vec4 c = vec4(0); vec4 c = vec4(0);
c += s(uv2,1,2) * texture(iTex5, uv2); c += s(uv2,1,2) * texture(iTex7, uv2);
c += s(uv2,2,2) * texture(iTex7, uv2); c += s(uv2,2,2) * texture(iTex9, uv2);
c += s(uv2,1,1) * texture(iTex6, uv2); c += s(uv2,1,0) * texture(iTex8, uv2);
c += s(uv2,2,1) * texture(iTex8, uv2); c += s(uv2,2,0) * texture(iTex10, uv2);
c += s(uv2,0,0) * debug(mod(uv2, 1)); c += s(uv2,0,1) * debug(mod(uv2, 1));
c += s(uv2,1,0) * texture(iTex9, uv2); c += s(uv2,1,1) * texture(iTex11, uv2);
c += s(uv2,2,0) * texture(iTex0, uv2); c += s(uv2,2,1) * texture(iTex0, uv2);
float sel = 0;
sel += iSelected == 3 ? h_rect(uv2, vec2(1.5, 2.5), vec2(0.5), 0.01) : 0;
sel += iSelected == 4 ? h_rect(uv2, vec2(1.5, 0.5), vec2(0.5), 0.01) : 0;
sel += iSelected == 5 ? h_rect(uv2, vec2(2.5, 2.5), vec2(0.5), 0.01) : 0;
sel += iSelected == 6 ? h_rect(uv2, vec2(2.5, 0.5), vec2(0.5), 0.01) : 0;
sel += iSelected == 8 ? h_rect(uv2, vec2(2.5, 1.5), vec2(0.5), 0.01) : 0;
c = mix(c, 1 - c, sel);
float f = 0; float f = 0;
float t = 0; float t = 0;
f += rect(uv3, vec2(-51, 28.5), vec2(2.1, 0.7)); f += rect(uv3, vec2(-35, 28.5), vec2(2.1, 0.7));
t += write_5(uv3, vec2(-53,28), texts[0]); t += write_5(uv3, vec2(-37,28), texts[0]);
if (iInputResolution1.x > 0) { if (iInputResolution1.x > 0) {
c += s(uv2,0,2) * texture(iTex3, uv2); c += s(uv2,0,2) * texture(iTex5, uv2);
f += rect(uv3, vec2(-50.4, 26.5), vec2(2.8, 0.7)); f += rect(uv3, vec2(-35, 26.75), vec2(2.8, 0.7));
t += write_int(uv3, vec2(-53,26), iInputFPS1, 2); t += write_int(uv3, vec2(-37.6,26.1), iInputFPS1, 2);
t += write_5(uv3, vec2(-50.5,26), texts[8]); t += write_5(uv3, vec2(-35.1,26.1), texts[8]);
} else { } else {
f += rect(uv3, vec2(-51.5, 26.5), vec2(1.6, 0.7)); f += rect(uv3, vec2(-35, 26.75), vec2(1.6, 0.7));
t += write_5(uv3, vec2(-53,26), texts[9]); t += write_5(uv3, vec2(-36.5,26.1), texts[9]);
} }
f += rect(uv3, vec2(-51, 8.5), vec2(2.1, 0.7)); f += rect(uv3, vec2(-35, -11.5), vec2(2.1, 0.7));
t += write_5(uv3, vec2(-53,8), texts[1]); t += write_5(uv3, vec2(-37,-12), texts[1]);
if (iInputResolution2.x > 0) { if (iInputResolution2.x > 0) {
c += s(uv2,0,1) * texture(iTex4, uv2); c += s(uv2,0,0) * texture(iTex6, uv2);
f += rect(uv3, vec2(-50.4, 6.5), vec2(2.8, 0.7)); f += rect(uv3, vec2(-35, -13.25), vec2(2.8, 0.7));
t += write_int(uv3, vec2(-53,6), iInputFPS2, 2); t += write_int(uv3, vec2(-37.6,-13.9), iInputFPS2, 2);
t += write_5(uv3, vec2(-50.5,6), texts[8]); t += write_5(uv3, vec2(-35.1,-13.9), texts[8]);
} else { } else {
f += rect(uv3, vec2(-51.5, 6.5), vec2(1.6, 0.7)); f += rect(uv3, vec2(-35, -13.25), vec2(1.6, 0.7));
t += write_5(uv3, vec2(-53,6), texts[9]); t += write_5(uv3, vec2(-36.5,-13.9), texts[9]);
} }
f += rect(uv3, vec2(-14.5, 28.5), vec2(2.6, 0.7));
t += write_5(uv3, vec2(-17,28), texts[2]);
f += rect(uv3, vec2(-14.5, 8.5), vec2(2.6, 0.7));
t += write_5(uv3, vec2(-17,8), texts[3]);
f += rect(uv3, vec2(21, 28.5), vec2(2.1, 0.7));
t += write_5(uv3, vec2(19,28), texts[4]);
f += rect(uv3, vec2(21, 8.5), vec2(2.1, 0.7));
t += write_5(uv3, vec2(19,8), texts[5]);
f += rect(uv3, vec2(-15.5, -11.5), vec2(1.6, 0.7));
t += write_5(uv3, vec2(-17,-12), texts[6]);
f += rect(uv3, vec2(20.5, -11.5), vec2(1.6, 0.7));
t += write_5(uv3, vec2(19,-12), texts[7]);
fragColor = mix(c, vec4(f - t), f); f += rect(uv3, vec2(-35, 8.5), vec2(2.1, 0.7));
if (iDemo > 0) {
t += write_5(uv3, vec2(-37,8), texts[10]);
} else {
t += write_5(uv3, vec2(-37,8), texts[11]);
}
f += rect(uv3, vec2(0, 28.5), vec2(2.6, 0.7));
f += iActive1 == 1 ? h_rect(uv3, vec2(0, 28.5), vec2(3, 1), 0.2) : 0;
t += write_5(uv3, vec2(-2.5,28), texts[2]);
f += rect(uv3, vec2(0, -11.5), vec2(2.6, 0.7));
f += iActive1 == 2 ? h_rect(uv3, vec2(0, -11.5), vec2(3, 1), 0.2) : 0;
t += write_5(uv3, vec2(-2.5,-12), texts[3]);
f += rect(uv3, vec2(35, 28.5), vec2(2.1, 0.7));
f += iActive2 == 1 ? h_rect(uv3, vec2(35, 28.5), vec2(2.5, 1), 0.2) : 0;
t += write_5(uv3, vec2(33,28), texts[4]);
f += rect(uv3, vec2(35, -11.5), vec2(2.1, 0.7));
f += iActive2 == 2 ? h_rect(uv3, vec2(35, -11.5), vec2(2.5, 1), 0.2) : 0;
t += write_5(uv3, vec2(33,-12), texts[5]);
f += rect(uv3, vec2(0, 8.5), vec2(1.6, 0.7));
t += write_5(uv3, vec2(-1.5,8), texts[6]);
f += rect(uv3, vec2(35, 8.5), vec2(1.6, 0.7));
f += iActive2 == 3 ? h_rect(uv3, vec2(35, 8.5), vec2(2, 1), 0.2) : 0;
t += write_5(uv3, vec2(33.5,8), texts[7]);
f += rect(uv3, vec2(35, 6.75), vec2(2.8, 0.7));
t += write_int(uv3, vec2(32.4,6.1), iFPS, 2);
t += write_5(uv3, vec2(34.9,6.1), texts[8]);
fragColor = mix(c, vec4(min(1, f) - t), min(1, f));
} }
+10 -4
View File
@@ -2,22 +2,28 @@
// VIDEO 2 // VIDEO 2
// ----------- // -----------
// IN: 2 (RAW IN B) // IN: 2+4 (RAW IN B)
// OUT: 4 (IN B) // OUT: 6 (IN B)
in vec2 vUV; in vec2 vUV;
out vec4 fragColor; out vec4 fragColor;
#include inc_yuv.glsl #include inc_yuyv.glsl
uniform sampler2D iTex0; uniform sampler2D iTex0;
uniform sampler2D iTex2; uniform sampler2D iTex2;
uniform sampler2D iTex4;
uniform int iInputFormat2; uniform int iInputFormat2;
uniform int iInputSwap2;
uniform vec2 iInputResolution2; uniform vec2 iInputResolution2;
void main() { void main() {
if (iInputFormat2 == YUYV_FOURCC) { if (iInputFormat2 == YUYV_FOURCC) {
fragColor = yuyvTex(iTex2, vUV, int(iInputResolution2.x)); if (iInputSwap2 > 0) {
fragColor = yuyvTex(iTex4, vUV, int(iInputResolution2.x));
} else {
fragColor = yuyvTex(iTex2, vUV, int(iInputResolution2.x));
}
} else { } else {
fragColor = texture(iTex0, vUV); fragColor = texture(iTex0, vUV);
} }
+1 -1
View File
@@ -3,7 +3,7 @@
// SRC A // SRC A
// ----------- // -----------
// OUT: 5 (FX A) // OUT: 7 (FX A)
in vec2 vUV; in vec2 vUV;
out vec4 fragColor; out vec4 fragColor;
+1 -1
View File
@@ -2,7 +2,7 @@
// SRC B // SRC B
// ----------- // -----------
// OUT: 6 (FX B) // OUT: 8 (FX B)
in vec2 vUV; in vec2 vUV;
out vec4 fragColor; out vec4 fragColor;
+5 -5
View File
@@ -2,20 +2,20 @@
// FX A // FX A
// ------------- // -------------
// IN: 5 (SRC A) // IN: 7 (SRC A)
// IN: 7 (FX A) // IN: 9 (FX A)
// OUT: 7 (A+B) // OUT: 9 (A+B)
in vec2 vUV; in vec2 vUV;
out vec4 fragColor; out vec4 fragColor;
#include inc_fx.glsl #include inc_fx.glsl
uniform sampler2D iTex5;
uniform sampler2D iTex7; uniform sampler2D iTex7;
uniform sampler2D iTex9;
uniform int iSeed5; uniform int iSeed5;
uniform vec3 iMidi2_1[7]; uniform vec3 iMidi2_1[7];
void main() { void main() {
fragColor = fx_stage(vUV, iTex5, iTex7, 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, iMidi2_1[0], iMidi2_1[1].xy, iMidi2_1[2], iMidi2_1[3].xy, iMidi2_1[4], iMidi2_1[5].xy, iMidi2_1[6]);
} }
+5 -5
View File
@@ -2,20 +2,20 @@
// FX B // FX B
// ------------- // -------------
// IN: 6 (SRC B) // IN: 8 (SRC B)
// IN: 8 (FX B) // IN: 10 (FX B)
// OUT: 8 (A+B) // OUT: 10 (A+B)
in vec2 vUV; in vec2 vUV;
out vec4 fragColor; out vec4 fragColor;
#include inc_fx.glsl #include inc_fx.glsl
uniform sampler2D iTex6;
uniform sampler2D iTex8; uniform sampler2D iTex8;
uniform sampler2D iTex10;
uniform int iSeed6; uniform int iSeed6;
uniform vec3 iMidi2_2[7]; uniform vec3 iMidi2_2[7];
void main() { void main() {
fragColor = fx_stage(vUV, iTex6, iTex8, 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, iMidi2_2[0], iMidi2_2[1].xy, iMidi2_2[2], iMidi2_2[3].xy, iMidi2_2[4], iMidi2_2[5].xy, iMidi2_2[6]);
} }
+12 -8
View File
@@ -2,9 +2,9 @@
// A+B // A+B
// ------------ // ------------
// IN: 7 (FX A) // IN: 9 (FX A)
// IN: 8 (FX B) // IN: 10 (FX B)
// OUT: 9 (MFX) // OUT: 11 (MFX)
in vec2 vUV; in vec2 vUV;
out vec4 fragColor; out vec4 fragColor;
@@ -13,8 +13,8 @@ out vec4 fragColor;
#include inc_functions.glsl #include inc_functions.glsl
uniform int iDemo; uniform int iDemo;
uniform sampler2D iTex7; uniform sampler2D iTex9;
uniform sampler2D iTex8; uniform sampler2D iTex10;
uniform int iSeed7; uniform int iSeed7;
uniform vec3 iMidi3_1[2]; uniform vec3 iMidi3_1[2];
@@ -22,12 +22,16 @@ void main() {
float mix_value = magic(iMidi3_1[1].xy, vec3(1, 0, 0), iSeed7); 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); bool mix_type = magic_trigger(vec3(iMidi3_1[0].x, 0, 0), iSeed7 + 10);
vec4 color_a = texture(iTex7, vUV); vec4 color_a = texture(iTex9, vUV);
vec4 color_b = texture(iTex8, vUV); vec4 color_b = texture(iTex10, vUV);
float k = mean(color_a); float k = mean(color_a);
mix_value = mix(mix_value, mix_value * 0.9 + 0.05, iDemo); mix_value = mix(mix_value, mix_value * 0.9 + 0.05, iDemo);
fragColor = mix(color_b, color_a, mix_type ? step(mix_value, k) : mix_value); if (mix_type) {
fragColor = mix(color_a, color_b, step(mix_value, k));
} else {
fragColor = mix(color_b, color_a, mix_value);
}
} }
+9 -5
View File
@@ -2,7 +2,7 @@
// MFX // MFX
// ------------ // ------------
// IN: 9 (A+B) // IN: 11 (A+B)
// IN: 0 (OUT) // IN: 0 (OUT)
// OUT: 0 (OUT) // OUT: 0 (OUT)
@@ -11,16 +11,20 @@ out vec4 fragColor;
#include inc_fx.glsl #include inc_fx.glsl
uniform sampler2D iTex9; uniform sampler2D iTex11;
uniform sampler2D iTex0; uniform sampler2D iTex0;
uniform int iSeed8; uniform int iSeed8;
uniform vec3 iMidi2_3[7]; uniform vec3 iMidi2_3[7];
uniform vec3 iMidi3_1[2]; uniform vec3 iMidi3_1[2];
uniform int iDemo;
uniform int iAutoRand;
void main() { void main() {
vec4 color = fx_stage(vUV, iTex9, 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, iMidi2_3[0], iMidi2_3[1].xy, iMidi2_3[2], iMidi2_3[3].xy, iMidi2_3[4], iMidi2_3[5].xy, iMidi2_3[6]);
color = mix(color, vec4(0), iMidi3_1[0].y); if (iDemo < 1 && iAutoRand < 1) {
color = mix(color, vec4(0), iMidi3_1[0].y);
}
fragColor = color; fragColor = color;
} }
+14
View File
@@ -372,6 +372,20 @@ float write_int(vec2 uv, vec2 pos, uint value, uint magnitude)
return d; return d;
} }
float write_int_left(vec2 uv, vec2 pos, uint value, uint magnitude)
{
int i;
uint m = 1;
float d = 0;
for (i = 0; i < magnitude; i++) {
if (i == 0 || value >= m) {
pos.x += 1;
m *= 10u;
}
}
return write_int(uv, pos, value, magnitude);
}
int read(sampler2D tex, vec2 uv, float k, int d, float t) int read(sampler2D tex, vec2 uv, float k, int d, float t)
{ {
float inv_k = 1 / k; float inv_k = 1 / k;
+37 -34
View File
@@ -1,13 +1,11 @@
#include inc_magic.glsl #include inc_magic.glsl
#include inc_functions.glsl #include inc_functions.glsl
#include inc_yuv.glsl #include inc_yuyv.glsl
#include inc_cp437.glsl #include inc_cp437.glsl
#ifndef INC_DEBUG #ifndef INC_DEBUG
#define INC_DEBUG #define INC_DEBUG
uniform int iFPS;
uniform vec2 iInputResolution1; uniform vec2 iInputResolution1;
uniform vec2 iInputResolution2; uniform vec2 iInputResolution2;
uniform int iInputFormat1; uniform int iInputFormat1;
@@ -15,6 +13,7 @@ uniform int iInputFormat2;
uniform int iDemo; uniform int iDemo;
uniform int iAutoRand; uniform int iAutoRand;
uniform int iAutoRandCycle;
uniform int iPage; uniform int iPage;
uniform int iSelected; uniform int iSelected;
@@ -111,7 +110,7 @@ vec4 debug(vec2 vUV)
{0x54, 0x49, 0x4D, 0x45, 0x00}, // TIME {0x54, 0x49, 0x4D, 0x45, 0x00}, // TIME
{0x44, 0x45, 0x4D, 0x4F, 0x00}, // DEMO {0x44, 0x45, 0x4D, 0x4F, 0x00}, // DEMO
{0x4C, 0x49, 0x56, 0x45, 0x00}, // LIVE {0x4C, 0x49, 0x56, 0x45, 0x00}, // LIVE
{0x2B, 0x52, 0x41, 0x4E, 0x44}, // +RAND {0x52, 0x41, 0x4E, 0x44, 0x00}, // RAND
{0x53, 0x52, 0x43, 0x00, 0x00}, // SRC {0x53, 0x52, 0x43, 0x00, 0x00}, // SRC
{0x46, 0x58, 0x00, 0x00, 0x00}, // FX {0x46, 0x58, 0x00, 0x00, 0x00}, // FX
{0x49, 0x4E, 0x00, 0x00, 0x00}, // IN {0x49, 0x4E, 0x00, 0x00, 0x00}, // IN
@@ -141,8 +140,17 @@ vec4 debug(vec2 vUV)
rect(uv2, vec2(2, -1.55), vec2(0.1, 0.55)) + 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(0.55, 2), vec2(1.5, 0.1)) +
rect(uv2, vec2(2, 1.55), vec2(0.1, 0.55)) + rect(uv2, vec2(2, 1.55), vec2(0.1, 0.55)) +
rect(uv2, vec2(7.5, 0), vec2(1.5, 0.1)) + rect(uv2, vec2(6.8, 0), vec2(0.75, 0.1)) +
h_rect(uv2, vec2(-9, -3.9), vec2(1), 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);
}
if (iDemo < 1 && iInputFormat2 == YUYV_FOURCC) {
f += circle(uv2, vec2(-9, -2), 0.3);
}
// show selected src/fx // show selected src/fx
f += char_at(uv2, vec2(-5.4, 1.45), hex_chars[selected_srca]); f += char_at(uv2, vec2(-5.4, 1.45), hex_chars[selected_srca]);
@@ -177,17 +185,17 @@ vec4 debug(vec2 vUV)
f += write_5(uv3 * 0.75, vec2(-11.6,-1.8), texts[6]); 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(-4.2,-1.8), texts[7]);
f += char_at(uv3 * 0.5, vec2(0.5, 0.5), 0x41); f += char_at(uv3 * 0.5, vec2(-11.5, 3.5), 0x41);
f += char_at(uv3 * 0.5, vec2(0.5, -3), 0x42); f += char_at(uv3 * 0.5, vec2(-11.5, -6), 0x42);
if (iDemo < 1 && (iInputFormat1 == YUYV_FOURCC || iInputFormat2 == YUYV_FOURCC)) { 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 // show inputs / feedback
float line_a_a = rect(uv2, vec2(-8, 2), vec2(2, 0.1)); float line_a_a = rect(uv2, vec2(-7.5, 2), vec2(1.5, 0.1));
float line_a_b = rect(uv2, vec2(-7, 2), vec2(1, 0.1)) + rect(uv2, vec2(-8, 0.5), vec2(0.1, 1.6)) + rect(uv2, vec2(-9, -1), vec2(1, 0.1)); float line_a_b = rect(uv2, vec2(-6.5, 2), vec2(0.5, 0.1)) + rect(uv2, vec2(-8.5, -2), vec2(0.5, 0.1)) + line(uv2, vec2(-7, 2.1), vec2(-8, -2.1), 0.2);
float line_a_f = rect(uv2, vec2(-6.5, 2), vec2(0.5, 0.1)) + rect(uv2, vec2(0, 4), vec2(7, 0.1)) + rect(uv2, vec2(-7, 3), vec2(0.1, 1.1)) + rect(uv2, vec2(7, 2), vec2(0.1, 2.1)); float line_a_f = rect(uv2, vec2(-6.5, 2), vec2(0.5, 0.1)) + rect(uv2, vec2(0, 3.6), vec2(7, 0.1)) + rect(uv2, vec2(-7, 2.8), vec2(0.1, 0.9)) + rect(uv2, vec2(7, 1.8), vec2(0.1, 1.9));
if (selected_srca == 5 && iInputFormat1 == YUYV_FOURCC) { if (selected_srca == 5 && iInputFormat1 == YUYV_FOURCC) {
f += line_a_a; f += line_a_a;
} else if (selected_srca == 10 && iInputFormat2 == YUYV_FOURCC) { } else if (selected_srca == 10 && iInputFormat2 == YUYV_FOURCC) {
@@ -195,9 +203,10 @@ vec4 debug(vec2 vUV)
} else if (selected_srca % 5 == 0) { } else if (selected_srca % 5 == 0) {
f += line_a_f; f += line_a_f;
} }
float line_b_a = rect(uv2, vec2(-6.5, -2), vec2(0.5, 0.1)) + rect(uv2, vec2(-7, -0.5), vec2(0.1, 1.6)) + rect(uv2, vec2(-8.5, 1), vec2(1.5, 0.1));
float line_b_b = rect(uv2, vec2(-8, -2), vec2(2, 0.1)); float line_b_a = rect(uv2, vec2(-6.5, -2), vec2(0.5, 0.1)) + rect(uv2, vec2(-8.5, 2), vec2(0.5, 0.1)) + line(uv2, vec2(-7, -2.1), vec2(-8, 2.1), 0.2);
float line_b_f = rect(uv2, vec2(-6.5, -2), vec2(0.5, 0.1)) + rect(uv2, vec2(0, -4), vec2(7, 0.1)) + rect(uv2, vec2(-7, -3), vec2(0.1, 1.1)) + rect(uv2, vec2(7, -2), vec2(0.1, 2.1)); float line_b_b = rect(uv2, vec2(-7.5, -2), vec2(1.5, 0.1));
float line_b_f = rect(uv2, vec2(-6.5, -2), vec2(0.5, 0.1)) + rect(uv2, vec2(0, -3.6), vec2(7, 0.1)) + rect(uv2, vec2(-7, -2.8), vec2(0.1, 0.9)) + rect(uv2, vec2(7, -1.8), vec2(0.1, 1.9));
if (selected_srcb == 5 && iInputFormat1 == YUYV_FOURCC) { if (selected_srcb == 5 && iInputFormat1 == YUYV_FOURCC) {
f += line_b_a; f += line_b_a;
} else if (selected_srcb == 10 && iInputFormat2 == YUYV_FOURCC) { } else if (selected_srcb == 10 && iInputFormat2 == YUYV_FOURCC) {
@@ -207,7 +216,9 @@ vec4 debug(vec2 vUV)
} }
// show page // show page
f += char_at(uv2, vec2(-9.2, -4.3), hex_chars[iPage]); f += iPage == 0 ? circle(uv2, vec2(-0.75, -4.1), 0.3) : h_circle(uv2, vec2(-0.75, -4.1), 0.25, 0.1);
f += iPage == 1 ? circle(uv2, vec2(0, -4.1), 0.3) : h_circle(uv2, vec2(0, -4.1), 0.20, 0.1);
f += iPage == 2 ? circle(uv2, vec2(0.75, -4.1), 0.3) : h_circle(uv2, vec2(0.75, -4.1), 0.20, 0.1);
// show fx values // show fx values
float fx_rect = 0; float fx_rect = 0;
@@ -225,33 +236,25 @@ vec4 debug(vec2 vUV)
float x = 0; float x = 0;
x = -15; x = -15;
f += write_5(uv3, vec2(x,13), texts[0]); f += write_5(uv3, vec2(x - 4.5,13), texts[1]);
f += write_int(uv3, vec2(x - 3.5,13), iFPS, 3); f += write_int(uv3, vec2(x + 1.5,13), int(iTempo), 3);
v = min(1, iFPS/60.0);
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 = 0;
f += write_5(uv3, vec2(x,13), texts[1]);
f += write_int(uv3, vec2(x - 3.5,13), int(iTempo), 3);
v = fract(iBeats); v = fract(iBeats);
f += h_rect(uv3, vec2(x, 12), vec2(4, 0.5), 0.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)); f += rect(uv3, vec2(x + 4 * v - 4, 12), vec2(4 * v, 0.4));
x = 15; x = 15;
f += write_5(uv3, vec2(x,13), texts[2]); if (iAutoRand > 0) {
f += write_int(uv3, vec2(x - 5.5,13), int(iTime), 5); f += write_5(uv3, vec2(x - 4.5,13), texts[5]);
v = fract(iTime); 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);
v = fract(iTime);
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 += 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)); f += rect(uv3, vec2(x + 4 * v - 4, 12), vec2(4 * v, 0.4));
if (iAutoRand > 0) {
f += write_5(uv3, vec2(-4,-15), iDemo > 0 ? texts[3] : texts[4]);
f += write_5(uv3, vec2(0,-15), texts[5]);
} else {
f += write_5(uv3, vec2(-2,-15), iDemo > 0 ? texts[3] : texts[4]);
}
return vec4(f); return vec4(f);
} }
+6
View File
@@ -2,6 +2,8 @@
#define PI 3.1415927 #define PI 3.1415927
#endif #endif
#include inc_time.glsl
#include inc_rand.glsl
#include inc_res.glsl #include inc_res.glsl
#ifndef INC_FUNCTIONS #ifndef INC_FUNCTIONS
@@ -224,6 +226,10 @@ float circle(vec2 uv, vec2 c, float size) {
return istep(size, length(uv - c)); return istep(size, length(uv - c));
} }
float h_circle(vec2 uv, vec2 c, float size, float k) {
return circle(uv, c, size + k * 0.5) - circle(uv, c, size - k * 0.5);
}
float stripe(float x, float k1, float k2) float stripe(float x, float k1, float k2)
{ {
return k2 > k1 ? (1 - step(x, k1)) * (step(x, k2)) : ((1 - step(x, k2)) * (step(x, k1))); return k2 > k1 ? (1 - step(x, k1)) * (step(x, k2)) : ((1 - step(x, k2)) * (step(x, k1)));
+115 -26
View File
@@ -101,7 +101,7 @@ subroutine(src_stage_sub) vec4 src_3(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
return vec4(f); return vec4(f);
} }
// SRC 4 : waves // SRC 4 : circuit
subroutine(src_stage_sub) vec4 src_4(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3) subroutine(src_stage_sub) vec4 src_4(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3 b2, vec2 f2, vec3 b3, vec2 f3)
{ {
// start // start
@@ -112,34 +112,123 @@ subroutine(src_stage_sub) vec4 src_4(vec2 vUV, int seed, vec3 b1, vec2 f1, vec3
// controls // controls
float spacing = magic(f1, b1, seed + 10); float z = 10 + magic(f1, b1, 123) * 20;
float thickness = magic(f2, b2, seed + 20); float h = magic(f2, b2, seed + 20) * 0.8 + 0.1;
float scroll = magic_reverse(f3, b3, seed + 30); float v = magic_reverse(f3, b3, seed + 30) * 0.8 + 0.1;
// logic // logic
vec2 uv2 = uv1; uv1 *= z;
uv2.y += 0.5; uv1 += iBeats;
uv2 *= 2.25;
uv2 = vec2((uv2.x + 1) * 0.5, -uv2.y); float s0 = rand(floor(mod(uv1, 1000))) * 1000;
float m1 = spacing * 4.5 + 0.5; float s1 = rand(floor(mod(uv1 + vec2(0, 1), 1000))) * 1000;
float y = log(-uv2.y) * m1; float s2 = rand(floor(mod(uv1 - vec2(1, 0), 1000))) * 1000;
y = mod(y + scroll * 5.0 - iBeats / 16, 5.);
float id = floor(y) * 32; bool up = rand(s1 + 1) < h;
float s = cos(uv2.x * rand(id + 837) * 100 + rand(id + 281) * PI) bool left = rand(s2 + 2) < v;
+ cos(uv2.x * rand(id + 231) * 100 + rand(id + 526) * PI) bool down = rand(s0 + 1) < h;
+ cos(uv2.x * rand(id + 746) * 100 + rand(id + 621) * PI) bool right = rand(s0 + 2) < v;
+ cos(uv2.x * rand(id + 235) * 100 + rand(id + 315) * PI) bool up_down = up && down;
+ cos(uv2.x * rand(id + 782) * 100 + rand(id + 314) * PI) bool left_right = left && right;
+ cos(uv2.x * rand(id + 241) * 100 + rand(id + 734) * PI)
+ cos(uv2.x * rand(id + 416) * 100 + rand(id + 425) * PI) uv1 = mod(uv1, 1.0) - 0.5;
+ cos(uv2.x * rand(id + 315) * 100 + rand(id + 525) * PI)
+ cos(uv2.x * rand(id + 423) * 100 + rand(id + 743) * PI) const float t = 0.1;
+ cos(uv2.x * rand(id + 637) * 100 + rand(id + 245) * PI);
s *= 0.1; float f = 0;
float cut = 0.025 + thickness * 0.475; int c = 0;
float y2 = min(1.0, -(uv2.y));
float f = (0.1 + 0.9 * (cos((y2 + 1.0) * PI) * 0.5 + 0.5)) * istep(0, uv2.y) * istep(cut, fract(y + (s - 1) * (1 - cut) * 0.5)); if (up) {
f += stripe(uv1.x, -t * 0.5, t * 0.5) * step(-t * 0.5, uv1.y);
c += 1;
}
if (down) {
f += stripe(uv1.x, -t * 0.5, t * 0.5) * istep(t * 0.5, uv1.y);
c += 1;
}
if (left) {
f += stripe(uv1.y, -t * 0.5, t * 0.5) * istep(t * 0.5, uv1.x);
c += 1;
}
if (right) {
f += stripe(uv1.y, -t * 0.5, t * 0.5) * step(-t * 0.5, uv1.x);
c += 1;
}
if (c == 1) {
f += istep(t, length(uv1));
}
f = min(f, 1);
if ((up_down ^^ left_right) && c == 2) {
if (up_down) {
uv1.xy = uv1.yx;
}
if (rand(s0 + 3) < 0.5) {
uv1.x = -uv1.x;
}
float k = rand(s0 + 4) * 60;
f -= rect(uv1, vec2(0), vec2(t * 3, t));
f = max(0, f);
if (k < 10) { // resistor
f += line(uv1, vec2(-t * 3.25, -t * 0.5), vec2(-t * 2.5, t * 2), t * 0.75);
f += line(uv1, vec2(-t * 2.5, t * 2), vec2(-t * 1.5, -t * 2), t * 0.75);
f += line(uv1, vec2(-t * 1.5, -t * 2), vec2(-t * 0.5, t * 2), t * 0.75);
f += line(uv1, vec2(-t * 0.5, t * 2), vec2(t * 0.5, -t * 2), t * 0.75);
f += line(uv1, vec2(t * 0.5, -t * 2), vec2(t * 1.5, t * 2), t * 0.75);
f += line(uv1, vec2(t * 1.5, t * 2), vec2(t * 2.5, -t * 2), t * 0.75);
f += line(uv1, vec2(t * 2.5, -t * 2), vec2(t * 3.25, t * 0.5), t * 0.75);
} else if (k < 20) { // capacitor
f += rect(uv1, vec2(-t * 2, 0), vec2(t, t * 0.5));
f += rect(uv1, vec2(t * 2, 0), vec2(t, t * 0.5));
f += rect(uv1, vec2(t, 0), vec2(t * 0.5, t * 3.5));
f += rect(uv1, vec2(-t, 0), vec2(t * 0.5, t * 3.5));
} else if (k < 30) { // diode
f += line(uv1, vec2(-t * 2, t * 2.5), vec2(t * 2, 0), t);
f += line(uv1, vec2(-t * 2, -t * 2.5), vec2(t * 2, 0), t);
f += rect(uv1, vec2(t * 2.5, 0), vec2(t * 0.5, t * 3));
f += rect(uv1, vec2(-t * 2.5, 0), vec2(t * 0.5, t * 3));
} else if (k < 40) { // lamp
f += istep(t * 3.5, length(uv1));
f -= istep(t * 2.5, length(uv1));
f += line(uv1, vec2(-t * 2), vec2(t * 2), t);
f += line(uv1, vec2(-t * 2, t * 2), vec2(t * 2, -t * 2), t);
} else if (k < 50) { // inductor
f += istep(t * 2, length(uv1 - vec2(t * 2.5,0)));
f += istep(t * 2, length(uv1 - vec2(0,0)));
f += istep(t * 2, length(uv1 - vec2(-t * 2.5,0)));
f -= 2 * istep(t, length(uv1 - vec2(t * 2.5,0)));
f -= 2 * istep(t, length(uv1 - vec2(0,0)));
f -= 2 * istep(t, length(uv1 - vec2(-t * 2.5,0)));
f *= step(-t * 0.5, uv1.y);
} else if (k < 60) { // switch
f += istep(t, length(uv1 - vec2(t * 2.5, 0)));
f += istep(t, length(uv1 + vec2(t * 2.5, 0)));
f += line(uv1, vec2(t * 2, 0), vec2(-t * 2.5, t * (k < 55 ? 3 : 1)), t);
}
} else if (c == 3) {
if (left_right) {
uv1.xy = uv1.yx;
if (up) {
uv1.x = -uv1.x;
}
} else if (right) {
uv1.x = -uv1.x;
}
float k = rand(s0 + 4) * 20;
if (k < 10) {
f -= rect(uv1, vec2(0), vec2(t * 3));
f = max(0, f);
f += rect(uv1, vec2(-t * 3, 0), vec2(t * 0.5, t * 3));
f += line(uv1, vec2(t * 0.25, t * 3.25), vec2(-t * 3, t), t);
f += line(uv1, vec2(t * 0.25, -t * 3.25), vec2(-t * 3, -t), t);
}
}
return vec4(f); return vec4(f);
} }
@@ -1,9 +1,9 @@
#ifndef INC_YUV #ifndef INC_YUYV
#define INC_YUV #define INC_YUYV
const int YUYV_FOURCC = 1448695129; const int YUYV_FOURCC = 1448695129;
const mat3x3 yuv_to_rgb = {{1,1,1},{0,-0.39465,2.03211},{1.13983,-0.5806,0}}; const mat3x3 yuyv_to_rgb = {{1,1,1},{0,-0.39465,2.03211},{1.13983,-0.5806,0}};
vec4 yuyvTex(sampler2D tex, vec2 vUV, int base_width) { vec4 yuyvTex(sampler2D tex, vec2 vUV, int base_width) {
float w = base_width - 1; float w = base_width - 1;
@@ -22,7 +22,7 @@ vec4 yuyvTex(sampler2D tex, vec2 vUV, int base_width) {
tV.y - 0.5 tV.y - 0.5
); );
return vec4(yuv_to_rgb * yuv, 1.0); return vec4(yuyv_to_rgb * yuv, 1.0);
} }
#endif #endif
+54 -50
View File
@@ -1,165 +1,169 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/28.2.8 Chrome/140.0.7339.249 Electron/38.5.0 Safari/537.36" version="28.2.8"> <mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.1 Chrome/112.0.5615.204 Electron/24.6.1 Safari/537.36" modified="2025-11-13T08:04:14.940Z" version="21.6.1" etag="Os_oMlk_pEsBuxfeX0ax" type="device">
<diagram name="Page-1" id="uAijJZJkDDyZqolEMDYd"> <diagram name="Page-1" id="uAijJZJkDDyZqolEMDYd">
<mxGraphModel dx="1848" dy="672" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="2022" dy="698" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root> <root>
<mxCell id="0" /> <mxCell id="0" />
<mxCell id="1" parent="0" /> <mxCell id="1" parent="0" />
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-1" value="F.O.R.G.E." style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-1" value="F.O.R.G.E." style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="154" y="210" width="546" height="350" as="geometry" /> <mxGeometry x="154" y="210" width="546" height="350" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-2" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-2" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="270" y="250" width="120" height="60" as="geometry" /> <mxGeometry x="270" y="250" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-3" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-3" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="274" y="410" width="120" height="60" as="geometry" /> <mxGeometry x="274" y="410" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-4" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-4" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="280" y="260" width="120" height="60" as="geometry" /> <mxGeometry x="280" y="260" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-5" value="TEXTURE&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-5" value="TEXTURES&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="290" y="270" width="120" height="60" as="geometry" /> <mxGeometry x="290" y="270" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-6" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-6" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="284" y="420" width="120" height="60" as="geometry" /> <mxGeometry x="284" y="420" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-7" value="SHADER&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-7" value="FRAGMENT&lt;br&gt;SHADERS&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="294" y="430" width="120" height="60" as="geometry" /> <mxGeometry x="294" y="430" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-9" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-9" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="333.5" y="340" as="sourcePoint" /> <mxPoint x="333.5" y="340" as="sourcePoint" />
<mxPoint x="333.5" y="400.5" as="targetPoint" /> <mxPoint x="333.5" y="400.5" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-11" value="UNIFORM&lt;br&gt;SAMPLERS" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="3Uv0AfHo8SbA6Qcxonjd-9"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-11" value="UNIFORM&lt;br&gt;SAMPLERS" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="3Uv0AfHo8SbA6Qcxonjd-9" vertex="1" connectable="0">
<mxGeometry x="0.1111" relative="1" as="geometry"> <mxGeometry x="0.1111" relative="1" as="geometry">
<mxPoint x="-40" y="-13" as="offset" /> <mxPoint x="-40" y="-13" as="offset" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-10" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-10" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="370" y="400" as="sourcePoint" /> <mxPoint x="370" y="400" as="sourcePoint" />
<mxPoint x="370" y="340" as="targetPoint" /> <mxPoint x="370" y="340" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-12" value="RENDERS TO&lt;br&gt;FRAMEBUFFERS" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="3Uv0AfHo8SbA6Qcxonjd-10"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-12" value="RENDERS TO&lt;br&gt;FRAMEBUFFERS" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="3Uv0AfHo8SbA6Qcxonjd-10" vertex="1" connectable="0">
<mxGeometry x="-0.3556" y="2" relative="1" as="geometry"> <mxGeometry x="-0.3556" y="2" relative="1" as="geometry">
<mxPoint x="62" y="-1" as="offset" /> <mxPoint x="62" y="-1" as="offset" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-13" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-13" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="140" y="300" as="sourcePoint" /> <mxPoint x="140" y="300" as="sourcePoint" />
<mxPoint x="260" y="299.5" as="targetPoint" /> <mxPoint x="260" y="299.5" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-14" value="DMA&lt;div&gt;BUFFERS&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="3Uv0AfHo8SbA6Qcxonjd-13"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-14" value="DMA&lt;div&gt;BUFFERS&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="3Uv0AfHo8SbA6Qcxonjd-13" vertex="1" connectable="0">
<mxGeometry x="0.1111" relative="1" as="geometry"> <mxGeometry x="0.1111" relative="1" as="geometry">
<mxPoint x="-10" y="-19" as="offset" /> <mxPoint x="-10" y="-19" as="offset" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-15" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-19" value="OUTPUT&lt;br&gt;SHADER" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="140" y="450.5" as="sourcePoint" />
<mxPoint x="260" y="450" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-16" value="UNIFORMS" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="3Uv0AfHo8SbA6Qcxonjd-15">
<mxGeometry x="0.1111" relative="1" as="geometry">
<mxPoint x="-10" y="-19" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-19" value="OUTPUT&lt;br&gt;SHADER" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="530" y="270" width="120" height="60" as="geometry" /> <mxGeometry x="530" y="270" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-23" value="MONITOR&lt;br&gt;SHADER" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-23" value="MONITOR&lt;br&gt;SHADER" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
<mxGeometry x="530" y="360" width="120" height="60" as="geometry" /> <mxGeometry x="530" y="360" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-24" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;dashed=1;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-24" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;dashed=1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="420" y="320" as="sourcePoint" /> <mxPoint x="420" y="320" as="sourcePoint" />
<mxPoint x="520" y="360" as="targetPoint" /> <mxPoint x="520" y="360" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-25" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-25" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="420" y="290" as="sourcePoint" /> <mxPoint x="420" y="290" as="sourcePoint" />
<mxPoint x="520" y="290" as="targetPoint" /> <mxPoint x="520" y="290" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-26" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-26" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="349.5" y="585" as="sourcePoint" /> <mxPoint x="349.5" y="585" as="sourcePoint" />
<mxPoint x="349.5" y="495" as="targetPoint" /> <mxPoint x="350" y="500" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-27" value="COMPILES&lt;br&gt;TO" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="3Uv0AfHo8SbA6Qcxonjd-26"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-27" value="COMPILES&lt;br&gt;TO" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="3Uv0AfHo8SbA6Qcxonjd-26" vertex="1" connectable="0">
<mxGeometry x="0.1111" relative="1" as="geometry"> <mxGeometry x="0.1111" relative="1" as="geometry">
<mxPoint x="-55" y="-15" as="offset" /> <mxPoint x="-55" y="-15" as="offset" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-28" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-28" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="274" y="590" width="120" height="60" as="geometry" /> <mxGeometry x="274" y="590" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-29" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-29" value="SHADER" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="284" y="600" width="120" height="60" as="geometry" /> <mxGeometry x="284" y="600" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-30" value="GLSL FILE&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-30" value="GLSL FILES&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="294" y="610" width="120" height="60" as="geometry" /> <mxGeometry x="294" y="610" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-31" value="MIDI INPUT" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-31" value="MIDI&lt;br&gt;INPUT" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="10" y="420" width="120" height="60" as="geometry" /> <mxGeometry x="460" y="610" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-36" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-36" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-10" y="250" width="120" height="60" as="geometry" /> <mxGeometry x="-10" y="250" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-37" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-37" value="TEXTURE" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry y="260" width="120" height="60" as="geometry" /> <mxGeometry y="260" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-38" value="VIDEO DEVICE&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-38" value="VIDEO DEVICE&lt;br&gt;1...N" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="10" y="270" width="120" height="60" as="geometry" /> <mxGeometry x="10" y="270" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-39" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-39" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="660" y="299.5" as="sourcePoint" /> <mxPoint x="660" y="299.5" as="sourcePoint" />
<mxPoint x="760" y="299.5" as="targetPoint" /> <mxPoint x="760" y="299.5" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-40" value="OUTPUT&lt;br&gt;WINDOW" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-40" value="OUTPUT&lt;br&gt;WINDOW" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="770" y="270" width="120" height="60" as="geometry" /> <mxGeometry x="770" y="270" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-41" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;dashed=1;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-41" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;dashed=1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="660" y="389.5" as="sourcePoint" /> <mxPoint x="660" y="389.5" as="sourcePoint" />
<mxPoint x="760" y="389.5" as="targetPoint" /> <mxPoint x="760" y="389.5" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-42" value="MONITOR&lt;br&gt;WINDOW" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-42" value="MONITOR&lt;br&gt;WINDOW" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
<mxGeometry x="770" y="360" width="120" height="60" as="geometry" /> <mxGeometry x="770" y="360" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-43" value="CONTEXT" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-43" value="STATE" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="530" y="460" width="120" height="60" as="geometry" /> <mxGeometry x="530" y="460" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-46" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-46" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="510" y="490" as="sourcePoint" /> <mxPoint x="510" y="490" as="sourcePoint" />
<mxPoint x="430" y="450" as="targetPoint" /> <mxPoint x="430" y="450" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-47" value="UNIFORMS" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="3Uv0AfHo8SbA6Qcxonjd-46"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-47" value="UNIFORMS" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="3Uv0AfHo8SbA6Qcxonjd-46" vertex="1" connectable="0">
<mxGeometry x="0.1111" relative="1" as="geometry"> <mxGeometry x="0.1111" relative="1" as="geometry">
<mxPoint x="25" y="-18" as="offset" /> <mxPoint x="25" y="-18" as="offset" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-52" value="MIDI OUTPUT" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-52" value="MIDI OUTPUT" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="770" y="460" width="120" height="60" as="geometry" /> <mxGeometry x="770" y="460" width="120" height="60" as="geometry" />
</mxCell> </mxCell>
<mxCell id="3Uv0AfHo8SbA6Qcxonjd-55" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1"> <mxCell id="3Uv0AfHo8SbA6Qcxonjd-55" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="660" y="490" as="sourcePoint" /> <mxPoint x="660" y="490" as="sourcePoint" />
<mxPoint x="760" y="490" as="targetPoint" /> <mxPoint x="760" y="490" as="targetPoint" />
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="WElHySgPOBnYc4yB3MP0-2" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;entryX=0.744;entryY=0.914;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" target="3Uv0AfHo8SbA6Qcxonjd-1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="520" y="600" as="sourcePoint" />
<mxPoint x="539.58" y="530" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="WElHySgPOBnYc4yB3MP0-4" value="KEYBOARD&lt;br&gt;INPUT" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="620" y="610" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="WElHySgPOBnYc4yB3MP0-5" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;entryX=0.872;entryY=0.914;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" target="3Uv0AfHo8SbA6Qcxonjd-1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="680" y="600" as="sourcePoint" />
<mxPoint x="679.58" y="530" as="targetPoint" />
</mxGeometry>
</mxCell>
</root> </root>
</mxGraphModel> </mxGraphModel>
</diagram> </diagram>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 71 KiB

-2
View File
@@ -33,8 +33,6 @@ UNIFORM_BEATS=iBeats
UNIFORM_FPS=iFPS UNIFORM_FPS=iFPS
# 0/1 if demo # 0/1 if demo
UNIFORM_DEMO=iDemo UNIFORM_DEMO=iDemo
# 0/1 if auto random
UNIFORM_AUTORAND=iAutoRand
# Seed for shader X # Seed for shader X
UNIFORM_SEED_PREFIX=iSeed UNIFORM_SEED_PREFIX=iSeed
-1
View File
@@ -10,7 +10,6 @@ uniform float iTempo; // current tempo in bpm
uniform float iBeats; // elapsed beats since last tempo reset uniform float iBeats; // elapsed beats since last tempo reset
uniform int iFPS; // output window frames per seconds uniform int iFPS; // output window frames per seconds
uniform int iDemo; // 0/1 if demo mode uniform int iDemo; // 0/1 if demo mode
uniform int iAutoRand; // 0/1 if auto random mode
uniform int iSeed1; // a random seed assigned at start uniform int iSeed1; // a random seed assigned at start
uniform vec2 iResolution; // output window resolution in pixels uniform vec2 iResolution; // output window resolution in pixels
uniform vec3 iMidi1_1[20]; // all midi inputs defined uniform vec3 iMidi1_1[20]; // all midi inputs defined
+74 -59
View File
@@ -13,61 +13,65 @@
#include "string.h" #include "string.h"
static void print_help(int status_code) { static void print_help(int status_code) {
puts(PACKAGE puts(
" " VERSION "\n\n" PACKAGE
"usage: " PACKAGE " " " " VERSION "\n\n"
"[-h] " "usage: " PACKAGE " "
"[-v] " "[-h] "
"[-p=PROJECT_PATH] " "[-v] "
"[-c=CFG_FILE] " "[-p=PROJECT_PATH] "
"[-hr] " "[-c=CFG_FILE] "
"[-s=SCREEN] " "[-hr] "
"[-m=SCREEN] " "[-s=SCREEN] "
"[-mo] " "[-m=SCREEN] "
"[-w] " "[-mo] "
"[-t=TEMPO] " "[-w] "
"[-d] " "[-t=TEMPO] "
"[-ar / -nar] " "[-d] "
"[-v=FILE] " "[-ar / -nar] "
"[-vs=SIZE] " "[-arc=CYCLES] "
"[-is=SIZE] " #ifdef VIDEO_IN
"[-sf=STATE_PATH] " "[-vi=FILE] "
"[-ls / -nls] " "[-vs=SIZE] "
"[-ss / -nss] " #endif /* VIDEO_IN */
"[-tm] " "[-is=SIZE] "
"[-tf] " "[-ls / -nls] "
"\n\n" "[-ss / -nss] "
"Fusion Of Real-time Generative Effects.\n\n" "[-tm] "
"options:\n" "[-tf] "
" -h, --help show this help message and exit\n" "\n\n"
" -v, --version print version\n" "Fusion Of Real-time Generative Effects.\n\n"
" -p, --project forge project directory (default: " DATADIR "options:\n"
"/default)\n" " -h, --help show this help message and exit\n"
" -c, --config config file name (default: " " -v, --version print version\n"
"forge_project.cfg)\n" " -p, --project forge project directory (default: " DATADIR
" -hr, --hot-reload hot reload of shaders scripts\n" "/default)\n"
" -s, --screen output screen number (default: primary)\n" " -c, --config config file name (default: "
" -m, --monitor monitor screen number (default: none)\n" "forge_project.cfg)\n"
" -mo, --monitor-only no output screen\n" " -hr, --hot-reload hot reload of shaders scripts\n"
" -w, --windowed not fullscreen\n" " -s, --screen output screen number (default: primary)\n"
" -t, --tempo base tempo (default: 60)\n" " -m, --monitor monitor screen number (default: none)\n"
" -d, --demo demonstration mode (assume " " -mo, --monitor-only no output screen\n"
"--no-save-state, --no-load-state, --auto-random)\n" " -w, --windowed not fullscreen\n"
" -ar, --auto-random randomize state every 4 beats\n" " -t, --tempo base tempo (default: 60)\n"
" -nar, --no-auto-random do not randomize state (default)\n" " -d, --demo demonstration mode (assume "
" -v, --video-in path to video capture device (multiple " "--no-save-state, --no-load-state, --auto-random)\n"
"allowed)\n" " -ar, --auto-random randomize state every cycle (4 beats)\n"
" -vs, --video-size video capture desired height (default: " " -nar, --no-auto-random do not randomize state (default)\n"
"internal texture height)\n" " -arc, --auto-random-cycle auto random cycle length (default: 4)\n"
" -is, --internal-size internal texture height (default: 720)\n" #ifdef VIDEO_IN
" -sf, --state-file saved state file (default: " " -vi, --video-in path to video capture device (multiple "
"forge_saved_state.txt)\n" "allowed)\n"
" -ls, --load-state load saved state (default)\n" " -vs, --video-size video capture desired height (default: "
" -nls, --no-load-state do not load saved state\n" "internal texture height)\n"
" -ss, --save-state save state (default)\n" #endif /* VIDEO_IN */
" -nss, --no-save-state do not save state\n" " -is, --internal-size internal texture height (default: 720)\n"
" -tm, --trace-midi print midi code and values\n" " -ls, --load-state load saved state (default)\n"
" -tf, --trace-fps print fps status of subsystems\n"); " -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"
" -tm, --trace-midi print midi code and values\n"
" -tf, --trace-fps print fps status of subsystems\n");
exit(status_code); exit(status_code);
} }
@@ -124,10 +128,10 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->base_tempo = 60.0f; params->base_tempo = 60.0f;
params->demo = false; params->demo = false;
params->auto_random = false; params->auto_random = false;
params->auto_random_cycle = 4;
params->video_in.length = 0; params->video_in.length = 0;
params->video_size = 0; params->video_size = 0;
params->internal_size = 720; params->internal_size = 720;
strlcpy(params->state_file, "forge_saved_state.txt", STR_LEN);
params->load_state = true; params->load_state = true;
params->save_state = true; params->save_state = true;
params->trace_midi = false; params->trace_midi = false;
@@ -168,25 +172,36 @@ void args_parse(Parameters *params, int argc, char **argv) {
params->auto_random = true; params->auto_random = true;
} else if (is_arg(arg, "-nar") || is_arg(arg, "--no-auto-random")) { } else if (is_arg(arg, "-nar") || is_arg(arg, "--no-auto-random")) {
params->auto_random = false; params->auto_random = false;
} else if (is_arg(arg, "-v") || is_arg(arg, "--video-in")) { } else if (is_arg(arg, "-arc") || is_arg(arg, "--auto-random-cycle")) {
params->auto_random_cycle = parse_uint(arg, value);
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) { if (params->video_in.length == MAX_VIDEO) {
log_error("maximum video input reached"); log_error("maximum video input reached");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
strlcpy(params->video_in.values[params->video_in.length++], value, strlcpy(params->video_in.values[params->video_in.length++], value,
STR_LEN); STR_LEN);
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-vs") || is_arg(arg, "--video-size")) { } else if (is_arg(arg, "-vs") || is_arg(arg, "--video-size")) {
#ifdef VIDEO_IN
params->video_size = parse_uint(arg, value); params->video_size = parse_uint(arg, value);
if (params->video_size == 0) { if (params->video_size == 0) {
invalid_value(arg, value); invalid_value(arg, value);
} }
#else
invalid_arg(arg);
#endif /* VIDEO_IN */
} else if (is_arg(arg, "-is") || is_arg(arg, "--internal-size")) { } else if (is_arg(arg, "-is") || is_arg(arg, "--internal-size")) {
params->internal_size = parse_uint(arg, value); params->internal_size = parse_uint(arg, value);
if (params->internal_size == 0) { if (params->internal_size == 0) {
invalid_value(arg, value); invalid_value(arg, value);
} }
} else if (is_arg(arg, "-sf") || is_arg(arg, "--state-file")) {
strlcpy(params->state_file, value, STR_LEN);
} else if (is_arg(arg, "-ls") || is_arg(arg, "--load-state")) { } else if (is_arg(arg, "-ls") || is_arg(arg, "--load-state")) {
params->load_state = true; params->load_state = true;
} else if (is_arg(arg, "-nls") || is_arg(arg, "--no-load-state")) { } else if (is_arg(arg, "-nls") || is_arg(arg, "--no-load-state")) {
+14
View File
@@ -75,9 +75,12 @@ void config_file_read(ConfigFile *config, const char *path) {
file_read(&file, path); file_read(&file, path);
if (file.error) { if (file.error) {
config->error = true;
return; return;
} }
config->error = false;
line = strtok_r(file.content, "\n", &rest); line = strtok_r(file.content, "\n", &rest);
while (line != NULL) { while (line != NULL) {
@@ -88,6 +91,17 @@ void config_file_read(ConfigFile *config, const char *path) {
file_free(&file); file_free(&file);
} }
bool config_file_has(const ConfigFile *config, const char *key) {
ConfigFileItem c_key;
const ConfigFileItem *item;
strlcpy(c_key.key, key, STR_LEN);
item = (const ConfigFileItem *)hashmap_get(config->map, &c_key);
return item != NULL && strnlen(item->value, STR_LEN) > 0;
}
const char *config_file_get_str(const ConfigFile *config, const char *key, const char *config_file_get_str(const ConfigFile *config, const char *key,
const char *default_value) { const char *default_value) {
ConfigFileItem c_key; ConfigFileItem c_key;
+2
View File
@@ -5,6 +5,8 @@
void config_file_read(ConfigFile *config, const char *path); void config_file_read(ConfigFile *config, const char *path);
bool config_file_has(const ConfigFile *config, const char *key);
const char *config_file_get_str(const ConfigFile *config, const char *key, const char *config_file_get_str(const ConfigFile *config, const char *key,
const char *default_value); const char *default_value);
+122 -113
View File
@@ -57,23 +57,18 @@ static void compute_fps(bool trace_fps) {
} }
} }
static void init_context(const Parameters *params, unsigned int in_count) { static void init_context(const Parameters *params) {
state_init(context, &project.state_config, params->demo, params->auto_random, context = shared_init_context("/" PACKAGE "_context");
params->base_tempo, params->state_file, params->load_state);
context->monitor = params->monitor; context->stop = false;
state_init(context, &project.state_config, params->demo, params->auto_random,
params->auto_random_cycle, params->base_tempo, params->load_state);
memset(context->input_resolutions, 0, sizeof(context->input_resolutions)); memset(context->input_resolutions, 0, sizeof(context->input_resolutions));
memset(context->input_formats, 0, sizeof(context->input_formats)); memset(context->input_formats, 0, sizeof(context->input_formats));
memset(context->input_fps, 0, sizeof(context->input_fps)); memset(context->input_fps, 0, sizeof(context->input_fps));
memset(context->input_swap, 0, sizeof(context->input_swap));
for (unsigned int i = 0; i < in_count; i++) {
if (!inputs.values[i].error) {
context->input_resolutions[i][0] = inputs.values[i].width;
context->input_resolutions[i][1] = inputs.values[i].height;
context->input_formats[i] = inputs.values[i].pixelformat;
}
}
} }
static void free_context() { shared_close_context(context); } static void free_context() { shared_close_context(context); }
@@ -82,11 +77,18 @@ static void reload_shader(unsigned int i) {
shaders_update(&program, &project.fragment_shaders[i][0], i, &project); shaders_update(&program, &project.fragment_shaders[i][0], i, &project);
} }
#ifdef VIDEO_IN
static void init_inputs(const StringArray *video_in, unsigned int video_size) { static void init_inputs(const StringArray *video_in, unsigned int video_size) {
inputs.length = video_in->length; inputs.length = video_in->length;
for (unsigned int i = 0; i < video_in->length; i++) { for (unsigned int i = 0; i < video_in->length; i++) {
video_init(&inputs.values[i], video_in->values[i], video_size); video_init(&inputs.values[i], video_in->values[i], video_size);
if (!inputs.values[i].error) {
context->input_resolutions[i][0] = inputs.values[i].width;
context->input_resolutions[i][1] = inputs.values[i].height;
context->input_formats[i] = inputs.values[i].pixelformat;
}
} }
} }
@@ -97,7 +99,6 @@ static bool start_video_captures(unsigned int video_count, bool trace_fps) {
return false; return false;
} }
} }
return true; return true;
} }
@@ -108,6 +109,7 @@ static void free_video_captures(unsigned int video_count) {
video_free(&inputs.values[i]); video_free(&inputs.values[i]);
} }
} }
#endif /* VIDEO_IN */
static void error_callback(int error, const char *description) { static void error_callback(int error, const char *description) {
log_error("[GLFW] %d: %s", error, description); log_error("[GLFW] %d: %s", error, description);
@@ -118,36 +120,105 @@ static void error_callback(int error, const char *description) {
static void key_callback(Window *window, int key, static void key_callback(Window *window, int key,
__attribute__((unused)) int scancode, int action, __attribute__((unused)) int scancode, int action,
__attribute__((unused)) int mods) { int mods) {
unsigned int event;
event = window_read_key(key, action, mods);
if (window_escape_key(key, action)) { if (window_escape_key(key, action)) {
// close window on escape key // close window on escape key
log_info("[ESC] Closing..."); log_info("[ESC] Closing...");
window_close(window); window_close(window);
} else if (window_char_key(key, action, 82)) { } else if (event > 0) {
// R: randomize state_key_event(context, &project.state_config, event, &midi);
log_info("[R] Randomized");
state_randomize(context, &project.state_config);
state_apply(context, &project.state_config, &midi);
} else if (window_char_key(key, action, 48)) {
// 0: reset
log_info("[0] Reset");
state_reset(context);
state_apply(context, &project.state_config, &midi);
} else if (window_char_key(key, action, 68)) {
// D: demo on/off
log_info((context->demo ? "[D] Demo OFF" : "[D] Demo ON"));
context->demo = !context->demo;
} else if (window_char_key(key, action, 65)) {
// A: auto random on/off
log_info(
(context->auto_random ? "[A] Auto Random OFF" : "[A] Auto Random ON"));
context->auto_random = !context->auto_random;
} }
} }
static void midi_callback(unsigned char code, unsigned char value) { static void midi_callback(unsigned char code, unsigned char value) {
state_apply_event(context, &project.state_config, &midi, code, value, state_midi_event(context, &project.state_config, &midi, code, value,
trace_midi); trace_midi);
}
static bool init(const Parameters *params) {
project_init(&project, params->project_path, params->config_file);
if (project.error) {
return false;
}
init_context(params);
#ifdef VIDEO_IN
init_inputs(&params->video_in, params->video_size);
if (!start_video_captures(params->video_in.length, params->trace_fps)) {
return false;
}
#endif /* VIDEO_IN */
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"));
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;
}
window_startup(error_callback);
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);
shaders_init(&program, &project, context, false);
} else {
window_output = NULL;
}
if (params->monitor) {
window_monitor =
window_init(PACKAGE " " VERSION " (monitor)", params->monitor_screen,
params->windowed, window_output, key_callback);
window_use(window_monitor, context);
shaders_init(&program, &project, context, window_output != NULL);
} else {
window_monitor = NULL;
}
#ifdef VIDEO_IN
shaders_link_inputs(&program, &project, &inputs);
#endif /* VIDEO_IN */
if (program.error) {
context->stop = true;
window_terminate();
exit(EXIT_FAILURE);
}
timer_init(&timer, 30);
log_info("Initialized");
return true;
}
static bool should_close() {
return (window_output != NULL && window_should_close(window_output)) ||
(window_monitor != NULL && window_should_close(window_monitor));
} }
static void loop(bool hr, bool trace_fps) { static void loop(bool hr, bool trace_fps) {
@@ -179,87 +250,11 @@ static void loop(bool hr, bool trace_fps) {
window_events(); window_events();
} }
void forge_run(const Parameters *params) { static void shutdown(const Parameters *params) {
context = shared_init_context("/" PACKAGE "_context");
context->stop = false;
project_init(&project, params->project_path, params->config_file);
if (project.error) {
return;
}
init_inputs(&params->video_in, params->video_size);
init_context(params, project.in_count);
if (!start_video_captures(params->video_in.length, params->trace_fps)) {
return;
}
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"));
if (midi.error) {
context->demo = true;
} else {
trace_midi = params->trace_midi;
if (!midi_background_listen(&midi, context, midi_callback)) {
return;
}
}
if (!state_background_write(context, &project.state_config, &midi)) {
return;
}
window_startup(error_callback);
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);
shaders_init(&program, &project, context, &inputs, false);
} else {
window_output = NULL;
}
if (params->monitor) {
window_monitor =
window_init(PACKAGE " " VERSION " (monitor)", params->monitor_screen,
params->windowed, window_output, key_callback);
window_use(window_monitor, context);
shaders_init(&program, &project, context, &inputs, window_output != NULL);
} else {
window_monitor = NULL;
}
if (program.error) {
context->stop = true;
window_terminate();
exit(EXIT_FAILURE);
}
timer_init(&timer, 30);
log_info("Initialized");
while ((window_output == NULL || !window_should_close(window_output)) &&
(window_monitor == NULL || !window_should_close(window_monitor))) {
loop(params->hot_reload, params->trace_fps);
}
context->stop = true; context->stop = true;
if (params->save_state) { if (params->save_state) {
state_save(context, &project.state_config, params->state_file); state_save(context, &project.state_config);
} }
shaders_free(&program); shaders_free(&program);
@@ -276,11 +271,25 @@ void forge_run(const Parameters *params) {
shaders_free_window(&program, params->output); shaders_free_window(&program, params->output);
} }
#ifdef VIDEO_IN
free_video_captures(params->video_in.length); free_video_captures(params->video_in.length);
#endif /* VIDEO_IN */
free_context(); free_context();
project_free(&project); project_free(&project);
window_terminate(); window_terminate();
}
void forge_run(const Parameters *params) {
if (!init(params)) {
return;
}
while (!should_close()) {
loop(params->hot_reload, params->trace_fps);
}
shutdown(params);
} }
+4
View File
@@ -21,6 +21,10 @@ void midi_open(MidiDevice *device, const char *name) {
void midi_write(const MidiDevice *device, unsigned char code, void midi_write(const MidiDevice *device, unsigned char code,
unsigned char value) { unsigned char value) {
if (device->error) {
return;
}
unsigned char buffer[3]; unsigned char buffer[3];
buffer[0] = 0xB0; buffer[0] = 0xB0;
+135 -92
View File
@@ -14,11 +14,15 @@
#define GLAD_GL_IMPLEMENTATION #define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h> #include <glad/gl.h>
#ifdef VIDEO_IN
#define GLAD_EGL_IMPLEMENTATION #define GLAD_EGL_IMPLEMENTATION
#include <glad/egl.h> #include <glad/egl.h>
#endif /* VIDEO_IN */
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#ifdef VIDEO_IN
#include <GLFW/glfw3native.h> #include <GLFW/glfw3native.h>
#endif /* VIDEO_IN */
static const GLuint unused_uniform = (GLuint)-1; static const GLuint unused_uniform = (GLuint)-1;
@@ -38,6 +42,7 @@ bool check_glerror(ShaderProgram *program) {
static void init_gl(ShaderProgram *program) { static void init_gl(ShaderProgram *program) {
gladLoadGL(glfwGetProcAddress); gladLoadGL(glfwGetProcAddress);
#ifdef VIDEO_IN
program->egl_display = glfwGetEGLDisplay(); program->egl_display = glfwGetEGLDisplay();
if (program->egl_display == EGL_NO_DISPLAY) { if (program->egl_display == EGL_NO_DISPLAY) {
log_error("error: glfwGetEGLDisplay no EGLDisplay returned"); log_error("error: glfwGetEGLDisplay no EGLDisplay returned");
@@ -46,6 +51,7 @@ static void init_gl(ShaderProgram *program) {
} }
gladLoadEGL(program->egl_display, glfwGetProcAddress); gladLoadEGL(program->egl_display, glfwGetProcAddress);
#endif /* VIDEO_IN */
} }
static void init_textures(ShaderProgram *program, static void init_textures(ShaderProgram *program,
@@ -80,9 +86,12 @@ static void rebind_textures(const ShaderProgram *program) {
} }
} }
#ifdef VIDEO_IN
static void link_input_to_texture(ShaderProgram *program, VideoCapture *input, static void link_input_to_texture(ShaderProgram *program, VideoCapture *input,
unsigned int texture_index) { unsigned int texture_index, bool swap) {
input->dma_image = EGL_NO_IMAGE_KHR; EGLImageKHR dma_image;
dma_image = EGL_NO_IMAGE_KHR;
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import.txt // https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
const EGLint attrib_list[] = {EGL_WIDTH, const EGLint attrib_list[] = {EGL_WIDTH,
@@ -92,18 +101,23 @@ static void link_input_to_texture(ShaderProgram *program, VideoCapture *input,
EGL_LINUX_DRM_FOURCC_EXT, EGL_LINUX_DRM_FOURCC_EXT,
input->pixelformat, input->pixelformat,
EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_FD_EXT,
input->exp_fd, swap ? input->exp_fd_swap : input->exp_fd,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT,
0, 0,
EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT,
input->bytesperline, input->bytesperline,
EGL_NONE}; EGL_NONE};
input->dma_image = dma_image = eglCreateImageKHR(program->egl_display, EGL_NO_CONTEXT,
eglCreateImageKHR(program->egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attrib_list);
EGL_LINUX_DMA_BUF_EXT, NULL, attrib_list);
if (input->dma_image == EGL_NO_IMAGE_KHR) { if (swap) {
input->dma_image_swap = dma_image;
} else {
input->dma_image = dma_image;
}
if (dma_image == EGL_NO_IMAGE_KHR) {
log_error("(%s) eglCreateImageKHR failed %04x", input->name, eglGetError()); log_error("(%s) eglCreateImageKHR failed %04x", input->name, eglGetError());
return; return;
} }
@@ -130,12 +144,18 @@ static void init_input(ShaderProgram *program, const ConfigFile *config,
if (i < inputs->length && !inputs->values[i].error) { if (i < inputs->length && !inputs->values[i].error) {
snprintf(name, STR_LEN, "IN_%d_OUT", i + 1); snprintf(name, STR_LEN, "IN_%d_OUT", i + 1);
tex_i = config_file_get_int(config, name, 0); tex_i = config_file_get_int(config, name, 0);
link_input_to_texture(program, &inputs->values[i], tex_i); link_input_to_texture(program, &inputs->values[i], tex_i, 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);
link_input_to_texture(program, &inputs->values[i], tex_i, true);
}
} else { } else {
log_warn("Cannot link input %d", i + 1); log_warn("Cannot link input %d", i + 1);
} }
} }
} }
#endif /* VIDEO_IN */
static void init_framebuffers(ShaderProgram *program, static void init_framebuffers(ShaderProgram *program,
const ConfigFile *config) { const ConfigFile *config) {
@@ -280,6 +300,9 @@ static void init_single_program(ShaderProgram *program, unsigned int i,
program->iautorand_locations[i] = glGetUniformLocation( program->iautorand_locations[i] = glGetUniformLocation(
program->programs[i], program->programs[i],
config_file_get_str(config, "UNIFORM_AUTORAND", "iAutoRand")); config_file_get_str(config, "UNIFORM_AUTORAND", "iAutoRand"));
program->iautorandcycle_locations[i] = glGetUniformLocation(
program->programs[i],
config_file_get_str(config, "UNIFORM_AUTORANDCYCLE", "iAutoRandCycle"));
program->ipage_locations[i] = glGetUniformLocation( program->ipage_locations[i] = glGetUniformLocation(
program->programs[i], program->programs[i],
config_file_get_str(config, "UNIFORM_PAGE", "iPage")); config_file_get_str(config, "UNIFORM_PAGE", "iPage"));
@@ -310,6 +333,13 @@ static void init_single_program(ShaderProgram *program, unsigned int i,
glGetUniformLocation(program->programs[i], name); 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"); prefix = config_file_get_str(config, "UNIFORM_SEED_PREFIX", "iSeed");
for (unsigned int j = 0; j < program->frag_count; j++) { for (unsigned int j = 0; j < program->frag_count; j++) {
snprintf(name, STR_LEN, "%s%d", prefix, j + 1); snprintf(name, STR_LEN, "%s%d", prefix, j + 1);
@@ -388,90 +418,6 @@ static void init_programs(ShaderProgram *program, const ConfigFile *config,
} }
} }
void shaders_init(ShaderProgram *program, const Project *project,
const SharedContext *context, VideoCaptureArray *inputs,
bool rebind) {
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);
program->frag_count = project->frag_count;
program->frag_output_index =
config_file_get_int(&project->config, "FRAG_OUTPUT", 1) - 1;
program->frag_monitor_index =
config_file_get_int(&project->config, "FRAG_MONITOR", 1) - 1;
program->sub_type_count =
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->midi_lengths.length = 0;
init_gl(program);
if (check_glerror(program)) {
return;
}
init_shaders(program, project);
if (program->error || check_glerror(program)) {
return;
}
init_textures(program, context);
if (check_glerror(program)) {
return;
}
init_input(program, &project->config, inputs);
if (check_glerror(program)) {
return;
}
init_framebuffers(program, &project->config);
if (check_glerror(program)) {
return;
}
init_programs(program, &project->config, &project->state_config);
if (check_glerror(program)) {
return;
}
init_vertices(program);
if (check_glerror(program)) {
return;
}
}
bind_vertices(program, rebind ? 1 : 0);
if (check_glerror(program)) {
return;
}
}
void shaders_update(ShaderProgram *program, const File *fragment_shader,
unsigned int i, const Project *project) {
bool result;
result = compile_shader(program->fragment_shaders[i], fragment_shader->path,
fragment_shader->content);
if (result) {
init_single_program(program, i, &project->config, &project->state_config);
log_info("Program %d updated", i + 1);
}
}
static void update_viewport(ShaderProgram *program, static void update_viewport(ShaderProgram *program,
const SharedContext *context) { const SharedContext *context) {
// viewport changed // viewport changed
@@ -545,6 +491,8 @@ static void use_program(const ShaderProgram *program, int i, bool output,
write_uniform_1i(program->idemo_locations[i], context->demo ? 1 : 0); write_uniform_1i(program->idemo_locations[i], context->demo ? 1 : 0);
write_uniform_1i(program->iautorand_locations[i], write_uniform_1i(program->iautorand_locations[i],
context->auto_random ? 1 : 0); context->auto_random ? 1 : 0);
write_uniform_1i(program->iautorandcycle_locations[i],
context->auto_random_cycle);
write_uniform_1i(program->ipage_locations[i], context->page); write_uniform_1i(program->ipage_locations[i], context->page);
write_uniform_1i(program->iselected_locations[i], context->selected + 1); write_uniform_1i(program->iselected_locations[i], context->selected + 1);
write_uniform_2f(program->ires_locations[i], &context->resolution); write_uniform_2f(program->ires_locations[i], &context->resolution);
@@ -562,6 +510,8 @@ static void use_program(const ShaderProgram *program, int i, bool output,
context->input_formats[j]); context->input_formats[j]);
write_uniform_1i(program->iinfps_locations[i * program->in_count + j], write_uniform_1i(program->iinfps_locations[i * program->in_count + j],
context->input_fps[j]); context->input_fps[j]);
write_uniform_1i(program->iinswap_locations[i * program->in_count + j],
context->input_swap[j] ? 1 : 0);
} }
// set seeds uniforms // set seeds uniforms
@@ -612,6 +562,94 @@ static void use_program(const ShaderProgram *program, int i, bool output,
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
} }
void shaders_init(ShaderProgram *program, const Project *project,
const SharedContext *context, bool rebind) {
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);
program->frag_count = project->frag_count;
program->frag_output_index =
config_file_get_int(&project->config, "FRAG_OUTPUT", 1) - 1;
program->frag_monitor_index =
config_file_get_int(&project->config, "FRAG_MONITOR", 1) - 1;
program->sub_type_count =
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->midi_lengths.length = 0;
init_gl(program);
if (check_glerror(program)) {
return;
}
init_shaders(program, project);
if (program->error || check_glerror(program)) {
return;
}
init_textures(program, context);
if (check_glerror(program)) {
return;
}
init_framebuffers(program, &project->config);
if (check_glerror(program)) {
return;
}
init_programs(program, &project->config, &project->state_config);
if (check_glerror(program)) {
return;
}
init_vertices(program);
if (check_glerror(program)) {
return;
}
}
bind_vertices(program, rebind ? 1 : 0);
if (check_glerror(program)) {
return;
}
}
void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs) {
#ifdef VIDEO_IN
init_input(program, &project->config, inputs);
if (check_glerror(program)) {
return;
}
#endif /* VIDEO_IN */
}
void shaders_update(ShaderProgram *program, const File *fragment_shader,
unsigned int i, const Project *project) {
bool result;
result = compile_shader(program->fragment_shaders[i], fragment_shader->path,
fragment_shader->content);
if (result) {
init_single_program(program, i, &project->config, &project->state_config);
log_info("Program %d updated", i + 1);
}
}
void shaders_compute(ShaderProgram *program, const SharedContext *context, void shaders_compute(ShaderProgram *program, const SharedContext *context,
bool monitor, bool output_only) { bool monitor, bool output_only) {
if (!output_only) { if (!output_only) {
@@ -652,7 +690,12 @@ void shaders_free_window(const ShaderProgram *program, bool secondary) {
void shaders_free_input(const ShaderProgram *program, void shaders_free_input(const ShaderProgram *program,
const VideoCapture *input) { const VideoCapture *input) {
#ifdef VIDEO_IN
if (!input->error && input->dma_image != EGL_NO_IMAGE_KHR) { if (!input->error && input->dma_image != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display, input->dma_image); eglDestroyImageKHR(program->egl_display, input->dma_image);
} }
if (!input->error && input->dma_image_swap != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display, input->dma_image_swap);
}
#endif /* VIDEO_IN */
} }
+4 -2
View File
@@ -4,8 +4,10 @@
#define SHADERS_H #define SHADERS_H
void shaders_init(ShaderProgram *program, const Project *project, void shaders_init(ShaderProgram *program, const Project *project,
const SharedContext *context, VideoCaptureArray *inputs, const SharedContext *context, bool rebind);
bool rebind);
void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs);
void shaders_update(ShaderProgram *program, const File *fragment_shader, void shaders_update(ShaderProgram *program, const File *fragment_shader,
unsigned int i, const Project *project); unsigned int i, const Project *project);
+359 -186
View File
@@ -12,6 +12,233 @@
#include "state.h" #include "state.h"
#include "tempo.h" #include "tempo.h"
static void safe_midi_write(const MidiDevice *midi, unsigned int code,
unsigned char value) {
if (code != UNSET_MIDI_CODE) {
midi_write(midi, code, value);
}
}
static void update_page(const SharedContext *context,
const StateConfig *state_config,
const 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],
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;
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],
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);
}
}
}
static void update_active(const SharedContext *context,
const StateConfig *state_config,
const MidiDevice *midi) {
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];
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);
}
}
}
static void update_values(const SharedContext *context,
const StateConfig *state_config,
const MidiDevice *midi) {
unsigned int j;
unsigned int k;
unsigned int part;
for (unsigned int i = 0; i < state_config->midi_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],
context->values[k][i % 3] * MIDI_MAX);
}
}
static void reset(SharedContext *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) {
unsigned int j;
unsigned int l;
unsigned int part;
for (unsigned int i = 0; i < state_config->midi_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];
k++) {
l = state_config->values_offsets.values[part] +
k * state_config->midi_counts.values[part] + j;
if (arr_uint_index_of(state_config->fader_codes,
state_config->midi_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;
}
}
}
for (unsigned int i = 0; i < context->state.length; i++) {
context->state.values[i] = rand_uint(state_config->state_max);
}
}
static void load_from_file(SharedContext *context,
const StateConfig *state_config,
const char *state_file) {
ConfigFile saved_state;
char key[STR_LEN];
config_file_read(&saved_state, state_file);
if (saved_state.error) {
return;
}
tempo_set(&context->tempo,
config_file_get_int(&saved_state, "tempo", context->tempo.tempo));
context->page = config_file_get_int(&saved_state, "page", 0);
context->selected = config_file_get_int(&saved_state, "selected", 0);
for (unsigned int i = 0; i < context->state.length; i++) {
snprintf(key, STR_LEN, "seed_%d", i);
context->seeds[i] =
config_file_get_int(&saved_state, key, context->seeds[i]);
snprintf(key, STR_LEN, "state_%d", i);
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++) {
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++) {
snprintf(key, STR_LEN, "value_%d_x", i);
context->values[i][0] =
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
snprintf(key, STR_LEN, "value_%d_y", i);
context->values[i][1] =
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
snprintf(key, STR_LEN, "value_%d_z", i);
context->values[i][2] =
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
}
config_file_free(&saved_state);
}
static void load_from_default_file(SharedContext *context,
const StateConfig *state_config) {
char state_file[STR_LEN];
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,
unsigned int index) {
char state_file[STR_LEN];
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,
const char *state_file) {
StringArray lines;
log_info("Saving state to '%s'...", state_file);
lines.length = 0;
snprintf(lines.values[lines.length++], STR_LEN, "tempo=%d",
(unsigned int)context->tempo.tempo);
snprintf(lines.values[lines.length++], STR_LEN, "page=%d", context->page);
snprintf(lines.values[lines.length++], STR_LEN, "selected=%d",
context->selected);
for (unsigned int i = 0; i < context->state.length; i++) {
snprintf(lines.values[lines.length++], STR_LEN, "seed_%d=%d", i,
context->seeds[i]);
snprintf(lines.values[lines.length++], STR_LEN, "state_%d=%d", i,
context->state.values[i]);
}
for (unsigned int i = 0; i < state_config->midi_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++) {
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,
(unsigned int)(context->values[i][1] * MIDI_MAX));
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_z=%d", i,
(unsigned int)(context->values[i][2] * MIDI_MAX));
}
file_write(state_file, &lines);
}
static void save_to_default_file(const SharedContext *context,
const StateConfig *state_config) {
char state_file[STR_LEN];
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,
unsigned int index) {
char state_file[STR_LEN];
snprintf(state_file, STR_LEN, "%s.%d.txt", state_config->save_file_prefix,
index);
save_to_file(context, state_config, state_file);
}
void state_parse_config(StateConfig *state_config, const ConfigFile *config) { void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
unsigned int offset; unsigned int offset;
unsigned int count; unsigned int count;
@@ -118,82 +345,81 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
state_config->tap_tempo_code = state_config->tap_tempo_code =
config_file_get_int(config, "TAP_TEMPO", UNSET_MIDI_CODE); config_file_get_int(config, "TAP_TEMPO", UNSET_MIDI_CODE);
}
static void safe_midi_write(const MidiDevice *midi, unsigned int code, strlcpy(state_config->save_file_prefix,
unsigned char value) { config_file_get_str(config, "SAVE_FILE_PREFIX", "forge_save"),
if (code != UNSET_MIDI_CODE) { STR_LEN);
midi_write(midi, code, value);
}
}
static void update_page(const SharedContext *context, state_config->hotkey_randomize =
const StateConfig *state_config, config_file_get_int(config, "HOTKEY_RANDOMIZE", 82);
const MidiDevice *midi) { state_config->hotkey_reset =
unsigned int page_item_min; config_file_get_int(config, "HOTKEY_RESET", 1082);
unsigned int page_item_max; state_config->hotkey_demo = config_file_get_int(config, "HOTKEY_DEMO", 68);
// SHOW PAGE state_config->hotkey_autorand =
for (unsigned int i = 0; i < state_config->select_page_codes.length; i++) { config_file_get_int(config, "HOTKEY_AUTORAND", 65);
safe_midi_write(midi, state_config->select_page_codes.values[i], state_config->hotkey_autorand_down =
i == context->page ? MIDI_MAX : 0); 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);
// SHOW PAGE ITEM if (config_file_has(config, "HOTKEY_LOAD_COUNT")) {
page_item_min = state_config->select_item_codes.length * context->page; state_config->hotkey_load.length =
page_item_max = page_item_min + state_config->select_item_codes.length; config_file_get_int(config, "HOTKEY_LOAD_COUNT", 0);
if (context->state.values[context->selected] >= page_item_min && for (unsigned int i = 0; i < state_config->hotkey_load.length; i++) {
context->state.values[context->selected] < page_item_max) { snprintf(name, STR_LEN, "HOTKEY_LOAD_%d", i + 1);
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) { state_config->hotkey_load.values[i] =
safe_midi_write(midi, state_config->select_item_codes.values[i], config_file_get_int(config, name, 0);
i == context->state.values[context->selected] -
page_item_min
? MIDI_MAX
: 0);
} }
} else { } else {
for (unsigned int i = 0; i < state_config->select_item_codes.length; i++) { state_config->hotkey_load.length = 10;
safe_midi_write(midi, state_config->select_item_codes.values[i], 0); 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;
}
if (config_file_has(config, "HOTKEY_SAVE_COUNT")) {
state_config->hotkey_save.length =
config_file_get_int(config, "HOTKEY_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);
} }
} 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;
} }
} }
static void update_active(const SharedContext *context, void state_midi_event(SharedContext *context, const StateConfig *state_config,
const StateConfig *state_config, const MidiDevice *midi, unsigned char code,
const MidiDevice *midi) { unsigned char value, bool trace_midi) {
unsigned int k; unsigned int i;
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];
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);
}
}
}
static void update_values(const SharedContext *context,
const StateConfig *state_config,
const MidiDevice *midi) {
unsigned int j; unsigned int j;
unsigned int k; unsigned int k;
unsigned int part; unsigned int part;
for (unsigned int i = 0; i < state_config->midi_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],
context->values[k][i % 3] * MIDI_MAX);
}
}
void state_apply_event(SharedContext *context, const StateConfig *state_config,
const MidiDevice *midi, unsigned char code,
unsigned char value, bool trace_midi) {
unsigned int i, j, k, part;
bool found; bool found;
found = false; found = false;
@@ -281,10 +507,60 @@ void state_apply_event(SharedContext *context, const StateConfig *state_config,
} }
} }
void state_apply(const SharedContext *context, const StateConfig *state_config, void state_key_event(SharedContext *context, const StateConfig *state_config,
const MidiDevice *midi) { unsigned int code, const MidiDevice *midi) {
if (!midi->error) { unsigned int index;
if (code == state_config->hotkey_randomize) {
log_info("[%d] Randomized", code);
randomize(context, state_config);
update_values(context, state_config, midi); 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);
} }
} }
@@ -292,7 +568,10 @@ bool state_background_write(SharedContext *context,
const StateConfig *state_config, const StateConfig *state_config,
const MidiDevice *midi) { const MidiDevice *midi) {
pid_t pid; pid_t pid;
bool beat_active, last_active, change, last_change; bool beat_active;
bool last_active;
bool change;
bool last_change;
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
@@ -314,7 +593,7 @@ bool state_background_write(SharedContext *context,
last_change = false; last_change = false;
while (!context->stop) { while (!context->stop) {
beat_active = tempo_progress(&context->tempo, 1.0) < 0.25; beat_active = tempo_progress(&context->tempo, 1.0) < 0.5;
if (!midi->error && beat_active != last_active) { if (!midi->error && beat_active != last_active) {
safe_midi_write(midi, state_config->tap_tempo_code, safe_midi_write(midi, state_config->tap_tempo_code,
@@ -327,12 +606,13 @@ bool state_background_write(SharedContext *context,
last_active = beat_active; last_active = beat_active;
change = tempo_progress(&context->tempo, 4.0) < 0.25; change = tempo_progress(&context->tempo,
(double)context->auto_random_cycle) < 0.5;
if (context->auto_random && change && !last_change) { if (context->auto_random && change && !last_change) {
state_randomize(context, state_config); randomize(context, state_config);
state_apply(context, state_config, midi); update_values(context, state_config, midi);
} }
last_change = change; last_change = change;
@@ -342,59 +622,19 @@ bool state_background_write(SharedContext *context,
return false; return false;
} }
static void state_load(SharedContext *context, const StateConfig *state_config,
const char *state_file) {
ConfigFile saved_state;
char key[STR_LEN];
config_file_read(&saved_state, state_file);
tempo_set(&context->tempo,
config_file_get_int(&saved_state, "tempo", context->tempo.tempo));
context->page = config_file_get_int(&saved_state, "page", 0);
context->selected = config_file_get_int(&saved_state, "selected", 0);
for (unsigned int i = 0; i < context->state.length; i++) {
snprintf(key, STR_LEN, "seed_%d", i);
context->seeds[i] =
config_file_get_int(&saved_state, key, context->seeds[i]);
snprintf(key, STR_LEN, "state_%d", i);
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++) {
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++) {
snprintf(key, STR_LEN, "value_%d_x", i);
context->values[i][0] =
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
snprintf(key, STR_LEN, "value_%d_y", i);
context->values[i][1] =
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
snprintf(key, STR_LEN, "value_%d_z", i);
context->values[i][2] =
(float)config_file_get_int(&saved_state, key, 0) / MIDI_MAX;
}
config_file_free(&saved_state);
}
void state_init(SharedContext *context, const StateConfig *state_config, void state_init(SharedContext *context, const StateConfig *state_config,
bool demo, bool auto_random, unsigned int base_tempo, bool demo, bool auto_random, unsigned int auto_random_cycles,
const char *state_file, bool load_state) { unsigned int base_tempo, bool load_state) {
tempo_init(&context->tempo); tempo_init(&context->tempo, base_tempo);
tempo_set(&context->tempo, base_tempo);
context->demo = demo; context->demo = demo;
context->auto_random = auto_random; 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)); memset(context->state.values, 0, sizeof(context->state.values));
if (auto_random) { if (auto_random) {
state_randomize(context, state_config); randomize(context, state_config);
} }
memset(context->active, 0, sizeof(context->active)); memset(context->active, 0, sizeof(context->active));
@@ -410,77 +650,10 @@ void state_init(SharedContext *context, const StateConfig *state_config,
} }
if (load_state) { if (load_state) {
state_load(context, state_config, state_file); load_from_default_file(context, state_config);
} }
} }
void state_reset(SharedContext *context) { void state_save(const SharedContext *context, const StateConfig *state_config) {
memset(context->values, 0, sizeof(context->values)); save_to_default_file(context, state_config);
memset(context->state.values, 0, sizeof(context->state.values));
}
void state_randomize(SharedContext *context, const StateConfig *state_config) {
unsigned int j;
unsigned int l;
unsigned int part;
for (unsigned int i = 0; i < state_config->midi_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];
k++) {
l = state_config->values_offsets.values[part] +
k * state_config->midi_counts.values[part] + j;
if (arr_uint_index_of(state_config->fader_codes,
state_config->midi_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;
}
}
}
for (unsigned int i = 0; i < context->state.length; i++) {
context->state.values[i] = rand_uint(state_config->state_max);
}
}
void state_save(const SharedContext *context, const StateConfig *state_config,
const char *state_file) {
StringArray lines;
log_info("Saving state to '%s'...", state_file);
lines.length = 0;
snprintf(lines.values[lines.length++], STR_LEN, "tempo=%d",
(unsigned int)context->tempo.tempo);
snprintf(lines.values[lines.length++], STR_LEN, "page=%d", context->page);
snprintf(lines.values[lines.length++], STR_LEN, "selected=%d",
context->selected);
for (unsigned int i = 0; i < context->state.length; i++) {
snprintf(lines.values[lines.length++], STR_LEN, "seed_%d=%d", i,
context->seeds[i]);
snprintf(lines.values[lines.length++], STR_LEN, "state_%d=%d", i,
context->state.values[i]);
}
for (unsigned int i = 0; i < state_config->midi_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++) {
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,
(unsigned int)(context->values[i][1] * MIDI_MAX));
snprintf(lines.values[lines.length++], STR_LEN, "value_%d_z=%d", i,
(unsigned int)(context->values[i][2] * MIDI_MAX));
}
file_write(state_file, &lines);
} }
+9 -14
View File
@@ -5,26 +5,21 @@
void state_parse_config(StateConfig *state_config, const ConfigFile *config); void state_parse_config(StateConfig *state_config, const ConfigFile *config);
void state_apply_event(SharedContext *context, const StateConfig *state_config, void state_midi_event(SharedContext *context, const StateConfig *state_config,
const MidiDevice *midi, unsigned char code, const MidiDevice *midi, unsigned char code,
unsigned char value, bool trace_midi); unsigned char value, bool trace_midi);
void state_key_event(SharedContext *context, const StateConfig *state_config,
unsigned int code, const MidiDevice *midi);
bool state_background_write(SharedContext *context, bool state_background_write(SharedContext *context,
const StateConfig *state_config, const StateConfig *state_config,
const MidiDevice *midi); const MidiDevice *midi);
void state_init(SharedContext *context, const StateConfig *state_config, void state_init(SharedContext *context, const StateConfig *state_config,
bool demo, bool auto_random, unsigned int base_tempo, bool demo, bool auto_random, unsigned int auto_random_cycles,
const char *state_file, bool load_state); unsigned int base_tempo, bool load_state);
void state_reset(SharedContext *context); void state_save(const SharedContext *context, const StateConfig *state_config);
void state_randomize(SharedContext *context, const StateConfig *state_config);
void state_apply(const SharedContext *context, const StateConfig *state_config,
const MidiDevice *midi);
void state_save(const SharedContext *context, const StateConfig *state_config,
const char *state_file);
#endif /* STATE_H */ #endif /* STATE_H */
+4 -3
View File
@@ -6,6 +6,8 @@
#include "config.h" #include "config.h"
#include "string.h" #include "string.h"
static bool is_digit(char c) { return c >= '0' && c <= '9'; }
unsigned int string_trim(char *str) { unsigned int string_trim(char *str) {
// https://www.delftstack.com/howto/c/trim-string-in-c/ // https://www.delftstack.com/howto/c/trim-string-in-c/
unsigned int start; unsigned int start;
@@ -37,8 +39,6 @@ unsigned int string_trim(char *str) {
return end - start + 1; return end - start + 1;
} }
static bool is_digit(char c) { return c >= '0' && c <= '9'; }
bool string_is_number(const char *value) { bool string_is_number(const char *value) {
unsigned long value_len; unsigned long value_len;
@@ -59,7 +59,8 @@ bool string_is_number(const char *value) {
char *string_replace_at(const char *src, unsigned int from, unsigned int to, char *string_replace_at(const char *src, unsigned int from, unsigned int to,
const char *rpl) { const char *rpl) {
unsigned long src_len, rpl_len; unsigned long src_len;
unsigned long rpl_len;
char *dst; char *dst;
src_len = strnlen(src, STR_LEN * STR_LEN); src_len = strnlen(src, STR_LEN * STR_LEN);
+21 -9
View File
@@ -25,14 +25,6 @@ static void reset_tap_chain(Tempo *tempo, long t) {
memset(tempo->tap_durations, 0, sizeof(tempo->tap_durations)); memset(tempo->tap_durations, 0, sizeof(tempo->tap_durations));
} }
void tempo_init(Tempo *tempo) {
long t;
t = now();
reset_tap_chain(tempo, t);
}
static bool is_chain_active(const Tempo tempo, long t) { static bool is_chain_active(const Tempo tempo, long t) {
return (tempo.last_tap + MAX_BEAT_LENGTH) > t && return (tempo.last_tap + MAX_BEAT_LENGTH) > t &&
(tempo.last_tap + (tempo.beat_length * BEATS_UNTIL_CHAIN_RESET)) > t; (tempo.last_tap + (tempo.beat_length * BEATS_UNTIL_CHAIN_RESET)) > t;
@@ -98,11 +90,31 @@ static void add_tap_to_chain(Tempo *tempo, long t) {
tempo->tempo = 60000.0 / tempo->beat_length; tempo->tempo = 60000.0 / tempo->beat_length;
} }
void tempo_set(Tempo *tempo, float value) { void tempo_init(Tempo *tempo, float value) {
long t;
t = now();
reset_tap_chain(tempo, t);
tempo->tempo = value; tempo->tempo = value;
tempo->beat_length = 60000.0 / value; tempo->beat_length = 60000.0 / value;
} }
void tempo_set(Tempo *tempo, float value) {
long t;
long progress;
t = now();
progress = (t - tempo->last_reset) % tempo->beat_length;
tempo->tempo = value;
tempo->beat_length = 60000.0 / value;
reset_tap_chain(tempo, t - progress);
}
void tempo_tap(Tempo *tempo) { void tempo_tap(Tempo *tempo) {
long t; long t;
+1 -1
View File
@@ -3,7 +3,7 @@
#ifndef TEMPO_H #ifndef TEMPO_H
#define TEMPO_H #define TEMPO_H
void tempo_init(Tempo *tempo); void tempo_init(Tempo *tempo, float value);
void tempo_tap(Tempo *tempo); void tempo_tap(Tempo *tempo);
+2 -1
View File
@@ -18,7 +18,8 @@ bool timer_inc(Timer *timer) {
double timer_reset(Timer *timer) { double timer_reset(Timer *timer) {
struct timeval stop; struct timeval stop;
double secs, per_secs; double secs;
double per_secs;
gettimeofday(&stop, NULL); gettimeofday(&stop, NULL);
+32 -8
View File
@@ -1,10 +1,12 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#ifdef VIDEO_IN
#include <glad/egl.h> #include <glad/egl.h>
#include <linux/videodev2.h>
#endif /* VIDEO_IN */
#include <glad/gl.h> #include <glad/gl.h>
#include <hashmap.h> #include <hashmap.h>
#include <linmath.h> #include <linmath.h>
#include <linux/videodev2.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
@@ -43,10 +45,10 @@ typedef struct Parameters {
float base_tempo; float base_tempo;
bool demo; bool demo;
bool auto_random; bool auto_random;
unsigned int auto_random_cycle;
StringArray video_in; StringArray video_in;
unsigned int video_size; unsigned int video_size;
unsigned int internal_size; unsigned int internal_size;
char state_file[STR_LEN];
bool load_state; bool load_state;
bool save_state; bool save_state;
bool trace_midi; bool trace_midi;
@@ -102,8 +104,10 @@ typedef struct ShaderProgram {
GLuint iinres_locations[ARRAY_SIZE]; GLuint iinres_locations[ARRAY_SIZE];
GLuint iinfmt_locations[ARRAY_SIZE]; GLuint iinfmt_locations[ARRAY_SIZE];
GLuint iinfps_locations[ARRAY_SIZE]; GLuint iinfps_locations[ARRAY_SIZE];
GLuint iinswap_locations[ARRAY_SIZE];
GLuint idemo_locations[ARRAY_SIZE]; GLuint idemo_locations[ARRAY_SIZE];
GLuint iautorand_locations[ARRAY_SIZE]; GLuint iautorand_locations[ARRAY_SIZE];
GLuint iautorandcycle_locations[ARRAY_SIZE];
GLuint iseed_locations[ARRAY_SIZE]; GLuint iseed_locations[ARRAY_SIZE];
GLuint istate_locations[ARRAY_SIZE]; GLuint istate_locations[ARRAY_SIZE];
GLuint ipage_locations[ARRAY_SIZE]; GLuint ipage_locations[ARRAY_SIZE];
@@ -124,7 +128,9 @@ typedef struct ShaderProgram {
unsigned int active_count; unsigned int active_count;
unsigned int in_count; unsigned int in_count;
#ifdef VIDEO_IN
EGLDisplay egl_display; EGLDisplay egl_display;
#endif /* VIDEO_IN */
} ShaderProgram; } ShaderProgram;
// video.c // video.c
@@ -132,15 +138,20 @@ typedef struct ShaderProgram {
typedef struct VideoCapture { typedef struct VideoCapture {
char name[STR_LEN]; char name[STR_LEN];
bool error; bool error;
bool with_swap;
int fd; int fd;
int exp_fd; int exp_fd;
int exp_fd_swap;
unsigned int width; unsigned int width;
unsigned int height; unsigned int height;
unsigned int pixelformat; unsigned int pixelformat;
unsigned int bytesperline; unsigned int bytesperline;
bool output; #ifdef VIDEO_IN
struct v4l2_buffer buf; struct v4l2_buffer buf;
struct v4l2_buffer buf_swap;
EGLImageKHR dma_image; EGLImageKHR dma_image;
EGLImageKHR dma_image_swap;
#endif /* VIDEO_IN */
} VideoCapture; } VideoCapture;
typedef ARRAY(VideoCaptureArray, VideoCapture); typedef ARRAY(VideoCaptureArray, VideoCapture);
@@ -166,13 +177,10 @@ typedef struct Tempo {
typedef struct SharedContext { typedef struct SharedContext {
int fd; int fd;
vec2 resolution; vec2 resolution;
vec2 tex_resolution; vec2 tex_resolution;
vec2 input_resolutions[MAX_VIDEO]; vec2 input_resolutions[MAX_VIDEO];
double time; double time;
unsigned int fps;
Tempo tempo; Tempo tempo;
double tempo_total; double tempo_total;
UintArray state; UintArray state;
@@ -182,11 +190,12 @@ typedef struct SharedContext {
vec3 values[ARRAY_SIZE]; vec3 values[ARRAY_SIZE];
bool demo; bool demo;
bool auto_random; bool auto_random;
unsigned int auto_random_cycle;
unsigned int seeds[MAX_FRAG]; unsigned int seeds[MAX_FRAG];
bool monitor; unsigned int fps;
unsigned int input_formats[MAX_VIDEO]; unsigned int input_formats[MAX_VIDEO];
unsigned int input_fps[MAX_VIDEO]; unsigned int input_fps[MAX_VIDEO];
bool input_swap[MAX_VIDEO];
bool stop; bool stop;
} SharedContext; } SharedContext;
@@ -210,6 +219,20 @@ typedef struct StateConfig {
unsigned int value_count; unsigned int value_count;
unsigned int tap_tempo_code; unsigned int tap_tempo_code;
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;
UintArray hotkey_load;
UintArray hotkey_save;
} StateConfig; } StateConfig;
// timer.c // timer.c
@@ -224,6 +247,7 @@ typedef struct Timer {
typedef struct ConfigFile { typedef struct ConfigFile {
struct hashmap *map; struct hashmap *map;
bool error;
} ConfigFile; } ConfigFile;
typedef struct ConfigFileItem { typedef struct ConfigFileItem {
+87 -59
View File
@@ -1,3 +1,5 @@
#ifdef VIDEO_IN
#include <bsd/string.h> #include <bsd/string.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@@ -15,6 +17,9 @@
#include "timer.h" #include "timer.h"
#include "video.h" #include "video.h"
static const unsigned int pixel_format = V4L2_PIX_FMT_YUYV;
static const enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
static void ioctl_error(VideoCapture *video_capture, const char *operation, static void ioctl_error(VideoCapture *video_capture, const char *operation,
const char *default_msg) { const char *default_msg) {
if (errno == EINVAL) { if (errno == EINVAL) {
@@ -70,9 +75,8 @@ static void open_device(VideoCapture *video_capture, const char *name) {
strlcpy(video_capture->name, name, STR_LEN); strlcpy(video_capture->name, name, STR_LEN);
video_capture->error = false; video_capture->error = false;
video_capture->fd = -1; video_capture->fd = -1;
video_capture->exp_fd = -1;
video_capture->fd = open(name, O_RDWR); video_capture->fd = open(name, O_RDWR | O_NONBLOCK);
if (video_capture->fd == -1) { if (video_capture->fd == -1) {
log_warn("(%s) Cannot open device", name); log_warn("(%s) Cannot open device", name);
video_capture->error = true; video_capture->error = true;
@@ -114,7 +118,7 @@ static bool get_available_sizes(VideoCapture *video_capture,
index = 0; index = 0;
fmt_enum.index = index; fmt_enum.index = index;
fmt_enum.pixel_format = V4L2_PIX_FMT_YUYV; fmt_enum.pixel_format = pixel_format;
found = false; found = false;
video_capture->width = 0; video_capture->width = 0;
@@ -146,7 +150,7 @@ static bool get_available_sizes(VideoCapture *video_capture,
memset(&fmt_enum, 0, sizeof(fmt_enum)); memset(&fmt_enum, 0, sizeof(fmt_enum));
fmt_enum.index = ++index; fmt_enum.index = ++index;
fmt_enum.pixel_format = V4L2_PIX_FMT_YUYV; fmt_enum.pixel_format = pixel_format;
} }
if (video_capture->height == 0) { if (video_capture->height == 0) {
@@ -161,26 +165,18 @@ static bool get_available_sizes(VideoCapture *video_capture,
static bool set_format(VideoCapture *video_capture) { static bool set_format(VideoCapture *video_capture) {
struct v4l2_format fmt; struct v4l2_format fmt;
video_capture->output = false;
memset(&fmt, 0, sizeof(fmt)); memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.type = buf_type;
fmt.fmt.pix.width = video_capture->width; fmt.fmt.pix.width = video_capture->width;
fmt.fmt.pix.height = video_capture->height; fmt.fmt.pix.height = video_capture->height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.pixelformat = pixel_format;
fmt.fmt.pix.field = V4L2_FIELD_ANY; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (ioctl(video_capture->fd, VIDIOC_S_FMT, &fmt) == -1) { if (ioctl(video_capture->fd, VIDIOC_S_FMT, &fmt) == -1) {
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ioctl_error(video_capture, "VIDIOC_S_FMT",
"Requested buffer type not supported");
video_capture->output = true; return false;
if (ioctl(video_capture->fd, VIDIOC_S_FMT, &fmt) == -1) {
ioctl_error(video_capture, "VIDIOC_S_FMT",
"Requested buffer type not supported");
return false;
}
} }
video_capture->width = fmt.fmt.pix.width; video_capture->width = fmt.fmt.pix.width;
@@ -202,10 +198,9 @@ static bool request_buffers(VideoCapture *video_capture) {
memset(&reqbuf, 0, sizeof(reqbuf)); memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.type = video_capture->output ? V4L2_BUF_TYPE_VIDEO_OUTPUT reqbuf.type = buf_type;
: V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP; reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 1; reqbuf.count = 2;
if (ioctl(video_capture->fd, VIDIOC_REQBUFS, &reqbuf) == -1) { if (ioctl(video_capture->fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
ioctl_error(video_capture, "VIDIOC_REQBUFS", ioctl_error(video_capture, "VIDIOC_REQBUFS",
@@ -215,19 +210,21 @@ static bool request_buffers(VideoCapture *video_capture) {
log_info("(%s) V4L2 Buffer Count: %d", video_capture->name, reqbuf.count); log_info("(%s) V4L2 Buffer Count: %d", video_capture->name, reqbuf.count);
video_capture->with_swap = reqbuf.count > 1;
return true; return true;
} }
static bool export_buffer(VideoCapture *video_capture) { static bool export_buffer(VideoCapture *video_capture, int *fd,
unsigned int index) {
struct v4l2_exportbuffer expbuf; struct v4l2_exportbuffer expbuf;
video_capture->exp_fd = -1; *fd = -1;
memset(&expbuf, 0, sizeof(expbuf)); memset(&expbuf, 0, sizeof(expbuf));
expbuf.type = video_capture->output ? V4L2_BUF_TYPE_VIDEO_OUTPUT expbuf.type = buf_type;
: V4L2_BUF_TYPE_VIDEO_CAPTURE; expbuf.index = index;
expbuf.index = 0;
expbuf.flags = O_RDONLY; expbuf.flags = O_RDONLY;
if (ioctl(video_capture->fd, VIDIOC_EXPBUF, &expbuf) == -1) { if (ioctl(video_capture->fd, VIDIOC_EXPBUF, &expbuf) == -1) {
@@ -237,12 +234,23 @@ static bool export_buffer(VideoCapture *video_capture) {
return false; return false;
} }
video_capture->exp_fd = expbuf.fd; *fd = expbuf.fd;
return true; return true;
} }
static const enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; static bool export_buffers(VideoCapture *video_capture) {
bool result;
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);
}
return result;
}
static bool open_stream(VideoCapture *video_capture) { static bool open_stream(VideoCapture *video_capture) {
if (ioctl(video_capture->fd, VIDIOC_STREAMON, &buf_type) == -1) { if (ioctl(video_capture->fd, VIDIOC_STREAMON, &buf_type) == -1) {
@@ -255,21 +263,50 @@ static bool open_stream(VideoCapture *video_capture) {
return true; return true;
} }
static void create_image_buffer(VideoCapture *video_capture) { static void create_image_buffer(const VideoCapture *video_capture,
memset(&video_capture->buf, 0, sizeof(video_capture->buf)); struct v4l2_buffer *buf, unsigned int index) {
memset(buf, 0, sizeof(*buf));
video_capture->buf.type = video_capture->output ? V4L2_BUF_TYPE_VIDEO_OUTPUT buf->type = buf_type;
: V4L2_BUF_TYPE_VIDEO_CAPTURE; buf->memory = V4L2_MEMORY_MMAP;
video_capture->buf.memory = V4L2_MEMORY_MMAP; buf->index = index;
video_capture->buf.index = 0;
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf); ioctl(video_capture->fd, VIDIOC_QBUF, buf);
}
static void create_image_buffers(VideoCapture *video_capture) {
create_image_buffer(video_capture, &video_capture->buf, 0);
if (video_capture->with_swap) {
create_image_buffer(video_capture, &video_capture->buf_swap, 1);
}
} }
static void close_stream(const VideoCapture *video_capture) { static void close_stream(const VideoCapture *video_capture) {
ioctl(video_capture->fd, VIDIOC_STREAMOFF, &buf_type); ioctl(video_capture->fd, VIDIOC_STREAMOFF, &buf_type);
} }
static unsigned int read_video(const VideoCapture *video_capture, bool swap) {
int result;
result = 0;
if ((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 (!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;
}
return result;
}
void video_init(VideoCapture *video_capture, const char *name, void video_init(VideoCapture *video_capture, const char *name,
unsigned int preferred_height) { unsigned int preferred_height) {
open_device(video_capture, name); open_device(video_capture, name);
@@ -294,7 +331,7 @@ void video_init(VideoCapture *video_capture, const char *name,
return; return;
} }
if (!export_buffer(video_capture)) { if (!export_buffers(video_capture)) {
return; return;
} }
@@ -302,25 +339,7 @@ void video_init(VideoCapture *video_capture, const char *name,
return; return;
} }
create_image_buffer(video_capture); create_image_buffers(video_capture);
}
static bool read_video(VideoCapture *video_capture) {
if (ioctl(video_capture->fd, VIDIOC_DQBUF, &video_capture->buf) == -1) {
ioctl_error(video_capture, "VIDIOC_DQBUF",
"buffer type not supported or no buffer allocated or the index "
"is out of bounds");
return false;
}
if (ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf) == -1) {
ioctl_error(video_capture, "VIDIOC_QBUF",
"buffer type not supported or no buffer allocated or the index "
"is out of bounds");
return false;
}
return true;
} }
bool video_background_read(VideoCapture *video_capture, SharedContext *context, bool video_background_read(VideoCapture *video_capture, SharedContext *context,
@@ -328,6 +347,7 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
pid_t pid; pid_t pid;
Timer timer; Timer timer;
double fps; double fps;
unsigned int video_result;
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
@@ -341,9 +361,9 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
pid); pid);
timer_init(&timer, 30); timer_init(&timer, 30);
while (!context->stop && read_video(video_capture)) { while (!context->stop) {
// repeat infinitely video_result = read_video(video_capture, context->input_swap[input_index]);
if (timer_inc(&timer)) { if (video_result > 0 && timer_inc(&timer)) {
fps = timer_reset(&timer); fps = timer_reset(&timer);
context->input_fps[input_index] = (unsigned int)round(fps); context->input_fps[input_index] = (unsigned int)round(fps);
@@ -351,6 +371,9 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
log_trace("(%s) %.2ffps", video_capture->name, fps); log_trace("(%s) %.2ffps", video_capture->name, fps);
} }
} }
if (video_result > 0) {
context->input_swap[input_index] = video_result == 2;
}
} }
if (context->stop) { if (context->stop) {
log_info("(%s) background acquisition stopped by main thread (pid: %d)", log_info("(%s) background acquisition stopped by main thread (pid: %d)",
@@ -370,7 +393,12 @@ void video_free(const VideoCapture *video_capture) {
if (video_capture->exp_fd != -1) { if (video_capture->exp_fd != -1) {
close(video_capture->exp_fd); close(video_capture->exp_fd);
} }
if (video_capture->exp_fd_swap != -1) {
close(video_capture->exp_fd);
}
if (video_capture->fd != -1) { if (video_capture->fd != -1) {
close(video_capture->fd); close(video_capture->fd);
} }
} }
#endif /* VIDEO_IN */
+21 -3
View File
@@ -121,7 +121,8 @@ void window_events() { glfwPollEvents(); }
double window_get_time() { return glfwGetTime(); } double window_get_time() { return glfwGetTime(); }
void window_use(Window *window, SharedContext *context) { void window_use(Window *window, SharedContext *context) {
int width, height; int width;
int height;
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
glfwGetFramebufferSize(window, &width, &height); glfwGetFramebufferSize(window, &width, &height);
@@ -145,6 +146,23 @@ bool window_escape_key(int key, int action) {
return key == GLFW_KEY_ESCAPE && action == GLFW_PRESS; return key == GLFW_KEY_ESCAPE && action == GLFW_PRESS;
} }
bool window_char_key(int key, int action, const int char_code) { unsigned int window_read_key(int key, int action, int mods) {
return key == char_code && action == GLFW_PRESS; unsigned int result;
if (action == GLFW_RELEASE || key == GLFW_KEY_LEFT_SHIFT ||
key == GLFW_KEY_RIGHT_SHIFT || key == GLFW_KEY_LEFT_CONTROL ||
key == GLFW_KEY_RIGHT_CONTROL || key == GLFW_KEY_LEFT_ALT ||
key == GLFW_KEY_RIGHT_ALT) {
return 0;
}
result = key;
if ((mods & GLFW_MOD_SHIFT) > 0) {
result += 1000;
}
if ((mods & GLFW_MOD_CONTROL) > 0) {
result += 10000;
}
if ((mods & GLFW_MOD_ALT) > 0) {
result += 100000;
}
return result;
} }
+1 -1
View File
@@ -27,6 +27,6 @@ bool window_should_close(Window *window);
bool window_escape_key(int key, int action); bool window_escape_key(int key, int action);
bool window_char_key(int key, int action, const int char_code); unsigned int window_read_key(int key, int action, int mods);
#endif /* WINDOW_H */ #endif /* WINDOW_H */