feat(rig): measure the chain, manage the server, seed the surface — three tools that end the silent-orbit era
Tonight cost an evening to "d4/d5/d9-d12 make no sound", and the reason it cost
an evening is that every layer was inspected by READING and none by MEASURING.
PLN's correction — "this is a great tidal-ears case, observe the audio streams"
— was right, and the measurement took six seconds.
## tools/probe-chain.py — where does the signal stop?
Taps the live chain at three points at once and reports peak+rms dBFS per orbit:
SuperCollider:out_N/N+1 the orbit as SuperDirt renders it
ardour:Tidal NN/out after Ardour's fader/mute/plugins
ardour:Master/out the mix
Taps are ADDITIVE PipeWire links into `pw-record --target 0` capture streams, so
nothing existing is unlinked and the audio path is untouched — safe mid-set. The
`--target 0` is the trick that makes it possible to tap a mid-chain node instead
of only a device.
Orbit->port mapping is DERIVED, not hardcoded: `~dirt.start(57120, [0,2,4,...])`
means orbit k writes bus 2k, and PipeWire exposes bus b as out_(b+1)/out_(b+2).
First run settled in one shot what four sessions of hypothesising could not:
d1 -28.6 rms d2 -42.5 d3 -37.4 d7 -21.5 d8 -29.8 <- audible
d4 d5 d6 d9 d10 d11 d12 = -inf, absolute digital silence
Exact match with what PLN could hear. **Ardour fully exonerated** — faders,
mutes, routing, all of it. The signal never existed. Every hypothesis that had
pointed downstream was wrong, and one reading killed them all.
It reports peak AND rms deliberately: a lone peak can be one stray sample or a
click, and judging an orbit "working" on peak alone is how you mistake a
transient for audio.
## parvagues-sc.service — SuperCollider becomes visible
The reason the real error was unreadable all evening: sclang's post window was a
bare /dev/pts/3. Not readable by tooling, gone when the terminal closes, gone on
a crash. The process was equally fragile — owned by a terminal, and killable by
a stray probe (which is exactly what happened: a probe script of mine attached to
the DEFAULT server, i.e. PLN's live one, and its `0.exit` sent `/quit`. `s` in
sclang is the default server; changing the SuperDirt OSC port isolates nothing).
As a systemd --user unit we get restart-by-command, survival across terminal
close and sandbox teardown, one place for The Bridge to show status — and the
post window in the JOURNAL, where it can be PARSED into readiness state instead
of squinted at. Within a minute of it landing:
like_sugar (22) the_revolution (36) diams_dj (16)
— the "missing" banks were registered correctly all along, plus 106 real
`File reading failed` warnings on `zz._`-prefixed files that had never once been
visible. That is the #45 lead, handed over by a log we could finally read.
`Restart=no` on purpose: a failing boot script under Restart=always would spam
the MIDI graph and hide the failure. Audio gear should fail loudly, never flap.
`QT_QPA_PLATFORM=offscreen` is load-bearing, not cosmetic — a Qt-linked sclang
dies with the display stack, which is how an overnight suspend used to kill the
music (d8346672).
## tools/lcxl-init.py — fix the mute-bomb at its actual root
Verified against tidal-1.9.5 source rather than guessed: `"^51"` is `cF_ "51"` is
`_cX_ getF "51"`, and `_cX_` returns **`silence`** on a missing key. `#` cannot
emit without a right-hand value, so any stream touching an unmoved control is
silent — and `dN` being `xfade N` means you hear the PREVIOUS pattern's 4-cycle
tail dying, which reads as a filter closing. That illusion is what cost the
evening.
BootTidal.hs's two mitigations are both weaker than this one. `orDef` reaches
only the ~13 g* helpers — never the ~1860 inline `^NN` refs across 343 track
files, nor arithmetic sites like `midiOn ("^34" - "^18")` where
touched-minus-untouched is itself empty. The `setF` seed block is correct in
principle (same `sStateMV` the OSC `/ctrl` handler writes — traced in Stream.hs)
but depends on boot order and on that ghci having actually run it.
lcxl-init instead emits real CC messages into the identical path a physical knob
takes: CC -> SC's `MIDIFunc.cc` -> `/ctrl` -> Tidal's map. By construction
indistinguishable from PLN sweeping the whole surface by hand — the one thing
already known to work. It cannot diverge from reality because it IS the reality
path, and it fixes helpers, inline refs and arithmetic sites at once, whatever
state ghci is in. PLN's framing: "most things that are templated lack an init
value" — so give them one, from the hardware side.
Encodes the corpus convention `range <neutral> <extreme>`, so 0 is neutral for
everything except the three DJ filters, whose neutral is the centre detent
(64/127 -> lpf 20000 / hpf 20 = true bypass).
D-row faders (77-84) are deliberately NOT seeded: they are MIDI-learned to Ardour
TRACK GAINS, so blasting values would move Ardour's faders while the physical
faders stay put — a surface/mix desync worse than the problem. `^77` is already
covered by `orDef 0.769`.
Two bugs found and fixed by verifying rather than assuming:
* port resolution used `aconnect -i` (READABLE ports) and so returned SC's
`out0` — a source. Sending to it goes nowhere. It must be `-o`, the writable
destinations. Resolved BY NAME on every call, never cached: the LCXL has
already moved 20:0 -> 24:0 across a replug, and a binding resolved once at
boot then silently invalidated is this rig's most expensive bug class.
* the first verification attempt tapped the destination port with aseqdump and
saw 0 messages — but you cannot SUBSCRIBE to a destination. Delivery was
then proven properly through Midi Through (which echoes in to out):
controller 49 value 64 and controller 50 value 64 observed arriving. The
send was always fine; the observation was broken. Report which one you
proved.
| Name |
Last commit
|
Last update |
|---|---|---|
| .gitea/workflows | Loading commit data... | |
| armada | Loading commit data... | |
| blocks | Loading commit data... | |
| blog | Loading commit data... | |
| copycat | Loading commit data... | |
| docs | Loading commit data... | |
| gifts | Loading commit data... | |
| lib-parvagues | Loading commit data... | |
| live | Loading commit data... | |
| output | Loading commit data... | |
| releases | Loading commit data... | |
| sandbox | Loading commit data... | |
| study | Loading commit data... | |
| test | Loading commit data... | |
| tools | Loading commit data... | |
| utils | Loading commit data... | |
| visuals/slop | Loading commit data... | |
| viz | Loading commit data... | |
| .gitignore | Loading commit data... | |
| BootTidal.hs | Loading commit data... | |
| BootTidal.hs.broken | Loading commit data... | |
| BootTidal.visuals.broken.hs | Loading commit data... | |
| CLAUDE.md | Loading commit data... | |
| FOH_MASTER.md | Loading commit data... | |
| LICENSE | Loading commit data... | |
| PatchBay_MultiChannel.xml | Loading commit data... | |
| PatchBay_Perf.xml | Loading commit data... | |
| PatchBay_Perf5.1.xml | Loading commit data... | |
| README.md | Loading commit data... | |
| RUNBOOK.md | Loading commit data... | |
| SHOPPING_LIST.md | Loading commit data... | |
| backlog.md | Loading commit data... | |
| bullet_train.tidal | Loading commit data... | |
| collide.sh | Loading commit data... | |
| debug.tidal | Loading commit data... | |
| didacticpatternvisualizer.pde | Loading commit data... | |
| gig-up.sh | Loading commit data... | |
| gpu-mode.sh | Loading commit data... | |
| hello.sc | Loading commit data... | |
| init_midi.sh | Loading commit data... | |
| intro.md | Loading commit data... | |
| library.txt | Loading commit data... | |
| link_jack.sh | Loading commit data... | |
| link_samples.sh | Loading commit data... | |
| link_samples_all.txt | Loading commit data... | |
| main_fairbanks.scd | Loading commit data... | |
| notes.md | Loading commit data... | |
| parvagues-sc.service | Loading commit data... | |
| perf-audio.sudoers | Loading commit data... | |
| perf-tray.py | Loading commit data... | |
| perf.sh | Loading commit data... | |
| samples.txt | Loading commit data... | |
| start_and_midi.scd | Loading commit data... | |
| test.tidal | Loading commit data... |