## The WirePlumber configuration

context.spa-libs = {
  ## SPA factory name to library mappings
  ## Used to find SPA factory names. It maps a SPA factory name regular
  ## expression to a library name that should contain that factory.
  ##
  ## Syntax:
  ## <factory-name regex> = <library-name>

  api.alsa.*      = alsa/libspa-alsa
  api.bluez5.*    = bluez5/libspa-bluez5
  api.v4l2.*      = v4l2/libspa-v4l2
  api.libcamera.* = libcamera/libspa-libcamera
  audio.convert.* = audioconvert/libspa-audioconvert
  support.*       = support/libspa-support
}

context.modules = [
  ## PipeWire modules to load.
  ## These modules are loaded before a connection to pipewire is attempted.
  ## This section should be kept minimal and load only the modules that are
  ## necessary for the protocol to work.
  ##
  ## If ifexists is given, the module is ignored when it is not found.
  ## If nofail is given, module initialization failures are ignored.
  ##
  ## Syntax:
  ## {
  ##    name = <module-name>
  ##    [ args = { <key> = <value> ... } ]
  ##    [ flags = [ ifexists | nofail ] ]
  ## }

  # Uses RTKit to boost the data thread priority. Also allows clamping
  # of utilisation when using the Completely Fair Scheduler on Linux.
  {
    name = libpipewire-module-rt
    args = {
        nice.level   = -11
        # rt.prio      = 88
        # rt.time.soft = -1
        # rt.time.hard = -1
        # uclamp.min = 0
        # uclamp.max = 1024
    }
    flags = [ ifexists, nofail ]
  }

  ## The native communication protocol.
  { name = libpipewire-module-protocol-native }

  ## Support for metadata objects
  { name = libpipewire-module-metadata }
]

wireplumber.profiles = {
  ## Syntax:
  ## <profile> = {
  ##   inherits = [ other, profile, names ]   # optional
  ##   # optional is the default
  ##   <feature name> = [ required | optional | disabled ]
  ##   ...
  ## }

  # The default profile
  main = {
    inherits = [ base ]

    metadata.sm-settings = required
    metadata.sm-objects = required

    policy.standard = required

    hardware.audio = required
    hardware.bluetooth = required
    hardware.video-capture = required
  }

  # Profile for running on a systemwide level
  main-systemwide = {
    inherits = [ main, mixin.systemwide-session ]
  }

  # Typical profile for embedded use cases, systemwide without maintaining state
  main-embedded = {
    inherits = [ main, mixin.systemwide-session, mixin.stateless ]
  }

  # Profile for video-only use cases (camera & screen sharing)
  video-only = {
    inherits = [ main ]
    hardware.audio = disabled
    hardware.bluetooth = disabled
  }

  ##
  ## Profiles for multi-instance setup
  ##

  policy = {
    inherits = [ base ]
    metadata.sm-settings = required
    metadata.sm-objects = required
    policy.standard = required
  }

  audio = {
    inherits = [ base ]
    hardware.audio = required
  }

  bluetooth = {
    inherits = [ base ]
    hardware.bluetooth = required
  }

  video-capture = {
    inherits = [ base ]
    hardware.video-capture = required
  }

  ##
  ## The following are not complete profiles, but blocks that can be inherited
  ##

  # The bare minimum that all instances should have by default
  base = {
    check.no-media-session = required
    support.settings = required
    support.log-settings = required
    support.session-services = required
  }

  # Disable features that are meant only for user sessions
  mixin.systemwide-session = {
    support.reserve-device = disabled
    monitor.alsa.reserve-device = disabled

    support.portal-permissionstore = disabled
    script.client.access-portal = disabled

    support.logind = disabled
    monitor.bluez.seat-monitoring = disabled
  }

  # Disable storing/restoring runtime state information regarding
  # default nodes selection, profiles, routes and stream volume / properties
  # This makes sense for embedded systems that should always reboot to a default
  # state, not remembering any of the user's runtime changes
  mixin.stateless = {
    hooks.device.profile.state = disabled
    hooks.device.routes.state = disabled
    hooks.default-nodes.state = disabled
    hooks.stream.state = disabled
  }
}

wireplumber.components = [
  ## WirePlumber components to load.
  ## These components are loaded after a connection to pipewire is established.
  ## type is mandatory; rest of the tags are optional
  ##
  ## Syntax:
  ## {
  ##   name = <component-name>
  ##   type = <component-type>
  ##   arguments = { <json object> }
  ##
  ##   # Feature that this component provides
  ##   provides = <feature>
  ##
  ##   # List of features that must be provided before this component is loaded
  ##   requires = [ <features> ]
  ##
  ##   # List of features that would offer additional functionality if provided
  ##   # but are not strictly required
  ##   wants = [ <features> ]
  ## }

  ## Check to avoid loading together with media-session
  {
    name = ensure-no-media-session, type = built-in
    provides = check.no-media-session
  }

  ## Makes a secondary connection to PipeWire for exporting objects
  {
    name = export-core, type = built-in
    provides = support.export-core
  }

  ## Enables creating local nodes that are exported to pipewire
  ## This is needed for LocalNode() / WpImplNode
  ## This should be used with the export-core to avoid protocol deadlocks,
  ## unless you know what you are doing
  {
    name = libpipewire-module-client-node, type = pw-module
    provides = pw.client-node
    wants = [ support.export-core ]
  }

  ## Enables creating local devices that are exported to pipewire
  ## This is needed for SpaDevice() / WpSpaDevice
  ## This should be used with the export-core to avoid protocol deadlocks,
  ## unless you know what you are doing
  {
    name = libpipewire-module-client-device, type = pw-module
    provides = pw.client-device
    wants = [ support.export-core ]
  }

  # Provides a node factory to create SPA nodes
  # You need this to use LocalNode("spa-node-factory", ...)
  {
    name = libpipewire-module-spa-node-factory, type = pw-module
    provides = pw.node-factory.spa
    requires = [ pw.client-node ]
  }

  ## Provides a node factory to create SPA nodes wrapped in an adapter
  ## You need this to use LocalNode("adapter", ...)
  {
    name = libpipewire-module-adapter, type = pw-module
    provides = pw.node-factory.adapter
    requires = [ pw.client-node ]
  }

  ## Provides the "sm-settings" metadata object
  {
    name = libwireplumber-module-settings, type = module
    arguments = { metadata.name = sm-settings }
    provides = metadata.sm-settings
  }

  ## Activates a global WpSettings instance, providing settings from
  ## the sm-settings metadata object. Note that this blocks and waits for the
  ## sm-settings metadata object to become available, so one instance must
  ## provide that, while others should only load this to access settings
  {
    name = settings-instance, type = built-in
    arguments = { metadata.name = sm-settings }
    provides = support.settings
    after = [ metadata.sm-settings ]
  }

  ## Log level settings
  {
    name = libwireplumber-module-log-settings, type = module
    provides = support.log-settings
  }

  ## The lua scripting engine
  {
    name = libwireplumber-module-lua-scripting, type = module
    provides = support.lua-scripting
  }

  ## Module listening for pipewire objects to push events
  {
    name = libwireplumber-module-standard-event-source, type = module
    provides = support.standard-event-source
  }

  ## The shared D-Bus connection
  {
    name = libwireplumber-module-dbus-connection, type = module
    arguments = { plugin.name = dbus-connection, bus.system = false }
    provides = support.dbus
  }

  ## The shared system D-Bus connection
  {
    name = libwireplumber-module-dbus-connection, type = module
    arguments = { plugin.name = system-dbus-connection, bus.system = true }
    provides = support.system-dbus
  }

  ## Module managing the portal permissions
  {
    name = libwireplumber-module-portal-permissionstore, type = module
    provides = support.portal-permissionstore
    requires = [ support.dbus ]
  }

  ## Needed for device reservation to work
  {
    name = libwireplumber-module-reserve-device, type = module
    provides = support.reserve-device
    requires = [ support.dbus ]
  }

  ## logind integration to enable certain functionality only on the active seat
  {
    name = libwireplumber-module-logind, type = module
    provides = support.logind
  }

  ## ModemManager integration to control call audio during a call
  {
    name = libwireplumber-module-modem-manager, type = module
    provides = support.modem-manager
    requires = [ support.system-dbus ]
  }

  ## Session item factories
  {
    name = libwireplumber-module-si-node, type = module
    provides = si.node
  }
  {
    name = libwireplumber-module-si-audio-adapter, type = module
    provides = si.audio-adapter
  }
  {
    name = libwireplumber-module-si-standard-link, type = module
    provides = si.standard-link
  }

  ## API to access default nodes from scripts
  {
    name = libwireplumber-module-default-nodes-api, type = module
    provides = api.default-nodes
  }

  ## API to access mixer controls
  {
    name = libwireplumber-module-mixer-api, type = module
    provides = api.mixer
  }

  ## API to get notified about file changes
  {
    name = libwireplumber-module-file-monitor-api, type = module
    provides = api.file-monitor
  }

  ## API to send D-Bus notifications
  {
    name = libwireplumber-module-notifications-api, type = module
    provides = api.notifications
    requires = [ support.dbus ]
  }


  ## Provide the "default" pw_metadata
  {
    name = metadata.lua, type = script/lua
    arguments = { metadata.name = default }
    provides = metadata.default
  }

  ## Provide the "filters" pw_metadata
  {
    name = metadata.lua, type = script/lua
    arguments = { metadata.name = filters }
    provides = metadata.filters
  }

  ## Provide the "sm-objects" pw_metadata, supporting dynamic loadable objects
  {
    name = sm-objects.lua, type = script/lua
    provides = metadata.sm-objects
  }

  ## Populates the "session.services" property on the WirePlumber client object
  {
    name = session-services.lua, type = script/lua
    provides = support.session-services
  }

  ## Provides support for MPRIS
  {
    name = libwireplumber-module-mpris, type = module
    provides = support.mpris
    requires = [ support.dbus ]
  }

  ## Device monitors' optional features
  {
    type = virtual, provides = monitor.alsa.reserve-device,
    requires = [ support.reserve-device ]
  }
  {
    type = virtual, provides = monitor.alsa-midi.monitoring,
    requires = [ api.file-monitor ]
  }
  {
    type = virtual, provides = monitor.bluez.seat-monitoring,
    requires = [ support.logind ]
  }

  ## Device monitors
  {
    name = monitors/alsa.lua, type = script/lua
    provides = monitor.alsa
    requires = [ support.export-core, pw.client-device ]
    wants = [ monitor.alsa.reserve-device ]
  }
  {
    name = monitors/bluez.lua, type = script/lua
    provides = monitor.bluez
    requires = [ support.export-core,
                 pw.client-device,
                 pw.client-node,
                 pw.node-factory.adapter ]
    wants = [ monitor.bluez.seat-monitoring ]
  }
  {
    name = monitors/bluez-midi.lua, type = script/lua
    provides = monitor.bluez-midi
    requires = [ support.export-core,
                 pw.client-device,
                 pw.client-node,
                 pw.node-factory.spa ]
    wants = [ monitor.bluez.seat-monitoring ]
  }
  {
    name = monitors/alsa-midi.lua, type = script/lua
    provides = monitor.alsa-midi
    wants = [ monitor.alsa-midi.monitoring ]
  }
  ## v4l2 monitor
  {
    name = monitors/v4l2/name-device.lua, type = script/lua
    provides = hooks.monitor.v4l2-name-device
  }
  {
    name = monitors/v4l2/create-device.lua, type = script/lua
    provides = hooks.monitor.v4l2-create-device
    requires = [ support.export-core,
                 pw.client-device,
                 support.standard-event-source ]
  }
  {
    name = monitors/v4l2/name-node.lua, type = script/lua
    provides = hooks.monitor.v4l2-name-node
  }
  {
    name = monitors/v4l2/create-node.lua, type = script/lua
    provides = hooks.monitor.v4l2-create-node
  }
  {
    name = monitors/v4l2/enumerate-device.lua, type = script/lua
    provides = monitor.v4l2
    requires = [ support.export-core,
                 pw.client-device,
                 support.standard-event-source,
                 hooks.monitor.v4l2-create-device,
                 hooks.monitor.v4l2-create-node ]
    wants = [ hooks.monitor.v4l2-name-device,
              hooks.monitor.v4l2-name-node ]
  }
  ## libcamera monitor
  {
    name = monitors/libcamera/name-device.lua, type = script/lua
    provides = hooks.monitor.libcamera-name-device
  }
  {
    name = monitors/libcamera/create-device.lua, type = script/lua
    provides = hooks.monitor.libcamera-create-device
    requires = [ support.export-core,
                 pw.client-device,
                 support.standard-event-source ]
  }
  {
    name = monitors/libcamera/name-node.lua, type = script/lua
    provides = hooks.monitor.libcamera-name-node
  }
  {
    name = monitors/libcamera/create-node.lua, type = script/lua
    provides = hooks.monitor.libcamera-create-node
  }
  {
    name = monitors/libcamera/enumerate-device.lua, type = script/lua
    provides = monitor.libcamera
    requires = [ support.export-core,
                 pw.client-device,
                 support.standard-event-source,
                 hooks.monitor.libcamera-create-device,
                 hooks.monitor.libcamera-create-node ]
    wants = [ hooks.monitor.libcamera-name-device,
              hooks.monitor.libcamera-name-node ]
  }

  ## Client access configuration hooks
  {
    name = client/access-default.lua, type = script/lua
    provides = script.client.access-default
  }
  {
    name = client/access-portal.lua, type = script/lua
    provides = script.client.access-portal
    requires = [ support.portal-permissionstore ]
  }
  {
    name = client/access-snap.lua, type = script/lua
    provides = script.client.access-snap
  }
  {
    type = virtual, provides = policy.client.access
    wants = [ script.client.access-default,
              script.client.access-portal,
              script.client.access-snap ]
  }

  ## Device profile selection hooks
  {
    name = device/select-profile.lua, type = script/lua
    provides = hooks.device.profile.select
  }
  {
    name = device/find-voice-call-profile.lua, type = script/lua
    provides = hooks.device.profile.find-voice-call
    requires = [ support.modem-manager ]
  }
  {
    name = device/find-preferred-profile.lua, type = script/lua
    provides = hooks.device.profile.find-preferred
  }
  {
    name = device/find-best-profile.lua, type = script/lua
    provides = hooks.device.profile.find-best
  }
  {
    name = device/state-profile.lua, type = script/lua
    provides = hooks.device.profile.state
  }
  {
    name = device/apply-profile.lua, type = script/lua
    provides = hooks.device.profile.apply
  }
  {
    name = device/autoswitch-bluetooth-profile.lua, type = script/lua
    provides = hooks.device.profile.autoswitch-bluetooth
  }
  {
    type = virtual, provides = policy.device.profile
    requires = [ hooks.device.profile.select,
                 hooks.device.profile.autoswitch-bluetooth,
                 hooks.device.profile.apply ]
    wants = [ hooks.device.profile.find-voice-call,
              hooks.device.profile.find-best,
              hooks.device.profile.find-preferred,
              hooks.device.profile.state ]
  }

  # Device route selection hooks
  {
    name = device/select-routes.lua, type = script/lua
    provides = hooks.device.routes.select
  }
  {
    name = device/find-best-routes.lua, type = script/lua
    provides = hooks.device.routes.find-best
  }
  {
    name = device/state-routes.lua, type = script/lua
    provides = hooks.device.routes.state
  }
  {
    name = device/apply-routes.lua, type = script/lua
    provides = hooks.device.routes.apply
  }
  {
    name = device/automute-alsa-routes.lua, type = script/lua
    provides = hooks.device.routes.automute-alsa
    wants = [ api.notifications ]
  }
  {
    type = virtual, provides = policy.device.routes
    requires = [ hooks.device.routes.select,
                 hooks.device.routes.apply ]
    wants = [ hooks.device.routes.find-best,
              hooks.device.routes.state,
              hooks.device.routes.automute-alsa ]
  }

  ## Default nodes selection hooks
  {
    name = default-nodes/rescan.lua, type = script/lua
    provides = hooks.default-nodes.rescan
  }
  {
    name = default-nodes/find-selected-default-node.lua, type = script/lua
    provides = hooks.default-nodes.find-selected
    requires = [ metadata.default ]
  }
  {
    name = default-nodes/find-best-default-node.lua, type = script/lua
    provides = hooks.default-nodes.find-best
  }
  {
    name = default-nodes/state-default-nodes.lua, type = script/lua
    provides = hooks.default-nodes.state
    requires = [ metadata.default ]
  }
  {
    name = default-nodes/apply-default-node.lua, type = script/lua,
    provides = hooks.default-nodes.apply
    requires = [ metadata.default ]
  }
  {
    type = virtual, provides = policy.default-nodes
    requires = [ hooks.default-nodes.rescan,
                 hooks.default-nodes.apply ]
    wants = [ hooks.default-nodes.find-selected,
              hooks.default-nodes.find-best,
              hooks.default-nodes.state ]
  }

  ## Node configuration hooks
  {
    name = node/create-item.lua, type = script/lua
    provides = hooks.node.create-session-item
    requires = [ si.audio-adapter, si.node ]
  }
  {
    name = node/suspend-node.lua, type = script/lua
    provides = hooks.node.suspend
  }
  {
    name = node/state-stream.lua, type = script/lua
    provides = hooks.stream.state
  }
  {
    name = node/filter-forward-format.lua, type = script/lua
    provides = hooks.filter.forward-format
  }
  {
    name = node/filter-graph.lua, type = script/lua
    provides = hooks.filter.graph
  }
  {
    type = virtual, provides = policy.node
    requires = [ hooks.node.create-session-item ]
    wants = [ hooks.node.suspend
              hooks.stream.state
              hooks.filter.forward-format
              hooks.filter.graph ]
  }
  {
    name = node/software-dsp.lua, type = script/lua
    provides = node.software-dsp
  }
  {
    name = node/audio-group.lua, type = script/lua
    provides = node.audio-group
  }

  ## Linking hooks
  {
    name = linking/rescan.lua, type = script/lua
    provides = hooks.linking.rescan
  }
  {
    name = linking/find-media-role-target.lua, type = script/lua
    provides = hooks.linking.target.find-media-role
  }
  {
    name = linking/find-defined-target.lua, type = script/lua
    provides = hooks.linking.target.find-defined
  }
  {
    name = linking/find-audio-group-target.lua, type = script/lua
    provides = hooks.linking.target.find-audio-group
    requires = [ node.audio-group ]
  }
  {
    name = linking/find-filter-target.lua, type = script/lua
    provides = hooks.linking.target.find-filter
    requires = [ metadata.filters ]
  }
  {
    name = linking/find-default-target.lua, type = script/lua
    provides = hooks.linking.target.find-default
    requires = [ api.default-nodes ]
  }
  {
    name = linking/find-best-target.lua, type = script/lua
    provides = hooks.linking.target.find-best
    requires = [ metadata.filters ]
  }
  {
    name = linking/get-filter-from-target.lua, type = script/lua
    provides = hooks.linking.target.get-filter-from
    requires = [ metadata.filters ]
  }
  {
    name = linking/prepare-link.lua, type = script/lua
    provides = hooks.linking.target.prepare-link
    requires = [ api.default-nodes ]
  }
  {
    name = linking/link-target.lua, type = script/lua
    provides = hooks.linking.target.link
    requires = [ si.standard-link ]
  }
  {
    name = linking/mpris-pause.lua, type = script/lua
    provides = hooks.linking.pause-playback
    requires = [ support.mpris ]
  }
  {
    type = virtual, provides = policy.linking.standard
    requires = [ hooks.linking.rescan,
                 hooks.linking.target.prepare-link,
                 hooks.linking.target.link ]
    wants = [ hooks.linking.target.find-media-role,
              hooks.linking.target.find-defined,
              hooks.linking.target.find-audio-group,
              hooks.linking.target.find-filter,
              hooks.linking.target.find-default,
              hooks.linking.target.find-best,
              hooks.linking.target.get-filter-from,
              hooks.linking.pause-playback ]
  }

  ## Linking: Role-based priority system
  {
    name = linking/rescan-media-role-links.lua, type = script/lua
    provides = hooks.linking.role-based.rescan
    requires = [ api.mixer ]
  }
  {
    name = node/find-media-role-default-volume.lua, type = script/lua
    provides = hooks.node.role-based.default-volume
    requires = [ hooks.linking.role-based.rescan ]
  }
  {
    name = linking/find-media-role-sink-target.lua, type = script/lua
    provides = hooks.linking.target.find-media-role-sink
  }
  {
    type = virtual, provides = policy.linking.role-based
    requires = [ policy.linking.standard,
                 hooks.linking.role-based.rescan,
                 hooks.node.role-based.default-volume,
                 hooks.linking.target.find-media-role-sink ]
  }

  ## Standard policy definition
  {
    type = virtual, provides = policy.standard
    requires = [ policy.client.access
                 policy.device.profile
                 policy.device.routes
                 policy.default-nodes
                 policy.linking.standard
                 policy.linking.role-based
                 policy.node
                 support.standard-event-source ]
  }

  ## Load targets
  {
    type = virtual, provides = hardware.audio
    wants = [ monitor.alsa, monitor.alsa-midi ]
  }
  {
    type = virtual, provides = hardware.bluetooth
    wants = [ monitor.bluez, monitor.bluez-midi ]
  }
  {
    type = virtual, provides = hardware.video-capture
    wants = [ monitor.v4l2, monitor.libcamera ]
  }
]

wireplumber.components.rules = [
  ## Rules to apply on top of wireplumber.components
  ## Syntax:
  ## {
  ##   matches = [
  ##     {
  ##       [ <key> = <value> ... ]
  ##     }
  ##     ...
  ##   ]
  ##   actions = {
  ##     <override|merge> = {
  ##       [ <key> = <value> ... ]
  ##     }
  ##     ...
  ##   }
  ## }

  {
    matches = [
      {
        type = "script/lua"
      }
    ]
    actions = {
      merge = {
        requires = [ support.lua-scripting ]
      }
    }
  }
  {
    matches = [
      {
        provides = "~hooks.*"
        name = "!~monitors/.*"
      }
    ]
    actions = {
      merge = {
        before = [ support.standard-event-source ]
      }
    }
  }
  {
    matches = [
      { provides = "~monitor.*" }
    ]
    actions = {
      merge = {
        after = [ support.standard-event-source ]
      }
    }
  }
  # session-services.lua must execute at the very end
  {
    matches = [
      { name = "!session-services.lua" }
    ]
    actions = {
      merge = {
        before = [ support.session-services ]
      }
    }
  }
]

wireplumber.settings.schema = {
  ## Bluetooth
  bluetooth.use-persistent-storage = {
    name = "Persistent storage"
    description = "Remember and restore Bluetooth headset mode status"
    type = "bool"
    default = true
  }
  bluetooth.autoswitch-to-headset-profile = {
    name = "Auto-switch to headset profile"
    description = "Always show microphone for Bluetooth headsets, and switch to headset mode when recording"
    type = "bool"
    default = true
  }

  ## Device
  device.restore-profile = {
    name = "Restore profile"
    description = "Remember and restore device profiles"
    type = "bool"
    default = true
  }
  device.restore-routes = {
    name = "Restore routes"
    description = "Remember and restore device routes"
    type = "bool"
    default = true
  }
  device.routes.default-sink-volume = {
    name = "Default sink volume"
    description = "The default volume for audio sinks"
    type = "float"
    default = 0.064
    min = 0.0
    max = 1.0
  }
  device.routes.default-source-volume = {
    name = "Default source volume"
    description = "The default volume for audio sources"
    type = "float"
    default = 1.0
    min = 0.0
    max = 1.0
  }
  device.routes.mute-on-alsa-playback-removed = {
    name = "Auto-mute on wired audio disconnect"
    description = "Automatically mute all audio devices when active wired headphones/speakers are disconnected to prevent unintended sound output"
    type = "bool"
    default = false
  }
  device.routes.mute-on-bluetooth-playback-removed = {
    name = "Auto-mute on Bluetooth audio disconnect"
    description = "Automatically mute all audio devices when active Bluetooth headphones/speakers are disconnected to prevent unintended sound output"
    type = "bool"
    default = false
  }


  ## Linking
  linking.role-based.duck-level = {
    name = "Ducking level"
    description = "The volume level to apply when ducking (= reducing volume for a higher priority stream to be audible) in the role-based linking policy"
    type = "float"
    default = 0.3
    min = 0.0
    max = 1.0
  }
  linking.allow-moving-streams = {
    name = "Allow moving streams"
    description = "Streams may be moved by adding PipeWire metadata at runtime"
    type = "bool"
    default = true
  }
  linking.follow-default-target = {
    name = "Follow default target"
    description = "Streams connected to the default device follow when default changes"
    type = "bool"
    default = true
  }
  linking.pause-playback = {
    name = "Pause playback if output removed"
    description = "Pause media players if their target sink is removed"
    type = "bool"
    default = true
  }

  ## Monitor
  monitor.camera-discovery-timeout = {
    name = "Discovery timeout"
    description = "The camera discovery timeout in milliseconds"
    type = "int"
    default = 1000
    min = 0
    max = 60000
  }
  monitor.alsa.autodetect-hdmi-channels = {
    name = "Automatically detect HDMI channels (experimental)"
    description = "Automatically detect channel count and positions for HDMI devices (experimental)"
    type = "bool"
    default = false
  }

  ## Node
  node.features.audio.no-dsp = {
    name = "No DSP"
    description = "Do not convert audio to F32 format"
    type = "bool"
    default = false
  }
  node.features.audio.monitor-ports = {
    name = "Monitor ports"
    description = "Enable monitor ports on audio nodes"
    type = "bool"
    default = true
  }
  node.features.audio.control-port = {
    name = "Control ports"
    description = "Enable control ports on audio nodes"
    type = "bool"
    default = false
  }
  node.features.audio.mono = {
    name = "Mono"
    description = "Configure all audio device sink nodes in MONO"
    type = "bool"
    default = false
  }
  node.stream.restore-props = {
    name = "Restore properties"
    description = "Remember and restore properties of streams"
    type = "bool"
    default = true
  }
  node.stream.restore-target = {
    name = "Restore target"
    description = "Remember and restore stream targets"
    type = "bool"
    default = true
  }
  node.stream.default-playback-volume = {
    name = "Default playback volume"
    description = "The default volume for playback nodes"
    type = "float"
    default = 1.0
    min = 0.0
    max = 1.0
  }
  node.stream.default-capture-volume = {
    name = "Default capture volume"
    description = "The default volume for capture nodes"
    type = "float"
    default = 1.0
    min = 0.0
    max = 1.0
  }
  node.stream.default-media-role = {
    name = "Default media role"
    description = "Default media.role to assign on streams that do not specify it"
    type = "string"
    default = null
  }
  node.filter.forward-format = {
    name = "Forward format"
    description = "Forward format on filter nodes or not"
    type = "bool"
    default = false
  }
  node.restore-default-targets = {
    name = "Restore default target"
    description = "Remember and restore default audio/video input/output devices"
    type = "bool"
    default = true
  }
}
