# 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. # This file is based on the upstream default configuration. This can be # found in upstream GitLab or in any distro with a recent version of WirePlumber. # The following changes have been made: # # - Conditions that have known values in Spectrum VMs are omitted. # - Modules for hardware devices Spectrum VMs don't have are not loaded. # - Settings for VMs are applied unconditionally. # - Most comments in the upstream files have been removed. # - Integration with udev and logind is removed. # - RT scheduling for the data thread is enabled. # - The settings are those appropriate for a system-wide instance. context.spa-libs = { api.alsa.* = alsa/libspa-alsa audio.convert.* = audioconvert/libspa-audioconvert support.* = support/libspa-support } # Upstream default, with RT scheduling enabled. context.modules = [ { name = libpipewire-module-rt args = { nice.level = -11, rt.prio = 88 } } { name = libpipewire-module-protocol-native } { name = libpipewire-module-metadata } ] wireplumber.profiles = { 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 = [ { name = export-core, type = built-in provides = support.export-core } { name = libpipewire-module-client-node, type = pw-module provides = pw.client-node wants = [ support.export-core ] } { name = libpipewire-module-client-device, type = pw-module provides = pw.client-device wants = [ support.export-core ] } { name = libpipewire-module-spa-node-factory, type = pw-module provides = pw.node-factory.spa requires = [ pw.client-node ] } { name = libpipewire-module-adapter, type = pw-module provides = pw.node-factory.adapter requires = [ pw.client-node ] } { name = libwireplumber-module-settings, type = module arguments = { metadata.name = sm-settings } provides = metadata.sm-settings } { name = settings-instance, type = built-in arguments = { metadata.name = sm-settings } provides = support.settings after = [ metadata.sm-settings ] } { name = libwireplumber-module-lua-scripting, type = module provides = support.lua-scripting } { name = libwireplumber-module-standard-event-source, type = module provides = support.standard-event-source } { 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 } { name = libwireplumber-module-default-nodes-api, type = module provides = api.default-nodes } { name = libwireplumber-module-mixer-api, type = module provides = api.mixer } { name = metadata.lua, type = script/lua arguments = { metadata.name = default } provides = metadata.default } { name = metadata.lua, type = script/lua arguments = { metadata.name = filters } provides = metadata.filters } { name = sm-objects.lua, type = script/lua provides = metadata.sm-objects } { name = session-services.lua, type = script/lua provides = support.session-services } { name = monitors/alsa.lua, type = script/lua provides = monitor.alsa requires = [ support.export-core, pw.client-device ] } { name = client/access-default.lua, type = script/lua provides = script.client.access-default } { type = virtual, provides = policy.client.access wants = [ script.client.access-default ] } { 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 ] } { 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 ] } { 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 ] } { 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/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 ] } { 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 ] } { 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 ] } { type = virtual, provides = hardware.audio wants = [ monitor.alsa ] } ] 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 } }