Merge branch 'pw-roles' into 'main'
Set media-roles and add example wireplumber config See merge request agx/feedbackd!170
This commit is contained in:
+6
-3
@@ -7,7 +7,8 @@
|
||||
{
|
||||
"event-name" : "alarm-clock-elapsed",
|
||||
"type" : "Sound",
|
||||
"effect" : "alarm-clock-elapsed"
|
||||
"effect" : "alarm-clock-elapsed",
|
||||
"media-role" : "Alarm"
|
||||
},
|
||||
{
|
||||
"event-name" : "button-pressed",
|
||||
@@ -42,7 +43,8 @@
|
||||
{
|
||||
"event-name" : "message-new-cellbroadcast",
|
||||
"type" : "Sound",
|
||||
"effect" : "cellbroadcast"
|
||||
"effect" : "cellbroadcast",
|
||||
"media-role" : "Emergency"
|
||||
},
|
||||
{
|
||||
"event-name" : "message-new-email",
|
||||
@@ -62,7 +64,8 @@
|
||||
{
|
||||
"event-name" : "phone-incoming-call",
|
||||
"type" : "Sound",
|
||||
"effect" : "phone-incoming-call"
|
||||
"effect" : "phone-incoming-call",
|
||||
"media-role" : "Ringtone"
|
||||
},
|
||||
{
|
||||
"event-name" : "phone-hangup",
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
# Based on https://gitlab.freedesktop.org/pipewire/wireplumber/-/blob/master/src/config/wireplumber.conf.d.examples/media-role-nodes.conf
|
||||
|
||||
wireplumber.profiles = {
|
||||
main = {
|
||||
policy.linking.role-based.loopbacks = required
|
||||
}
|
||||
}
|
||||
|
||||
wireplumber.settings = {
|
||||
# Default role
|
||||
node.stream.default-media-role = "Multimedia"
|
||||
linking.role-based.duck-level = 0.5
|
||||
}
|
||||
|
||||
wireplumber.components.rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
provides = "~loopback.sink.*"
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
merge = {
|
||||
arguments = {
|
||||
capture.props = {
|
||||
# Explicitly mark all these sinks as valid role-based policy
|
||||
# targets, meaning that any links between streams and these sinks
|
||||
# will be managed by the role-based policy
|
||||
policy.role-based.target = true
|
||||
|
||||
audio.position = [ FL, FR ]
|
||||
media.class = Audio/Sink
|
||||
}
|
||||
playback.props = {
|
||||
# This must be set to ensure that the real audio sink is suspended
|
||||
# when there is no active client stream linked
|
||||
node.passive = true
|
||||
# Set this to an unused role to make sure that loopbacks don't
|
||||
# accidentally chain-link on to one another, especially when
|
||||
# node.stream.default-media-role is configured in the settings
|
||||
media.role = "Loopback"
|
||||
}
|
||||
}
|
||||
requires = [ support.export-core, pw.node-factory.adapter ]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
wireplumber.components = [
|
||||
{
|
||||
type = virtual, provides = policy.linking.role-based.loopbacks
|
||||
requires = [ loopback.sink.role.multimedia
|
||||
loopback.sink.role.notification
|
||||
loopback.sink.role.alarm
|
||||
loopback.sink.role.ringtone
|
||||
loopback.sink.role.alert ]
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
node.name = "loopback.sink.role.multimedia"
|
||||
node.description = "Multimedia"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Music", "Movie", "Game", "Multimedia" ]
|
||||
device.icon-name = "applications-multimedia"
|
||||
policy.role-based.priority = 10
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "mix"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.multimedia
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# System events from e.g. notifications
|
||||
node.name = "loopback.sink.role.notification"
|
||||
node.description = "System Events and Notifications"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Notification" ]
|
||||
device.icon-name = "preferences-system-notifications"
|
||||
policy.role-based.priority = 20
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "duck"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.notification
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# Alarm clocks
|
||||
node.name = "loopback.sink.role.alarm"
|
||||
node.description = "Alarm Clocks"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Alarm" ]
|
||||
device.icon-name = "alarm"
|
||||
policy.role-based.priority = 30
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "duck"
|
||||
policy.role-based.preferred-target = "Speaker"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.alarm
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# Phone call ring tones
|
||||
node.name = "loopback.sink.role.ringtone"
|
||||
node.description = "Ringtones"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Ringtone" ]
|
||||
device.icon-name = "call-ring"
|
||||
policy.role-based.priority = 40
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "cork"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.ringtone
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# Cell Broadcasts, etc
|
||||
node.name = "loopback.sink.role.alert"
|
||||
node.description = "Alerts"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Alert", "Emergency" ]
|
||||
device.icon-name = "network-cellular"
|
||||
policy.role-based.priority = 100
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "cork"
|
||||
policy.role-based.preferred-target = "Speaker"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.alert
|
||||
}
|
||||
]
|
||||
@@ -91,4 +91,7 @@ if get_option('daemon')
|
||||
install_dir: systemd_user_unit_dir,
|
||||
)
|
||||
|
||||
if get_option('media-roles')
|
||||
install_data('media-role-nodes.conf', install_dir: wireplumberdir)
|
||||
endif
|
||||
endif # daemon
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
# Based on https://gitlab.freedesktop.org/pipewire/wireplumber/-/blob/master/src/config/wireplumber.conf.d.examples/media-role-nodes.conf
|
||||
|
||||
wireplumber.profiles = {
|
||||
main = {
|
||||
policy.linking.role-based.loopbacks = required
|
||||
}
|
||||
}
|
||||
|
||||
wireplumber.settings = {
|
||||
# Default role
|
||||
node.stream.default-media-role = "Multimedia"
|
||||
linking.role-based.duck-level = 0.5
|
||||
}
|
||||
|
||||
wireplumber.components.rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
provides = "~loopback.sink.*"
|
||||
}
|
||||
]
|
||||
actions = {
|
||||
merge = {
|
||||
arguments = {
|
||||
capture.props = {
|
||||
# Explicitly mark all these sinks as valid role-based policy
|
||||
# targets, meaning that any links between streams and these sinks
|
||||
# will be managed by the role-based policy
|
||||
policy.role-based.target = true
|
||||
|
||||
audio.position = [ FL, FR ]
|
||||
media.class = Audio/Sink
|
||||
}
|
||||
playback.props = {
|
||||
# This must be set to ensure that the real audio sink is suspended
|
||||
# when there is no active client stream linked
|
||||
node.passive = true
|
||||
# Set this to an unused role to make sure that loopbacks don't
|
||||
# accidentally chain-link on to one another, especially when
|
||||
# node.stream.default-media-role is configured in the settings
|
||||
media.role = "Loopback"
|
||||
}
|
||||
}
|
||||
requires = [ support.export-core, pw.node-factory.adapter ]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
wireplumber.components = [
|
||||
{
|
||||
type = virtual, provides = policy.linking.role-based.loopbacks
|
||||
requires = [ loopback.sink.role.multimedia
|
||||
loopback.sink.role.notification
|
||||
loopback.sink.role.alarm
|
||||
loopback.sink.role.ringtone
|
||||
loopback.sink.role.alert ]
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
node.name = "loopback.sink.role.multimedia"
|
||||
node.description = "Multimedia"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Music", "Movie", "Game", "Multimedia" ]
|
||||
device.icon-name = "applications-multimedia"
|
||||
policy.role-based.priority = 10
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "mix"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.multimedia
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# System events from e.g. notifications
|
||||
node.name = "loopback.sink.role.notification"
|
||||
node.description = "System Events and Notifications"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Notification" ]
|
||||
device.icon-name = "preferences-system-notifications"
|
||||
policy.role-based.priority = 20
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "duck"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.notification
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# Alarm clocks
|
||||
node.name = "loopback.sink.role.alarm"
|
||||
node.description = "Alarm Clocks"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Alarm" ]
|
||||
device.icon-name = "alarm"
|
||||
policy.role-based.priority = 30
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "duck"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.alarm
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# Phone call ring tones
|
||||
node.name = "loopback.sink.role.ringtone"
|
||||
node.description = "Ringtones"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Ringtone" ]
|
||||
device.icon-name = "call-ring"
|
||||
policy.role-based.priority = 40
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "cork"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.ringtone
|
||||
}
|
||||
{
|
||||
name = libpipewire-module-loopback, type = pw-module
|
||||
arguments = {
|
||||
# Cell Broadcasts, etc
|
||||
node.name = "loopback.sink.role.alert"
|
||||
node.description = "Alerts"
|
||||
capture.props = {
|
||||
device.intended-roles = [ "Alert", "Emergency" ]
|
||||
device.icon-name = "network-cellular"
|
||||
policy.role-based.priority = 100
|
||||
policy.role-based.action.same-priority = "mix"
|
||||
policy.role-based.action.lower-priority = "cork"
|
||||
}
|
||||
}
|
||||
provides = loopback.sink.role.alert
|
||||
}
|
||||
]
|
||||
@@ -42,6 +42,7 @@ if udev.found() and get_option('daemon')
|
||||
else
|
||||
udevdir = prefix / 'lib' / 'udev' / 'rules.d'
|
||||
endif
|
||||
wireplumberdir = datadir / 'wireplumber' / 'wireplumber.conf.d'
|
||||
|
||||
run_data = configuration_data()
|
||||
run_data.set('ABS_BUILDDIR', meson.current_build_dir())
|
||||
@@ -124,6 +125,7 @@ summary = [
|
||||
' Introspection: @0@'.format(introspection),
|
||||
' Vapi: @0@'.format(get_option('vapi')),
|
||||
' Tests: @0@'.format(get_option('tests')),
|
||||
' Media Roles: @0@'.format(get_option('media-roles')),
|
||||
'---------------',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -19,3 +19,6 @@ option('tests',
|
||||
option('systemd_user_unit_dir',
|
||||
type: 'string', value: '',
|
||||
description: 'Directory for systemd user units')
|
||||
option('media-roles',
|
||||
type: 'boolean', value: false,
|
||||
description: 'Use media roles')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
ABS_BUILDDIR='@ABS_BUILDDIR@'
|
||||
@@ -14,5 +14,7 @@ if [ "${FBD_GDB}" = 1 ]; then
|
||||
WRAPPER="gdb --args"
|
||||
fi
|
||||
|
||||
EXEC=${@:-${ABS_BUILDDIR}/src/feedbackd}
|
||||
|
||||
set -x
|
||||
exec ${WRAPPER} "$@"
|
||||
exec ${WRAPPER} $EXEC
|
||||
|
||||
+9
-2
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
* 2025 Phosh.mobi e.V.
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*
|
||||
@@ -9,6 +10,8 @@
|
||||
|
||||
#define G_LOG_DOMAIN "fbd-dev-sound"
|
||||
|
||||
#include "fbd-config.h"
|
||||
|
||||
#include "fbd-dev-sound.h"
|
||||
#include "fbd-feedback-sound.h"
|
||||
|
||||
@@ -199,11 +202,13 @@ on_sound_play_finished_callback (GSoundContext *ctx,
|
||||
|
||||
|
||||
gboolean
|
||||
fbd_dev_sound_play (FbdDevSound *self, FbdFeedbackSound *feedback, FbdDevSoundPlayedCallback callback)
|
||||
fbd_dev_sound_play (FbdDevSound *self,
|
||||
FbdFeedbackSound *feedback,
|
||||
FbdDevSoundPlayedCallback callback)
|
||||
{
|
||||
FbdAsyncData *data;
|
||||
const char *filename;
|
||||
const char *role;
|
||||
const char *role = NULL;
|
||||
|
||||
g_return_val_if_fail (FBD_IS_DEV_SOUND (self), FALSE);
|
||||
g_return_val_if_fail (GSOUND_IS_CONTEXT (self->ctx), FALSE);
|
||||
@@ -213,7 +218,9 @@ fbd_dev_sound_play (FbdDevSound *self, FbdFeedbackSound *feedback, FbdDevSoundPl
|
||||
if (!g_hash_table_insert (self->playbacks, feedback, data))
|
||||
g_warning ("Feedback %p already present", feedback);
|
||||
|
||||
#ifdef FBD_USE_MEDIA_ROLES
|
||||
role = fbd_feedback_sound_get_media_role (feedback);
|
||||
#endif
|
||||
if (!role)
|
||||
role = "event";
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# Feedback daemon
|
||||
if get_option('daemon')
|
||||
|
||||
config_h = configuration_data()
|
||||
config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
|
||||
config_h.set('FBD_USE_MEDIA_ROLES', get_option('media-roles'))
|
||||
configure_file(output: 'fbd-config.h', configuration: config_h)
|
||||
|
||||
fbd_enum_headers = files('fbd-event.h', 'fbd-feedback-led.h', 'fbd-feedback-vibra.h')
|
||||
fbd_enum_sources = gnome.mkenums_simple('fbd-enums', sources: fbd_enum_headers)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user