feat(liveaudiorouter): add a live matrix audio router with capability parity - #737
feat(liveaudiorouter): add a live matrix audio router with capability parity#737srperens wants to merge 2 commits into
Conversation
4eafedf to
ff29aa8
Compare
ff29aa8 to
e3e57c2
Compare
srperens
left a comment
There was a problem hiding this comment.
Verdict: Request changes — mechanical, per REVIEW.md: Check (Linux) is red on a test this diff itself adds, and the crosspoint-fade claim underlying the design is contradicted by the code. Also: a stronger, tested alternative for the same issue (#740) now exists and should factor into the maintainer's decision here.
Claims
| Claim | Verdict | Evidence |
|---|---|---|
Live routing_matrix writes route through a dedicated intercept, not the generic translate path |
CONFIRMED |
backend/src/gst/pipeline/properties.rs:245 calls liveaudiorouter::try_apply_live_matrix ahead of the generic translation block |
The deinterleave_out pad-added closure captures no strong pipeline/element ref |
CONFIRMED |
backend/src/blocks/builtin/liveaudiorouter.rs:404-432 — only instance_id.to_string() and WeakRefs move in; the bin comes from element.parent() |
Check (Linux) is green on liveaudiorouter_test at head e3e57c2 |
CONTRADICTED |
CI run 33498186703/job 99825164281, re-pulled directly (not just the PR body's own quote): test result: FAILED. 7 passed; 1 failed, panic at liveaudiorouter_test.rs:437:5: `expected audio on output channel 0 before the change, got peaks [-inf, -inf]` — this is the pre-change baseline assertion, not the live-move assertion |
| A routing change fades smoothly rather than stepping | CONTRADICTED |
liveaudiorouter.rs:182 `element.set_property("matrix", matrix_to_value(&matrix));` — a hard 0.0/1.0 coefficient replace on audiomixmatrix; no crosspoint_fade_ms property exists anywhere in this block's definition (zero matches in the file) |
| The per-instance channel-layout registry is reclaimed when a flow tears down | CONTRADICTED |
record_layout (liveaudiorouter.rs:212-213) inserts into the process-global LAYOUTS static; the file has no matching .remove(...) call. Self-flagged in the PR body as "worth a look" — confirmed still open |
Diagnosis — The module doc (liveaudiorouter.rs:1-14) collects every input channel through a single interleave ahead of the audiomixmatrix. The failing test configures one input stream and fails before any routing_matrix write — baseline audio flow is unreliable even in the simplest case, worse than "a live move stalls." This matches the interleave-stall failure mode #740's design record names as its reason to reject this approach; #740 ships a green equivalent test against a non-interleave crossbar.
Blast radius — SHARED: the new intercept in properties.rs sits ahead of the generic per-property path, but is guarded by element_id.ends_with(":matrix") and prop_name == "routing_matrix", so it cannot affect builtin.audiorouter or any other block. New file liveaudiorouter.rs only; mod.rs registration is additive.
Tests & CI — API Contract Check, Check & Build (WASM), both Build (Linux) jobs green; macOS/Windows skipping as expected. Check (Linux) red as above — the covering test for this change, so a blocking finding per REVIEW.md, not a caveat. Frontend changes (audiorouter.rs, graph/rendering.rs, properties.rs) have no CI coverage — strom-frontend never appears in a cargo test --package line in .github/workflows/ci.yml (same gap noted against #740 this run).
Design record — First review of this PR; no prior marker to supersede. #661 now has two live candidates: this PR's single-audiomixmatrix design (fewer elements, but the collect-pads stall shown above and no fade support) versus #740's tee/volume/audiomixer crossbar (a queue per output bus, per-crosspoint volume fades, its own equivalent test green). The maintainer's choice should weigh #740's passing baseline against this PR's failing one.
Confidence: HIGH
BLOCKED — the baseline failure is unresolved, and #740 supersedes this design.
Refs #661. Nothing was compiled on the agent runner (nocargo, no GStreamer); every claim below comes from CI.Status 2026-09-02
Run 33498186703 on
e3e57c2: fmt, clippy, WASM, API contract and both Linux builds green, so it compiles.Check (Linux)fails at Run tests on backend —liveaudiorouter_test: 7 passed, 1 failed. The 5 parity tests pass; the 2 matrix tests are purematrix_from_propertiesunits with no audio.The end-to-end test fails at its baseline, before any matrix change:
expected audio on output channel 0 before the change, got peaks [-inf, -inf]. So no audio has been shown to pass through this block at all, and the live-change claim is untested.I could not resolve it: the block's
warn!/error!emit nothing undercargo test(no logger), so CI gives no diagnostic, and there is no GStreamer here to bisect with.#740 supersedes this by design, not by fixing this failure. It measures three properties of the
interleave+audiomixmatrixfoundation and replaces it withtee → volume → audiomixer. One correction: itsinterleave-stall measurement concerns an input pad requested but left unconnected, which does not explain this failure — the failing test configures a single connected input (backend/tests/liveaudiorouter_test.rs:378—("num_inputs", PropertyValue::UInt(1)),). The baseline cause is still unknown.Fixing this would mean adopting #740's design and duplicating that PR, so I stopped. Keeping this open as the comparison point or closing it is the maintainer's call; I have not touched #740.
Reading of the decision
The reply of 2026-08-31 11:06Z carries
/agent-fix C --accept-radius SHARED --accept-excluded none, superseding the retraction three minutes earlier; I read it as a decision. The marker records no radius andwork=unset; I assessed radius SHARED, excluded=none.Problem
builtin.audiorouterencodes routing as topology — anaudiomixerper summed output channel (backend/src/blocks/builtin/audiorouter.rs:181—let mixer = gst::ElementFactory::make("audiomixer")) — so changing a crosspoint rebuilds the graph, hencelive: falseon that block.Change
New
builtin.liveaudiorouter: same property names, types, defaults and pad shape,routing_matrixlive: true;builtin.audiorouteruntouched.The reply asked for a
translate_propertyhandler likeaudiogain.rs. Not usable:matrixis a double array-of-arrays andPropertyValuehas no array variant (types/src/element.rs:138—pub enum PropertyValue {). I used the interceptor the repo already has —backend/src/blocks/builtin/time_offset.rs:72—pub fn try_apply_live_offset(. The only deviation from the decision's letter.Not verified
The routing path, end to end; more than 2 channels; rates other than 48 kHz; macOS and Windows;
openapi.json. The frontend change has no test and was not run in a browser. The layout registry (iXcYkeys) is keyed by element id and never pruned.Blast radius
get_builderandget_all_builtin_blocksgain one arm each; no existing arm changes.update_element_propertygains one interceptor, returning early only forrouting_matrixon an id ending:matrix.