Two pieces of the cockpit (#155, #41), both written the same way: resolve by
IDENTITY every time, never cache an address.
tools/bridge/surface.py — THE control-surface tap
-------------------------------------------------
Four tools each spawned their own aseqdump. This morning three of them were
bound to ALSA client 24 (an Arturia KeyStep) instead of 20 (the Launch Control
XL) because a replug had renumbered the clients, and each resolves its port
once, at start. I "fixed" them by moving them to 20:0.
Then PLN replugged again this evening and the numbers SWAPPED BACK:
client 20: 'Arturia KeyStep 32' <- was the LCXL
client 24: 'Launch Control XL' <- was the KeyStep
So the two I fixed are wrong again and the one I called broken is now right.
An address is a lease, not an identity. This module resolves "Launch Control
XL" by name on EVERY respawn, so a replug costs one backoff interval instead of
a silent session. First run against the live rig, with zero configuration:
starting port=None
ok port=24:0
It also keeps the last value of every CC, and — the part that matters — records
`seen: false` distinctly from `value: 0`. An untouched control is what silences
a Tidal stream (a bare "^NN" with no value yields NO EVENTS), so a cockpit that
rendered untouched-as-zero would hide the exact failure it exists to show.
And it does not litter. Three smoke runs of this file each orphaned an
aseqdump to pid 1 — precisely the mess PLN complained about this morning
("i see 4 aseqdumps... you had one job"). try/finally is not enough, because
SIGKILL, OOM and `timeout` all skip it. PR_SET_PDEATHSIG puts the guarantee in
the kernel: the tap dies with its parent however the parent dies.
tools/master-out.py — Ardour Master follows a preference list
-------------------------------------------------------------
PLN: "can we control this tedious routing... ideally theres a tie breaking list
of choice, from uhc to headphones to speakers". Ardour's master does not follow
the PipeWire default sink, so every rig move means re-patching in qjackctl.
Not "route to X" but "route to the best X actually present": UMC -> Headphones
-> Speakers, matched as substrings of the node name so serial numbers and
profile suffixes do not matter.
Two properties make it safe to run mid-set:
* IDEMPOTENT — computes wanted links, diffs against existing, applies only
the delta. Already correct = zero pw-link calls = cannot glitch the audio.
The naive unlink-then-relink is a guaranteed dropout even when nothing
changed.
* EXCLUSIVE BUT NARROW — removes Master links to other sinks (or plugging the
UMC back in gives you doubled output), and touches nothing else. The limit
is a predicate, `_is_ours`, not a good intention: apply() refuses to unlink
any source that is not a Master output.
Verified against KNOWN-BAD states, not just against agreement with reality —
the whole lesson of this session. Ten checks: preference order at each of the
three tiers, the "master stranded on headphones when the UMC appears" case
(2 adds + 2 removes), idempotency (0 and 0), and the guard refusing both a
foreign link in plan() and a foreign source handed straight to apply().
All pass. Live dry-run reports "already correct — 0 changes".
| Name |
Last commit
|
Last update |
|---|---|---|
| .gitea/workflows | Loading commit data... | |
| armada | Loading commit data... | |
| blocks | Loading commit data... | |
| blog | Loading commit data... | |
| config/wireplumber | 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... | |
| EVENING.md | Loading commit data... | |
| FOH_MASTER.md | Loading commit data... | |
| LAST_THIRD.md | Loading commit data... | |
| LICENSE | Loading commit data... | |
| MORNING.md | 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... | |
| sample_watch.scd | Loading commit data... | |
| samples.txt | Loading commit data... | |
| setlist_opal2026.txt | Loading commit data... | |
| start_and_midi.scd | Loading commit data... | |
| test.tidal | Loading commit data... |