# SPDX-License-Identifier: MIT # Copyright © 2019-2021 Collabora Ltd. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice (including the next # paragraph) shall be included in all copies or substantial portions of the # Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. 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: ## = api.alsa.* = alsa/libspa-alsa 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 = ## [ args = { = ... } ] ## [ 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: ## = { ## inherits = [ other, profile, names ] # optional ## # optional is the default ## = [ required | optional | disabled ] ## ... ## } # The default profile main = { metadata.sm-settings = required metadata.sm-objects = required policy.standard = required hardware.audio = required # PipeWire Media Session is not running. # Do not bother to check. check.no-media-session = disabled # No Bluetooth hardware.bluetooth = disabled bluetooth.use-persistent-storage = disabled bluetooth.autoswitch-to-headset-profile = disabled # No video capture hardware.video-capture = disabled # There is only one instance of WirePlumber, so # pretend that this is a system-wide instance. # See mixin.systemwide-session in the upstream wireplumber.conf. 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 anything that could store state. # See mixin.stateless in the upstream wireplumber.conf. 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 = ## type = ## arguments = { } ## ## # Feature that this component provides ## provides = ## ## # List of features that must be provided before this component is loaded ## requires = [ ] ## ## # List of features that would offer additional functionality if provided ## # but are not strictly required ## wants = [ ] ## } ## 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 } ## 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 } ## 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 } ## Device monitors' optional features { type = virtual, provides = monitor.alsa-midi.monitoring, requires = [ api.file-monitor ] } ## Device monitors { name = monitors/alsa.lua, type = script/lua provides = monitor.alsa requires = [ support.export-core, pw.client-device ] } { name = monitors/alsa-midi.lua, type = script/lua provides = monitor.alsa-midi wants = [ monitor.alsa-midi.monitoring ] } ## Client access configuration hooks { name = client/access-default.lua, type = script/lua provides = script.client.access-default } { type = virtual, provides = policy.client.access wants = [ script.client.access-default ] } ## Device profile selection hooks { name = device/select-profile.lua, type = script/lua provides = hooks.device.profile.select } { 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/apply-profile.lua, type = script/lua provides = hooks.device.profile.apply } { type = virtual, provides = policy.device.profile requires = [ hooks.device.profile.select, hooks.device.profile.apply ] wants = [ hooks.device.profile.find-best, hooks.device.profile.find-preferred ] } # 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/apply-routes.lua, type = script/lua provides = hooks.device.routes.apply } { type = virtual, provides = policy.device.routes requires = [ hooks.device.routes.select, hooks.device.routes.apply ] wants = [ hooks.device.routes.find-best ] } ## 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/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 ] } ## 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/filter-forward-format.lua, type = script/lua provides = hooks.filter.forward-format } { type = virtual, provides = policy.node requires = [ hooks.node.create-session-item ] wants = [ hooks.node.suspend hooks.filter.forward-format ] } { 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 ] } { 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 ] } ## Linking: Role-based priority system { name = linking/rescan-media-role-links.lua, type = script/lua provides = hooks.linking.role-based.rescan requires = [ api.mixer ] } { type = virtual, provides = policy.linking.role-based requires = [ policy.linking.standard, hooks.linking.role-based.rescan ] } ## 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 ] } ] wireplumber.components.rules = [ ## Rules to apply on top of wireplumber.components { 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 = { description = "Whether to use persistent BT storage or not" type = "bool" default = true } bluetooth.autoswitch-to-headset-profile = { description = "Whether to autoswitch to BT headset profile or not" type = "bool" default = true } ## Device device.restore-profile = { description = "Whether to restore device profile or not" type = "bool" default = true } device.restore-routes = { description = "Whether to restore device routes or not" type = "bool" default = true } device.routes.default-sink-volume = { description = "The default volume for sink devices" type = "float" default = 0.064 min = 0.0 max = 1.0 } device.routes.default-source-volume = { description = "The default volume for source devices" type = "float" default = 1.0 min = 0.0 max = 1.0 } ## Linking linking.role-based.duck-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 = { description = "Whether to allow metadata to move streams at runtime or not" type = "bool" default = true } linking.follow-default-target = { description = "Whether to allow streams follow the default device or not" type = "bool" default = true } ## Monitor monitor.camera-discovery-timeout = { description = "The camera discovery timeout in milliseconds" type = "int" default = 1000 min = 0 max = 60000 } ## Node node.features.audio.no-dsp = { description = "Whether to never convert audio to F32 format or not" type = "bool" default = false } node.features.audio.monitor-ports = { description = "Whether to enable monitor ports on audio nodes or not" type = "bool" default = true } node.features.audio.control-port = { description = "Whether to enable control ports on audio nodes or not" type = "bool" default = false } node.stream.restore-props = { description = "Whether to restore properties on stream nodes or not" type = "bool" default = true } node.stream.restore-target = { description = "Whether to restore target on stream nodes or not" type = "bool" default = true } node.stream.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 = { description = "The default volume for capture nodes" type = "float" default = 1.0 min = 0.0 max = 1.0 } node.stream.default-media-role = { description = "A media.role to assign on streams that have none specified" type = "string" default = null } node.filter.forward-format = { description = "Whether to forward format on filter nodes or not" type = "bool" default = false } node.restore-default-targets = { description = "Whether to restore default targets or not" type = "bool" default = true } }