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 state = Rc::new(RefCell::new(State {
nodes: IntMap::new(),
main_loop: main_loop.downgrade(),
proxies: Proxies {
devices: IntMap::new(),
metadata: IntMap::new(),
nodes: IntMap::new(),
},
nodes: IntMap::new(),
active_routes: IntMap::new(),
routes: IntMap::new(),
node_devices: IntMap::new(),
node_card_profile_device: IntMap::new(),
node_props: IntMap::new(),
main_loop: main_loop.downgrade(),
on_event,
}));
@ -207,7 +208,9 @@ fn run_service(
match param_type {
ParamType::EnumProfile => {
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.
AddNode(Node),
/// A profile was enumerated
AddProfile(DeviceId, Profile),
AddProfile(DeviceId, u32, Profile),
/// A route was enumerated
AddRoute(DeviceId, u32, Route),
/// The default sink was changed.
@ -496,13 +499,21 @@ struct Proxies {
}
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,
/// 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.
on_event: Box<dyn FnMut(Event)>,
}
@ -513,6 +524,16 @@ impl State {
}
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));
}
@ -554,8 +575,8 @@ impl State {
self.on_event(Event::AddNode(node));
}
fn add_profile(&mut self, id: DeviceId, profile: Profile) {
self.on_event(Event::AddProfile(id, profile));
fn add_profile(&mut self, id: DeviceId, index: u32, profile: Profile) {
self.on_event(Event::AddProfile(id, index, profile));
}
fn add_route(&mut self, id: DeviceId, index: u32, route: Route) {
@ -592,8 +613,8 @@ impl State {
self.on_event(Event::DefaultSource(name));
}
fn node_route(&self, device_id: DeviceId, route_device: i32) -> Option<&Route> {
self.routes
fn active_node_route(&self, device_id: DeviceId, route_device: i32) -> Option<&Route> {
self.active_routes
.get(device_id)?
.iter()
.find(|r| r.devices.contains(&route_device))
@ -682,14 +703,14 @@ impl State {
fn set_mute_node(&self, id: NodeId, mute: bool) {
// Prefer to mute the device instead of the node.
// 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
.get(id)
.zip(self.node_card_profile_device.get(id))
{
let route_device = route_device as i32;
if let Some(route) = self.node_route(device_id, route_device) {
self.set_mute(device_id, route_device, route, mute);
let card_profile_device = card_profile_device as i32;
if let Some(route) = self.active_node_route(device_id, card_profile_device) {
self.set_mute(device_id, card_profile_device, route, mute);
return;
};
}
@ -762,14 +783,21 @@ impl State {
};
// 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
.get(id)
.zip(self.node_card_profile_device.get(id))
{
let route_device = route_device as i32;
if let Some(route) = self.node_route(device_id, route_device) {
self.set_volume(device_id, props, route_device, route, volume, balance);
let card_profile_device = card_profile_device as i32;
if let Some(route) = self.active_node_route(device_id, card_profile_device) {
self.set_volume(
device_id,
props,
card_profile_device,
route,
volume,
balance,
);
return;
};
}

View file

@ -4,7 +4,7 @@
use crate::{Availability, spa_utils::string_from_pod};
use libspa::pod::Pod;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct Profile {
pub index: 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 p.index == profile.index {
*p = profile.clone();
@ -534,16 +534,17 @@ impl Model {
}
let profiles = self.device_profiles.entry(id).or_default();
for p in profiles.iter_mut() {
if p.index == profile.index {
*p = profile;
self.update_ui_profiles();
return;
}
if profiles.len() < index as usize + 1 {
let additional = (index as usize + 1) - profiles.capacity();
profiles.reserve_exact(additional);
profiles.extend(std::iter::repeat_n(
pipewire::Profile::default(),
additional,
));
}
profiles.push(profile);
profiles[index as usize] = profile;
self.update_ui_profiles();
}
@ -676,12 +677,22 @@ impl Model {
fn add_route(&mut self, id: DeviceId, index: u32, route: pipewire::Route) {
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();
if routes.len() < index as usize + 1 {
let additional = (index as usize + 1) - routes.capacity();
routes.reserve_exact(additional);
routes.extend(std::iter::repeat_n(pipewire::Route::default(), additional));
}
routes[index as usize] = route;
}