-
feat(pv-at): --track proves every control on a REAL track moves the sound (#74) · ab368180
PLN's ask, verbatim: "all controls have impact on sound... even the crushes should change noticeably". The existing suite proves the RIG works against a fixture; this proves a TRACK is wired, which is the thing that bites at 160 BPM when a knob turns out to do nothing. python3 tools/at --track live/.../gimme_acid.tidal --dry-run # no audio python3 tools/at --track live/.../gimme_acid.tidal [--json] Per control: park at REST, take TWO baselines, probe, restore, judge. Four outcomes, not two — MOVED / NO_IMPACT / INCONCLUSIVE / UNMEASURABLE — because a swing that does not clearly beat the pattern's own drift is genuinely unknown, and calling that a pass is how a green suite comes to mean nothing. NO_SIGNAL is kept separate from NO_IMPACT: "nothing was playing" and "the control is dead" have different fixes, and conflating them once cost a whole evening chasing SuperDirt. THE BUG THE FIRST DRY RUN FOUND, which is the real content of this commit The plan for gimme_acid came back "0 bipolar, no CC 49/50/51 at all" — on a track that applies gF1/gF2/gF3 to SIX orbits. gF1/gF2/gF3 are defined in BootTidal.hs, not in the track, so lens.py's scan of the .tidal file alone was structurally blind to the three DJ FILTERS and to EVERY MUTE in the rig. The controls PLN reaches for most were the ones the acceptance test could not see. Fixed by PARSING BootTidal.hs (parsers-over-copy — it is the source of truth and it moves: gF1 was rewired onto gDJF, midiGGlobal was retired hours ago, and a hardcoded table would rot into a confidently wrong report). boot_helpers() now resolves: * `gF1 = gDJF "^49"` -> CC49, and BORROWS its lens from gDJF's body, since "gDJF" is not a keyword any table knows and the lpf/hpf evidence sits on two separate lines * `gM1 = gMask . gMute1` -> composition, inheriting CC41 AND CC73 * a track's own `let gMute = ...` SHADOWS the boot one, as Tidal does Result on gimme_acid: 18 controls -> 22, with 49/50/51 present, bipolar, resting at 64 and probing downward. Across the OPAL set: 275 controls, 15-29 per track, all three filters resolved in all 13, zero Ardour-owned CCs. Two parse bugs found and fixed on the way, both by reading the output instead of trusting it: * `mask "f*16"` was classified as DENSITY. An all-false mask is a MUTE — same keyword, opposite lens (rms vs onsets) — and my first all-false test asked "does it contain a 1", which the *16 repeat count satisfies. So gMute1/2/3, three textually IDENTICAL helpers, came back mute/density/density: rms would have been the wrong lens on two of PLN's three mutes, and two working mutes would have reported NO_IMPACT. * `gM3 = gMask . gMute3` swallowed the Launchpad block that follows it and reported CC 7 and CC 9 as mutes, because continuation lines were appended to whatever was defined last. A continuation now has to LOOK like one. SAFETY CC93 is added to a new lens.NEVER_PROBE. It arms panic — gPanic gates on "^93", so sending it high silences every gPanic'd stream. "Probe every control" plus "one control is a kill switch" is how a self-test mutes a rehearsal and gets blamed on the rig. It is reported and never swept. CC77-84 stay excluded by construction in lens AND re-asserted at the probe; CC77 down is total silence, so belt and braces is proportionate there. Controls are restored per control, not at exit, so Ctrl-C mid-run is safe. Restore goes to Control.rest — 64 for a DJ filter, because djf 0.05 is a ~26 Hz low-pass, i.e. silence, and parking one at 0 to get a "baseline" measures silence and then calls the track broken (#48). --dry-run exists deliberately: it prints the plan with no audio and no MIDI, so the half that can be checked tonight — which controls were found, which lens each gets, what will be sent, what will be restored — is checkable at all. "Built but never run" is exactly how the LED work shipped a colour ramp that was never called. TESTS: +79 (22 in test_lens.py, 57 in the new test_track_runner.py), suite 345 -> 424, all green. Every boot-helper regression above is pinned, including a parametrised all-false-mask table with `f*16` in it; each lens is asserted to have a floor (2 * max(drift, 0) is zero, so without one any tiny swing looks significant on a quiet capture); the probe is asserted to REFUSE all of CC77-84; and the dry-run plan is built for all 13 OPAL tracks with the DJ filters asserted bipolar-and-resting-at-64 in each. STATUS: the judgement half is validated. The AUDIO half is built but unrun — it needs monitors, and the house is quiet. First thing to run with sound up.PLN (Algolia) authoredab368180
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| fixtures | Loading commit data... | |
| tests | Loading commit data... | |
| __init__.py | Loading commit data... | |
| __main__.py | Loading commit data... | |
| harness.py | Loading commit data... | |
| lens.py | Loading commit data... |