fix(sound): only consider active routes when setting volume and mute-ness

This commit is contained in:
Michael Aaron Murphy 2026-02-12 16:52:25 +01:00 • committed by Ashley Wulber
parent 9db1681e0d
commit 1139e34928
3 changed files with 71 additions and 32 deletions

View file

@ -66,17 +66,18 @@ fn run_service(
let registry = core.get_registry_rc()?; let registry = core.get_registry_rc()?;
let state = Rc::new(RefCell::new(State { let state = Rc::new(RefCell::new(State {
nodes: IntMap::new(), main_loop: main_loop.downgrade(),
proxies: Proxies { proxies: Proxies {
devices: IntMap::new(), devices: IntMap::new(),
metadata: IntMap::new(), metadata: IntMap::new(),
nodes: IntMap::new(), nodes: IntMap::new(),
}, },
nodes: IntMap::new(),
active_routes: IntMap::new(),
routes: IntMap::new(), routes: IntMap::new(),
node_devices: IntMap::new(), node_devices: IntMap::new(),
node_card_profile_device: IntMap::new(), node_card_profile_device: IntMap::new(),
node_props: IntMap::new(), node_props: IntMap::new(),
main_loop: main_loop.downgrade(),
on_event, on_event,
})); }));
@ -207,7 +208,9 @@ fn run_service(
match param_type { match param_type {
ParamType::EnumProfile => { ParamType::EnumProfile => {
if let Some(profile) = Profile::from_pod(pod) { if let Some(profile) = Profile::from_pod(pod) {
state.borrow_mut().add_profile(device_id, profile); state
.borrow_mut()
.add_profile(device_id, index, profile);
} }
} }
@ -423,7 +426,7 @@ pub enum Event {
/// A new node was detected. /// A new node was detected.
AddNode(Node), AddNode(Node),
/// A profile was enumerated /// A profile was enumerated
AddProfile(DeviceId, Profile), AddProfile(DeviceId, u32, Profile),
/// A route was enumerated /// A route was enumerated
AddRoute(DeviceId, u32, Route), AddRoute(DeviceId, u32, Route),
/// The default sink was changed. /// The default sink was changed.
@ -496,13 +499,21 @@ struct Proxies {
} }
struct State { struct State {
nodes: IntMap<PipewireId, (NodeId, Option<DeviceId>)>,
pub(self) proxies: Proxies,
routes: IntMap<DeviceId, Vec<Route>>,
node_devices: IntMap<NodeId, DeviceId>,
node_props: IntMap<NodeId, NodeProps>,
node_card_profile_device: IntMap<NodeId, u32>,
main_loop: MainLoopWeak, main_loop: MainLoopWeak,
/// Stores pipewire objects that we are monitoring.
pub(self) proxies: Proxies,
/// Associates the pipewire ID of a node to its node and device IDs.
nodes: IntMap<PipewireId, (NodeId, Option<DeviceId>)>,
/// Routes which are currently in use by devices.
active_routes: IntMap<DeviceId, Vec<Route>>,
/// Routes which are supported by devices.
routes: IntMap<DeviceId, Vec<Route>>,
/// Associates node objects to their device objects.
node_devices: IntMap<NodeId, DeviceId>,
/// Additional properties of nodes for managing volume, mute, etc.
node_props: IntMap<NodeId, NodeProps>,
/// Associates a node with a card profile device for matching nodes to routes.
node_card_profile_device: IntMap<NodeId, u32>,
/// Handle events and exit the loop when `true` is returned. /// Handle events and exit the loop when `true` is returned.
on_event: Box<dyn FnMut(Event)>, on_event: Box<dyn FnMut(Event)>,
} }
@ -513,6 +524,16 @@ impl State {
} }
fn active_route(&mut self, id: DeviceId, index: u32, route: Route) { fn active_route(&mut self, id: DeviceId, index: u32, route: Route) {
// Keep a record of routes attached to a device for setting properties.
// This will overwrite routes on updates to
let routes = self.active_routes.entry(id).or_default();
if routes.len() < index as usize + 1 {
let additional = (index as usize + 1) - routes.capacity();
routes.reserve_exact(additional);
routes.extend(std::iter::repeat(Route::default()).take(additional));
}
routes[index as usize] = route.clone();
self.on_event(Event::ActiveRoute(id, index, route)); self.on_event(Event::ActiveRoute(id, index, route));
} }
@ -554,8 +575,8 @@ impl State {
self.on_event(Event::AddNode(node)); self.on_event(Event::AddNode(node));
} }
fn add_profile(&mut self, id: DeviceId, profile: Profile) { fn add_profile(&mut self, id: DeviceId, index: u32, profile: Profile) {
self.on_event(Event::AddProfile(id, profile)); self.on_event(Event::AddProfile(id, index, profile));
} }
fn add_route(&mut self, id: DeviceId, index: u32, route: Route) { fn add_route(&mut self, id: DeviceId, index: u32, route: Route) {
@ -592,8 +613,8 @@ impl State {
self.on_event(Event::DefaultSource(name)); self.on_event(Event::DefaultSource(name));
} }
fn node_route(&self, device_id: DeviceId, route_device: i32) -> Option<&Route> { fn active_node_route(&self, device_id: DeviceId, route_device: i32) -> Option<&Route> {
self.routes self.active_routes
.get(device_id)? .get(device_id)?
.iter() .iter()
.find(|r| r.devices.contains(&route_device)) .find(|r| r.devices.contains(&route_device))
@ -682,14 +703,14 @@ impl State {
fn set_mute_node(&self, id: NodeId, mute: bool) { fn set_mute_node(&self, id: NodeId, mute: bool) {
// Prefer to mute the device instead of the node. // Prefer to mute the device instead of the node.
// Muting a node will not emit a notification. // Muting a node will not emit a notification.
if let Some((&device_id, &route_device)) = self if let Some((&device_id, &card_profile_device)) = self
.node_devices .node_devices
.get(id) .get(id)
.zip(self.node_card_profile_device.get(id)) .zip(self.node_card_profile_device.get(id))
{ {
let route_device = route_device as i32; let card_profile_device = card_profile_device as i32;
if let Some(route) = self.node_route(device_id, route_device) { if let Some(route) = self.active_node_route(device_id, card_profile_device) {
self.set_mute(device_id, route_device, route, mute); self.set_mute(device_id, card_profile_device, route, mute);
return; return;
}; };
} }
@ -762,14 +783,21 @@ impl State {
}; };
// Prefer to change the volume of the device instead of the node. // Prefer to change the volume of the device instead of the node.
if let Some((&device_id, &route_device)) = self if let Some((&device_id, &card_profile_device)) = self
.node_devices .node_devices
.get(id) .get(id)
.zip(self.node_card_profile_device.get(id)) .zip(self.node_card_profile_device.get(id))
{ {
let route_device = route_device as i32; let card_profile_device = card_profile_device as i32;
if let Some(route) = self.node_route(device_id, route_device) { if let Some(route) = self.active_node_route(device_id, card_profile_device) {
self.set_volume(device_id, props, route_device, route, volume, balance); self.set_volume(
device_id,
props,
card_profile_device,
route,
volume,
balance,
);
return; return;
}; };
} }

View file

@ -4,7 +4,7 @@
use crate::{Availability, spa_utils::string_from_pod}; use crate::{Availability, spa_utils::string_from_pod};
use libspa::pod::Pod; use libspa::pod::Pod;
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct Profile { pub struct Profile {
pub index: i32, pub index: i32,
pub priority: i32, pub priority: i32,

View file

@ -526,7 +526,7 @@ impl Model {
} }
} }
pipewire::Event::AddProfile(id, profile) => { pipewire::Event::AddProfile(id, index, profile) => {
if let Some(p) = self.active_profiles.get_mut(id) { if let Some(p) = self.active_profiles.get_mut(id) {
if p.index == profile.index { if p.index == profile.index {
*p = profile.clone(); *p = profile.clone();
@ -534,16 +534,17 @@ impl Model {
} }
let profiles = self.device_profiles.entry(id).or_default(); let profiles = self.device_profiles.entry(id).or_default();
for p in profiles.iter_mut() { if profiles.len() < index as usize + 1 {
if p.index == profile.index { let additional = (index as usize + 1) - profiles.capacity();
*p = profile; profiles.reserve_exact(additional);
profiles.extend(std::iter::repeat_n(
self.update_ui_profiles(); pipewire::Profile::default(),
return; additional,
} ));
} }
profiles.push(profile); profiles[index as usize] = profile;
self.update_ui_profiles(); self.update_ui_profiles();
} }
@ -676,12 +677,22 @@ impl Model {
fn add_route(&mut self, id: DeviceId, index: u32, route: pipewire::Route) { fn add_route(&mut self, id: DeviceId, index: u32, route: pipewire::Route) {
self.update_device_route_name(&route, id); self.update_device_route_name(&route, id);
tracing::debug!(target: "sound",
"Device {} added route {} ({:?}); {:?}",
id,
route.name,
route.direction,
route.available
);
let routes = self.device_routes.entry(id).or_default(); let routes = self.device_routes.entry(id).or_default();
if routes.len() < index as usize + 1 { if routes.len() < index as usize + 1 {
let additional = (index as usize + 1) - routes.capacity(); let additional = (index as usize + 1) - routes.capacity();
routes.reserve_exact(additional); routes.reserve_exact(additional);
routes.extend(std::iter::repeat_n(pipewire::Route::default(), additional)); routes.extend(std::iter::repeat_n(pipewire::Route::default(), additional));
} }
routes[index as usize] = route; routes[index as usize] = route;
} }