# This example configuration file specifies loopback nodes that can be used
# to apply a role-based linking policy.
#
# The loopbacks are simply created by loading the libpipewire-module-loopback
# pipewire module with the appropriate options. You may also create these
# loopbacks in the pipewire daemon process by configuring them in pipewire.conf
# (using pipewire's syntax) instead of here.

wireplumber.profiles = {
  main = {
    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"

  ## This allows you to tweak the volume level that is applied when ducking
  #linking.role-based.duck-level = 0.3
}

wireplumber.components.rules = [
  # This encodes common arguments and dependencies of the role loopbacks so that
  # we don't have to repeatedly 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 = [ 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.navigation
                 loopback.sink.role.assistant
                 loopback.sink.role.communication
                 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" ]
        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 = {
      node.name = "loopback.sink.role.notification"
      node.description = "System Notifications"
      capture.props = {
        device.intended-roles = [ "Notification" ]
        policy.role-based.priority = 20
        policy.role-based.action.same-priority = "mix"
        policy.role-based.action.lower-priority = "mix"
      }
    }
    provides = loopback.sink.role.notification
  }
  {
    name = libpipewire-module-loopback, type = pw-module
    arguments = {
      node.name = "loopback.sink.role.navigation"
      node.description = "Navigation"
      capture.props = {
        device.intended-roles = [ "Navigation", "GPS" ]
        policy.role-based.priority = 30
        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.assistant"
      node.description = "Voice Assistant"
      capture.props = {
        device.intended-roles = [ "Assistant" ]
        policy.role-based.priority = 40
        policy.role-based.action.same-priority = "mix"
        policy.role-based.action.lower-priority = "cork"
      }
    }
    provides = loopback.sink.role.assistant
  }
  {
    name = libpipewire-module-loopback, type = pw-module
    arguments = {
      node.name = "loopback.sink.role.communication"
      node.description = "Communication"
      capture.props = {
        device.intended-roles = [ "Communication", "Phone" ]
        policy.role-based.priority = 50
        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.alert"
      node.description = "Alert"
      capture.props = {
        device.intended-roles = [ "Alert", "Emergency" ]
        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
  }
]
