wireplumber.profiles = { policy = { agl.policy.linking.role-based.loopbacks = required } } wireplumber.settings = { # This sets a default media role to be applied to streams that don't have # a role already set. This allows you to force all streams to go through # the role loopbacks. If not set, then streams without a role will follow # the standard desktop policy and will link to the default sink node.stream.default-media-role = "Multimedia" # The volume level that is applied when ducking # Note that this is a raw linear volume, not cubic (0.2 is 58%) linking.role-based.duck-level = 0.2 # Do not allow changing the targets of streams via `target.object` metadata linking.allow-moving-streams = false } wireplumber.components.rules = [ # This encodes common arguments and dependencies of the role loopbacks so that # we don't have to repeateadly write them on all instances below { 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 = [ pw.node-factory.adapter ] after = [ support.standard-event-source ] } } } ] wireplumber.components = [ { type = virtual, provides = agl.policy.linking.role-based.loopbacks requires = [ loopback.sink.role.multimedia loopback.sink.role.speech-low loopback.sink.role.custom-low loopback.sink.role.navigation loopback.sink.role.speech-high loopback.sink.role.custom-high loopback.sink.role.communication loopback.sink.role.emergency ] } { 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" ] policy.role-based.priority = 25 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 = { node.name = "loopback.sink.role.speech-low" node.description = "Speech (Low Priority)" capture.props = { device.intended-roles = [ "Speech-Low" ] policy.role-based.priority = 30 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "cork" } } provides = loopback.sink.role.speech-low } { name = libpipewire-module-loopback, type = pw-module arguments = { node.name = "loopback.sink.role.custom-low" node.description = "Custom role (Low Priority)" capture.props = { device.intended-roles = [ "Custom-Low" ] policy.role-based.priority = 35 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "cork" } } provides = loopback.sink.role.custom-low } { name = libpipewire-module-loopback, type = pw-module arguments = { node.name = "loopback.sink.role.navigation" node.description = "Navigation" capture.props = { device.intended-roles = [ "Navigation" ] policy.role-based.priority = 50 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "duck" } } provides = loopback.sink.role.navigation } { name = libpipewire-module-loopback, type = pw-module arguments = { node.name = "loopback.sink.role.speech-high" node.description = "Speech (High Priority)" capture.props = { device.intended-roles = [ "Speech-High" ] policy.role-based.priority = 60 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "cork" } } provides = loopback.sink.role.speech-high } { name = libpipewire-module-loopback, type = pw-module arguments = { node.name = "loopback.sink.role.custom-high" node.description = "Custom role (High Priority)" capture.props = { device.intended-roles = [ "Custom-High" ] policy.role-based.priority = 65 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "cork" } } provides = loopback.sink.role.custom-high } { name = libpipewire-module-loopback, type = pw-module arguments = { node.name = "loopback.sink.role.communication" node.description = "Communication" capture.props = { device.intended-roles = [ "Communication" ] policy.role-based.priority = 75 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "cork" } } provides = loopback.sink.role.communication } { name = libpipewire-module-loopback, type = pw-module arguments = { node.name = "loopback.sink.role.emergency" node.description = "Emergency" capture.props = { device.intended-roles = [ "Emergency", "Alert" ] policy.role-based.priority = 99 policy.role-based.action.same-priority = "mix" policy.role-based.action.lower-priority = "cork" } } provides = loopback.sink.role.emergency } ]