Commit 6fbd4256 by PLN (Algolia)

feat(at): pv-at — acceptance tests that play the rig and assert on real audio

Green unit tests on pure functions prove nothing about whether the rig makes
sound. Every failure that has cost this project an evening lived in the SEAM:
a helper that typechecks but silences an orbit, a knob that moves but changes
nothing, an orbit still playing from the previous track. pytest cannot see any
of it; a microphone can.

So pv-at drives the REAL rig — boots a track through tidal-remote, taps the
real PipeWire graph through probe-chain's `Tap`, and asserts on measured audio.
Reusable by construction: a case is a named assertion, not a one-shot script,
so tonight's investigation is tomorrow's regression suite.

The fixture is a track, not a test tone: claude.tidal, a slow D-minor roller at
124. Test tones prove the signal path works and nothing about whether MUSIC
survives it, and they are miserable to listen to while debugging. Each of its
eight orbits is simultaneously a part of the arrangement and an assertion —
kick/presence, hats/density, sub/register, stab/brightness, pad/sustain,
arp/mask, riser/time-axis, break/mute.

8/8 passing against the live rig. Three of the assertions had to be rewritten
first, and each rewrite is the same lesson in a new costume — the arithmetic is
never the bug, the QUESTION is:

  * sub_is_low asserted on spectral centroid and failed d3 at 837 Hz — an orbit
    carrying `# lpf 220`. A magnitude-weighted centroid integrates the whole
    spectrum, so a low-level broadband floor spread over 20 kHz drags the mean
    far above where the energy actually is (d5: lpf 1400, centroid 6344 Hz).
    Replaced with band-energy ratio: d3 now reads 95% of its energy below
    300 Hz. The centroid stays for RELATIVE tests, where the floor is common to
    both readings and cancels — filter_bites moves 2749 -> 2152 Hz cleanly.
  * riser_rises compared the first half of the window to the second and was a
    COIN FLIP: d7 is driven by a saw whose period is comparable to the capture
    window, so a fixed window lands on a random phase. Consecutive runs of the
    identical pattern scored +7.1 dB and -20.9 dB. Replaced with the maximum
    draw-up (largest rise from any bin to any later bin), which is true at every
    phase: 32.4 dB over a 34.1 dB span.
  * stab_is_bright demanded ">35% of energy above 1 kHz" and failed d4 at
    32.6%. That threshold was invented, and the honest response to a number
    that close is to fix the question rather than nudge the threshold until it
    passes. Now comparative — the stab must sit well above the sub (32.6% vs
    0.4%) — which is a musical invariant instead of a guess.

Safety is structural, not procedural: SAFE_CC is an allowlist, CC 77-84 are
excluded by construction (Ardour owns those faders; CC77 down is total
silence), the LCXL port is re-resolved BY NAME on every send, and the suite
hushes when it finishes. Exit code 2 means "rig not running", kept distinct
from 1 "a case failed" — conflating an unmet precondition with a pass is how a
green suite comes to mean nothing.
parent 81eb4dc0
-- claude.tidal — the acceptance-test track.
--
-- Every orbit here is two things at once: a part of a piece of music, and an
-- assertion about the rig. That is deliberate. A test track made of test tones
-- proves the signal path works and nothing about whether MUSIC survives it, and
-- it is miserable to listen to while you debug. This one is a slow D-minor
-- roller at 124 — dub techno's patience, one chord, a sub that breathes.
--
-- d1 KICK -> presence + duty (is the orbit alive and sustained?)
-- d2 HATS -> onset density (does `ply` actually multiply events?)
-- d3 SUB -> LOW spectral centroid (is the bass really low? measure, never
-- infer register from a sample name)
-- d4 STAB -> HIGH centroid, and the gF1 sweep target
-- d5 PAD -> DUTY > 0.7 (the 2026-07-28 bug: an orbit that
-- attacks then dies looks fine on an
-- aggregate and is a click to the ear)
-- d6 ARP -> gMask / density
-- d7 RISER -> a RISING timeline (proves the time axis is measured)
-- d8 BREAK -> gMute silences it
--
-- Constraints that make it testable:
-- * NO blank lines inside the block — a blank line is Tidal's separator and
-- would split this into fragments that evaluate independently.
-- * Every orbit sounds with NO control touched. An untouched `^NN` yields no
-- events at all, so a core pattern gated on one would be silence, not a
-- default (#55). CC-driven variation is additive only.
-- * Distinct `cut` groups per orbit — sharing one is how d5 and d11 of
-- you_my_sunshine came to truncate each other.
-- * No reference to CC 77-84 (Ardour owns those faders).
do
setcps (124/60/4)
d1 $ gF1 $ gMute1 -- KICK: four on the floor, with a ghost on the last bar
$ sometimesBy 0.12 (superimpose ((0.005 ~>) . (|* gain 0.7)))
$ s "bd:3" # n 0
# gain 1.15
# lpf 900
# cut 1
d2 $ gF1 $ gM1 -- HATS: offbeat, opening up over 16 bars
$ midiOn "^44" (ply 2)
$ s "~ hh ~ <hh hh*2>"
# n "<3 3 5 3>"
# gain 0.85
# legato 0.3
# pan 0.62
# room 0.12 # sz 0.3
# cut 2
d3 $ gF2 $ gM3 -- SUB: D minor root, long and patient. MUST measure LOW.
$ note (scale "aeolian" "0 ~ <0 -3> ~" + 2 - 24)
# s "moog" # n 2
# lpf 220 # resonance 0.1
# gain 1.1
# sustain 0.9
# cut 3
d4 $ gF1 $ gM2 -- STAB: the gF1 sweep target. MUST measure BRIGHT.
$ off 0.125 ((|+ note 12) . (|* gain 0.55))
$ note (scale "aeolian" "~ 4 ~ [7 9]" + 2)
# s "arpy" # n 4
# hpf 400
# gain 0.95
# room 0.3 # sz 0.6
# delay 0.35 # delayt 0.375 # delayfb 0.35
# cut 4
d5 $ gF3 $ gM3 -- PAD: the SUSTAIN test. duty must stay high, not attack-and-die.
$ note (scale "aeolian" "[0,3,7]" + 2 - 12)
# s "moog" # n 5
# lpf 1400 # resonance 0.05
# gain 0.8
# sustain 3.4
# legato 1
# room 0.45 # sz 0.85
# cut 5
d6 $ gF2 $ gM1 -- ARP: density under gMask
$ arp "<up updown>"
$ note (scale "aeolian" "<[0,3,7] [-3,0,4]>" + 2)
# s "arpy" # n 7
# gain 0.7
# legato 0.5
# pan 0.38
# room 0.25
# cut 6
d7 $ gF1 $ gM2 -- RISER: must measure as RISING over the window
$ s "hh*16"
# n 9
# hpf (slow 4 $ range 300 6000 saw)
# gain (slow 4 $ range 0.25 0.95 saw)
# legato 0.4
# room 0.4 # sz 0.7
# cut 7
d8 $ gF1 $ gM1 -- BREAK: the gMute target
$ chop 8
$ s "jvbass:2"
# gain 0.75
# lpf 2200
# cut 8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment