-
midiviz: state as well as events — the DJFs know where they are resting · 8171325c
"as it maintains a state, it should be 'not all same default state' when you look at it between two movements? like, the DJFs are at zero, in the Low, or in the High, and this doesnt really grasp from viewing the midimon" Correct, and it was not a theming problem. This module draws EVENTS, and makes recency a visual axis on purpose: an event's identity is its position, its value is a bar, its recency is brightness. So between two movements every cell decays to the same floor tint, and a filter resting at zero is the same picture as one parked at the top. No palette fixes that — an event stream carries changes, and rest is the absence of a change. A viewer cannot derive it either. The LCXL3 has no readback, and rows B/C run relative (the surface sends v - 64), so the hardware holds no absolute position. Exactly one process integrates those deltas and therefore owns the value, and it is not the viewer: lcxl3-driver's `self.values` is not a cache of something else, it is the only copy. So: two channels, because two physics. Events stay edge-timed off the wire; state is a level, published last-value-wins. driver `~/.cache/parvagues/surface-state.json`, temp file + os.replace, on the existing loop at 10 Hz, written ONLY when the snapshot changed. Every failure swallowed: this is a courtesy to viewers, and a read-only /home must never stop the faders reaching Ardour. midiviz one stat() per frame; an unchanged `seq` means zero parsing and zero repainting, which is the "pay once" property asked for. Absent, unreadable or wrong-shaped file all mean "no state", and the window draws exactly what it drew before this layer existed. Deliberately not a socket or a broker: ~32 integers, idempotent, no history worth queueing. A socket buys a connection, backpressure and a dead-consumer failure mode in exchange for nothing. `cat` is the debugger. First slice is the three family filters, drawn in the cells they physically occupy (C1/C2/C3) rather than a strip of its own — the layout does not move, and this module's founding idea is that identity IS position, so state belongs where the events about it are, which is where his hands are. ZERO IS THE CENTRE DETENT, 64, not 0: gDJF is an lpf and an hpf section both wide open at ch=0.5, so centre is bypass and both ends filter. The event bar is left-anchored, which is exactly the lie — a filter at rest draws a half-width bar that reads as "half of something". The state strip sits above it, centre-anchored, with the detent drawn: a bar growing left of centre is LOW, right is HIGH, bare detent is ZERO. Static at rest, no decay, no pulse — being legible when nothing has moved for a minute is the entire point. Found by looking at it rather than by reasoning: the first version drew into the bottom of the cell, where the event bar already lives, and C1-C3 rendered as mud. Checked at 900x560 and 1600x1000, and 3 units tall beat 2 in sunlight. 16 tests, the load-bearing one being a real round trip: the real state_write and the real SurfaceState.poll over one temp path, so a change to either side that breaks the other fails here and not at a gig. That round trip caught a live bug — `null` is valid JSON of the wrong shape, and the AttributeError from the first .get escaped the handler and would have taken the window down. Suites 526 passed, 2 failed (both pre-existing); --selftest PASS across three themes and four shapes. Takes effect on the next driver restart; the running driver was not touched.PLN (Algolia) authored8171325c
×