# 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.phone 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 = { # Voice calls node.name = "loopback.sink.role.phone" node.description = "Phone Calls" capture.props = { device.intended-roles = [ "Communication", "Phone" ] device.icon-name = "phone-ongoing-call" policy.role-based.priority = 25 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "cork" } } provides = loopback.sink.role.phone } { 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 } ]