From 3d50717dbedb49df081f279e5b9fca44fd3ecab5 Mon Sep 17 00:00:00 2001 From: klemek Date: Mon, 2 Nov 2026 13:07:02 +0100 Subject: [PATCH] feat: live fx/mix values from time/beats --- src/forge_invar/display.py | 3 + src/forge_invar/forge_data.py | 35 ++++++---- src/forge_invar/glsl_funcs.py | 121 ++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 src/forge_invar/glsl_funcs.py diff --git a/src/forge_invar/display.py b/src/forge_invar/display.py index 7545608..9032b5a 100644 --- a/src/forge_invar/display.py +++ b/src/forge_invar/display.py @@ -84,10 +84,13 @@ def __get_running_display(data: ForgeData) -> Image.Image: draw2.text((45, 44), hex(data.frag_state(3))[2].upper(), fill="white", font=FONT_2) draw2.text((103, 32), hex(data.frag_state(5))[2].upper(), fill="white", font=FONT_2) # TODO print when random + # TODO print when inverted draw2.text((74, 32), "K" if data.mix_mode_key else "M", fill="white", font=FONT_2) # TODO show feedback + # TODO show blank out + # TODO show inverted out return __xor(im, im_overlay) diff --git a/src/forge_invar/forge_data.py b/src/forge_invar/forge_data.py index da397cb..6f4fe4b 100644 --- a/src/forge_invar/forge_data.py +++ b/src/forge_invar/forge_data.py @@ -4,6 +4,7 @@ import typing from .constants import FORGE_SAVE_BACKUP_PATH, FORGE_SAVE_PATH, MOCK_SAVE_PATH from .enums import ForgeState +from .glsl_funcs import fract, magic, magic_trigger class ForgeRawData: @@ -77,13 +78,16 @@ class ForgeData: def active(self, n: int) -> int: return int(self.__data[f"active_{n}"]) + def seed(self, n: int) -> int: + return int(self.__data[f"seed_{n}"]) + @property def time(self) -> float: return self.__data["time"] @property def time_progress(self) -> float: - return self.time - int(self.time) + return fract(self.time) @property def tempo(self) -> float: @@ -95,7 +99,7 @@ class ForgeData: @property def beat_progress(self) -> float: - return self.beats - int(self.beats) + return fract(self.beats) @property def page(self) -> int: @@ -107,24 +111,33 @@ class ForgeData: @property def fxa_value(self) -> float: - # todo random and time based - return self.value(18)[0] + return magic( + self.time, self.beats, self.value(18), (1.0, 0.0, 0.0), self.seed(2) + ) @property def fxb_value(self) -> float: - return self.value(25)[0] - - @property - def mfx_value(self) -> float: - return self.value(32)[0] + return magic( + self.time, self.beats, self.value(25), (1.0, 0.0, 0.0), self.seed(3) + ) @property def mix_value(self) -> float: - return self.value(34)[0] + return magic( + self.time, self.beats, self.value(34), (1.0, 0.0, 0.0), self.seed(4) + ) @property def mix_mode_key(self) -> bool: - return self.value(33)[0] > 0. + return magic_trigger( + self.time, self.beats, (self.value(33)[0], 0.0, 0.0), self.seed(4) + 10.0 + ) + + @property + def mfx_value(self) -> float: + return magic( + self.time, self.beats, self.value(32), (1.0, 0.0, 0.0), self.seed(5) + ) def mock(self) -> typing.Self: self.__data.read(MOCK_SAVE_PATH) diff --git a/src/forge_invar/glsl_funcs.py b/src/forge_invar/glsl_funcs.py new file mode 100644 index 0000000..7af59f5 --- /dev/null +++ b/src/forge_invar/glsl_funcs.py @@ -0,0 +1,121 @@ +from math import cos, floor, sin + +# glsl base + +PI = 3.1415927 + + +def mod(x: float, y: float) -> float: + return x % y + + +def fract(x: float) -> float: + return mod(x, 1.0) + + +def mix(x: float, y: float, a: float) -> float: + if a <= 0.0: + return x + if a >= 1.0: + return y + return x + (y - x) * a + + +def step(edge: float, x: float) -> float: + if x < edge: + return 0.0 + return 1.0 + + +# inc_rand.glsl + +RAND_C = 43758.5453 + + +def rand(seed: float) -> float: + return fract(sin(seed) * RAND_C) + + +# inc_time.glsl + + +def rand_time(itime: float, ibeats: float, seed: float, beats: int = 4) -> float: + return rand(seed + floor(ibeats / beats)) + + +def divider(x: float) -> float: + if x < 0.1: + return 0.0 + if x < 0.2: + return 0.125 + if x < 0.3: + return 0.25 + if x < 0.4: + return 0.5 + if x < 0.6: + return 1.0 + if x < 0.8: + return 2.0 + return 4.0 + + +def mod_time(ibeats: float, k: float, k2: float = 1.0) -> float: + return mod(divider(k) * ibeats * k2 * 0.25, 1.0) + + +def cos_time(ibeats: float, k: float) -> float: + return cos(mod_time(ibeats, k, 5.0) * 2.0 * PI) + + +# inc_magic.glsl + + +def magic_f( + itime: float, + ibeats: float, + f: tuple[float, float, float], + b: tuple[float, float, float], + i: float, +) -> tuple[float, float]: + return ( + mix(f[0], rand_time(itime, ibeats, i + 1.0), min(1.0, b[2])), + mix(f[1], rand_time(itime, ibeats, i + 2.0), min(1.0, b[2])), + ) + + +def magic_b( + itime: float, ibeats: float, b: tuple[float, float, float], i: float +) -> tuple[float, float, float]: + return ( + mix(b[0], step(0.2, rand_time(itime, ibeats, i + 3.0)), min(1.0, b[2])), + mix(b[1], step(0.5, rand_time(itime, ibeats, i + 4.0)), min(1.0, b[2])), + min(1.0, b[2]), + ) + + +def magic_trigger( + itime: float, ibeats: float, b: tuple[float, float, float], i: float +) -> bool: + return magic_b(itime, ibeats, b, i)[0] > 0.0 + + +def magic( + itime: float, + ibeats: float, + f: tuple[float, float, float], + b: tuple[float, float, float], + i: float, +) -> float: + mf = magic_f(itime, ibeats, f, b, i) + mb = magic_b(itime, ibeats, b, i) + + return mix( + 0.0, + mf[0] + * mix( + 1.0 - mod_time(ibeats, mf[1]), + cos_time(ibeats, mf[1]) * 0.5 + 0.5, + mb[1], + ), + mb[0], + )