diff --git a/data/media-role-nodes.conf b/data/media-role-nodes.conf new file mode 100644 index 0000000..f25fbf5 --- /dev/null +++ b/data/media-role-nodes.conf @@ -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 + } +] diff --git a/examples/media-role-nodes.conf b/examples/media-role-nodes.conf new file mode 100644 index 0000000..f25fbf5 --- /dev/null +++ b/examples/media-role-nodes.conf @@ -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 + } +]