Rebuild routing when PipeWire moves the Wave's nodes - #10
Open
Sharpienero wants to merge 2 commits into
Open
Conversation
_spawn_loopback treats a key's presence in _procs as proof the loopback exists, and nothing removes an entry whose child has died. A PipeWire restart takes every pw-loopback with it, so afterwards the dict holds exited processes that reconcile still counts as live routing and never rebuilds. Check the child is running before trusting the entry.
Mixer resolves mic and hp once in its constructor, and every routing decision afterwards uses those two strings. PipeWire can destroy and recreate those nodes at any time and nothing reports it, so the cached names go stale and the mixer keeps addressing nodes that are gone. The visible case is the Personal Mix -> headphones loopback, spawned once in _do_start behind `if self.hp`. Start before the Wave enumerates, or restart PipeWire after it, and hp is still the None from construction: the loopback is skipped, apps keep feeding openwave_personal_mix, and that mix goes nowhere. Output is silent while the matrix and the volumes all read as correct. Re-resolve both names on the worker, tear down what was built against a name that moved, and let reconcile rebuild it. That makes the headphone loopback a reconciled resource like any cell, so it also appears when the Wave arrives late. The mic meter binds a node name at spawn and reads flat zero against a destroyed one, so device changes are reported to the UI and the meter follows.
This was referenced Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two fixes for the same failure. The mixer keeps addressing PipeWire nodes that no longer exist, and the routing it thinks it has stops existing with them. Output goes silent while the UI reads as correct, because the only broken thing is a link the UI never draws.
Re-detect the Wave's nodes instead of caching them at startup
Mixerresolvesmicandhponce in its constructor, and every routing decision afterwards uses those two strings. PipeWire is free to destroy and recreate those nodes at any point (a daemon restart, a suspend, the Wave unplugged and returned), and nothing reports it.The case I hit: the Personal Mix to headphones loopback is spawned once in
_do_startbehindif self.hp. Start the app before the Wave enumerates, or restart PipeWire after it, andhpis still theNonefrom construction. The loopback is skipped, every app keeps feedingopenwave_personal_mix, and that mix terminates nowhere. The mix matrix, the volumes and the device page all look right.The worker now re-resolves both names between tasks, tears down whatever was built against a name that moved, and lets reconcile rebuild it.
_ensure_hp_loopbackmakes the headphone loopback a reconciled resource like any other cell, so it also appears when the Wave arrives late and goes away when it leaves.The mic meter has the same latch.
pw-catbinds a node name at spawn, so a meter left pointed at a destroyed node reads flat zero forever, which is indistinguishable from a dead mic.Mixerreports device changes to the UI and the meter is re-pointed with them.Interval is 5s, two
pactlcalls, on the worker thread. It is a recovery path rather than a control one, so nothing the user does waits on it, and it does no work when nothing has moved.Respawn a loopback whose subprocess has exited
Separate bug on the same surface.
_spawn_loopbacktreats a key's presence in_procsas proof the loopback exists, and nothing removes an entry whose child has died. A PipeWire restart takes everypw-loopbackwith it, so afterwards the dict holds exited processes that reconcile still counts as live routing and never rebuilds.Testing
There is no suite in the repo, so I drove the watchdog with the subprocess layer stubbed and checked: the HP loopback is created by reconcile rather than
_do_start; a dead loopback is respawned; a node rename tears down the stale loopback and rebuilds against the new name; unplugging removes it and replugging restores it; and an idle pass spawns and destroys nothing. Happy to add that as a real test file if you want one in-tree.