Commit 872676a2 by PLN (Algolia)

fix(probe-chain): refuse to report signal the tap was not actually measuring

The tool invented signal, and the invented signal sent an entire investigation
down the wrong road.

A run on 2026-07-28 came back with five different orbits at an IDENTICAL +0.0
dBFS peak. Twelve independent orbits do not peak at exactly full scale together
— that was one shared source (the mix) being measured five times, because the
capture nodes had acquired PipeWire links nobody asked for. `--target 0` is
supposed to stop pw-record auto-connecting to the default source, and usually
does, but not reliably.

The consequence was worse than a missing number. The contaminated table said
d4 and d5 had "recovered after seeding", so that got reported as a result, while
Ardour's own meters showed no input on those tracks at all. PLN was looking at
the truth and being told otherwise. A measurement tool that fabricates signal is
not a tool — it is a confident liar, and it outranks the human's ears in an
argument it should always lose.

Two changes:

  * link() now CUTS every pre-existing link into the capture node before making
    its own, so an auto-connection cannot ride along.
  * verify() re-reads the graph AFTER the capture window and asserts the node was
    fed by exactly the two ports we intended. Checked after, not before, because
    an auto-connection can appear at any point while the node is alive — a setup
    -time check would still let contamination through.

Rows that fail verification print "UNRELIABLE — ignore this row" INSTEAD of a
diagnosis, plus a summary naming what each bad tap was really linked to. Refusing
to answer is the correct behaviour here; a plausible wrong number costs hours.

Validated by re-running against the live rig: the clean table (d1/d2/d3/d7 with
signal, d4/d5/d9-d12 silent at source) matched what PLN could hear, where the
contaminated one had contradicted him. Confirmed on the way that the SC->Ardour
link mapping is exact (out_(2N-1)/out_(2N) -> ardour:Tidal NN for all 12), so the
disagreement was never routing.
parent d3de9bd5
...@@ -96,6 +96,7 @@ class Tap: ...@@ -96,6 +96,7 @@ class Tap:
self.path = outdir / f"{re.sub(r'[^A-Za-z0-9]+', '_', label)}.wav" self.path = outdir / f"{re.sub(r'[^A-Za-z0-9]+', '_', label)}.wav"
self.proc: subprocess.Popen | None = None self.proc: subprocess.Popen | None = None
self.linked = 0 self.linked = 0
self.node_name: str | None = None
def start(self, node_name: str) -> None: def start(self, node_name: str) -> None:
self.proc = subprocess.Popen( self.proc = subprocess.Popen(
...@@ -106,11 +107,54 @@ class Tap: ...@@ -106,11 +107,54 @@ class Tap:
def link(self, node_name: str) -> None: def link(self, node_name: str) -> None:
# pw-record names its inputs input_FL / input_FR. # pw-record names its inputs input_FL / input_FR.
for src, dst in zip(self.srcs, (f"{node_name}:input_FL", f"{node_name}:input_FR")): self.node_name = node_name
dsts = (f"{node_name}:input_FL", f"{node_name}:input_FR")
# CUT ANY AUTO-CONNECTION FIRST.
#
# `--target 0` is supposed to stop pw-record connecting itself to the
# default source, and usually does — but not reliably. On 2026-07-28 a
# run came back showing five different orbits at an IDENTICAL +0.0 dBFS
# peak, which is not something twelve independent orbits do: it was one
# shared source (the mix) being measured five times, because the capture
# nodes had picked up links nobody asked for. That produced a table
# saying orbits had recovered while Ardour's own meters showed no input
# on those tracks — a FALSE POSITIVE, which is far worse than a missing
# reading. A measurement tool that invents signal is not a tool.
for src, dst in self._current_inputs():
subprocess.run(["pw-link", "-d", src, dst],
capture_output=True, text=True)
for src, dst in zip(self.srcs, dsts):
r = subprocess.run(["pw-link", src, dst], capture_output=True, text=True) r = subprocess.run(["pw-link", src, dst], capture_output=True, text=True)
if r.returncode == 0: if r.returncode == 0:
self.linked += 1 self.linked += 1
def _current_inputs(self) -> list[tuple[str, str]]:
"""Every (source, dest) link currently terminating on this capture node."""
if not self.node_name:
return []
pairs, src = [], None
for line in sh(["pw-link", "-l"]).splitlines():
if not line.startswith((" ", "\t")):
src = line.strip()
elif "|->" in line and src:
dst = line.split("|->", 1)[1].strip()
if dst.startswith(self.node_name + ":"):
pairs.append((src, dst))
return pairs
def verify(self) -> tuple[bool, list[str]]:
"""Confirm we are measuring EXACTLY the two ports we intended.
Called after the capture window, not before: an auto-connection can
appear at any point while the node is alive, so checking only at setup
would still let a contaminated reading through.
"""
actual = sorted(s for s, _ in self._current_inputs())
expected = sorted(self.srcs)
return (actual == expected), actual
def stop(self) -> None: def stop(self) -> None:
if self.proc: if self.proc:
self.proc.terminate() self.proc.terminate()
...@@ -199,6 +243,13 @@ def main() -> int: ...@@ -199,6 +243,13 @@ def main() -> int:
for tap, node in all_taps: for tap, node in all_taps:
tap.link(node) tap.link(node)
time.sleep(args.seconds) time.sleep(args.seconds)
# Verify while the nodes are STILL ALIVE — their links disappear with the
# process, so this cannot be done after stop().
contaminated = {}
for tap, _ in all_taps:
ok, actual = tap.verify()
if not ok:
contaminated[tap.label] = actual
finally: finally:
for tap, _ in all_taps: for tap, _ in all_taps:
tap.stop() tap.stop()
...@@ -218,8 +269,18 @@ def main() -> int: ...@@ -218,8 +269,18 @@ def main() -> int:
unlinked.append(f"d{dn} SC") unlinked.append(f"d{dn} SC")
if trk.linked < 2: if trk.linked < 2:
unlinked.append(f"d{dn} track") unlinked.append(f"d{dn} track")
bad = [lbl for lbl in (sc.label, trk.label) if lbl in contaminated]
note = ("!! UNRELIABLE — tap picked up extra sources; ignore this row"
if bad else verdict(s_peak, t_peak, m_peak))
print(f" d{dn:<5d} {fmt(s_peak)} {fmt(s_rms)} {fmt(t_peak):>9s} " print(f" d{dn:<5d} {fmt(s_peak)} {fmt(s_rms)} {fmt(t_peak):>9s} "
f"{fmt(t_rms):>8s} {verdict(s_peak, t_peak, m_peak)}") f"{fmt(t_rms):>8s} {note}")
if contaminated:
print(f"\n !! {len(contaminated)} tap(s) were NOT measuring only what they claimed.")
print(" Rows above marked UNRELIABLE must be discarded, not interpreted.")
for label, actual in list(contaminated.items())[:6]:
print(f" {label}: actually linked to {actual or '(nothing)'}")
print(" Re-run; if it persists, something is auto-connecting capture streams.")
print(f"\n Master bus: peak {fmt(m_peak).strip()} dBFS, rms {fmt(m_rms).strip()} dBFS") print(f"\n Master bus: peak {fmt(m_peak).strip()} dBFS, rms {fmt(m_rms).strip()} dBFS")
if unlinked: if unlinked:
......
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