9 Commits

Author SHA1 Message Date
klemek f124cfacfb forge (steel) v1.2.1
Clang Build CI / run-no-video (push) Successful in 2m44s
Clang Build CI / build-release (push) Failing after 52s
Clang Build CI / run-video (push) Successful in 2m41s
Clang Lint CI / lint-no-video (push) Successful in 2m38s
Clang Lint CI / lint-video (push) Successful in 1m22s
2026-05-24 23:32:25 +02:00
klemek 4e14404726 docs: add projection mapping docs 2026-05-24 23:31:55 +02:00
klemek db77846f1b feat: default project up to 4 masks 2026-05-24 23:29:41 +02:00
klemek 57fa627bed forge (steel) v1.2.0
Clang Build CI / run-no-video (push) Successful in 1m8s
Clang Lint CI / lint-no-video (push) Successful in 1m7s
Clang Build CI / build-release (push) Successful in 1m48s
Clang Build CI / run-video (push) Successful in 1m13s
Clang Lint CI / lint-video (push) Successful in 1m55s
2026-05-21 00:43:02 +02:00
klemek 9dbfd34566 docs: update DEVELOPMENT.md 2026-05-21 00:42:42 +02:00
klemek 3dc2a77529 fix: same key multiple uses
Clang Lint CI / lint-no-video (push) Successful in 1m4s
Clang Build CI / run-video (push) Successful in 1m4s
Clang Build CI / run-no-video (push) Successful in 1m4s
Clang Build CI / build-release (push) Has been cancelled
Clang Lint CI / lint-video (push) Successful in 1m27s
2026-05-21 00:41:44 +02:00
klemek 7dbce62182 feat: ignore some values in auto random 2026-05-21 00:32:03 +02:00
klemek bb84350591 feat: default projection mapping with shift + M 2026-05-20 23:20:55 +02:00
klemek 7db7f4b89e Update README.md 2026-05-17 01:01:53 +02:00
12 changed files with 320 additions and 34 deletions
+1 -1
View File
@@ -124,7 +124,7 @@ make -f Makefile.dev release-arch
- [x] (clean) static functions at top of files
- [x] Configurable key codes
- [x] Monitor improvements
- [ ] Ignore some values in auto random
- [x] Ignore some values in auto random
- [x] build without video in
- [ ] Update README monitor/keymap
- [x] Auto reconnect midi input
+2 -2
View File
@@ -1,12 +1,12 @@
pkgname=forge-steel
pkgver=1.1.3
pkgver=1.2.1
pkgrel=1
pkgdesc="Fusion Of Real Time Generative Effects"
arch=('i686' 'pentium4' 'x86_64' 'arm' 'armv7h' 'armv6h' 'aarch64' 'riscv64')
depends=('glfw>=1:3', 'v4l-utils>=1.32', 'alsa-lib>=1.2', 'libglvnd>=1.7')
url="https://git.klemek.fr/klemek/forge-steel"
source=("${pkgname}-steel-${pkgver}.tar.gz::https://git.klemek.fr/klemek/forge-steel/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
sha256sums=('536370c14aaac35729b29270cecca01a6c966bcae306fa2ec20f8a514a3ed8bd')
sha256sums=('57559bde7a524e3c9e70de4bc9b675a5a086590a8d85697cebd80c0ebd09f149')
srcdir=build
backup=("usr/share/${pkgname}")
+23 -1
View File
@@ -1,4 +1,4 @@
[![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/lint.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=lint.yml) [![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/build.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=build.yml) ![LOC](https://img.shields.io/badge/cloc-3.4k-blue?style=flat-square)
[![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/lint.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=lint.yml) [![](https://git.klemek.fr/klemek/forge-steel/actions/workflows/build.yml/badge.svg?branch=main&style=flat-square)](https://git.klemek.fr/klemek/forge-steel/actions?workflow=build.yml) ![LOC](https://img.shields.io/badge/cloc-3.6k-blue?style=flat-square)
<!-- omit from toc -->
# F.O.R.G.E. (Steel)
@@ -263,6 +263,28 @@ Working with pages and items, you can use the following predefined sources and e
See the [printable version](./docs/forge_default_mapping.pdf).
### Available hotkeys
| Hotkey | Function |
| ------ | -------- |
| <kbd>Shift</kbd> + <kbd>F</kbd> | Switch between shader-decoded or hardware decoded YUYV |
| <kbd>M</kbd> | Projection mapping 1 |
| <kbd>Shift</kbd> + <kbd>M</kbd> | Projection mapping 2 |
| <kbd>Ctrl</kbd> + <kbd>M</kbd> | Projection mapping 3 |
| <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd> | Projection mapping 4 |
### Projection Mappings
In any of the projection mapping mode, use the nanoKONTROL2 last 4 columns to control 4 on-screen points:
| Midi Control | Function |
| ------ | -------- |
| Fader N | point N - X position |
| Knob N | point N - Y position |
| Last column top button | activate projection mapping |
| Last column middle button | invert projection mapping |
| Last column bottom button | show projection borders |
## Making your own FORGE project
You want to embrace the "user" in "user-defined"? It's time to make your own project.
+1 -1
View File
@@ -1,4 +1,4 @@
AC_INIT([forge], [steel-1.1.3], [klemek.dev@proton.me])
AC_INIT([forge], [steel-1.2.1], [klemek.dev@proton.me])
AM_INIT_AUTOMAKE
AC_PROG_CC
+70 -1
View File
@@ -235,10 +235,14 @@ GROUP_1_6_Y=19
GROUP_1_6_Z=
# Same for group 2
GROUP_2_ACTIVE_COUNT=3
GROUP_2_ACTIVE_COUNT=7
GROUP_2_ACTIVE_1=36
GROUP_2_ACTIVE_2=68
GROUP_2_ACTIVE_3=52
GROUP_2_ACTIVE_4=1077
GROUP_2_ACTIVE_5=11077
GROUP_2_ACTIVE_6=101077
GROUP_2_ACTIVE_7=111077
GROUP_2_COUNT=7
GROUP_2_1_X=37
GROUP_2_1_Y=53
@@ -271,6 +275,71 @@ GROUP_3_2_X=0
GROUP_3_2_Y=16
GROUP_3_2_Z=11089
# Auto-Random Ignore (ARI) prefixes
ARI_COUNT=62
ARI_1=GROUP_3_1_Y
ARI_2=GROUP_3_2_Z
ARI_3=GROUP_2_5_4
ARI_4=GROUP_2_5_4
ARI_5=GROUP_2_5_4
ARI_6=GROUP_2_2_X_4
ARI_7=GROUP_2_2_Y_4
ARI_8=GROUP_2_2_Z_4
ARI_9=GROUP_2_4_X_4
ARI_10=GROUP_2_4_Y_4
ARI_11=GROUP_2_4_Z_4
ARI_12=GROUP_2_6_X_4
ARI_13=GROUP_2_6_Y_4
ARI_14=GROUP_2_6_Z_4
ARI_15=GROUP_2_7_X_4
ARI_16=GROUP_2_7_Y_4
ARI_17=GROUP_2_7_Z_4
ARI_18=GROUP_2_5_5
ARI_19=GROUP_2_5_5
ARI_20=GROUP_2_5_5
ARI_21=GROUP_2_2_X_5
ARI_22=GROUP_2_2_Y_5
ARI_23=GROUP_2_2_Z_5
ARI_24=GROUP_2_4_X_5
ARI_25=GROUP_2_4_Y_5
ARI_26=GROUP_2_4_Z_5
ARI_27=GROUP_2_6_X_5
ARI_28=GROUP_2_6_Y_5
ARI_29=GROUP_2_6_Z_5
ARI_30=GROUP_2_7_X_5
ARI_31=GROUP_2_7_Y_5
ARI_32=GROUP_2_7_Z_5
ARI_33=GROUP_2_5_6
ARI_34=GROUP_2_5_6
ARI_35=GROUP_2_5_6
ARI_36=GROUP_2_2_X_6
ARI_37=GROUP_2_2_Y_6
ARI_38=GROUP_2_2_Z_6
ARI_39=GROUP_2_4_X_6
ARI_40=GROUP_2_4_Y_6
ARI_41=GROUP_2_4_Z_6
ARI_42=GROUP_2_6_X_6
ARI_43=GROUP_2_6_Y_6
ARI_44=GROUP_2_6_Z_6
ARI_45=GROUP_2_7_X_6
ARI_46=GROUP_2_7_Y_6
ARI_47=GROUP_2_7_Z_6
ARI_48=GROUP_2_5_7
ARI_49=GROUP_2_5_7
ARI_50=GROUP_2_5_7
ARI_51=GROUP_2_2_X_7
ARI_52=GROUP_2_2_Y_7
ARI_53=GROUP_2_2_Z_7
ARI_54=GROUP_2_4_X_7
ARI_55=GROUP_2_4_Y_7
ARI_56=GROUP_2_4_Z_7
ARI_57=GROUP_2_6_X_7
ARI_58=GROUP_2_6_Y_7
ARI_59=GROUP_2_6_Z_7
ARI_60=GROUP_2_7_X_7
ARI_61=GROUP_2_7_Y_7
ARI_62=GROUP_2_7_Z_7
# =====
# OTHER
# =====
+1 -5
View File
@@ -16,15 +16,11 @@ uniform sampler2D iTex0;
uniform int iSeed8;
uniform vec3 iGroup2_3[7];
uniform vec3 iGroup3_1[2];
uniform int iDemo;
uniform int iAutoRand;
void main() {
vec4 color = fx_stage(vUV, iTex9, iTex0, iSeed8, iGroup2_3[0], iGroup2_3[1].xy, iGroup2_3[2], iGroup2_3[3].xy, iGroup2_3[4], iGroup2_3[5].xy, iGroup2_3[6]);
if (iDemo < 1 && iAutoRand < 1) {
color = mix(color, vec4(0), iGroup3_1[0].y);
}
color = mix(color, vec4(0), iGroup3_1[0].y);
fragColor = color;
}
+102 -1
View File
@@ -6,8 +6,109 @@
in vec2 vUV;
out vec4 fragColor;
#include inc_map.glsl
uniform sampler2D iTex0;
uniform vec3 iGroup2_4[7];
uniform vec3 iGroup2_5[7];
uniform vec3 iGroup2_6[7];
uniform vec3 iGroup2_7[7];
void main() {
fragColor = texture(iTex0, vUV);
bool visible = (iGroup2_4[4].x + iGroup2_5[4].x + iGroup2_6[4].x + iGroup2_7[4].x) > 0;
bool invert = (iGroup2_4[4].y + iGroup2_5[4].y + iGroup2_6[4].y + iGroup2_7[4].y) > 0;
float rect = clamp(iGroup2_4[4].z + iGroup2_5[4].z + iGroup2_6[4].z + iGroup2_7[4].z, 0, 1);
bool modified = false;
vec2 uv = mix(vec2(0), vUV, base_mask(vUV));
if (iGroup2_4[4].x > 0) {
vec2 p11 = vec2(iGroup2_4[6].xy);
vec2 p12 = vec2(iGroup2_4[1].xy);
vec2 p13 = vec2(iGroup2_4[3].xy);
vec2 p14 = vec2(iGroup2_4[5].xy);
p12.x = 1 - p12.x;
p13.y = 1 - p13.y;
p14.x = 1 - p14.x;
p14.y = 1 - p14.y;
vec2 uv1 = project_4p(vUV, p11, p12, p13, p14);
uv1 = mix(vec2(0), uv1, base_mask(uv1));
uv = uv1;
modified = true;
}
if (iGroup2_5[4].x > 0) {
vec2 p21 = vec2(iGroup2_5[6].xy);
vec2 p22 = vec2(iGroup2_5[1].xy);
vec2 p23 = vec2(iGroup2_5[3].xy);
vec2 p24 = vec2(iGroup2_5[5].xy);
p22.x = 1 - p22.x;
p23.y = 1 - p23.y;
p24.x = 1 - p24.x;
p24.y = 1 - p24.y;
vec2 uv2 = project_4p(vUV, p21, p22, p23, p24);
uv2 = mix(vec2(0), uv2, base_mask(uv2));
if (modified) {
uv = mix(uv, uv2, step(0.0001, length(uv2)));
} else {
uv = uv2;
}
modified = true;
}
if (iGroup2_6[4].x > 0) {
vec2 p31 = vec2(iGroup2_6[6].xy);
vec2 p32 = vec2(iGroup2_6[1].xy);
vec2 p33 = vec2(iGroup2_6[3].xy);
vec2 p34 = vec2(iGroup2_6[5].xy);
p32.x = 1 - p32.x;
p33.y = 1 - p33.y;
p34.x = 1 - p34.x;
p34.y = 1 - p34.y;
vec2 uv3 = project_4p(vUV, p31, p32, p33, p34);
uv3 = mix(vec2(0), uv3, base_mask(uv3));
if (modified) {
uv = mix(uv, uv3, step(0.0001, length(uv3)));
} else {
uv = uv3;
}
modified = true;
}
if (iGroup2_7[4].x > 0) {
vec2 p41 = vec2(iGroup2_7[6].xy);
vec2 p42 = vec2(iGroup2_7[1].xy);
vec2 p43 = vec2(iGroup2_7[3].xy);
vec2 p44 = vec2(iGroup2_7[5].xy);
p42.x = 1 - p42.x;
p43.y = 1 - p43.y;
p44.x = 1 - p44.x;
p44.y = 1 - p44.y;
vec2 uv4 = project_4p(vUV, p41, p42, p43, p44);
uv4 = mix(vec2(0), uv4, base_mask(uv4));
if (modified) {
uv = mix(uv, uv4, step(0.0001, length(uv4)));
} else {
uv = uv4;
}
modified = true;
}
vec4 color = texture(iTex0, invert ? vUV : uv);
if (visible) {
float mask = step(0.0001, length(uv));
color *= mix(mask, 1 - mask, invert ? 1 : 0);
color = mix(color, color + mask * vec4(1), rect * (step(uv.x, 0.01) + step(uv.y, 0.01) + step(1 - uv.y, 0.01) + step(1 - uv.x, 0.01)));
}
fragColor = color;
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef INC_MAP
#define INC_MAP
vec2 project_4p(vec2 uv, vec2 p1, vec2 p2, vec2 p3, vec2 p4) {
float k1 = (p4.y - p3.y) / (p2.y - p3.y);
float k2 = (p3.y - p1.y) / (p2.y - p3.y);
float a = (p4.x - p3.x + k1 * p3.x - k1 * p2.x) / (p1.x - p3.x - k2 * p3.x + k2 * p2.x);
float b = k1 + a * k2;
float c = 1 - a - b;
// https://math.stackexchange.com/questions/296794/finding-the-transform-matrix-from-4-projected-points-with-javascript/339033#339033
mat3 projection_a = mat3(
a * p1.x, a * p1.y, a,
b * p2.x, b * p2.y, b,
c * p3.x, c * p3.y, c
);
mat3 projection_a_prime = inverse(projection_a);
mat3 projection_b = mat3(
0, 0, -1,
1, 0, 1,
0, 1, 1
);
vec3 tmp = projection_b * projection_a_prime * vec3(uv, 1);
return tmp.xy / tmp.z;
}
float base_mask(vec2 uv) {
return step(0, uv.x) * step(-1, -uv.x) * step(0, uv.y) * step(-1, -uv.y);
}
#endif
+10
View File
@@ -12,6 +12,16 @@ unsigned int arr_uint_index_of(UintArray array, unsigned int value) {
return ARRAY_NOT_FOUND;
}
bool arr_string_match(StringArray array, const char *needle) {
for (unsigned int i = 0; i < array.length; i++) {
if (strncmp(array.values[i], needle, strlen(array.values[i])) == 0) {
return true;
}
}
return false;
}
unsigned int arr_uint_remap_index(UintArray offsets, unsigned int *index) {
if (offsets.length == 0) {
return 0;
+2
View File
@@ -7,4 +7,6 @@ unsigned int arr_uint_index_of(UintArray array, unsigned int value);
unsigned int arr_uint_remap_index(UintArray offsets, unsigned int *index);
bool arr_string_match(StringArray array, const char *needle);
#endif /* ARR_H */
+77 -22
View File
@@ -98,11 +98,15 @@ static void randomize(Context *context, StateConfig state_config) {
l = state_config.values_offsets.values[part] +
k * state_config.group_counts.values[part] + j;
if (arr_uint_index_of(state_config.fader_codes,
state_config.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;
if (arr_uint_index_of(state_config.random_ignored, (l * 3) + (i % 3)) ==
ARRAY_NOT_FOUND) {
if (arr_uint_index_of(state_config.fader_codes,
state_config.codes.values[i]) !=
ARRAY_NOT_FOUND) {
context->values[l][i % 3] = (float)rand_uint(MIDI_MAX + 1) / MIDI_MAX;
} else {
context->values[l][i % 3] = rand_uint(2) == 1 ? 1 : 0;
}
}
}
}
@@ -271,6 +275,8 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
unsigned int offset;
unsigned int count;
unsigned int length;
unsigned int value_offset;
StringArray ignored_codes;
char name[STR_LEN];
length = config_file_get_int(config, "SELECT_PAGE_COUNT", 0);
@@ -312,6 +318,18 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
config_file_get_int(config, name, UNSET_MIDI_CODE);
}
length = config_file_get_int(config, "ARI_COUNT", 0);
if (length > ARRAY_SIZE) {
length = ARRAY_SIZE;
}
ignored_codes.length = length;
for (unsigned int i = 0; i < ignored_codes.length; i++) {
snprintf(name, STR_LEN, "ARI_%d", i + 1);
strlcpy(ignored_codes.values[i],
config_file_get_str(config, name, "unkown"), STR_LEN);
}
length = config_file_get_int(config, "GROUP_COUNT", 0);
if (length > ARRAY_SIZE) {
length = ARRAY_SIZE;
@@ -361,26 +379,60 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
length = ARRAY_SIZE;
}
state_config->codes.length = count * 3;
state_config->random_ignored.length = 0;
for (unsigned int i = 0; i < state_config->group_counts.length; i++) {
offset = state_config->group_offsets.values[i];
value_offset = state_config->values_offsets.values[i];
for (unsigned int j = 0; j < state_config->group_counts.values[i]; j++) {
if ((offset + j) * 3 < ARRAY_SIZE) {
snprintf(name, STR_LEN, "GROUP_%d_%d_X", i + 1, j + 1);
state_config->codes.values[(offset + j) * 3] =
config_file_get_int(config, name, UNSET_MIDI_CODE);
for (unsigned int k = 0;
k < state_config->group_active_counts.values[i]; k++) {
snprintf(name, STR_LEN, "GROUP_%d_%d_X_%d", i + 1, j + 1, k + 1);
if (arr_string_match(ignored_codes, name)) {
state_config->random_ignored
.values[state_config->random_ignored.length++] =
(value_offset + k * state_config->group_counts.values[i] + j) *
3;
}
}
}
if ((offset + j) * 3 + 1 < ARRAY_SIZE) {
snprintf(name, STR_LEN, "GROUP_%d_%d_Y", i + 1, j + 1);
state_config->codes.values[(offset + j) * 3 + 1] =
config_file_get_int(config, name, UNSET_MIDI_CODE);
for (unsigned int k = 0;
k < state_config->group_active_counts.values[i]; k++) {
snprintf(name, STR_LEN, "GROUP_%d_%d_Y_%d", i + 1, j + 1, k + 1);
if (arr_string_match(ignored_codes, name)) {
state_config->random_ignored
.values[state_config->random_ignored.length++] =
(value_offset + k * state_config->group_counts.values[i] + j) *
3 +
1;
}
}
}
if ((offset + j) * 3 + 2 < ARRAY_SIZE) {
snprintf(name, STR_LEN, "GROUP_%d_%d_Z", i + 1, j + 1);
state_config->codes.values[(offset + j) * 3 + 2] =
config_file_get_int(config, name, UNSET_MIDI_CODE);
for (unsigned int k = 0;
k < state_config->group_active_counts.values[i]; k++) {
snprintf(name, STR_LEN, "GROUP_%d_%d_Z_%d", i + 1, j + 1, k + 1);
if (arr_string_match(ignored_codes, name)) {
state_config->random_ignored
.values[state_config->random_ignored.length++] =
(value_offset + k * state_config->group_counts.values[i] + j) *
3 +
2;
}
}
}
}
}
@@ -477,6 +529,9 @@ static bool compute_event(Context *context, StateConfig state_config,
unsigned int j;
unsigned int k;
unsigned int part;
bool found;
found = false;
// PAGE CHANGE
i = arr_uint_index_of(state_config.select_page_codes, code);
@@ -485,7 +540,7 @@ static bool compute_event(Context *context, StateConfig state_config,
context->page = i;
update_page(context, state_config, midi);
}
return true;
found = true;
}
// TARGET CHANGE
@@ -495,7 +550,7 @@ static bool compute_event(Context *context, StateConfig state_config,
context->selected = i;
update_page(context, state_config, midi);
}
return true;
found = true;
}
// ITEM CHANGE
@@ -506,7 +561,7 @@ static bool compute_event(Context *context, StateConfig state_config,
context->page * state_config.select_item_codes.length + i;
update_page(context, state_config, midi);
}
return true;
found = true;
}
// ACTIVE CHANGE
@@ -518,7 +573,7 @@ static bool compute_event(Context *context, StateConfig state_config,
update_active(context, state_config, midi, true);
update_values(context, state_config, midi);
}
return true;
found = true;
}
// VALUE CHANGE
@@ -540,7 +595,7 @@ static bool compute_event(Context *context, StateConfig state_config,
safe_midi_write(midi, code, MIDI_MAX);
}
}
return true;
found = true;
}
// TAP TEMPO
@@ -549,7 +604,7 @@ static bool compute_event(Context *context, StateConfig state_config,
if (value > 0) {
tempo_tap(&context->tempo);
}
return true;
found = true;
}
// OTHER KEYS
@@ -559,7 +614,7 @@ static bool compute_event(Context *context, StateConfig state_config,
randomize(context, state_config);
update_values(context, state_config, midi);
}
return true;
found = true;
}
if (code == state_config.key_reset) {
@@ -568,7 +623,7 @@ static bool compute_event(Context *context, StateConfig state_config,
reset(context);
update_values(context, state_config, midi);
}
return true;
found = true;
}
if (code == state_config.key_demo) {
@@ -576,7 +631,7 @@ static bool compute_event(Context *context, StateConfig state_config,
log_info((context->demo ? "[%d] Demo OFF" : "[%d] Demo ON"), code);
context->demo = !context->demo;
}
return true;
found = true;
}
if (code == state_config.key_autorand) {
@@ -585,7 +640,7 @@ static bool compute_event(Context *context, StateConfig state_config,
log_info("[%d] Auto Random %s", code,
context->auto_random ? "ON" : "OFF");
}
return true;
found = true;
}
if (code == state_config.key_autorand_down) {
@@ -595,7 +650,7 @@ static bool compute_event(Context *context, StateConfig state_config,
}
log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
}
return true;
found = true;
}
if (code == state_config.key_autorand_up) {
@@ -603,7 +658,7 @@ static bool compute_event(Context *context, StateConfig state_config,
context->auto_random_cycle += 1;
log_info("[%d] Auto Random Cycle: %d", code, context->auto_random_cycle);
}
return true;
found = true;
}
if (code == state_config.key_tempo_up) {
@@ -611,7 +666,7 @@ static bool compute_event(Context *context, StateConfig state_config,
tempo_set(&context->tempo, context->tempo.tempo + 1);
log_info("[%d] Tempo: %f", code, context->tempo);
}
return true;
found = true;
}
if (code == state_config.key_tempo_down) {
@@ -621,7 +676,7 @@ static bool compute_event(Context *context, StateConfig state_config,
}
log_info("[%d] Tempo: %f", code, context->tempo.tempo);
}
return true;
found = true;
}
// LOAD STATE
@@ -632,7 +687,7 @@ static bool compute_event(Context *context, StateConfig state_config,
log_info("[%d] Loading state %d", code, i + 1);
load_from_index_file(context, state_config, i + 1);
}
return true;
found = true;
}
// SAVE STATE
@@ -643,10 +698,10 @@ static bool compute_event(Context *context, StateConfig state_config,
log_info("[%d] Saving state %d", code, i + 1);
save_to_index_file(context, state_config, i + 1);
}
return true;
found = true;
}
return false;
return found;
}
void state_midi_event(Context *context, StateConfig state_config,
+1
View File
@@ -251,6 +251,7 @@ typedef struct StateConfig {
UintArray codes;
UintArray fader_codes;
UintArray values_offsets;
UintArray random_ignored;
unsigned int value_count;