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
  }
]
