Skip to content

fix(pipewire): match route mute to profile device - #3873

Draft
Robin-Everaars wants to merge 1 commit into
noctalia-dev:mainfrom
Robin-Everaars:fix/pipewire-route-device-mute
Draft

fix(pipewire): match route mute to profile device#3873
Robin-Everaars wants to merge 1 commit into
noctalia-dev:mainfrom
Robin-Everaars:fix/pipewire-route-device-mute

Conversation

@Robin-Everaars

Copy link
Copy Markdown

Summary

  • match card-level PipeWire routes to the node's card.profile.device
  • keep the direction-wide fallback for node-local routes and nodes without a profile-device binding
  • add a regression test for two output routes with different device IDs and mute states

Problem

A PipeWire card's ParamRoute table can contain several routes in the same direction. The existing selector chooses the highest-priority route for that direction without checking which profile device owns it.

On an ASUS ProArt PX13, the active speaker node has card.profile.device = 0, while the inactive headphone route has device = 1, a higher priority and a stored mute. Noctalia therefore shows the unmuted speakers as muted. The same mismatch affects the internal microphone when the inactive headset-microphone route is muted.

This change records card.profile.device from node properties and uses it when selecting a route from the owning card. Node-local route lists retain their existing direction-only selection.

Testing

  • audio_route_selection: passes
  • full suite on current main: 79/80 pass
  • the remaining process failure (completion-only async command stdout was wrong) reproduces on clean main after stashing this change
  • runtime check on PipeWire 1.6.8: Speakers at 42% and Internal Microphone at 100% both changed from false-muted to unmuted, matching wpctl

@ItsLemmy

Copy link
Copy Markdown
Collaborator

[P2] Match the availability check to profileDevice too

File: src/pipewire/pipewire_service.cpp:1615-1622

The new selector correctly returns nullptr when the card has no route matching nd->profileDevice. However, hasDirRoutes still treats any same-direction card route as relevant:

activeRoute = activeAudioDeviceRoute(device->routes, wantDir, nd->profileDevice);
// ...
std::ranges::any_of(device->routes, matchesDir)

Consequently, a valid node without its own card route is marked unavailable whenever the card contains an unrelated
same-direction route:

matching route: none
same-direction route: yes, for another profile device
node.available: false

This affects route-less profiles such as Pro Audio and can also surface with retained routes after a profile change.
WirePlumber explicitly treats profiles without a matching route as available (linking-utils.lua:403-406).

The device-side route-presence predicate should apply the same profileDevice condition as activeAudioDeviceRoute;
node-local routes should remain direction-only. Add caller-level coverage for both cases:

  • unrelated same-direction card route → node remains available;
  • matching card route with available=no → node is unavailable.

The added test’s unmatched-device case only checks that the selector returns nullptr, so it misses the resulting
availability regression.

Verification: Reproduced the exact selector/caller state: matching_route=null, has_direction_route=true,
node_available=false.

@ItsLemmy
ItsLemmy marked this pull request as draft August 17, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants