Everything — your hand, the room, a satellite, the markets, the sky — becomes an OSC control signal fused through a modulation matrix. Includes: - hub + normalize (One-Euro) + matrix (curves/gates/quantize) + transform (tweaks/groups) - 20+ workers: sim sensors, 8 keyless world feeds, ephemeris/almanac/clock (computed), time-warp replay (quakes/weather/db), and the dealgod market warehouse feed - planetary orbital LFOs, SQLite recorder, city targeting - live browser console: mute, group macros, drag-to-patch, inspector, Web MIDI Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Simulated ESP32 sensor rig: a virtual hand gliding over a VL53L5CX ToF matrix
|
|
plus a BH1750 ambient-light cell. It is musical because the smooth Lissajous
|
|
motion gives the visualizer a living, breathing performer to react to before any
|
|
real hardware exists — hand position and light become continuous playable knobs.
|
|
"""
|
|
import argparse
|
|
import math
|
|
import random
|
|
import time
|
|
|
|
from pythonosc.udp_client import SimpleUDPClient
|
|
|
|
NAME = "sim_esp32"
|
|
RATE_HZ = 60.0
|
|
DT = 1.0 / RATE_HZ
|
|
|
|
|
|
def clamp(v, lo, hi):
|
|
return lo if v < lo else hi if v > hi else v
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser(description="Simulate the future ESP32 ToF + light sensor rig.")
|
|
ap.add_argument("--host", default="127.0.0.1")
|
|
ap.add_argument("--port", type=int, default=9000)
|
|
args = ap.parse_args()
|
|
|
|
client = SimpleUDPClient(args.host, args.port)
|
|
|
|
# A fixed phase offset so multiple instances don't march in lockstep.
|
|
seed = random.random() * 1000.0
|
|
t0 = time.time()
|
|
|
|
# Schedule the first simulated flash 8-20s out.
|
|
next_flash = t0 + random.uniform(8.0, 20.0)
|
|
|
|
print(f"[{NAME}] emitting /gs/tof/*, /gs/light/* every {DT:.4f}s")
|
|
|
|
while True:
|
|
try:
|
|
now = time.time()
|
|
t = (now - t0) + seed
|
|
|
|
# --- Hand X/Y: sums of a few sines at irrational-ish rates + slow drift.
|
|
# Lissajous-feel motion, never white noise. Kept smoothly inside 0..1.
|
|
cx = (
|
|
0.50
|
|
+ 0.30 * math.sin(t * 0.31700)
|
|
+ 0.12 * math.sin(t * 0.71300 + 1.3)
|
|
+ 0.06 * math.sin(t * 1.61803 + 2.1) # golden-ratio wobble
|
|
)
|
|
cy = (
|
|
0.50
|
|
+ 0.28 * math.sin(t * 0.27100 + 0.7)
|
|
+ 0.13 * math.sin(t * 0.61800 + 2.4) # 1/phi
|
|
+ 0.05 * math.sin(t * 1.41421 + 0.2) # sqrt(2)
|
|
)
|
|
|
|
# --- Proximity: slow breathing in/out, hand drifting toward/away.
|
|
near = (
|
|
0.50
|
|
+ 0.35 * math.sin(t * 0.19000)
|
|
+ 0.10 * math.sin(t * 0.53000 + 1.1)
|
|
)
|
|
|
|
# --- Ambient light: drifts in the hundreds of lux, with slow occasional dips.
|
|
base = 500.0 + 250.0 * math.sin(t * 0.041 + 0.5) + 90.0 * math.sin(t * 0.113 + 2.0)
|
|
# A slow, mostly-shallow dip that deepens rarely (raising to a power keeps it near 1
|
|
# most of the time and only occasionally sinks toward a shadow).
|
|
dip = 0.55 + 0.45 * math.sin(t * 0.023 + 1.7)
|
|
dip = clamp(dip, 0.0, 1.0) ** 3 # mostly bright, sometimes dark
|
|
lux = clamp(base * (0.35 + 0.65 * dip), 0.0, 1000.0)
|
|
|
|
client.send_message("/gs/tof/cx", clamp(cx, 0.0, 1.0))
|
|
client.send_message("/gs/tof/cy", clamp(cy, 0.0, 1.0))
|
|
client.send_message("/gs/tof/near", clamp(near, 0.0, 1.0))
|
|
client.send_message("/gs/light/lux", lux)
|
|
|
|
# --- Occasional strobe/camera flash event.
|
|
if now >= next_flash:
|
|
client.send_message("/gs/light/flash/event", 1.0)
|
|
next_flash = now + random.uniform(8.0, 20.0)
|
|
|
|
except OSError as e:
|
|
# Transient network/socket hiccup: warn, back off briefly, keep going forever.
|
|
print(f"[{NAME}] send failed ({e}); retrying")
|
|
time.sleep(0.5)
|
|
continue
|
|
|
|
time.sleep(DT)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|