From 5ecfe9f5552835e5fe7327efce24f7490eb197b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 5 May 2025 17:14:34 +0200 Subject: [PATCH] scripts: Add script to find a suitable volume control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using role based priorities a volume slider presented to the user should adjust the volume of the currently playing role. E.g. when a phone has an incoming call and is ringing the default volume slider should adjust the ringing volume and once the call has been picked up it should adjust the call volume and once the call ended it should adjust the media volume again. It's currently hard for e.g. desktop shells to find out what a suitable sink for volume control is so add a script to find a suitable target for volume control (based on media role priority) by storing it's name in the metadata. Signed-off-by: Guido Günther --- .../node/find-media-role-default-volume.lua | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/scripts/node/find-media-role-default-volume.lua diff --git a/src/scripts/node/find-media-role-default-volume.lua b/src/scripts/node/find-media-role-default-volume.lua new file mode 100644 index 00000000..8900ad98 --- /dev/null +++ b/src/scripts/node/find-media-role-default-volume.lua @@ -0,0 +1,94 @@ +-- WirePlumber +-- +-- Copyright © 2025 Phosh.mobi e.V. +-- @author Guido Günther +-- +-- SPDX-License-Identifier: MIT +-- +-- Select the media role default volume + +log = Log.open_topic("s-node") + +local cutils = require ("common-utils") + +function findHighestPriorityRoleNode (node_om) + local best_role = nil + local best_prio = 0 + + local default_role = Settings.get ("node.stream.default-media-role") + if default_role then + default_role = default_role:parse() + end + + for ni in node_om:iterate { + type = "node", + Constraint { "media.class", "=", "Audio/Sink" }, + Constraint { "node.name", "#", "input.loopback.sink.role.*" }, + } do + local ni_props = ni.properties + local roles = ni_props["device.intended-roles"] + local node_name = ni_props ["node.name"] + local prio = tonumber(ni_props ["policy.role-based.priority"]) + + -- Use the node that handles the default_role as fallback + -- when no node is in running state + if best_role == nil and roles and default_role then + local roles_table = Json.Raw(roles):parse() + for i, v in ipairs (roles_table) do + if default_role == v then + best_role = node_name + best_prio = prio + break + end + end + end + + if ni.state == "running" then + if prio > best_prio then + best_role = node_name + best_prio = prio + end + end + end + + log:info (string.format ("Volume control is on : '%s', prio %d", best_role, best_prio)) + local metadata = cutils.get_default_metadata_object () + metadata:set (0, "current.role-based.volume.control", "Spa:String:JSON", + Json.Object { ["name"] = best_role }:to_string ()) +end + +SimpleEventHook { + name = "node/rescan-for-media-role-volume", + interests = { + EventInterest { + Constraint { "event.type", "=", "rescan-for-media-role-volume" } + }, + }, + execute = function (event) + local source = event:get_source () + local node_om = source:call ("get-object-manager", "node") + findHighestPriorityRoleNode (node_om) + end +}:register () + +-- Track best volume control for media role based priorities +SimpleEventHook { + name = "node/find-media-role-default-volume", + interests = { + EventInterest { + Constraint { "event.type", "=", "node-added" }, + Constraint { "media.class", "=", "Audio/Sink" }, + Constraint { "node.name", "#", "input.loopback.sink.role.*" } + }, + EventInterest { + Constraint { "event.type", "=", "node-state-changed" }, + Constraint { "media.class", "=", "Audio/Sink" }, + Constraint { "node.name", "#", "input.loopback.sink.role.*" } + }, + }, + execute = function (event) + local source = event:get_source () + local node_om = source:call ("get-object-manager", "node") + source:call ("schedule-rescan", "media-role-volume") + end +}:register ()