F3: force-bypass surround streams; generalise N-channel pairing
Codex flagged that routing was channel-blind and the explicit-link
pairer hardcoded `take(2)`. For a 5.1 stream the consequences
depended on the route decision: Route::Processed silently dropped
the centre, LFE, and both surround channels (only FL/FR linked to
the stereo processed sink); Route::Bypass to a 5.1-capable real
sink had the destruction pass kill 4 of 6 links because they
weren't in the 2-pair `want_set`. Either way the user lost
channels.
PLAN §12 already documented the intent ("anything >2ch is routed
directly to the real sink, bypass behaviour, regardless of profile
rule") but the code didn't honour it. This commit makes the
contract load-bearing.
Changes
- `PwNodeInfo` gains `audio_channels: Option<u32>`, populated
in `build_node_info` from the stream's `audio.channels`
property. `None` for clients that don't advertise (older PW,
odd toolkits) — those fall through to normal rule evaluation
on the assumption they're stereo or mono.
- `routing::evaluate` short-circuits to `Route(Bypass)` when
`audio_channels > 2`, ahead of rule matching. The bus filter
is F32 stereo by construction, so this is the only honest
answer: forcing surround into the processed path either drops
channels or invents an unrequested downmix.
- `apply_pending_routes`' link pairing generalised from
`take(2)` to `take(min(src_outs.len(), target_ins.len()))`.
Stereo → stereo is unchanged (`min(2, 2) = 2`); 5.1 → 5.1
real sink now pairs all six channels; 5.1 → stereo real sink
pairs two (PipeWire's source-side adapter does the downmix,
which is its job, not ours). The destruction pass already
only nukes links to *known sinks*, so taps + non-sink
consumers stay untouched as before.
- PLAN §12 updated: the surround bullet now describes enforced
behaviour rather than aspirational documentation.
Tests
- `routing::tests::surround_streams_force_bypass_regardless_of_rule_match`
— a 6-channel stream matching the default profile's "browser
is processed" rule must still bypass.
- `routing::tests::stereo_and_mono_streams_follow_normal_rules`
— confirms the forcer only triggers for `>2ch` (None, Some(1),
Some(2) all flow through to the rule).
188 tests pass; clippy clean at -D warnings --all-targets.
Live regression check (stereo 1 kHz sine into processed): 51
non-floor meter ticks over 3 s, bus DSP path still flowing,
integrated LUFS around -28. Stereo path unaffected by the
generalised pairing.
This commit is contained in:
parent
03edb17180
commit
3427ec56fc
3 changed files with 78 additions and 6 deletions
13
PLAN.md
13
PLAN.md
|
|
@ -1114,8 +1114,17 @@ for current status per risk.
|
|||
filter should source its rate from the real sink and convert on the
|
||||
capture side only.
|
||||
- **Surround content downmix vs. passthrough.** v0 punts: anything
|
||||
>2ch is routed directly to the real sink (bypass behaviour)
|
||||
regardless of profile rule. Documented behaviour.
|
||||
`>2ch` is force-bypassed regardless of profile rule. The bus
|
||||
filter is F32 stereo by construction and pulling a 5.1+ stream
|
||||
into it would either drop the centre/LFE/surround channels (with
|
||||
explicit links pairing only the first two ports) or run our DSP
|
||||
on a downmix that wasn't asked for. The check fires in
|
||||
`routing::evaluate` based on `PwNodeInfo.audio_channels` (parsed
|
||||
from the stream's `audio.channels` property). The explicit-link
|
||||
pairing in `apply_pending_routes` was generalised from `take(2)`
|
||||
to `take(min(src, dst))` so wide bypass to a wide real sink links
|
||||
all channels; narrower sinks let PipeWire's source-side adapter
|
||||
handle downmix as usual.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue