mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-08 23:18:06 +02:00
Compare commits
No commits in common. "master" and "0.4.15" have entirely different histories.
486 changed files with 15703 additions and 45181 deletions
124
.gitlab-ci.yml
124
.gitlab-ci.yml
|
|
@ -1,15 +1,6 @@
|
||||||
# Create merge request pipelines for open merge requests, branch pipelines
|
|
||||||
# otherwise. This allows MRs for new users to run CI, and prevents duplicate
|
|
||||||
# pipelines for branches with open MRs.
|
|
||||||
workflow:
|
|
||||||
rules:
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
||||||
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
|
|
||||||
when: never
|
|
||||||
- if: $CI_COMMIT_BRANCH
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- container
|
- container
|
||||||
|
- container_coverity
|
||||||
- build
|
- build
|
||||||
- analysis
|
- analysis
|
||||||
- pages
|
- pages
|
||||||
|
|
@ -19,8 +10,8 @@ variables:
|
||||||
# change to build against a different tag or branch of pipewire
|
# change to build against a different tag or branch of pipewire
|
||||||
PIPEWIRE_HEAD: 'master'
|
PIPEWIRE_HEAD: 'master'
|
||||||
|
|
||||||
# ci-templates as of Feb 14th 2025
|
# ci-templates as of Mar 24th 2023
|
||||||
.templates_sha: &templates_sha ef5e4669b7500834a17ffe9277e15fbb6d977fff
|
.templates_sha: &templates_sha dd90ac0d7a03b574eb4f18d7358083f0c97825f3
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- project: 'freedesktop/ci-templates'
|
- project: 'freedesktop/ci-templates'
|
||||||
|
|
@ -36,8 +27,8 @@ include:
|
||||||
.fedora:
|
.fedora:
|
||||||
variables:
|
variables:
|
||||||
# Update this tag when you want to trigger a rebuild
|
# Update this tag when you want to trigger a rebuild
|
||||||
FDO_DISTRIBUTION_TAG: '2025-03-05.1'
|
FDO_DISTRIBUTION_TAG: '2023-03-24.1'
|
||||||
FDO_DISTRIBUTION_VERSION: '41'
|
FDO_DISTRIBUTION_VERSION: '37'
|
||||||
# findutils: used by the .build script below
|
# findutils: used by the .build script below
|
||||||
# dbus-devel: required by pipewire
|
# dbus-devel: required by pipewire
|
||||||
# dbus-daemon: required by GDBus unit tests
|
# dbus-daemon: required by GDBus unit tests
|
||||||
|
|
@ -68,13 +59,12 @@ include:
|
||||||
.ubuntu:
|
.ubuntu:
|
||||||
variables:
|
variables:
|
||||||
# Update this tag when you want to trigger a rebuild
|
# Update this tag when you want to trigger a rebuild
|
||||||
FDO_DISTRIBUTION_TAG: '2023-06-16.1'
|
FDO_DISTRIBUTION_TAG: '2023-03-24.1'
|
||||||
FDO_DISTRIBUTION_VERSION: '22.04'
|
FDO_DISTRIBUTION_VERSION: '20.04'
|
||||||
FDO_DISTRIBUTION_PACKAGES: >-
|
FDO_DISTRIBUTION_PACKAGES: >-
|
||||||
debhelper-compat
|
debhelper-compat
|
||||||
findutils
|
findutils
|
||||||
git
|
git
|
||||||
meson
|
|
||||||
ninja-build
|
ninja-build
|
||||||
pkg-config
|
pkg-config
|
||||||
python3-pip
|
python3-pip
|
||||||
|
|
@ -85,12 +75,14 @@ include:
|
||||||
libgirepository1.0-dev
|
libgirepository1.0-dev
|
||||||
doxygen
|
doxygen
|
||||||
python3-lxml
|
python3-lxml
|
||||||
|
FDO_DISTRIBUTION_EXEC: >-
|
||||||
|
pip3 install meson
|
||||||
|
|
||||||
.alpine:
|
.alpine:
|
||||||
variables:
|
variables:
|
||||||
# Update this tag when you want to trigger a rebuild
|
# Update this tag when you want to trigger a rebuild
|
||||||
FDO_DISTRIBUTION_TAG: '2025-03-05.1'
|
FDO_DISTRIBUTION_TAG: '2023-03-24.1'
|
||||||
FDO_DISTRIBUTION_VERSION: '3.21'
|
FDO_DISTRIBUTION_VERSION: '3.15'
|
||||||
FDO_DISTRIBUTION_PACKAGES: >-
|
FDO_DISTRIBUTION_PACKAGES: >-
|
||||||
dbus
|
dbus
|
||||||
dbus-dev
|
dbus-dev
|
||||||
|
|
@ -121,23 +113,14 @@ include:
|
||||||
tar xf /tmp/cov-analysis-linux64.tgz ;
|
tar xf /tmp/cov-analysis-linux64.tgz ;
|
||||||
mv cov-analysis-linux64-* coverity ;
|
mv cov-analysis-linux64-* coverity ;
|
||||||
rm /tmp/cov-analysis-linux64.tgz
|
rm /tmp/cov-analysis-linux64.tgz
|
||||||
|
only:
|
||||||
|
variables:
|
||||||
|
- $COVERITY
|
||||||
|
|
||||||
.rules_on_success_except_coverity:
|
.not_coverity:
|
||||||
rules:
|
except:
|
||||||
- if: $COVERITY
|
variables:
|
||||||
when: never
|
- $COVERITY
|
||||||
- when: on_success
|
|
||||||
|
|
||||||
.rules_only_on_coverity:
|
|
||||||
rules:
|
|
||||||
- if: $COVERITY
|
|
||||||
|
|
||||||
.rules_only_on_mr_and_branch:
|
|
||||||
rules:
|
|
||||||
- if: $COVERITY
|
|
||||||
when: never
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
||||||
- if: $CI_COMMIT_BRANCH != "master"
|
|
||||||
|
|
||||||
.build:
|
.build:
|
||||||
before_script:
|
before_script:
|
||||||
|
|
@ -154,22 +137,13 @@ include:
|
||||||
# Fedora also ships that, but without the test plugins that we need...
|
# Fedora also ships that, but without the test plugins that we need...
|
||||||
- git clone --depth=1 --branch="$PIPEWIRE_HEAD"
|
- git clone --depth=1 --branch="$PIPEWIRE_HEAD"
|
||||||
https://gitlab.freedesktop.org/pipewire/pipewire.git
|
https://gitlab.freedesktop.org/pipewire/pipewire.git
|
||||||
# Set build options based on PipeWire version
|
- meson "$PW_BUILD_DIR" pipewire --prefix="$PREFIX"
|
||||||
- |
|
-Dpipewire-alsa=disabled -Dpipewire-jack=disabled
|
||||||
case "$PIPEWIRE_HEAD" in
|
-Dalsa=disabled -Dv4l2=disabled -Djack=disabled -Dbluez5=disabled
|
||||||
1.0|1.2|1.4)
|
-Dvulkan=disabled -Dgstreamer=disabled -Dsystemd=disabled
|
||||||
export PIPEWIRE_BUILD_OPTIONS="-Dsystemd=disabled"
|
-Ddocs=disabled -Dman=disabled -Dexamples=disabled -Dpw-cat=disabled
|
||||||
;;
|
-Dsdl2=disabled -Dsndfile=disabled -Dlibpulse=disabled -Davahi=disabled
|
||||||
*)
|
-Decho-cancel-webrtc=disabled -Dsession-managers=[]
|
||||||
export PIPEWIRE_BUILD_OPTIONS="-Dlibsystemd=disabled"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
- meson "$PW_BUILD_DIR" pipewire --prefix="$PREFIX" $PIPEWIRE_BUILD_OPTIONS
|
|
||||||
-Dpipewire-alsa=disabled -Dpipewire-jack=disabled -Dalsa=disabled
|
|
||||||
-Dv4l2=disabled -Djack=disabled -Dbluez5=disabled -Dvulkan=disabled
|
|
||||||
-Dgstreamer=disabled -Ddocs=disabled -Dman=disabled -Dexamples=disabled
|
|
||||||
-Dpw-cat=disabled -Dsdl2=disabled -Dsndfile=disabled -Dlibpulse=disabled
|
|
||||||
-Davahi=disabled -Decho-cancel-webrtc=disabled -Dsession-managers=[]
|
|
||||||
-Dvideotestsrc=enabled -Daudiotestsrc=enabled -Dtest=enabled
|
-Dvideotestsrc=enabled -Daudiotestsrc=enabled -Dtest=enabled
|
||||||
- ninja $NINJA_ARGS -C "$PW_BUILD_DIR" install
|
- ninja $NINJA_ARGS -C "$PW_BUILD_DIR" install
|
||||||
# misc environment only for wireplumber
|
# misc environment only for wireplumber
|
||||||
|
|
@ -191,7 +165,6 @@ include:
|
||||||
|
|
||||||
container_fedora:
|
container_fedora:
|
||||||
extends:
|
extends:
|
||||||
- .rules_on_success_except_coverity
|
|
||||||
- .fedora
|
- .fedora
|
||||||
- .fdo.container-build@fedora
|
- .fdo.container-build@fedora
|
||||||
stage: container
|
stage: container
|
||||||
|
|
@ -200,7 +173,6 @@ container_fedora:
|
||||||
|
|
||||||
container_ubuntu:
|
container_ubuntu:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_mr_and_branch
|
|
||||||
- .ubuntu
|
- .ubuntu
|
||||||
- .fdo.container-build@ubuntu
|
- .fdo.container-build@ubuntu
|
||||||
stage: container
|
stage: container
|
||||||
|
|
@ -209,7 +181,6 @@ container_ubuntu:
|
||||||
|
|
||||||
container_alpine:
|
container_alpine:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_mr_and_branch
|
|
||||||
- .alpine
|
- .alpine
|
||||||
- .fdo.container-build@alpine
|
- .fdo.container-build@alpine
|
||||||
stage: container
|
stage: container
|
||||||
|
|
@ -218,18 +189,17 @@ container_alpine:
|
||||||
|
|
||||||
container_coverity:
|
container_coverity:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_coverity
|
|
||||||
- .fedora
|
- .fedora
|
||||||
- .coverity
|
- .coverity
|
||||||
- .fdo.container-build@fedora
|
- .fdo.container-build@fedora
|
||||||
stage: container
|
stage: container_coverity
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none
|
GIT_STRATEGY: none
|
||||||
|
|
||||||
build_on_fedora_with_docs:
|
build_on_fedora_with_docs:
|
||||||
extends:
|
extends:
|
||||||
- .rules_on_success_except_coverity
|
|
||||||
- .fedora
|
- .fedora
|
||||||
|
- .not_coverity
|
||||||
- .fdo.distribution-image@fedora
|
- .fdo.distribution-image@fedora
|
||||||
- .build
|
- .build
|
||||||
stage: build
|
stage: build
|
||||||
|
|
@ -238,21 +208,18 @@ build_on_fedora_with_docs:
|
||||||
|
|
||||||
build_on_fedora_no_docs:
|
build_on_fedora_no_docs:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_mr_and_branch
|
|
||||||
- .fedora
|
- .fedora
|
||||||
|
- .not_coverity
|
||||||
- .fdo.distribution-image@fedora
|
- .fdo.distribution-image@fedora
|
||||||
- .build
|
- .build
|
||||||
stage: build
|
stage: build
|
||||||
variables:
|
variables:
|
||||||
BUILD_OPTIONS: -Dintrospection=enabled -Ddoc=disabled -Dsystem-lua=false
|
BUILD_OPTIONS: -Dintrospection=enabled -Ddoc=disabled -Dsystem-lua=false
|
||||||
parallel:
|
|
||||||
matrix:
|
|
||||||
- PIPEWIRE_HEAD: ['master', '1.4', '1.2', '1.0']
|
|
||||||
|
|
||||||
build_on_ubuntu_with_gir:
|
build_on_ubuntu_with_gir:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_mr_and_branch
|
|
||||||
- .ubuntu
|
- .ubuntu
|
||||||
|
- .not_coverity
|
||||||
- .fdo.distribution-image@ubuntu
|
- .fdo.distribution-image@ubuntu
|
||||||
- .build
|
- .build
|
||||||
stage: build
|
stage: build
|
||||||
|
|
@ -261,8 +228,8 @@ build_on_ubuntu_with_gir:
|
||||||
|
|
||||||
build_on_ubuntu_no_gir:
|
build_on_ubuntu_no_gir:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_mr_and_branch
|
|
||||||
- .ubuntu
|
- .ubuntu
|
||||||
|
- .not_coverity
|
||||||
- .fdo.distribution-image@ubuntu
|
- .fdo.distribution-image@ubuntu
|
||||||
- .build
|
- .build
|
||||||
stage: build
|
stage: build
|
||||||
|
|
@ -271,8 +238,8 @@ build_on_ubuntu_no_gir:
|
||||||
|
|
||||||
build_on_alpine:
|
build_on_alpine:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_mr_and_branch
|
|
||||||
- .alpine
|
- .alpine
|
||||||
|
- .not_coverity
|
||||||
- .fdo.distribution-image@alpine
|
- .fdo.distribution-image@alpine
|
||||||
- .build
|
- .build
|
||||||
stage: build
|
stage: build
|
||||||
|
|
@ -281,7 +248,6 @@ build_on_alpine:
|
||||||
|
|
||||||
build_with_coverity:
|
build_with_coverity:
|
||||||
extends:
|
extends:
|
||||||
- .rules_only_on_coverity
|
|
||||||
- .fedora
|
- .fedora
|
||||||
- .coverity
|
- .coverity
|
||||||
- .fdo.suffixed-image@fedora
|
- .fdo.suffixed-image@fedora
|
||||||
|
|
@ -311,23 +277,16 @@ build_with_coverity:
|
||||||
shellcheck:
|
shellcheck:
|
||||||
extends:
|
extends:
|
||||||
- .fedora
|
- .fedora
|
||||||
|
- .not_coverity
|
||||||
- .fdo.distribution-image@fedora
|
- .fdo.distribution-image@fedora
|
||||||
stage: analysis
|
stage: analysis
|
||||||
script:
|
script:
|
||||||
- shellcheck $(git grep -l "#\!/.*bin/.*sh")
|
- shellcheck $(git grep -l "#\!/.*bin/.*sh")
|
||||||
rules:
|
|
||||||
- if: $COVERITY
|
|
||||||
when: never
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
||||||
changes:
|
|
||||||
- "**/*.sh"
|
|
||||||
- if: $CI_COMMIT_BRANCH != "master"
|
|
||||||
changes:
|
|
||||||
- "**/*.sh"
|
|
||||||
|
|
||||||
linguas_check:
|
linguas_check:
|
||||||
extends:
|
extends:
|
||||||
- .fedora
|
- .fedora
|
||||||
|
- .not_coverity
|
||||||
- .fdo.distribution-image@fedora
|
- .fdo.distribution-image@fedora
|
||||||
stage: analysis
|
stage: analysis
|
||||||
script:
|
script:
|
||||||
|
|
@ -336,17 +295,10 @@ linguas_check:
|
||||||
- ls *.po | sed s/.po//g | sort > LINGUAS.new
|
- ls *.po | sed s/.po//g | sort > LINGUAS.new
|
||||||
- diff -u LINGUAS.sorted LINGUAS.new
|
- diff -u LINGUAS.sorted LINGUAS.new
|
||||||
- rm -f LINGUAS.*
|
- rm -f LINGUAS.*
|
||||||
rules:
|
|
||||||
- if: $COVERITY
|
|
||||||
when: never
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
||||||
changes:
|
|
||||||
- po/*
|
|
||||||
- if: $CI_COMMIT_BRANCH != "master"
|
|
||||||
changes:
|
|
||||||
- po/*
|
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
|
extends:
|
||||||
|
- .not_coverity
|
||||||
stage: pages
|
stage: pages
|
||||||
dependencies:
|
dependencies:
|
||||||
- build_on_fedora_with_docs
|
- build_on_fedora_with_docs
|
||||||
|
|
@ -356,7 +308,5 @@ pages:
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
rules:
|
only:
|
||||||
- if: $COVERITY
|
- master
|
||||||
when: never
|
|
||||||
- if: $CI_COMMIT_BRANCH == "master"
|
|
||||||
|
|
|
||||||
37
AGENTS.md
37
AGENTS.md
|
|
@ -1,37 +0,0 @@
|
||||||
## Building and Testing
|
|
||||||
|
|
||||||
- To compile the project: `meson compile -C build` (compiles everything, no target needed)
|
|
||||||
- To run tests: `meson test -C build`
|
|
||||||
- The build artifacts always live in a directory called `build` or `builddir`.
|
|
||||||
If `build` doesn't exist, use `-C builddir` in the meson commands.
|
|
||||||
|
|
||||||
## Git Workflow
|
|
||||||
|
|
||||||
- Main branch: `master`
|
|
||||||
- Always create feature branches for new work
|
|
||||||
- Use descriptive commit messages following project conventions
|
|
||||||
- Reference GitLab MR/issue numbers in commits where applicable
|
|
||||||
- Never commit build artifacts or temporary files
|
|
||||||
- Use `glab` CLI tool for GitLab interactions (MRs, issues, etc.)
|
|
||||||
|
|
||||||
## Making a release
|
|
||||||
|
|
||||||
- Each release always consists of an entry in NEWS.rst, at the top of the file, which describes
|
|
||||||
the changes between the previous release and the current one. In addition, each release is given
|
|
||||||
a unique version number, which is present:
|
|
||||||
1. on the section header of that NEWS.rst entry
|
|
||||||
2. in the project() command in meson.build
|
|
||||||
3. on the commit message of the commit that introduces the above 2 changes
|
|
||||||
4. on the git tag that marks the above commit
|
|
||||||
- In order to make a release:
|
|
||||||
- Begin by analyzing the git history and the merged MRs from GitLab between the previous release
|
|
||||||
and today. GitLab MRs that are relevant always have the new release's version number set as a
|
|
||||||
"milestone"
|
|
||||||
- Create a new entry in NEWS.rst describing the changes, in a similar style and format as the
|
|
||||||
previous entries. Consolidate the changes to larger work items and also reference the relevant
|
|
||||||
gitlab MR that corresponds to each change and/or the gitlab issues that were addressed by each
|
|
||||||
change.
|
|
||||||
- Make sure to move the "Past releases" section header up, so that the only 2 top-level sections
|
|
||||||
are the new release section and the "Past releases" section.
|
|
||||||
- Edit meson.build to change the project version to the new release number
|
|
||||||
- Do not commit anything to git. Let the user review the changes and commit manually.
|
|
||||||
778
NEWS.rst
778
NEWS.rst
|
|
@ -1,778 +1,5 @@
|
||||||
WirePlumber 0.5.14
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Additions & Enhancements:
|
|
||||||
|
|
||||||
- Added per-device default volume configuration via the
|
|
||||||
``device.routes.default-{source,sink}-volume`` property, allowing device-specific volume
|
|
||||||
defaults (e.g. a comfortable default for internal speakers or no attenuation for HDMI) (!772)
|
|
||||||
|
|
||||||
- Added Lua 5.5 support; the bundled Lua subproject wrap has also been updated to 5.5.0
|
|
||||||
(!775, !788)
|
|
||||||
|
|
||||||
- Enhanced libcamera monitor to load camera nodes locally within the WirePlumber
|
|
||||||
process instead of the PipeWire daemon, eliminating race conditions that could occur
|
|
||||||
during initial enumeration and hotplug events (!790)
|
|
||||||
|
|
||||||
- Enhanced Bluetooth loopback nodes to always be created when a device supports both
|
|
||||||
A2DP and HSP/HFP profiles, simplifying the logic and making the BT profile autoswitch
|
|
||||||
setting take effect immediately without requiring device reconnection (!782)
|
|
||||||
|
|
||||||
- Enhanced Bluetooth loopback nodes to use ``target.object`` property instead of smart
|
|
||||||
filters, fixing issues that prevented users from setting them as default nodes and
|
|
||||||
also allowing smart filters to be used with them (#898; !792)
|
|
||||||
|
|
||||||
- Enhanced Bluetooth profile autoswitch logic with further robustness improvements,
|
|
||||||
including better headset profile detection using profile name patterns and resolving
|
|
||||||
race conditions by running profile switching after ``device/apply-profile`` in a
|
|
||||||
dedicated event hook (#926, #923; !776, !777, !808)
|
|
||||||
|
|
||||||
- Enhanced wpctl ``set-default`` command to accept virtual nodes (e.g.
|
|
||||||
``Audio/Source/Virtual``) in addition to regular device nodes (#896; !787)
|
|
||||||
|
|
||||||
- Improved stream linking to make the full graph rescan optional when linkable items
|
|
||||||
change, saving CPU on low-end systems and reducing audio startup latency when
|
|
||||||
connecting multiple streams in quick succession (!800)
|
|
||||||
|
|
||||||
- Allowed installation of systemd service units without libsystemd being present,
|
|
||||||
useful for distributions like Alpine Linux that allow systemd service subpackages
|
|
||||||
(!793)
|
|
||||||
|
|
||||||
- Allowed the ``mincore`` syscall in the WirePlumber systemd sandbox, required for
|
|
||||||
Mesa/EGL (e.g. for the libcamera GPUISP pipeline)
|
|
||||||
|
|
||||||
- Allowed passing ``WIREPLUMBER_CONFIG_DIR`` via the ``wp-uninstalled`` script,
|
|
||||||
useful for passing additional configuration paths in an uninstalled environment (!801)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Removed Bluetooth sink loopback node, which was causing issues with KDE and GNOME (!794)
|
|
||||||
|
|
||||||
- Fixed default audio source selection to never automatically use ``Audio/Sink`` nodes
|
|
||||||
as the default source unless explicitly selected by the user (#886; !781)
|
|
||||||
|
|
||||||
- Fixed crash in ``state-stream`` when the Format parameter has a Choice for the
|
|
||||||
number of channels (#903; !795)
|
|
||||||
|
|
||||||
- Fixed BAP Bluetooth device set channel properties, where ``audio.position`` was
|
|
||||||
incorrectly serialized as a pointer address instead of the channel array (!786)
|
|
||||||
|
|
||||||
- Fixed memory leaks in ``wp_interest_event_hook_get_matching_event_types`` and in
|
|
||||||
the Lua ``LocalModule()`` implementation (!784, !810)
|
|
||||||
|
|
||||||
- Fixed HFP HF stream media class being incorrectly assigned due to
|
|
||||||
``api.bluez5.internal=true`` being set on HFP HF streams (!809)
|
|
||||||
|
|
||||||
- Fixed Lua 5.4 compatibility in ``state-stream`` script
|
|
||||||
|
|
||||||
- Updated translations: Bulgarian, Georgian, Kazakh, Swedish
|
|
||||||
|
|
||||||
Past releases
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
WirePlumber 0.5.13
|
|
||||||
..................
|
|
||||||
|
|
||||||
Additions & Enhancements:
|
|
||||||
|
|
||||||
- Added internal filter graph support for audio nodes, allowing users to
|
|
||||||
create audio preprocessing and postprocessing chains without exposing
|
|
||||||
filters to applications, useful for software DSP (!743)
|
|
||||||
|
|
||||||
- Added new Lua Properties API that significantly improves performance by
|
|
||||||
avoiding constant serialization between WpProperties and Lua tables,
|
|
||||||
resulting in approximately 40% faster node linking (!757)
|
|
||||||
|
|
||||||
- Added WpIterator Lua API for more efficient parameter enumeration (!746)
|
|
||||||
|
|
||||||
- Added bash completions for wpctl command (!762)
|
|
||||||
|
|
||||||
- Added script to find suitable volume control when using role-based policy,
|
|
||||||
allowing volume sliders to automatically adjust the volume of the currently
|
|
||||||
active role (e.g., ringing, call, media) (!711)
|
|
||||||
|
|
||||||
- Added experimental HDMI channel detection setting to use HDMI ELD
|
|
||||||
information for channel configuration (!749)
|
|
||||||
|
|
||||||
- Enhanced role-based policy to allow setting preferred target sinks for
|
|
||||||
media role loopbacks via ``policy.role-based.preferred-target`` (!754)
|
|
||||||
|
|
||||||
- Enhanced Bluetooth profile autoswitch logic to be more robust and handle
|
|
||||||
saved profiles correctly, including support for loopback sink nodes (!739)
|
|
||||||
|
|
||||||
- Enhanced ALSA monitor to include ``alsa.*`` device properties on nodes for
|
|
||||||
rule matching (!761)
|
|
||||||
|
|
||||||
- Optimized stream node linking for common cases to reduce latency when new
|
|
||||||
audio/video streams are added (!760)
|
|
||||||
|
|
||||||
- Improved event dispatcher performance by using hash table registration for
|
|
||||||
event hooks, eliminating performance degradation as more hooks are
|
|
||||||
registered (!765)
|
|
||||||
|
|
||||||
- Increased audio headroom for VMware and VirtualBox virtual machines (!756)
|
|
||||||
|
|
||||||
- Added setting to prevent restoring "Off" profiles via
|
|
||||||
``session.dont-restore-off-profile`` property (!753)
|
|
||||||
|
|
||||||
- Added support for 128 audio channels when compiled with a recent version of
|
|
||||||
PipeWire (pipewire#4995; CI checks in !768)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed memory leaks and issues in the modem manager module (!770, !764)
|
|
||||||
|
|
||||||
- Fixed MPRIS module incorrectly treating GHashTable as GObject (!759)
|
|
||||||
|
|
||||||
- Fixed warning messages when process files in ``/proc/<pid>/*`` don't exist,
|
|
||||||
particularly when processes are removed quickly (#816, !717)
|
|
||||||
|
|
||||||
- Fixed MONO audio configuration to only apply to device sink nodes, allowing
|
|
||||||
multi-channel mixing in the graph (!769)
|
|
||||||
|
|
||||||
- Fixed event dispatcher hook registration and removal to avoid spurious
|
|
||||||
errors (!747)
|
|
||||||
|
|
||||||
- Improved logging for standard-link activation failures (!744)
|
|
||||||
|
|
||||||
- Simplified event-hook interest matching for better performance (!758)
|
|
||||||
|
|
||||||
WirePlumber 0.5.12
|
|
||||||
..................
|
|
||||||
|
|
||||||
Additions & Enhancements:
|
|
||||||
|
|
||||||
- Added mono audio configuration support via ``node.features.audio.mono``
|
|
||||||
setting that can be changed at runtime with wpctl (!721)
|
|
||||||
|
|
||||||
- Added automatic muting of ALSA devices when a running node is removed,
|
|
||||||
helping prevent loud audio on speakers when headsets are unplugged (!734)
|
|
||||||
|
|
||||||
- Added notifications API module for sending system notifications (!734)
|
|
||||||
|
|
||||||
- Added comprehensive wpctl man page and documentation (!735, #825)
|
|
||||||
|
|
||||||
- Enhanced object interest handling for PipeWire properties on session items (!738)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed race condition during shutdown in the permissions portal module that
|
|
||||||
could cause crashes in GDBus signal handling (!748)
|
|
||||||
|
|
||||||
- Added device validity check in state-routes handling to prevent issues
|
|
||||||
when devices are removed during async operations (!737, #844)
|
|
||||||
|
|
||||||
- Fixed Log.critical undefined function error in device-info-cache (!733)
|
|
||||||
|
|
||||||
- Improved device hook documentation and configuration (!736)
|
|
||||||
|
|
||||||
WirePlumber 0.5.11
|
|
||||||
..................
|
|
||||||
|
|
||||||
Additions & Enhancements:
|
|
||||||
|
|
||||||
- Added modem manager module for tracking voice call status and voice call
|
|
||||||
device profile selection hooks to improve phone call audio routing on
|
|
||||||
mobile devices (!722, !729, #819)
|
|
||||||
|
|
||||||
- Added MPRIS media player pause functionality that automatically pauses
|
|
||||||
media playback when the audio target (e.g. headphones) is removed (!699, #764)
|
|
||||||
|
|
||||||
- Added support for human-readable names and localization of settings in
|
|
||||||
``wireplumber.conf`` with ``wpctl`` displaying localized setting descriptions (!712)
|
|
||||||
|
|
||||||
- Improved default node selection logic to use both session and route
|
|
||||||
priorities when nodes have equal session priorities (!720)
|
|
||||||
|
|
||||||
- Increased USB device priority in the ALSA monitor (!719)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed multiple Lua runtime issues including type confusion bugs, stack
|
|
||||||
overflow prevention, and SPA POD array/choice builders (!723, !728)
|
|
||||||
|
|
||||||
- Fixed proxy object lifecycle management by properly clearing the
|
|
||||||
OWNED_BY_PROXY flag when proxies are destroyed to prevent dangling
|
|
||||||
pointers (!732)
|
|
||||||
|
|
||||||
- Fixed state-routes handling to prevent saving unavailable routes and
|
|
||||||
eliminate race conditions during profile switching (!730, #762)
|
|
||||||
|
|
||||||
- Fixed some memory leaks in the script tester and the settings iterator (!727, !726)
|
|
||||||
|
|
||||||
- Fixed a potential crash caused by module-loopback destroying itself when the
|
|
||||||
pipewire connection is closed (#812)
|
|
||||||
|
|
||||||
- Fixed profile saving behavior in ``wpctl set-profile`` command (#808)
|
|
||||||
|
|
||||||
- Fixed GObject introspection closure annotation
|
|
||||||
|
|
||||||
WirePlumber 0.5.10
|
|
||||||
..................
|
|
||||||
|
|
||||||
Fixed a critical crash in ``linking-utils.haveAvailableRoutes`` that was
|
|
||||||
introduced accidentally in 0.5.9 and caused loss of audio output on affected
|
|
||||||
systems (#797, #799, #800, !713)
|
|
||||||
|
|
||||||
WirePlumber 0.5.9
|
|
||||||
.................
|
|
||||||
|
|
||||||
Additions & Enhancements:
|
|
||||||
|
|
||||||
- Added a new audio node grouping functionality using an external command line
|
|
||||||
tool (!646)
|
|
||||||
|
|
||||||
- The libcamera monitor now supports devices that are not associated with
|
|
||||||
device ids (!701)
|
|
||||||
|
|
||||||
- The wireplumber user systemd service is now associated with dbus.service to
|
|
||||||
avoid strange warnings when dbus exits (!702)
|
|
||||||
|
|
||||||
- Added "SYSLOG_IDENTIFIER", "SYSLOG_FACILITY", "SYSLOG_PID" and "TID" to log
|
|
||||||
messages that are sent to the journal (!709)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed a crash of ``wpctl set-default`` on 32-bit architectures (#773)
|
|
||||||
|
|
||||||
- Fixed a crash when a configuration component had no 'provides' field (#771)
|
|
||||||
|
|
||||||
- Reduced the log level of some messages that didn't need to be as high (!695)
|
|
||||||
|
|
||||||
- Fixed another nil reference issue in the alsa.lua monitor script (!704)
|
|
||||||
|
|
||||||
- Fixed name deduplication of v4l2 and libcamera devices (!705)
|
|
||||||
|
|
||||||
- Fixed an issue with wpctl not being able to save settings sometimes (!708, #749)
|
|
||||||
|
|
||||||
WirePlumber 0.5.8
|
|
||||||
.................
|
|
||||||
|
|
||||||
Additions & Enhancements:
|
|
||||||
|
|
||||||
- Added support for handling UCM SplitPCM nodes in the ALSA monitor, which
|
|
||||||
allows native PipeWire channel remapping using loopbacks for devices that
|
|
||||||
use this feature (!685)
|
|
||||||
|
|
||||||
- Introduced new functions to mark WpSpaDevice child objects as pending.
|
|
||||||
This allows properly associating asynchronously created loopback nodes with
|
|
||||||
their parent WpSpaDevice without losing ObjectConfig events (!687, !689)
|
|
||||||
|
|
||||||
- Improved the node name deduplication logic in the ALSA monitor to prevent
|
|
||||||
node names with .2, .3, etc appended to them in some more cases (!688)
|
|
||||||
|
|
||||||
- Added a new script to populate ``session.services``. This is a step towards
|
|
||||||
implementing detection of features that PipeWire can service (!686)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed an issue that was causing duplicate Bluetooth SCO (HSP/HFP) source
|
|
||||||
nodes to be shown in UIs (#701, !683)
|
|
||||||
|
|
||||||
- In the BlueZ monitor, marked the source loopback node as non-virtual,
|
|
||||||
addressing how it appears on UIs (#729)
|
|
||||||
|
|
||||||
- Disabled stream-restore for device loopback nodes to prevent unwanted
|
|
||||||
property changes (!691)
|
|
||||||
|
|
||||||
- Fixed ``wp_lua_log_topic_copy()`` to correctly copy topic names (#757)
|
|
||||||
|
|
||||||
- Updated script tests to handle differences in object identifiers
|
|
||||||
(``object.serial`` vs ``node.id``), ensuring proper test behavior (#761)
|
|
||||||
|
|
||||||
WirePlumber 0.5.7
|
|
||||||
.................
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- Fixed an issue that would cause random profile switching when an application
|
|
||||||
was trying to capture from non-Bluetooth devices (#715, #634, !669)
|
|
||||||
|
|
||||||
- Fixed an issue that would cause strange profile selection issues [choices
|
|
||||||
not being remembered or unavailable routes being selected] (#734)
|
|
||||||
|
|
||||||
- Added a timer that delays switching Bluetooth headsets to the HSP/HFP
|
|
||||||
profile, avoiding needless rapid switching when an application is trying to
|
|
||||||
probe device capabilities instead of actually capturing audio (!664)
|
|
||||||
|
|
||||||
- Improved libcamera/v4l2 device deduplication logic to work with more complex
|
|
||||||
devices (!674, !675, #689, #708)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed two memory leaks in module-mixer-api and module-dbus-connection
|
|
||||||
(!672, !673)
|
|
||||||
|
|
||||||
- Fixed a crash that could occur in module-reserve-device (!680, #742)
|
|
||||||
|
|
||||||
- Fixed an issue that would cause the warning "[string "alsa.lua"]:182:
|
|
||||||
attempt to concatenate a nil value (local 'node_name')" to appear in the
|
|
||||||
logs when an ALSA device was busy, breaking node name deduplication (!681)
|
|
||||||
|
|
||||||
- Fixed an issue that could make find-preferred-profile.lua crash instead of
|
|
||||||
properly applying profile priority rules (#751)
|
|
||||||
|
|
||||||
WirePlumber 0.5.6
|
|
||||||
.................
|
|
||||||
|
|
||||||
Additions:
|
|
||||||
|
|
||||||
- Implemented before/after dependencies for components, to ensure correct
|
|
||||||
load order in custom configurations (#600)
|
|
||||||
|
|
||||||
- Implemented profile inheritance in the configuration file. This allows
|
|
||||||
profiles to inherit all the feature specifications of other profiles, which
|
|
||||||
is useful to avoid copying long lists of features just to make small changes
|
|
||||||
|
|
||||||
- Added multi-instance configuration profiles, tested and documented them
|
|
||||||
|
|
||||||
- Added a ``main-systemwide`` profile, which is now the default for instances
|
|
||||||
started via the system-wide systemd service and disables features that
|
|
||||||
depend on the user session (#608)
|
|
||||||
|
|
||||||
- Added a ``wp_core_connect_fd`` method, which allows making a connection to
|
|
||||||
PipeWire via an existing open socket (useful for portal-based connections)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- The Bluetooth auto-switch script now uses the common event source object
|
|
||||||
managers, which should improve its stability (!663)
|
|
||||||
|
|
||||||
- Fix an issue where switching between Bluetooth profiles would temporarily
|
|
||||||
link active audio streams to the internal speakers (!655)
|
|
||||||
|
|
||||||
WirePlumber 0.5.5
|
|
||||||
.................
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- Hotfix release to address crashes in the Bluetooth HSP/HFP autoswitch
|
|
||||||
functionality that were side-effects of some changes that were part
|
|
||||||
of the role-based linking policy (#682)
|
|
||||||
|
|
||||||
Improvements:
|
|
||||||
|
|
||||||
- wpctl will now properly show a '*' in front of sink filters when they are
|
|
||||||
selected as the default sink (!660)
|
|
||||||
|
|
||||||
WirePlumber 0.5.4
|
|
||||||
.................
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- Refactored the role-based linking policy (previously known also as
|
|
||||||
"endpoints" or "virtual items" policy) to blend in with the standard desktop
|
|
||||||
policy. It is now possible use role-based sinks alongside standard desktop
|
|
||||||
audio operations and they will only be used for streams that have a
|
|
||||||
"media.role" defined. It is also possible to force streams to have a
|
|
||||||
media.role, using a setting. Other features include: blending with smart
|
|
||||||
filters in the graph and allowing hardware DSP nodes to be also used easily
|
|
||||||
instead of requiring software loopbacks for all roles. (#610, !649)
|
|
||||||
|
|
||||||
Improvements:
|
|
||||||
|
|
||||||
- Filters that are not declared as smart will now behave again as normal
|
|
||||||
application streams, instead of being treated sometimes differently (!657)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed an issue that would cause WirePlumber to crash at startup if an
|
|
||||||
empty configuration file was present in one of the search paths (#671)
|
|
||||||
|
|
||||||
- Fixed Bluetooth profile auto-switching when a filter is permanently linked
|
|
||||||
to the Bluetooth source (!650)
|
|
||||||
|
|
||||||
- Fixed an issue in the software-dsp script that would cause DSP filters to
|
|
||||||
stay around and cause issues after their device node was destroyed (!651)
|
|
||||||
|
|
||||||
- Fixed an issue in the autoswitch-bluetooth-profile script that could cause
|
|
||||||
an infinite loop of switching between profiles (!652, #617)
|
|
||||||
|
|
||||||
- Fixed a rare issue that could cause WirePlumber to crash when dealing with
|
|
||||||
a device object that didn't have the "device.name" property set (#674)
|
|
||||||
|
|
||||||
WirePlumber 0.5.3
|
|
||||||
.................
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed a long standing issue that would cause many device nodes to have
|
|
||||||
inconsistent naming, with a '.N' suffix (where N is a number >= 2) being
|
|
||||||
appended at seemingly random times (#500)
|
|
||||||
|
|
||||||
- Fixed an issue that would cause unavailable device profiles to be selected
|
|
||||||
if they were previously stored in the state file, sometimes requiring users
|
|
||||||
to manually remove the state file to get things working again (#613)
|
|
||||||
|
|
||||||
- Fixed an occasional crash that could sometimes be triggered by hovering
|
|
||||||
the volume icon on the KDE taskbar, and possibly other similar actions
|
|
||||||
(#628, !644)
|
|
||||||
|
|
||||||
- Fixed camera device deduplication logic when the same device is available
|
|
||||||
through both V4L2 and libcamera, and the libcamera one groups multiple V4L2
|
|
||||||
devices together (#623, !636)
|
|
||||||
|
|
||||||
- Fixed applying the default volume on streams that have no volume previously
|
|
||||||
stored in the state file (#655)
|
|
||||||
|
|
||||||
- Fixed an issue that would prevent some camera nodes - in some cases -
|
|
||||||
from being destroyed when the camera device is removed (#640)
|
|
||||||
|
|
||||||
- Fixed an issue that would cause video stream nodes to be linked with audio
|
|
||||||
smart filters, if smart audio filters were configured (!647)
|
|
||||||
|
|
||||||
- Fixed an issue that would cause WP to re-activate device profiles even
|
|
||||||
though they were already active (!639)
|
|
||||||
|
|
||||||
- Configuration files in standard JSON format (starting with a '{', among
|
|
||||||
other things) are now correctly parsed (#633)
|
|
||||||
|
|
||||||
- Fixed overriding non-container values when merging JSON objects (#653)
|
|
||||||
|
|
||||||
- Functions marked with WP_PRIVATE_API are now also marked as
|
|
||||||
non-introspectable in the gobject-introspection metadata (#599)
|
|
||||||
|
|
||||||
Improvements:
|
|
||||||
|
|
||||||
- Logging on the systemd journal now includes the log topic and also the log
|
|
||||||
level and location directly on the message string when the log level is
|
|
||||||
high enough, which is useful for gathering additional context in logs
|
|
||||||
submitted by users (!640)
|
|
||||||
|
|
||||||
- Added a video-only profile in wireplumber.conf, for systems where only
|
|
||||||
camera & screensharing are to be used (#652)
|
|
||||||
|
|
||||||
- Improved seat state monitoring so that Bluetooth devices are only enabled
|
|
||||||
when the user is active on a local seat, instead of allowing remote users
|
|
||||||
as well (!641)
|
|
||||||
|
|
||||||
- Improved how main filter nodes are detected for the smart filters (!642)
|
|
||||||
|
|
||||||
- Added Lua method to merge JSON containers (!637)
|
|
||||||
|
|
||||||
WirePlumber 0.5.2
|
|
||||||
.................
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- Added support for loading configuration files other than the default
|
|
||||||
wireplumber.conf within Lua scripts (!629)
|
|
||||||
|
|
||||||
- Added support for loading single-section configuration files, without
|
|
||||||
fragments (!629)
|
|
||||||
|
|
||||||
- Updated the node.software-dsp script to be able to load filter-chain graphs
|
|
||||||
from external configuration files, which is needed for Asahi Linux audio
|
|
||||||
DSP configuration (!629)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed destroying camera nodes when the camera device is removed (#627, !631)
|
|
||||||
|
|
||||||
- Fixed an issue with Bluetooth BAP device set naming (!632)
|
|
||||||
|
|
||||||
- Fixed an issue caused by the pipewire event loop not being "entered" as
|
|
||||||
expected (!634, #638)
|
|
||||||
|
|
||||||
- A false positive warning about no modules being loaded is now suppressed
|
|
||||||
when using libpipewire >= 1.0.5 (#620)
|
|
||||||
|
|
||||||
- Default nodes can now be selected using priority.driver when
|
|
||||||
priority.session is not set (#642)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- The library version is now generated following pipewire's versioning scheme:
|
|
||||||
libwireplumber-0.5.so.0.5.2 becomes libwireplumber-0.5.so.0.0502.0 (!633)
|
|
||||||
|
|
||||||
WirePlumber 0.5.1
|
|
||||||
.................
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- Added a guide documenting how to migrate configuration from 0.4 to 0.5,
|
|
||||||
also available online at:
|
|
||||||
https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration/migration.html
|
|
||||||
If you are packaging WirePlumber for a distribution, please consider
|
|
||||||
informing users about this.
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed an odd issue where microphones would stop being usable when a
|
|
||||||
Bluetooth headset was connected in the HSP/HFP profile (#598, !620)
|
|
||||||
|
|
||||||
- Fixed an issue where it was not possible to store the volume/mute state of
|
|
||||||
system notifications (#604)
|
|
||||||
|
|
||||||
- Fixed a rare crash that could occur when a node was destroyed while the
|
|
||||||
'select-target' event was still being processed (!621)
|
|
||||||
|
|
||||||
- Fixed deleting all the persistent settings via ``wpctl --delete`` (!622)
|
|
||||||
|
|
||||||
- Fixed using Bluetooth autoswitch with A2DP profiles that have an input route
|
|
||||||
(!624)
|
|
||||||
|
|
||||||
- Fixed sending an error to clients when linking fails due to a format
|
|
||||||
mismatch (!625)
|
|
||||||
|
|
||||||
Additions:
|
|
||||||
|
|
||||||
- Added a check that prints a verbose warning when old-style 0.4.x Lua
|
|
||||||
configuration files are found in the system. (#611)
|
|
||||||
|
|
||||||
- The "policy-dsp" script, used in Asahi Linux to provide a software DSP
|
|
||||||
for Apple Sillicon devices, has now been ported to 0.5 properly and
|
|
||||||
documented (#619, !627)
|
|
||||||
|
|
||||||
WirePlumber 0.5.0
|
|
||||||
.................
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Bumped the minimum required version of PipeWire to 1.0.2, because we
|
|
||||||
make use of the 'api.bluez5.internal' property of the BlueZ monitor (!613)
|
|
||||||
|
|
||||||
- Improved the naming of Bluetooth nodes when the auto-switching loopback
|
|
||||||
node is present (!614)
|
|
||||||
|
|
||||||
- Updated the documentation on "settings", the Bluetooth monitor, the Access
|
|
||||||
configuration, the file search locations and added a document on how to
|
|
||||||
modify the configuration file (#595, !616)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed checking for available routes when selecting the default node (!609)
|
|
||||||
|
|
||||||
- Fixed an issue that was causing an infinite loop storing routes in the
|
|
||||||
state file (!610)
|
|
||||||
|
|
||||||
- Fixed the interpretation of boolean values in the alsa monitor rules (#586, !611)
|
|
||||||
|
|
||||||
- Fixes a Lua crash when we have 2 smart filters, one with a target and one
|
|
||||||
without (!612)
|
|
||||||
|
|
||||||
- Fixed an issue where the default nodes would not be updated when the
|
|
||||||
currently selected default node became unavailable (#588, !615)
|
|
||||||
|
|
||||||
- Fixed an issue that would cause the Props (volume, mute, etc) of loopbacks
|
|
||||||
and other filter nodes to not be restored at startup (#577, !617)
|
|
||||||
|
|
||||||
- Fixed how some constants were represented in the gobject-introspection file,
|
|
||||||
mostly by converting them from defines to enums (#540, #591)
|
|
||||||
|
|
||||||
- Fixed an issue using WirePlumber headers in other projects due to
|
|
||||||
redefinition of G_LOG_DOMAIN (#571)
|
|
||||||
|
|
||||||
WirePlumber 0.4.90
|
|
||||||
..................
|
|
||||||
|
|
||||||
This is the first release candidate (RC1) of WirePlumber 0.5.0.
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- The configuration system has been changed back to load files from the
|
|
||||||
WirePlumber configuration directories, such as ``/etc/wireplumber`` and
|
|
||||||
``$XDG_CONFIG_HOME/wireplumber``, unlike in the pre-releases. This was done
|
|
||||||
because issues were observed with installations that use a different prefix
|
|
||||||
for pipewire and wireplumber. If you had a ``wireplumber.conf`` file in
|
|
||||||
``/etc/pipewire`` or ``$XDG_CONFIG_HOME/pipewire``, you should move it to
|
|
||||||
``/etc/wireplumber`` or ``$XDG_CONFIG_HOME/wireplumber`` respectively (!601)
|
|
||||||
|
|
||||||
- The internal base directories lookup system now also respects the
|
|
||||||
``XDG_CONFIG_DIRS`` and ``XDG_DATA_DIRS`` environment variables, and their
|
|
||||||
default values as per the XDG spec, so it is possible to install
|
|
||||||
configuration files also in places like ``/etc/xdg/wireplumber`` and
|
|
||||||
override system-wide data paths (!601)
|
|
||||||
|
|
||||||
- ``wpctl`` now has a ``settings`` subcommand to show, change and delete
|
|
||||||
settings at runtime. This comes with changes in the ``WpSettings`` system to
|
|
||||||
validate settings using a schema that is defined in the configuration file.
|
|
||||||
The schema is also exported on a metadata object, so it is available to any
|
|
||||||
client that wants to expose WirePlumber settings (!599, !600)
|
|
||||||
|
|
||||||
- The ``WpConf`` API has changed to not be a singleton and support opening
|
|
||||||
arbitrary config files. The main config file now needs to be opened prior to
|
|
||||||
creating a ``WpCore`` and passed to the core using a property. The core uses
|
|
||||||
that without letting the underlying ``pw_context`` open and read the default
|
|
||||||
``client.conf``. The core also closes the ``WpConf`` after all components
|
|
||||||
are loaded, which means all the config loading is done early at startup.
|
|
||||||
Finally, ``WpConf`` loads all sections lazily, keeping the underlying files
|
|
||||||
memory mapped until it is closed and merging them on demand (!601, !606)
|
|
||||||
|
|
||||||
WirePlumber 0.4.82
|
|
||||||
..................
|
|
||||||
|
|
||||||
This is a second pre-release of WirePlumber 0.5.0, made available for testing
|
|
||||||
purposes. This is not API/ABI stable yet and there is still pending work to do
|
|
||||||
before the final 0.5.0 release, both in the codebase and the documentation.
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- Bluetooth auto-switching is now implemented with a virtual source node. When
|
|
||||||
an application links to it, the actual device switches to the HSP/HFP
|
|
||||||
profile to provide the real audio stream. This is a more robust solution
|
|
||||||
that works with more applications and is more user-friendly than the
|
|
||||||
previous application whitelist approach
|
|
||||||
|
|
||||||
- Added support for dynamic log level changes via the PipeWire ``settings``
|
|
||||||
metadata. Also added support for log level patterns in the configuration
|
|
||||||
file
|
|
||||||
|
|
||||||
- The "persistent" (i.e. stored) settings approach has changed to use two
|
|
||||||
different metadata objects: ``sm-settings`` and ``persistent-sm-settings``.
|
|
||||||
Changes in the former are applied in the current session but not stored,
|
|
||||||
while changes in the latter are stored and restored at startup. Some work
|
|
||||||
was also done to expose a ``wpctl`` interface to read and change these
|
|
||||||
settings, but more is underway
|
|
||||||
|
|
||||||
- Several WirePlumber-specific node properties that used to be called
|
|
||||||
``target.*`` have been renamed to ``node.*`` to match the PipeWire
|
|
||||||
convention of ``node.dont-reconnect``. These are also now fully documented
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
|
|
||||||
- Many documentation updates
|
|
||||||
|
|
||||||
- Added support for SNAP container permissions
|
|
||||||
|
|
||||||
- Fixed multiple issues related to restoring the Route parameter of devices,
|
|
||||||
which includes volume state (#551)
|
|
||||||
|
|
||||||
- Smart filters can now be targetted by specific streams directly when
|
|
||||||
the ``filter.smart.targetable`` property is set (#554)
|
|
||||||
|
|
||||||
- Ported the mechanism to override device profile priorities in the
|
|
||||||
configuration, which is used to re-prioritize Bluetooth codecs
|
|
||||||
|
|
||||||
- WpSettings is no longer a singleton class and there is a built-in component
|
|
||||||
to preload an instance of it
|
|
||||||
|
|
||||||
WirePlumber 0.4.81
|
|
||||||
..................
|
|
||||||
|
|
||||||
This is a preliminary release of WirePlumber 0.5.0, which is made available
|
|
||||||
for testing purposes. Please test it and report feedback (merge requests are
|
|
||||||
also welcome ;) ). This is not API/ABI stable yet and there is still pending
|
|
||||||
work to do before the final 0.5.0 release, both in the codebase and the
|
|
||||||
documentation.
|
|
||||||
|
|
||||||
Highlights:
|
|
||||||
|
|
||||||
- Lua scripts have been refactored to use the new event dispatcher API, which
|
|
||||||
allows them to be split into multiple small fragments that react to
|
|
||||||
events in a specified order. This allows scripts to be more modular and
|
|
||||||
easier to maintain, as well as more predictable in terms of execution
|
|
||||||
order.
|
|
||||||
|
|
||||||
- The configuration system has been refactored to use a single SPA-JSON file,
|
|
||||||
like PipeWire does, with support for fragments that can override options.
|
|
||||||
This file is also now loaded using PipeWire's configuration API, which
|
|
||||||
effectively means that the file is now loaded from the PipeWire configuration
|
|
||||||
directories, such as ``/etc/pipewire`` and ``$XDG_CONFIG_HOME/pipewire``.
|
|
||||||
|
|
||||||
- The configuration system now has the concept of profiles, which are groups
|
|
||||||
of components that can be loaded together, with the ability to mark certain
|
|
||||||
components as optional. This allows having multiple configurations that
|
|
||||||
can be loaded using the same configuration file. Optional components also
|
|
||||||
allow loading the same profile gracefully on different setups, where some
|
|
||||||
components may not be available (ex, loading of the session D-Bus plugin on
|
|
||||||
a system-wide PipeWire setup now does not fail).
|
|
||||||
|
|
||||||
- Many configuration options are now exposed in the ``sm-settings`` metadata,
|
|
||||||
which allows changing them at runtime. This can be leveraged in the future
|
|
||||||
to implement configuration tools that can modify WirePlumber's behaviour
|
|
||||||
dynamically, without restarting.
|
|
||||||
|
|
||||||
- A new "filters" system has been implemented, which allows specifying chains
|
|
||||||
of "filter" nodes to be dynamically linked in-between streams and devices.
|
|
||||||
This is achieved with certain properties and metadata that can be set on
|
|
||||||
the filter nodes themselves.
|
|
||||||
|
|
||||||
- The default linking policy now reads some more ``target.*`` properties from
|
|
||||||
nodes, which allows fine-tuning some aspects of their linking behaviour,
|
|
||||||
such as whether they are allowed to be re-linked or whether an error should
|
|
||||||
be sent to the client if they cannot be linked.
|
|
||||||
|
|
||||||
- Some state files have been renamed and some have changed format to use JSON
|
|
||||||
for storing complex values, such as arrays. This may cause some of the old
|
|
||||||
state to be lost on upgrade, as there is no transition path implemented.
|
|
||||||
|
|
||||||
- The libcamera and V4L2 monitors have a "device deduplication" logic built-in,
|
|
||||||
which means that for each physical camera device, only one node will be
|
|
||||||
created, either from libcamera or V4L2, depending on which one is considered
|
|
||||||
better for the device. This is mainly to avoid having multiple nodes for
|
|
||||||
the same camera device, which can cause confusion when looking at the list
|
|
||||||
of available cameras in applications.
|
|
||||||
|
|
||||||
WirePlumber 0.4.17
|
|
||||||
..................
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed a reference counting issue in the object managers that could cause
|
|
||||||
crashes due to memory corruption (#534)
|
|
||||||
|
|
||||||
- Fixed an issue with filters linking to wrong targets, often with two sets
|
|
||||||
of links (#536)
|
|
||||||
|
|
||||||
- Fixed a crash in the endpoints policy that would show up when log messages
|
|
||||||
were enabled at level 3 or higher
|
|
||||||
|
|
||||||
WirePlumber 0.4.16
|
|
||||||
..................
|
|
||||||
|
|
||||||
Additions:
|
|
||||||
|
|
||||||
- Added a new "sm-objects" script that allows loading objects on demand
|
|
||||||
via metadata entries that describe the object to load; this can be used to
|
|
||||||
load pipewire modules, such as filters or network sources/sinks, on demand
|
|
||||||
|
|
||||||
- Added a mechanism to override device profile priorities in the configuration,
|
|
||||||
mainly as a way to re-prioritize Bluetooth codecs, but this also can be used
|
|
||||||
for other devices
|
|
||||||
|
|
||||||
- Added a mechanism in the endpoints policy to allow connecting filters
|
|
||||||
between a certain endpoint's virtual sink and the device sink; this is
|
|
||||||
specifically intended to allow plugging a filter-chain to act as equalizer
|
|
||||||
on the Multimedia endpoint
|
|
||||||
|
|
||||||
- Added wp_core_get_own_bound_id() method in WpCore
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- PipeWire 0.3.68 is now required
|
|
||||||
|
|
||||||
- policy-dsp now has the ability to hide hardware nodes behind the DSP sink
|
|
||||||
to prevent hardware misuse or damage
|
|
||||||
|
|
||||||
- JSON parsing in Lua now allows keys inside objects to be without quotes
|
|
||||||
|
|
||||||
- Added optional argument in the Lua JSON parse() method to limit recursions,
|
|
||||||
making it possible to partially parse a JSON object
|
|
||||||
|
|
||||||
- It is now possible to pass ``nil`` in Lua object constructors that expect an
|
|
||||||
optional properties object; previously, omitting the argument was the only
|
|
||||||
way to skip the properties
|
|
||||||
|
|
||||||
- The endpoints policy now marks the endpoint nodes as "passive" instead of
|
|
||||||
marking their links, adjusting for the behavior change in PipeWire 0.3.68
|
|
||||||
|
|
||||||
- Removed the "passive" property from si-standard-link, since only nodes are
|
|
||||||
marked as passive now
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
|
|
||||||
- Fixed the ``wpctl clear-default`` command to completely clear all the
|
|
||||||
default nodes state instead of only the last set default
|
|
||||||
|
|
||||||
- Reduced the amount of globals that initially match the interest in the
|
|
||||||
object manager
|
|
||||||
|
|
||||||
- Used an idle callback instead of pw_core_sync() in the object manager to
|
|
||||||
expose tmp globals
|
|
||||||
|
|
||||||
WirePlumber 0.4.15
|
WirePlumber 0.4.15
|
||||||
..................
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Additions:
|
Additions:
|
||||||
|
|
||||||
|
|
@ -824,6 +51,9 @@ Changes/Fixes:
|
||||||
- Added some missing `\since` annotations and made them show up in the
|
- Added some missing `\since` annotations and made them show up in the
|
||||||
generated gobject-introspection file, to help bindings generators
|
generated gobject-introspection file, to help bindings generators
|
||||||
|
|
||||||
|
Past releases
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
WirePlumber 0.4.14
|
WirePlumber 0.4.14
|
||||||
..................
|
..................
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ WirePlumber
|
||||||
.. image:: https://scan.coverity.com/projects/21488/badge.svg
|
.. image:: https://scan.coverity.com/projects/21488/badge.svg
|
||||||
:alt: Coverity Scan Build Status
|
:alt: Coverity Scan Build Status
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/tokei/lines/gitlab.freedesktop.org/pipewire/wireplumber
|
||||||
|
:alt: Lines of code
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/license-MIT-green
|
.. image:: https://img.shields.io/badge/license-MIT-green
|
||||||
:alt: License
|
:alt: License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1240,6 +1240,15 @@ HTML_COLORSTYLE_SAT = 100
|
||||||
|
|
||||||
HTML_COLORSTYLE_GAMMA = 80
|
HTML_COLORSTYLE_GAMMA = 80
|
||||||
|
|
||||||
|
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||||
|
# page will contain the date and time when the page was generated. Setting this
|
||||||
|
# to YES can help to show when doxygen was last run and thus if the
|
||||||
|
# documentation is up to date.
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
|
HTML_TIMESTAMP = NO
|
||||||
|
|
||||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||||
# documentation will contain a main index with vertical navigation menus that
|
# documentation will contain a main index with vertical navigation menus that
|
||||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||||
|
|
@ -1534,6 +1543,17 @@ HTML_FORMULA_FORMAT = png
|
||||||
|
|
||||||
FORMULA_FONTSIZE = 10
|
FORMULA_FONTSIZE = 10
|
||||||
|
|
||||||
|
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||||
|
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||||
|
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||||
|
#
|
||||||
|
# Note that when changing this option you need to delete any form_*.png files in
|
||||||
|
# the HTML output directory before the changes have effect.
|
||||||
|
# The default value is: YES.
|
||||||
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
|
FORMULA_TRANSPARENT = YES
|
||||||
|
|
||||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||||
# the section "Including formulas" for details.
|
# the section "Including formulas" for details.
|
||||||
|
|
@ -1845,6 +1865,14 @@ LATEX_HIDE_INDICES = NO
|
||||||
|
|
||||||
LATEX_BIB_STYLE = plain
|
LATEX_BIB_STYLE = plain
|
||||||
|
|
||||||
|
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||||
|
# page will contain the date and time when the page was generated. Setting this
|
||||||
|
# to NO can help when comparing the output of multiple runs.
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
|
LATEX_TIMESTAMP = NO
|
||||||
|
|
||||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||||
# path from which the emoji images will be read. If a relative path is entered,
|
# path from which the emoji images will be read. If a relative path is entered,
|
||||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||||
|
|
@ -2237,6 +2265,23 @@ HAVE_DOT = YES
|
||||||
|
|
||||||
DOT_NUM_THREADS = 0
|
DOT_NUM_THREADS = 0
|
||||||
|
|
||||||
|
# When you want a differently looking font in the dot files that doxygen
|
||||||
|
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||||
|
# sure dot is able to find the font, which can be done by putting it in a
|
||||||
|
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||||
|
# setting DOT_FONTPATH to the directory containing the font.
|
||||||
|
# The default value is: Helvetica.
|
||||||
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
|
DOT_FONTNAME = Helvetica
|
||||||
|
|
||||||
|
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||||
|
# dot graphs.
|
||||||
|
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||||
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
|
DOT_FONTSIZE = 10
|
||||||
|
|
||||||
# By default doxygen will tell dot to use the default font as specified with
|
# By default doxygen will tell dot to use the default font as specified with
|
||||||
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||||
# the path where dot can find it using this tag.
|
# the path where dot can find it using this tag.
|
||||||
|
|
@ -2473,6 +2518,18 @@ DOT_GRAPH_MAX_NODES = 50
|
||||||
|
|
||||||
MAX_DOT_GRAPH_DEPTH = 0
|
MAX_DOT_GRAPH_DEPTH = 0
|
||||||
|
|
||||||
|
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||||
|
# background. This is disabled by default, because dot on Windows does not seem
|
||||||
|
# to support this out of the box.
|
||||||
|
#
|
||||||
|
# Warning: Depending on the platform used, enabling this option may lead to
|
||||||
|
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||||
|
# read).
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
|
DOT_TRANSPARENT = NO
|
||||||
|
|
||||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'WirePlumber'
|
project = 'WirePlumber'
|
||||||
copyright = '2020-2025, Collabora & contributors'
|
copyright = '2021, Collabora'
|
||||||
author = 'The WirePlumber Developers'
|
author = 'Collabora'
|
||||||
release = '@VERSION@'
|
release = '@VERSION@'
|
||||||
version = '@VERSION@'
|
version = '@VERSION@'
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
|
||||||
|
|
||||||
smartquotes = False
|
|
||||||
|
|
||||||
# -- Breathe configuration ---------------------------------------------------
|
# -- Breathe configuration ---------------------------------------------------
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
|
|
@ -47,9 +43,3 @@ html_css_files = ['custom.css']
|
||||||
graphviz_output_format = "svg"
|
graphviz_output_format = "svg"
|
||||||
|
|
||||||
pygments_style = "friendly"
|
pygments_style = "friendly"
|
||||||
|
|
||||||
# -- Options for manual page output -----------------------------------------
|
|
||||||
|
|
||||||
man_pages = [
|
|
||||||
('tools/wpctl', 'wpctl', 'WirePlumber Control CLI', ['The WirePlumber Developers'], 1)
|
|
||||||
]
|
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ class DoxygenProcess(object):
|
||||||
def __process_element(self, xml):
|
def __process_element(self, xml):
|
||||||
s = ""
|
s = ""
|
||||||
|
|
||||||
if xml.text and re.search(r'\S', xml.text):
|
if xml.text and re.search('\S', xml.text):
|
||||||
s += xml.text
|
s += xml.text
|
||||||
for n in xml.getchildren():
|
for n in xml.getchildren():
|
||||||
if n.tag == "emphasis":
|
if n.tag == "emphasis":
|
||||||
|
|
@ -143,7 +143,7 @@ class DoxygenProcess(object):
|
||||||
s += " - " + self.__process_element(n)
|
s += " - " + self.__process_element(n)
|
||||||
if n.tag == "para":
|
if n.tag == "para":
|
||||||
p = self.__process_element(n)
|
p = self.__process_element(n)
|
||||||
if re.search(r'\S', p):
|
if re.search('\S', p):
|
||||||
s += p + "\n"
|
s += p + "\n"
|
||||||
if n.tag == "ref":
|
if n.tag == "ref":
|
||||||
s += n.text if n.text else ""
|
s += n.text if n.text else ""
|
||||||
|
|
@ -168,7 +168,7 @@ class DoxygenProcess(object):
|
||||||
if n.tag == "htmlonly":
|
if n.tag == "htmlonly":
|
||||||
s += ""
|
s += ""
|
||||||
if n.tail:
|
if n.tail:
|
||||||
if re.search(r'\S', n.tail):
|
if re.search('\S', n.tail):
|
||||||
s += n.tail
|
s += n.tail
|
||||||
if n.tag.startswith("param"):
|
if n.tag.startswith("param"):
|
||||||
pass # parameters are handled separately in DoxyFunction::from_memberdef()
|
pass # parameters are handled separately in DoxyFunction::from_memberdef()
|
||||||
|
|
@ -319,8 +319,6 @@ class DoxyFunction(DoxyElement):
|
||||||
d = normalize_text(d)
|
d = normalize_text(d)
|
||||||
|
|
||||||
e = DoxyFunction(name, d)
|
e = DoxyFunction(name, d)
|
||||||
if (xml.get("prot") == "private"):
|
|
||||||
e.extra = "(skip)"
|
|
||||||
e.add_brief(xml.find("briefdescription"))
|
e.add_brief(xml.find("briefdescription"))
|
||||||
e.add_detail(xml.find("detaileddescription"))
|
e.add_detail(xml.find("detaileddescription"))
|
||||||
for p in xml.xpath(".//detaileddescription/*/parameterlist[@kind='param']/parameteritem"):
|
for p in xml.xpath(".//detaileddescription/*/parameterlist[@kind='param']/parameteritem"):
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,6 @@ if build_doc
|
||||||
meson.current_source_dir()/'..'/'README.rst',
|
meson.current_source_dir()/'..'/'README.rst',
|
||||||
meson.current_source_dir()/'..'/'NEWS.rst',
|
meson.current_source_dir()/'..'/'NEWS.rst',
|
||||||
)
|
)
|
||||||
sphinx_files += scripts_doc_files
|
|
||||||
subdir('rst')
|
subdir('rst')
|
||||||
|
|
||||||
sphinx_conf_data = configuration_data()
|
sphinx_conf_data = configuration_data()
|
||||||
|
|
@ -99,27 +98,7 @@ if build_doc
|
||||||
depends: [doxyxml_wp],
|
depends: [doxyxml_wp],
|
||||||
output: 'html',
|
output: 'html',
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: wireplumber_doc_dir,
|
install_dir: get_option('datadir') / 'doc' / 'wireplumber',
|
||||||
build_by_default: true,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate man pages directory with sphinx
|
|
||||||
custom_target('manpages',
|
|
||||||
command: [sphinx_p,
|
|
||||||
'-q', # quiet
|
|
||||||
'-E', # rebuild from scratch
|
|
||||||
'-b', 'man', # man page builder
|
|
||||||
'-d', '@PRIVATE_DIR@', # doctrees dir
|
|
||||||
'-c', '@OUTDIR@', # conf.py dir
|
|
||||||
'@CURRENT_SOURCE_DIR@/rst', # source dir
|
|
||||||
'@OUTDIR@', # output directory
|
|
||||||
],
|
|
||||||
depend_files: [
|
|
||||||
sphinx_conf, sphinx_files,
|
|
||||||
],
|
|
||||||
output: ['wpctl.1'],
|
|
||||||
install: true,
|
|
||||||
install_dir: get_option('mandir') / 'man1',
|
|
||||||
build_by_default: true,
|
build_by_default: true,
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
@ -161,7 +140,7 @@ if build_gir
|
||||||
dependencies: [wp_dep, dummy_dep],
|
dependencies: [wp_dep, dummy_dep],
|
||||||
namespace: 'Wp',
|
namespace: 'Wp',
|
||||||
nsversion: wireplumber_api_version,
|
nsversion: wireplumber_api_version,
|
||||||
export_packages: 'wireplumber-' + wireplumber_api_version,
|
export_packages: 'wireplumber-0.4',
|
||||||
header: 'wp/wp.h',
|
header: 'wp/wp.h',
|
||||||
sources: [wpenums_h, wp_gtkdoc_h, wp_lib_headers],
|
sources: [wpenums_h, wp_gtkdoc_h, wp_lib_headers],
|
||||||
include_directories: [wpenums_include_dir],
|
include_directories: [wpenums_include_dir],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.. _library_c_api:
|
.. _library_root:
|
||||||
|
|
||||||
C API Documentation
|
C API Documentation
|
||||||
===================
|
===================
|
||||||
|
|
@ -23,8 +23,8 @@ C API Documentation
|
||||||
c_api/link_api.rst
|
c_api/link_api.rst
|
||||||
c_api/device_api.rst
|
c_api/device_api.rst
|
||||||
c_api/client_api.rst
|
c_api/client_api.rst
|
||||||
c_api/permission_manager_api.rst
|
|
||||||
c_api/metadata_api.rst
|
c_api/metadata_api.rst
|
||||||
|
c_api/endpoint_api.rst
|
||||||
c_api/spa_device_api.rst
|
c_api/spa_device_api.rst
|
||||||
c_api/impl_node_api.rst
|
c_api/impl_node_api.rst
|
||||||
c_api/impl_module_api.rst
|
c_api/impl_module_api.rst
|
||||||
|
|
@ -34,10 +34,7 @@ C API Documentation
|
||||||
c_api/spa_pod_api.rst
|
c_api/spa_pod_api.rst
|
||||||
c_api/plugin_api.rst
|
c_api/plugin_api.rst
|
||||||
c_api/component_loader_api.rst
|
c_api/component_loader_api.rst
|
||||||
c_api/settings_api.rst
|
|
||||||
c_api/conf_api.rst
|
|
||||||
c_api/session_item_api.rst
|
c_api/session_item_api.rst
|
||||||
c_api/si_interfaces_api.rst
|
c_api/si_interfaces_api.rst
|
||||||
c_api/si_factory_api.rst
|
c_api/si_factory_api.rst
|
||||||
c_api/state_api.rst
|
c_api/state_api.rst
|
||||||
c_api/base_dirs_api.rst
|
|
||||||
|
|
@ -7,12 +7,14 @@ Component Loader
|
||||||
|
|
||||||
digraph inheritance {
|
digraph inheritance {
|
||||||
rankdir=LR;
|
rankdir=LR;
|
||||||
GInterface -> WpComponentLoader;
|
GObject -> WpObject;
|
||||||
|
WpObject -> WpPlugin;
|
||||||
|
WpPlugin -> WpComponentLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
.. doxygenstruct:: WpComponentLoader
|
.. doxygenstruct:: WpComponentLoader
|
||||||
|
|
||||||
.. doxygenstruct:: _WpComponentLoaderInterface
|
.. doxygenstruct:: _WpComponentLoaderClass
|
||||||
|
|
||||||
.. doxygengroup:: wpcomponentloader
|
.. doxygengroup:: wpcomponentloader
|
||||||
:content-only:
|
:content-only:
|
||||||
|
|
@ -7,8 +7,7 @@ Core
|
||||||
|
|
||||||
digraph inheritance {
|
digraph inheritance {
|
||||||
rankdir=LR;
|
rankdir=LR;
|
||||||
GObject -> WpObject;
|
GObject -> WpCore;
|
||||||
WpObject -> WpCore;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.. doxygenstruct:: WpCore
|
.. doxygenstruct:: WpCore
|
||||||
24
docs/rst/c_api/endpoint_api.rst
Normal file
24
docs/rst/c_api/endpoint_api.rst
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
.. _endpoint_api:
|
||||||
|
|
||||||
|
PipeWire Endpoint
|
||||||
|
=================
|
||||||
|
.. graphviz::
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
digraph inheritance {
|
||||||
|
rankdir=LR;
|
||||||
|
GObject -> WpObject;
|
||||||
|
WpObject -> WpProxy;
|
||||||
|
WpProxy -> WpGlobalProxy;
|
||||||
|
WpGlobalProxy -> WpEndpoint;
|
||||||
|
GInterface -> WpPipewireObject;
|
||||||
|
WpPipewireObject -> WpEndpoint;
|
||||||
|
WpEndpoint -> WpImplEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
.. doxygenstruct:: WpEndpoint
|
||||||
|
|
||||||
|
.. doxygenstruct:: WpImplEndpoint
|
||||||
|
|
||||||
|
.. doxygengroup:: wpendpoint
|
||||||
|
:content-only:
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
# you need to add here any files you add to the api directory as well
|
# you need to add here any files you add to the api directory as well
|
||||||
sphinx_files += files(
|
sphinx_files += files(
|
||||||
'base_dirs_api.rst',
|
|
||||||
'client_api.rst',
|
'client_api.rst',
|
||||||
'component_loader_api.rst',
|
'component_loader_api.rst',
|
||||||
'conf_api.rst',
|
|
||||||
'core_api.rst',
|
'core_api.rst',
|
||||||
'device_api.rst',
|
'device_api.rst',
|
||||||
|
'endpoint_api.rst',
|
||||||
'global_proxy_api.rst',
|
'global_proxy_api.rst',
|
||||||
'impl_module_api.rst',
|
'impl_module_api.rst',
|
||||||
'impl_node_api.rst',
|
'impl_node_api.rst',
|
||||||
|
|
@ -18,13 +17,11 @@ sphinx_files += files(
|
||||||
'obj_manager_api.rst',
|
'obj_manager_api.rst',
|
||||||
'object_api.rst',
|
'object_api.rst',
|
||||||
'pipewire_object_api.rst',
|
'pipewire_object_api.rst',
|
||||||
'permission_manager_api.rst',
|
|
||||||
'plugin_api.rst',
|
'plugin_api.rst',
|
||||||
'port_api.rst',
|
'port_api.rst',
|
||||||
'properties_api.rst',
|
'properties_api.rst',
|
||||||
'proxy_api.rst',
|
'proxy_api.rst',
|
||||||
'session_item_api.rst',
|
'session_item_api.rst',
|
||||||
'settings_api.rst',
|
|
||||||
'si_factory_api.rst',
|
'si_factory_api.rst',
|
||||||
'si_interfaces_api.rst',
|
'si_interfaces_api.rst',
|
||||||
'spa_device_api.rst',
|
'spa_device_api.rst',
|
||||||
|
|
@ -7,7 +7,6 @@ PipeWire Metadata
|
||||||
|
|
||||||
digraph inheritance {
|
digraph inheritance {
|
||||||
rankdir=LR;
|
rankdir=LR;
|
||||||
GBoxed -> WpMetadataItem
|
|
||||||
GObject -> WpObject;
|
GObject -> WpObject;
|
||||||
WpObject -> WpProxy;
|
WpObject -> WpProxy;
|
||||||
WpProxy -> WpGlobalProxy;
|
WpProxy -> WpGlobalProxy;
|
||||||
|
|
@ -15,8 +14,6 @@ PipeWire Metadata
|
||||||
WpMetadata-> WpImplMetadata;
|
WpMetadata-> WpImplMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
.. doxygenstruct:: WpMetadataItem
|
|
||||||
|
|
||||||
.. doxygenstruct:: WpMetadata
|
.. doxygenstruct:: WpMetadata
|
||||||
|
|
||||||
.. doxygenstruct:: WpImplMetadata
|
.. doxygenstruct:: WpImplMetadata
|
||||||
|
|
@ -7,12 +7,17 @@ Session Items Interfaces
|
||||||
|
|
||||||
digraph inheritance {
|
digraph inheritance {
|
||||||
rankdir=LR;
|
rankdir=LR;
|
||||||
|
GInterface -> WpSiEndpoint;
|
||||||
GInterface -> WpSiAdapter;
|
GInterface -> WpSiAdapter;
|
||||||
GInterface -> WpSiLinkable;
|
GInterface -> WpSiLinkable;
|
||||||
GInterface -> WpSiLink;
|
GInterface -> WpSiLink;
|
||||||
GInterface -> WpSiAcquisition;
|
GInterface -> WpSiAcquisition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.. doxygenstruct:: WpSiEndpoint
|
||||||
|
|
||||||
|
.. doxygenstruct:: _WpSiEndpointInterface
|
||||||
|
|
||||||
.. doxygenstruct:: WpSiAdapter
|
.. doxygenstruct:: WpSiAdapter
|
||||||
|
|
||||||
.. doxygenstruct:: _WpSiAdapterInterface
|
.. doxygenstruct:: _WpSiAdapterInterface
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.. _resources_community:
|
.. _community:
|
||||||
|
|
||||||
Community Resources
|
Community Resources
|
||||||
===================
|
===================
|
||||||
34
docs/rst/configuration.rst
Normal file
34
docs/rst/configuration.rst
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
.. _configuration:
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
WirePlumber is a heavily modular daemon. By itself, it doesn't do anything
|
||||||
|
except load the configured modules. All the rest of the logic is implemented
|
||||||
|
inside those modules.
|
||||||
|
|
||||||
|
Modular design ensures that it is possible to swap the implementation of
|
||||||
|
specific functionality without having to re-implement the rest of it, allowing
|
||||||
|
flexibility on target-sensitive parts, such as policy management and
|
||||||
|
making use of non-standard hardware.
|
||||||
|
|
||||||
|
At startup, WirePlumber first reads its **main** configuration file.
|
||||||
|
This file configures the operation context (properties of the daemon,
|
||||||
|
modules to be loaded, etc). This file may also specify additional, secondary
|
||||||
|
configuration files which will be loaded as well at the time of parsing the
|
||||||
|
main file.
|
||||||
|
|
||||||
|
All files and modules are specified relative to their standard search locations,
|
||||||
|
which are documented later in this chapter.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
configuration/locations.rst
|
||||||
|
configuration/main.rst
|
||||||
|
configuration/config_lua.rst
|
||||||
|
configuration/multi_instance.rst
|
||||||
|
configuration/alsa.rst
|
||||||
|
configuration/bluetooth.rst
|
||||||
|
configuration/policy.rst
|
||||||
|
configuration/access.rst
|
||||||
63
docs/rst/configuration/access.rst
Normal file
63
docs/rst/configuration/access.rst
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
.. _config_access:
|
||||||
|
|
||||||
|
Access configuration
|
||||||
|
====================
|
||||||
|
|
||||||
|
main.lua.d/50-default-access-config.lua
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Using a similar format as the :ref:`ALSA monitor <config_alsa>`, this
|
||||||
|
configuration file is charged to configure the client objects created by
|
||||||
|
PipeWire.
|
||||||
|
|
||||||
|
* *default_access.properties*
|
||||||
|
|
||||||
|
A Lua object that contains generic client configuration properties in the
|
||||||
|
for of key pairs.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
default_access.properties = {
|
||||||
|
["enable-flatpak-portal"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
The above example sets to ``true`` the ``enable-flatpak-portal`` property.
|
||||||
|
|
||||||
|
The list of valid properties are:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["enable-flatpak-portal"] = true,
|
||||||
|
|
||||||
|
Whether to enable the flatpak portal or not.
|
||||||
|
|
||||||
|
* *default_access.rules*
|
||||||
|
|
||||||
|
This is a Lua array that can contain objects with rules for a client object.
|
||||||
|
Those Lua objects have 2 properties. Similar to the
|
||||||
|
:ref:`ALSA configuration <config_alsa>`, the first property is ``matches``,
|
||||||
|
which allow users to define rules to match a client object.
|
||||||
|
The second property is ``default_permissions``, and it is used to set
|
||||||
|
permissions on the matched client object.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
{
|
||||||
|
matches = {
|
||||||
|
{
|
||||||
|
{ "pipewire.access", "=", "flatpak" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default_permissions = "rx",
|
||||||
|
}
|
||||||
|
|
||||||
|
This grants read and execute permissions to all clients that have the
|
||||||
|
``pipewire.access`` property set to ``flatpak``.
|
||||||
|
|
||||||
|
Possible permissions are any combination of ``r``, ``w`` and ``x`` for read,
|
||||||
|
write and execute; or ``all`` for all kind of permissions.
|
||||||
|
|
||||||
478
docs/rst/configuration/alsa.rst
Normal file
478
docs/rst/configuration/alsa.rst
Normal file
|
|
@ -0,0 +1,478 @@
|
||||||
|
.. _config_alsa:
|
||||||
|
|
||||||
|
ALSA configuration
|
||||||
|
==================
|
||||||
|
|
||||||
|
Modifying the default configuration
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
ALSA devices are created and managed by the session manager with the *alsa.lua*
|
||||||
|
monitor script. In the default configuration, this script is loaded by
|
||||||
|
``main.lua.d/30-alsa-monitor.lua``, which also specifies an ``alsa_monitor``
|
||||||
|
global table that can be filled in with properties and rules in subsequent
|
||||||
|
config files. By default, these are filled in ``main.lua.d/50-alsa-config.lua``.
|
||||||
|
|
||||||
|
The ``alsa_monitor`` global table has 2 sub-tables:
|
||||||
|
|
||||||
|
* *alsa_monitor.properties*
|
||||||
|
|
||||||
|
This is a simple Lua table that has key value pairs used as properties.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
alsa_monitor.properties = {
|
||||||
|
["alsa.jack-device"] = false,
|
||||||
|
["alsa.reserve"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
The above example will configure the ALSA monitor to not enable the JACK
|
||||||
|
device, and do ALSA device reservation using the mentioned DBus interface.
|
||||||
|
|
||||||
|
A list of valid properties are:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["alsa.jack-device"] = false
|
||||||
|
|
||||||
|
Creates a JACK device if set to ``true``. This is not enabled by default because
|
||||||
|
it requires that the PipeWire JACK replacement libraries are not used by the
|
||||||
|
session manager, in order to be able to connect to the real JACK server.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["alsa.reserve"] = true
|
||||||
|
|
||||||
|
Reserve ALSA devices via *org.freedesktop.ReserveDevice1* on D-Bus.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["alsa.reserve.priority"] = -20
|
||||||
|
|
||||||
|
The used ALSA device reservation priority.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["alsa.reserve.application-name"] = "WirePlumber"
|
||||||
|
|
||||||
|
The used ALSA device reservation application name.
|
||||||
|
|
||||||
|
|
||||||
|
* *alsa_monitor.rules*
|
||||||
|
|
||||||
|
This is a Lua array that can contain objects with rules for a device or node.
|
||||||
|
Those objects have 2 properties. The first one is ``matches``, which allow
|
||||||
|
users to define rules to match a device or node. The second property is
|
||||||
|
``apply_properties``, and it is used to apply properties on the matched object.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
alsa_monitor.rules = {
|
||||||
|
matches = {
|
||||||
|
{
|
||||||
|
{ "device.name", "matches", "alsa_card.*" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apply_properties = {
|
||||||
|
["api.alsa.use-acp"] = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
This sets the API ALSA use ACP property to all devices with a name that
|
||||||
|
matches the ``alsa_card.*`` pattern.
|
||||||
|
|
||||||
|
The ``matches`` section is an array of arrays. On the first level, the rules
|
||||||
|
are ORed together, so any rule match is going to apply the properties. On
|
||||||
|
the second level, the rules are merged with AND, so they must all match.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
matches = {
|
||||||
|
{
|
||||||
|
{ "node.name", "matches", "alsa_input.*" },
|
||||||
|
{ "alsa.driver_name", "equals", "snd_hda_intel" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ "node.name", "matches", "alsa_output.*" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
This is equivalent to the following logic, in pseudocode:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
if ("node.name" MATCHES "alsa_input.*" AND "alsa.driver_name" EQUALS "snd_hda_intel" )
|
||||||
|
OR
|
||||||
|
("node.name" MATCHES "alsa_output.*")
|
||||||
|
then
|
||||||
|
... apply the properties ...
|
||||||
|
end
|
||||||
|
|
||||||
|
As you can notice, the individual rules are themselves also lua arrays. The
|
||||||
|
first element is a property name (ex "node.name"), the second element is a
|
||||||
|
verb and the third element is an expected value, which depends on the verb.
|
||||||
|
Internally, this uses the ``Constraint`` API, which is documented in the
|
||||||
|
:ref:`Object Interet API <lua_object_interest_api>` section. All the verbs
|
||||||
|
that you can use on ``Constraint`` are also allowed here.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When using the "matches" verb, the values are not complete regular expressions.
|
||||||
|
They are wildcard patterns, which means that '*' matches an arbitrary,
|
||||||
|
possibly empty, string and '?' matches an arbitrary character.
|
||||||
|
|
||||||
|
All the possible properties that you can apply to devices and nodes of the
|
||||||
|
ALSA monitor are described in the sections below.
|
||||||
|
|
||||||
|
Device properties
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
PipeWire devices correspond to the ALSA cards.
|
||||||
|
The following properties can be configured on devices created by the monitor:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.use-acp"] = true
|
||||||
|
|
||||||
|
Use the ACP (alsa card profile) code to manage the device. This will probe the
|
||||||
|
device and configure the available profiles, ports and mixer settings. The
|
||||||
|
code to do this is taken directly from PulseAudio and provides devices that
|
||||||
|
look and feel exactly like the PulseAudio devices.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.use-ucm"] = true
|
||||||
|
|
||||||
|
By default, the UCM configuration is used when it is available for your device.
|
||||||
|
With this option you can disable this and use the ACP profiles instead.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.soft-mixer"] = false
|
||||||
|
|
||||||
|
Setting this option to true will disable the hardware mixer for volume control
|
||||||
|
and mute. All volume handling will then use software volume and mute, leaving
|
||||||
|
the hardware mixer untouched. The hardware mixer will still be used to mute
|
||||||
|
unused audio paths in the device.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.ignore-dB"] = false
|
||||||
|
|
||||||
|
Setting this option to true will ignore the decibel setting configured by the
|
||||||
|
driver. Use this when the driver reports wrong settings.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["device.profile-set"] = "profileset-name"
|
||||||
|
|
||||||
|
This option can be used to select a custom profile set name for the device.
|
||||||
|
Usually this is configured in Udev rules but it can also be specified here.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["device.profile"] = "default profile name"
|
||||||
|
|
||||||
|
The default active profile name.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.acp.auto-profile"] = false
|
||||||
|
|
||||||
|
Automatically select the best profile for the device. Normally this option is
|
||||||
|
disabled because the session manager will manage the profile of the device.
|
||||||
|
The session manager can save and load previously selected profiles. Enable
|
||||||
|
this if your session manager does not handle this feature.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.acp.auto-port"] = false
|
||||||
|
|
||||||
|
Automatically select the highest priority port that is available. This is by
|
||||||
|
default disabled because the session manager handles the task of selecting and
|
||||||
|
restoring ports. It can, for example, restore previously saved volumes. Enable
|
||||||
|
this here when the session manager does not handle port restore.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.acp.probe-rate"] = 48000
|
||||||
|
|
||||||
|
Sets the samplerate used for probing the ALSA devices and collecting the profiles
|
||||||
|
and ports.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.acp.pro-channels"] = 64
|
||||||
|
|
||||||
|
Sets the number of channels to use when probing the Pro Audio profile. Normally,
|
||||||
|
the maximum amount of channels will be used but with this setting this can be
|
||||||
|
reduced, which can make it possible to use other samplerates on some devices.
|
||||||
|
|
||||||
|
Some of the other properties that might be configured on devices:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["device.nick"] = "My Device",
|
||||||
|
["device.description"] = "My Device"
|
||||||
|
|
||||||
|
``device.description`` will show up in most apps when a device name is shown.
|
||||||
|
|
||||||
|
Node Properties
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Nodes are sinks or sources in the PipeWire graph. They correspond to the ALSA
|
||||||
|
devices. In addition to the generic stream node configuration options, there are
|
||||||
|
some alsa specific options as well:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["priority.driver"] = 2000
|
||||||
|
|
||||||
|
This configures the node driver priority. Nodes with higher priority will be
|
||||||
|
used as a driver in the graph. Other nodes with lower priority will have to
|
||||||
|
resample to the driver node when they are joined in the same graph. The default
|
||||||
|
value is set based on some heuristics.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["priority.session"] = 1200
|
||||||
|
|
||||||
|
This configures the priority of the node when selecting a default node.
|
||||||
|
Higher priority nodes will be more likely candidates as a default node.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
By default, sources have a ``priority.session`` value around 1600-2000 and
|
||||||
|
sinks have a value around 600-1000. If you are increasing the priority of a
|
||||||
|
sink, it is **not advised** to use a value higher than 1500, as it may cause
|
||||||
|
a sink's monitor to be selected as a default source.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["node.pause-on-idle"] = false
|
||||||
|
|
||||||
|
Pause-on-idle will stop the node when nothing is linked to it anymore.
|
||||||
|
This is by default false because some devices cause a pop when they are
|
||||||
|
opened/closed. The node will, normally, pause and suspend after a timeout
|
||||||
|
(see suspend-node.lua).
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["session.suspend-timeout-seconds"] = 5 -- 0 disables suspend
|
||||||
|
|
||||||
|
This option configures a different suspend timeout on the node.
|
||||||
|
By default this is 5 seconds. For some devices (HiFi amplifiers, for example)
|
||||||
|
it might make sense to set a higher timeout because they might require some
|
||||||
|
time to restart after being idle.
|
||||||
|
|
||||||
|
A value of 0 disables suspend for a node and will leave the ALSA device busy.
|
||||||
|
The device can then manually be suspended with ``pactl suspend-sink|source``.
|
||||||
|
|
||||||
|
**The following properties can be used to configure the format used by the
|
||||||
|
ALSA device:**
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["audio.format"] = "S16LE"
|
||||||
|
|
||||||
|
By default, PipeWire will use a 32 bits sample format but a different format
|
||||||
|
can be set here.
|
||||||
|
|
||||||
|
The Audio rate of a device can be set here:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["audio.rate"] = 44100
|
||||||
|
|
||||||
|
By default, the ALSA device will be configured with the same samplerate as the
|
||||||
|
global graph. If this is not supported, or a custom values is set here,
|
||||||
|
resampling will be used to match the graph rate.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["audio.channels"] = 2
|
||||||
|
["audio.position"] = "FL,FR"
|
||||||
|
|
||||||
|
By default the channels and their position are determined by the selected
|
||||||
|
Device profile. You can override this setting here and optionally swap or
|
||||||
|
reconfigure the channel positions.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.use-chmap"] = false
|
||||||
|
|
||||||
|
Use the channel map as reported by the driver. This is disabled by default
|
||||||
|
because it is often wrong and the ACP code handles this better.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.disable-mmap"] = true
|
||||||
|
|
||||||
|
PipeWire will by default access the memory of the device using mmap.
|
||||||
|
This can be disabled and force the usage of the slower read and write access
|
||||||
|
modes in case the mmap support of the device is not working properly.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["channelmix.normalize"] = true
|
||||||
|
|
||||||
|
Makes sure that during such mixing & resampling original 0 dB level is
|
||||||
|
preserved, so nothing sounds wildly quieter/louder.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["channelmix.mix-lfe"] = true
|
||||||
|
|
||||||
|
Creates "center" channel for X.0 recordings from front stereo on X.1 setups and
|
||||||
|
pushes some low-frequency/bass from "center" from X.1 recordings into front
|
||||||
|
stereo on X.0 setups.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["monitor.channel-volumes"] = false
|
||||||
|
|
||||||
|
By default, the volume of the sink/source does not influence the volume on the
|
||||||
|
monitor ports. Set this option to true to change this. PulseAudio has
|
||||||
|
inconsistent behaviour regarding this option, it applies channel-volumes only
|
||||||
|
when the sink/source is using software volumes.
|
||||||
|
|
||||||
|
ALSA buffer properties
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
PipeWire uses a timer to consume and produce samples to/from ALSA devices.
|
||||||
|
After every timeout, it queries the device hardware pointers of the device and
|
||||||
|
uses this information to set a new timeout. See also this example program.
|
||||||
|
|
||||||
|
By default, PipeWire handles ALSA batch devices differently from non-batch
|
||||||
|
devices. Batch devices only get their hardware pointers updated after each
|
||||||
|
hardware interrupt. Non-batch devices get updates independent of the interrupt.
|
||||||
|
This means that for batch devices we need to set the interrupt at a sufficiently
|
||||||
|
high frequency (at the cost of CPU usage) while for non-batch devices we want to
|
||||||
|
set the interrupt frequency as low as possible (to save CPU).
|
||||||
|
|
||||||
|
For batch devices we also need to take the extra buffering into account caused
|
||||||
|
by the delayed updates of the hardware pointers.
|
||||||
|
|
||||||
|
Most USB devices are batch devices and will be handled as such by PipeWire by
|
||||||
|
default.
|
||||||
|
|
||||||
|
There are 2 tunable parameters to control the buffering and timeouts in a
|
||||||
|
device
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.period-size"] = 1024
|
||||||
|
|
||||||
|
This sets the device interrupt to every period-size samples for non-batch
|
||||||
|
devices and to half of this for batch devices. For batch devices, the other
|
||||||
|
half of the period-size is used as extra buffering to compensate for the delayed
|
||||||
|
update. So, for batch devices, there is an additional period-size/2 delay.
|
||||||
|
It makes sense to lower the period-size for batch devices to reduce this delay.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.headroom"] = 0
|
||||||
|
|
||||||
|
This adds extra delay between the hardware pointers and software pointers.
|
||||||
|
In most cases this can be set to 0. For very bad devices or emulated devices
|
||||||
|
(like in a VM) it might be necessary to increase the headroom value.
|
||||||
|
In summary, this is the overview of buffering and timings:
|
||||||
|
|
||||||
|
|
||||||
|
============== ========================================== =========
|
||||||
|
Property Batch Non-Batch
|
||||||
|
============== ========================================== =========
|
||||||
|
IRQ Frequency api.alsa.period-size/2 api.alsa.period-size
|
||||||
|
Extra Delay api.alsa.headroom + api.alsa.period-size/2 api.alsa.headroom
|
||||||
|
============== ========================================== =========
|
||||||
|
|
||||||
|
It is possible to disable the batch device tweaks with:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.disable-batch"] = true
|
||||||
|
|
||||||
|
It removes the extra delay added of period-size/2 if the device can support this.
|
||||||
|
For batch devices it is also a good idea to lower the period-size
|
||||||
|
(and increase the IRQ frequency) to get smaller batch updates and lower latency.
|
||||||
|
|
||||||
|
ALSA extra latency properties
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Extra internal delay in the DAC and ADC converters of the device itself can be
|
||||||
|
set with the ``latency.internal.*`` properties:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["latency.internal.rate"] = 256
|
||||||
|
["latency.internal.ns"] = 0
|
||||||
|
|
||||||
|
You can configure a latency in samples (relative to rate with
|
||||||
|
``latency.internal.rate``) or in nanoseconds (``latency.internal.ns``).
|
||||||
|
This value will be added to the total reported latency by the node of the device.
|
||||||
|
|
||||||
|
You can use a tool like ``jack_iodelay`` to get the number of samples of
|
||||||
|
internal latency of your device.
|
||||||
|
|
||||||
|
This property is also adjustable at runtime with the ``ProcessLatency`` param.
|
||||||
|
You will need to find the id of the Node you want to change. For example:
|
||||||
|
Query the current internal latency of an ALSA node with id 58:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pw-cli e 58 ProcessLatency
|
||||||
|
Object: size 80, type Spa:Pod:Object:Param:ProcessLatency (262156), id Spa:Enum:ParamId:ProcessLatency (16)
|
||||||
|
Prop: key Spa:Pod:Object:Param:ProcessLatency:quantum (1), flags 00000000
|
||||||
|
Float 0.000000
|
||||||
|
Prop: key Spa:Pod:Object:Param:ProcessLatency:rate (2), flags 00000000
|
||||||
|
Int 0
|
||||||
|
Prop: key Spa:Pod:Object:Param:ProcessLatency:ns (3), flags 00000000
|
||||||
|
Long 0
|
||||||
|
|
||||||
|
Set the internal latency to 256 samples:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pw-cli s 58 ProcessLatency '{ rate = 256 }'
|
||||||
|
Object: size 32, type Spa:Pod:Object:Param:ProcessLatency (262156), id Spa:Enum:ParamId:ProcessLatency (16)
|
||||||
|
Prop: key Spa:Pod:Object:Param:ProcessLatency:rate (2), flags 00000000
|
||||||
|
Int 256
|
||||||
|
remote 0 node 58 changed
|
||||||
|
remote 0 port 70 changed
|
||||||
|
remote 0 port 72 changed
|
||||||
|
remote 0 port 74 changed
|
||||||
|
remote 0 port 76 changed
|
||||||
|
|
||||||
|
Startup tweaks
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Some devices need some time before they can report accurate hardware pointer
|
||||||
|
positions. In those cases, an extra start delay can be added that is used to
|
||||||
|
compensate for this startup delay:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["api.alsa.start-delay"] = 0
|
||||||
|
|
||||||
|
It is unsure when this tunable should be used.
|
||||||
|
|
||||||
|
IEC958 (S/PDIF) passthrough
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
S/PDIF passthrough will only be enabled when the accepted codecs are configured
|
||||||
|
on the ALSA device.
|
||||||
|
|
||||||
|
This can be done in 3 different ways:
|
||||||
|
|
||||||
|
1. Use pavucontrol and toggle the codecs in the output advanced section
|
||||||
|
|
||||||
|
2. Modify the ``["iec958.codecs"] = "[ PCM DTS AC3 MPEG MPEG2-AAC EAC3 TrueHD DTS-HD ]"``
|
||||||
|
node property to something.
|
||||||
|
|
||||||
|
3. Use ``pw-cli s <node-id> Props '{ iec958Codecs : [ PCM ] }'`` to modify
|
||||||
|
the codecs at runtime.
|
||||||
160
docs/rst/configuration/bluetooth.rst
Normal file
160
docs/rst/configuration/bluetooth.rst
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
.. _config_bluetooth:
|
||||||
|
|
||||||
|
Bluetooth configuration
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Using the same format as the :ref:`ALSA monitor <config_alsa>`, the
|
||||||
|
configuration file ``bluetooth.lua.d/50-bluez-config.lua`` is charged
|
||||||
|
to configure the Bluetooth devices and nodes created by WirePlumber.
|
||||||
|
|
||||||
|
* *bluez_monitor.properties*
|
||||||
|
|
||||||
|
A Lua object that contains generic client configuration properties in the
|
||||||
|
for of key pairs.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
bluez_monitor.properties = {
|
||||||
|
["bluez5.enable-msbc"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
This example will enable the MSBC codec in connected Bluetooth devices that
|
||||||
|
support it.
|
||||||
|
|
||||||
|
The list of valid properties are:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.enable-sbc-xq"] = true
|
||||||
|
|
||||||
|
Enables the SBC-XQ codec in connected Blueooth devices that support it
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.enable-msbc"] = true
|
||||||
|
|
||||||
|
Enables the MSBC codec in connected Blueooth devices that support it
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.enable-hw-volume"] = true
|
||||||
|
|
||||||
|
Enables hardware volume controls in Bluetooth devices that support it
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.headset-roles"] = "[ hsp_hs hsp_ag hfp_hf hfp_ag ]"
|
||||||
|
|
||||||
|
Enabled headset roles (default: [ hsp_hs hfp_ag ]), this property only applies
|
||||||
|
to native backend. Currently some headsets (Sony WH-1000XM3) are not working
|
||||||
|
with both hsp_ag and hfp_ag enabled, disable either hsp_ag or hfp_ag to work
|
||||||
|
around it.
|
||||||
|
|
||||||
|
Supported headset roles: ``hsp_hs`` (HSP Headset), ``hsp_ag`` (HSP Audio Gateway),
|
||||||
|
``hfp_hf`` (HFP Hands-Free) and ``hfp_ag`` (HFP Audio Gateway)
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.codecs"] = "[ sbc sbc_xq aac ]"
|
||||||
|
|
||||||
|
Enables ``sbc``, ``sbc_zq`` and ``aac`` A2DP codecs.
|
||||||
|
|
||||||
|
Supported codecs: ``sbc``, ``sbc_xq``, ``aac``, ``ldac``, ``aptx``,
|
||||||
|
``aptx_hd``, ``aptx_ll``, ``aptx_ll_duplex``, ``faststream``, ``faststream_duplex``.
|
||||||
|
|
||||||
|
All codecs are supported by default.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.hfphsp-backend"] = "native"
|
||||||
|
|
||||||
|
HFP/HSP backend (default: native). Available values: ``any``, ``none``,
|
||||||
|
``hsphfpd``, ``ofono`` or ``native``.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.default.rate"] = 48000
|
||||||
|
|
||||||
|
The bluetooth default audio rate.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.default.channels"] = 2
|
||||||
|
|
||||||
|
The bluetooth default number of channels.
|
||||||
|
|
||||||
|
* *bluez_monitor.rules*
|
||||||
|
|
||||||
|
Like in the :ref:`ALSA configuration <config_alsa>`, this is a Lua array that
|
||||||
|
can contain objects with rules for a Bluetooth device or node.
|
||||||
|
Those objects have 2 properties. The first one is ``matches``, which allows
|
||||||
|
users to define rules to match a Bluetooth device or node.
|
||||||
|
The second property is ``apply_properties``, and it is used to apply
|
||||||
|
properties on the matched Bluetooth device or node.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
{
|
||||||
|
matches = {
|
||||||
|
{
|
||||||
|
{ "device.name", "matches", "bluez_card.*" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apply_properties = {
|
||||||
|
["bluez5.auto-connect"] = "[ hfp_hf hsp_hs a2dp_sink ]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
This will set the auto-connect property to ``hfp_hf``, ``hsp_hs`` and ``a2dp_sink``
|
||||||
|
on bluetooth devices whose name matches the ``bluez_card.*`` pattern.
|
||||||
|
|
||||||
|
A list of valid properties are:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.auto-connect"] = "[ hfp_hf hsp_hs a2dp_sink ]"
|
||||||
|
|
||||||
|
Auto-connect device profiles on start up or when only partial profiles have
|
||||||
|
connected. Disabled by default if the property is not specified.
|
||||||
|
|
||||||
|
Supported values are: ``hfp_hf``, ``hsp_hs``, ``a2dp_sink``, ``hfp_ag``,
|
||||||
|
``hsp_ag`` and ``a2dp_source``.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.hw-volume"] = "[ hfp_ag hsp_ag a2dp_source ]"
|
||||||
|
|
||||||
|
Hardware volume controls (default: ``hfp_ag``, ``hsp_ag``, and ``a2dp_source``)
|
||||||
|
|
||||||
|
Supported values are: ``hfp_hf``, ``hsp_hs``, ``a2dp_sink``, ``hfp_ag``,
|
||||||
|
``hsp_ag`` and ``a2dp_source``.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.a2dp.ldac.quality"] = "auto"
|
||||||
|
|
||||||
|
LDAC encoding quality.
|
||||||
|
|
||||||
|
Available values: ``auto`` (Adaptive Bitrate, default),
|
||||||
|
``hq`` (High Quality, 990/909kbps), ``sq`` (Standard Quality, 660/606kbps) and
|
||||||
|
``mq`` (Mobile use Quality, 330/303kbps).
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["bluez5.a2dp.aac.bitratemode"] = 0
|
||||||
|
|
||||||
|
AAC variable bitrate mode.
|
||||||
|
|
||||||
|
Available values: 0 (cbr, default), 1-5 (quality level).
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["device.profile"] = "a2dp-sink"
|
||||||
|
|
||||||
|
Profile connected first.
|
||||||
|
|
||||||
|
Available values: ``a2dp-sink`` (default) or ``headset-head-unit``.
|
||||||
185
docs/rst/configuration/config_lua.rst
Normal file
185
docs/rst/configuration/config_lua.rst
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
.. _config_lua:
|
||||||
|
|
||||||
|
Lua Configuration Files
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Lua configuration files are similar to the main configuration file, but they
|
||||||
|
leverage the Lua language to enable advanced configuration of module arguments
|
||||||
|
and allow split-file configuration.
|
||||||
|
|
||||||
|
There is only one global section that WirePlumber reads from these files: the
|
||||||
|
**components** table. This table is equivalent to the **context.components**
|
||||||
|
object on the main configuration file. Its purpose is to list components that
|
||||||
|
WirePlumber should load on startup.
|
||||||
|
|
||||||
|
Every line on the **components** table should be another table that contains
|
||||||
|
information about the loaded component::
|
||||||
|
|
||||||
|
{
|
||||||
|
"component-name",
|
||||||
|
type = "component-type",
|
||||||
|
args = { additional arguments },
|
||||||
|
optional = true/false,
|
||||||
|
}
|
||||||
|
|
||||||
|
* **component-name**: Should be the name of the component to load
|
||||||
|
(ex. *"libwireplumber-module-mixer-api"*).
|
||||||
|
|
||||||
|
* **component-type**: Should be the type of the component.
|
||||||
|
Valid component types include:
|
||||||
|
|
||||||
|
* ``module``: A WirePlumber shared object module.
|
||||||
|
* ``script/lua``: A WirePlumber Lua script.
|
||||||
|
* ``pw_module``: A PipeWire shared object module (loaded on WirePlumber,
|
||||||
|
not on the PipeWire daemon).
|
||||||
|
|
||||||
|
* **args**: Is an optional table that can contain additional arguments to be
|
||||||
|
passed down to the module or script. Scripts can retrieve these arguments
|
||||||
|
by declaring a line that reads ``local config = ...`` at the top of the script.
|
||||||
|
Modules receive these arguments as a GVariant ``a{sv}`` table.
|
||||||
|
|
||||||
|
* **optional**: Is a boolean value that specifies whether loading of this
|
||||||
|
component is optional. The default value is ``false``. If set to ``true``,
|
||||||
|
then WirePlumber will not fail loading if the component is not found.
|
||||||
|
|
||||||
|
Split-File Configuration
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
When a Lua configuration file is loaded, the engine also looks for additional
|
||||||
|
files in a directory that has the same name as the configuration file and a
|
||||||
|
``.d`` suffix.
|
||||||
|
|
||||||
|
A Lua directory can contain a list of Lua configuration files. Those files are
|
||||||
|
loaded alphabetically by filename so that user can control the order in which
|
||||||
|
Lua configuration files are executed.
|
||||||
|
|
||||||
|
Lua files in the directory are always loaded *after* the configuration file
|
||||||
|
that is out of the directory. However, it is perfectly valid to not have any
|
||||||
|
configuration file out of the directory.
|
||||||
|
|
||||||
|
Example hierarchy with files both in and out of the directory
|
||||||
|
(in the order of loading)::
|
||||||
|
|
||||||
|
config.lua
|
||||||
|
config.lua.d/00-functions.lua
|
||||||
|
config.lua.d/01-alsa.lua
|
||||||
|
config.lua.d/10-policy.lua
|
||||||
|
config.lua.d/99-misc.lua
|
||||||
|
|
||||||
|
Example hierarchy with files only in the directory
|
||||||
|
(in the order of loading)::
|
||||||
|
|
||||||
|
config.lua.d/00-functions.lua
|
||||||
|
config.lua.d/01-alsa.lua
|
||||||
|
config.lua.d/10-policy.lua
|
||||||
|
config.lua.d/99-misc.lua
|
||||||
|
|
||||||
|
Example of a file using alsa_monitor.rules in a split-file configuration:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
table.insert (alsa_monitor.rules, {
|
||||||
|
matches = {
|
||||||
|
{
|
||||||
|
{ "device.name", "matches", "alsa_card.*" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apply_properties = {
|
||||||
|
["api.alsa.use-acp"] = true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Multi-Path Merging
|
||||||
|
------------------
|
||||||
|
|
||||||
|
WirePlumber looks for configuration files in 3 different places, as described
|
||||||
|
in the :ref:`Locations of files <config_locations>` section. When a split-file
|
||||||
|
configuration scheme is used, files will be merged from these different locations.
|
||||||
|
|
||||||
|
For example, consider these files exist on the filesystem::
|
||||||
|
|
||||||
|
/usr/share/wireplumber/config.lua.d/00-functions.lua
|
||||||
|
/usr/share/wireplumber/config.lua.d/01-alsa.lua
|
||||||
|
/usr/share/wireplumber/config.lua.d/10-policy.lua
|
||||||
|
/usr/share/wireplumber/config.lua.d/99-misc.lua
|
||||||
|
...
|
||||||
|
/etc/wireplumber/config.lua.d/01-alsa.lua
|
||||||
|
...
|
||||||
|
/home/user/.config/wireplumber/config.lua.d/11-policy-extras.lua
|
||||||
|
|
||||||
|
In this case, loading ``config.lua`` will result in loading these files
|
||||||
|
(in this order)::
|
||||||
|
|
||||||
|
/usr/share/wireplumber/config.lua.d/00-functions.lua
|
||||||
|
/etc/wireplumber/config.lua.d/01-alsa.lua
|
||||||
|
/usr/share/wireplumber/config.lua.d/10-policy.lua
|
||||||
|
/home/user/.config/wireplumber/config.lua.d/11-policy-extras.lua
|
||||||
|
/usr/share/wireplumber/config.lua.d/99-misc.lua
|
||||||
|
|
||||||
|
This is useful to keep the default configuration in /usr and override it
|
||||||
|
with host-specific and user-specific parts in /etc and /home respectively.
|
||||||
|
|
||||||
|
As an exception to this rule, if the configuration path is overridden with
|
||||||
|
the ``WIREPLUMBER_CONFIG_DIR`` environment variable, then configuration files
|
||||||
|
will only be loaded from this path and no merging will happen.
|
||||||
|
|
||||||
|
Functions
|
||||||
|
---------
|
||||||
|
|
||||||
|
Because of the nature of these files (they are scripts!), it is more convenient
|
||||||
|
to manage the **components** table through functions. In the default
|
||||||
|
configuration files shipped with WirePlumber, there is a file called
|
||||||
|
``00-functions.lua`` that defines some helper functions to load components.
|
||||||
|
|
||||||
|
When loading components through these functions, *duplicate calls are ignored*,
|
||||||
|
so it is possible to call a function to load a specific component multiple times
|
||||||
|
and it will only be loaded once.
|
||||||
|
|
||||||
|
.. function:: load_module(module, args)
|
||||||
|
|
||||||
|
Loads a WirePlumber shared object module.
|
||||||
|
|
||||||
|
:param string module: the module name, without the "libwireplumber-module-"
|
||||||
|
prefix (ex specify "mixer-api" to load "libwireplumber-module-mixer-api")
|
||||||
|
:param table args: optional module arguments table
|
||||||
|
|
||||||
|
.. function:: load_optional_module(module, args)
|
||||||
|
|
||||||
|
Loads an optional WirePlumber shared object module. Optional in this case
|
||||||
|
means that if the module is not present on the filesystem, it will be ignored.
|
||||||
|
|
||||||
|
:param string module: the module name, without the "libwireplumber-module-"
|
||||||
|
prefix (ex specify "mixer-api" to load "libwireplumber-module-mixer-api")
|
||||||
|
:param table args: optional module arguments table
|
||||||
|
|
||||||
|
.. function:: load_pw_module(module)
|
||||||
|
|
||||||
|
Loads a PipeWire shared object module
|
||||||
|
|
||||||
|
:param string module: the module name, without the "libpipewire-module-"
|
||||||
|
prefix (ex specify "adapter" to load "libpipewire-module-adapter")
|
||||||
|
|
||||||
|
.. function:: load_script(script, args)
|
||||||
|
|
||||||
|
Loads a Lua script (a functionality script, not a lua configuration file)
|
||||||
|
|
||||||
|
:param string script: the script's filename (ex. "policy-node.lua")
|
||||||
|
:param table args: optional script arguments table
|
||||||
|
|
||||||
|
.. function:: load_monitor(monitor, args)
|
||||||
|
|
||||||
|
Loads a Lua monitor script. Monitors are scripts found in the ``monitors/``
|
||||||
|
directory and their purpose is to monitor and load devices.
|
||||||
|
|
||||||
|
:param string monitor: the scripts's name without the directory or the .lua
|
||||||
|
extension (ex. "alsa" will load "monitors/alsa.lua")
|
||||||
|
:param table args: optional script arguments table
|
||||||
|
|
||||||
|
.. function:: load_access(access, args)
|
||||||
|
|
||||||
|
Loads a Lua access script. Access scripts are ones found in the ``access/``
|
||||||
|
directory and their purpose is to manage application permissions.
|
||||||
|
|
||||||
|
:param string access: the scripts's name without the directory or the .lua
|
||||||
|
extension (ex. "flatpak" will load "access/access-flatpak.lua")
|
||||||
|
:param table args: optional script arguments table
|
||||||
94
docs/rst/configuration/locations.rst
Normal file
94
docs/rst/configuration/locations.rst
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
.. _config_locations:
|
||||||
|
|
||||||
|
Locations of files
|
||||||
|
==================
|
||||||
|
|
||||||
|
Location of configuration files
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
WirePlumber's default locations of its configuration files are determined at
|
||||||
|
compile time by the build system. Typically, those end up being
|
||||||
|
``$XDG_CONFIG_DIR/wireplumber``, ``/etc/wireplumber``, and
|
||||||
|
``/usr/share/wireplumber``, in that order of priority.
|
||||||
|
|
||||||
|
In more detail, the latter two are controlled by the ``--sysconfdir`` and ``--datadir``
|
||||||
|
meson options. When those are set to an absolute path, such as ``/etc``, the
|
||||||
|
location of the configuration files is set to be ``$sysconfdir/wireplumber``.
|
||||||
|
When set to a relative path, such as ``etc``, then the installation prefix (``--prefix``)
|
||||||
|
is prepended to the path: ``$prefix/$sysconfdir/wireplumber``
|
||||||
|
|
||||||
|
The three locations are intended for custom user configuration,
|
||||||
|
host-specific configuration and distribution-provided configuration,
|
||||||
|
respectively. At runtime, WirePlumber will search the directories
|
||||||
|
for the highest-priority directory to contain the needed configuration file.
|
||||||
|
This allows a user or system administrator to easily override the distribution
|
||||||
|
provided configuration files by placing an equally named file in the respective
|
||||||
|
directory.
|
||||||
|
|
||||||
|
It is also possible to override the configuration directory by setting the
|
||||||
|
``WIREPLUMBER_CONFIG_DIR`` environment variable::
|
||||||
|
|
||||||
|
WIREPLUMBER_CONFIG_DIR=src/config wireplumber
|
||||||
|
|
||||||
|
If ``WIREPLUMBER_CONFIG_DIR`` is set, the default locations are ignored and
|
||||||
|
configuration files are *only* looked up in this directory.
|
||||||
|
|
||||||
|
|
||||||
|
Location of scripts
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
WirePlumber's default locations of its scripts are the same ones as for the
|
||||||
|
configuration files, but with the ``scripts`` directory appended.
|
||||||
|
Typically, these end up being ``$XDG_CONFIG_DIR/wireplumber/scripts``,
|
||||||
|
``/etc/wireplumber/scripts``, and ``/usr/share/wireplumber/scripts``,
|
||||||
|
in that order of priority.
|
||||||
|
|
||||||
|
The three locations are intended for custom user scripts,
|
||||||
|
host-specific scripts and distribution-provided scripts, respectively.
|
||||||
|
At runtime, WirePlumber will search the directories for the highest-priority
|
||||||
|
directory to contain the needed script.
|
||||||
|
|
||||||
|
It is also possible to override the scripts directory by setting the
|
||||||
|
``WIREPLUMBER_DATA_DIR`` environment variable::
|
||||||
|
|
||||||
|
WIREPLUMBER_DATA_DIR=src wireplumber
|
||||||
|
|
||||||
|
The "data" directory is a somewhat more generic path that may be used for
|
||||||
|
other kinds of data files in the future. For scripts, WirePlumber still expects
|
||||||
|
to find a ``scripts`` subdirectory in this "data" directory, so in the above
|
||||||
|
example the scripts would be in ``src/scripts``.
|
||||||
|
|
||||||
|
If ``WIREPLUMBER_DATA_DIR`` is set, the default locations are ignored and
|
||||||
|
scripts are *only* looked up in this directory.
|
||||||
|
|
||||||
|
Location of modules
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
WirePlumber modules
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Like with configuration files, WirePlumber's default location of its modules is
|
||||||
|
determined at compile time by the build system. Typically, it ends up being
|
||||||
|
``/usr/lib/wireplumber-0.4`` (or ``/usr/lib/<arch-triplet>/wireplumber-0.4`` on
|
||||||
|
multiarch systems)
|
||||||
|
|
||||||
|
In more detail, this is controlled by the ``--libdir`` meson option. When
|
||||||
|
this is set to an absolute path, such as ``/lib``, the location of the
|
||||||
|
modules is set to be ``$libdir/wireplumber-$abi_version``. When this is set
|
||||||
|
to a relative path, such as ``lib``, then the installation prefix (``--prefix``)
|
||||||
|
is prepended to the path: ``$prefix/$libdir/wireplumber-$abi_version``.
|
||||||
|
|
||||||
|
It is possible to override this directory at runtime by setting the
|
||||||
|
``WIREPLUMBER_MODULE_DIR`` environment variable::
|
||||||
|
|
||||||
|
WIREPLUMBER_MODULE_DIR=build/modules wireplumber
|
||||||
|
|
||||||
|
PipeWire and SPA modules
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
PipeWire and SPA modules are not loaded from the same location as WirePlumber's
|
||||||
|
modules. They are loaded from the location that PipeWire loads them.
|
||||||
|
|
||||||
|
It is also possible to override these locations by using environment variables:
|
||||||
|
``SPA_PLUGIN_DIR`` and ``PIPEWIRE_MODULE_DIR``. For more details, refer to
|
||||||
|
PipeWire's documentation.
|
||||||
126
docs/rst/configuration/main.rst
Normal file
126
docs/rst/configuration/main.rst
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
.. _config_main:
|
||||||
|
|
||||||
|
Main configuration file
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The main configuration file is by default called ``wireplumber.conf``. This can
|
||||||
|
be changed on the command line by passing the ``--config-file`` or ``-c`` option::
|
||||||
|
|
||||||
|
wireplumber --config-file=bluetooth.conf
|
||||||
|
|
||||||
|
The ``--config-file`` option is useful to run multiple instances of wireplumber
|
||||||
|
that do separate tasks each. For more information on this subject, see the
|
||||||
|
:ref:`Multiple Instances <config_multi_instance>` section.
|
||||||
|
|
||||||
|
The format of this configuration file is the variant of JSON that is also
|
||||||
|
used in PipeWire configuration files. Note that this is subject to change
|
||||||
|
in the future.
|
||||||
|
|
||||||
|
All sections are essentially JSON objects. Lines starting with *#* are treated
|
||||||
|
as comments and ignored. The list of all possible section JSON objects are:
|
||||||
|
|
||||||
|
* *context.properties*
|
||||||
|
|
||||||
|
Used to define properties to configure the PipeWire context and some modules.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
context.properties = {
|
||||||
|
application.name = WirePlumber
|
||||||
|
log.level = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
This sets the daemon's name to *WirePlumber* and the log level to *2*, which
|
||||||
|
only displays errors and warnings. See the
|
||||||
|
:ref:`Debug Logging <logging>` section for more details.
|
||||||
|
|
||||||
|
* *context.spa-libs*
|
||||||
|
|
||||||
|
Used to find spa factory names. It maps a spa factory name regular expression
|
||||||
|
to a library name that should contain that factory. The object property names
|
||||||
|
are the regular expression, and the object property values are the actual
|
||||||
|
library name::
|
||||||
|
|
||||||
|
<factory-name regex> = <library-name>
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
context.spa-libs = {
|
||||||
|
api.alsa.* = alsa/libspa-alsa
|
||||||
|
audio.convert.* = audioconvert/libspa-audioconvert
|
||||||
|
}
|
||||||
|
|
||||||
|
In this example, we instruct wireplumber to only any *api.alsa.** factory name
|
||||||
|
from the *libspa-alsa* library, and also any *audio.convert.** factory name
|
||||||
|
from the *libspa-audioconvert* library.
|
||||||
|
|
||||||
|
* *context.modules*
|
||||||
|
|
||||||
|
Used to load PipeWire modules. This does not affect the PipeWire daemon by any
|
||||||
|
means. It exists simply to allow loading *libpipewire* modules in the PipeWire
|
||||||
|
core that runs inside WirePlumber. This is usually useful to load PipeWire
|
||||||
|
protocol extensions, so that you can export custom objects to PipeWire and
|
||||||
|
other clients.
|
||||||
|
|
||||||
|
Users can also pass key-value pairs if the specific module has arguments, and
|
||||||
|
a combination of 2 flags: ``ifexists`` flag is given, the module is ignored when
|
||||||
|
not found; if ``nofail`` is given, module initialization failures are ignored::
|
||||||
|
|
||||||
|
{
|
||||||
|
name = <module-name>
|
||||||
|
[ args = { <key> = <value> ... } ]
|
||||||
|
[ flags = [ [ ifexists ] [ nofail ] ]
|
||||||
|
}
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
context.modules = [
|
||||||
|
{ name = libpipewire-module-adapter }
|
||||||
|
{
|
||||||
|
name = libpipewire-module-metadata,
|
||||||
|
flags = [ ifexists ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
The above example loads both PipeWire adapter and metadata modules. The
|
||||||
|
metadata module will be ignored if not found because of its ``ifexists`` flag.
|
||||||
|
|
||||||
|
* *context.components*
|
||||||
|
|
||||||
|
Used to load WirePlumber components. Components can be either WirePlumber
|
||||||
|
modules written in C, WirePlumber scripts or other configuration
|
||||||
|
files::
|
||||||
|
|
||||||
|
{ name = <component-name>, type = <component-type> }
|
||||||
|
|
||||||
|
Valid component types include:
|
||||||
|
|
||||||
|
* ``module``: A WirePlumber shared object module
|
||||||
|
* ``script/lua``: A WirePlumber Lua script
|
||||||
|
(requires ``libwireplumber-module-lua-scripting``)
|
||||||
|
* ``config/lua``: A WirePlumber Lua configuration file
|
||||||
|
(requires ``libwireplumber-module-lua-scripting``)
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
context.components = [
|
||||||
|
{ name = libwireplumber-module-lua-scripting, type = module }
|
||||||
|
{ name = main.lua, type = config/lua }
|
||||||
|
]
|
||||||
|
|
||||||
|
This will load the WirePlumber lua-scripting module, dynamically, and then
|
||||||
|
it will also load any components specified in the ``main.lua`` file.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When loading lua configuration files, WirePlumber will also look for
|
||||||
|
additional files in the directory suffixed with ``.d`` and will load
|
||||||
|
all of them as well. For example, loading ``example.lua`` will also load
|
||||||
|
any ``.lua`` files under ``example.lua.d/``. In addition, the presence of the
|
||||||
|
main file is optional, so it is valid to specify ``example.lua`` in the
|
||||||
|
component name, while ``example.lua`` doesn't exist, but ``example.lua.d/``
|
||||||
|
exists instead and has ``.lua`` files to load.
|
||||||
|
|
||||||
|
For more information about lua configuration files, see the
|
||||||
|
:ref:`Lua configuration files <config_lua>` section.
|
||||||
|
|
||||||
11
docs/rst/configuration/meson.build
Normal file
11
docs/rst/configuration/meson.build
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# you need to add here any files you add to the toc directory as well
|
||||||
|
sphinx_files += files(
|
||||||
|
'locations.rst',
|
||||||
|
'main.rst',
|
||||||
|
'config_lua.rst',
|
||||||
|
'multi_instance.rst',
|
||||||
|
'alsa.rst',
|
||||||
|
'bluetooth.rst',
|
||||||
|
'policy.rst',
|
||||||
|
'access.rst',
|
||||||
|
)
|
||||||
45
docs/rst/configuration/multi_instance.rst
Normal file
45
docs/rst/configuration/multi_instance.rst
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
.. _config_multi_instance:
|
||||||
|
|
||||||
|
Running multiple instances
|
||||||
|
==========================
|
||||||
|
|
||||||
|
WirePlumber has the ability to run either as a single instance daemon or as
|
||||||
|
multiple instances, meaning that there can be multiple processes, each one
|
||||||
|
doing a different task.
|
||||||
|
|
||||||
|
In the default configuration, both setups are supported. The default is to run
|
||||||
|
in single-instance mode.
|
||||||
|
|
||||||
|
In single-instance mode, WirePlumber reads ``wireplumber.conf``, which is the
|
||||||
|
default configuration file, and from there it loads ``main.lua``, ``policy.lua``
|
||||||
|
and ``bluetooth.lua``, which are lua configuration files (deployed as directories)
|
||||||
|
that enable all the relevant functionality.
|
||||||
|
|
||||||
|
In multi-instance mode, WirePlumber is meant to be started with the
|
||||||
|
``--config-file`` command line option 3 times:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ wireplumber --config-file=main.conf
|
||||||
|
$ wireplumber --config-file=policy.conf
|
||||||
|
$ wireplumber --config-file=bluetooth.conf
|
||||||
|
|
||||||
|
That loads one process which reads ``main.conf``, which then loads ``main.lua``
|
||||||
|
and enables core functionality. Then another process that reads ``policy.conf``,
|
||||||
|
which then loads ``policy.lua`` and enables policy functionality... and so on.
|
||||||
|
|
||||||
|
To make this easier to work with, a template systemd unit is provided, which is
|
||||||
|
meant to be started with the name of the main configuration file as a
|
||||||
|
template argument:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ systemctl --user disable wireplumber # disable the single instance
|
||||||
|
|
||||||
|
$ systemctl --user enable wireplumber@main
|
||||||
|
$ systemctl --user enable wireplumber@policy
|
||||||
|
$ systemctl --user enable wireplumber@bluetooth
|
||||||
|
|
||||||
|
It is obviously possible to start as many instances as desired, with manually
|
||||||
|
crafted configuration files, as long as it is ensured that these instances
|
||||||
|
serve a different purpose and they do not conflict with each other.
|
||||||
116
docs/rst/configuration/policy.rst
Normal file
116
docs/rst/configuration/policy.rst
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
.. _config_policy:
|
||||||
|
|
||||||
|
Policy Configuration
|
||||||
|
====================
|
||||||
|
|
||||||
|
policy.lua.d/10-default-policy.lua
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This file contains generic default policy properties that can be configured.
|
||||||
|
|
||||||
|
* *default_policy.policy*
|
||||||
|
|
||||||
|
This is a Lua object that contains several properties that change the
|
||||||
|
behavior of the default WirePlumber policy.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
default_policy.policy = {
|
||||||
|
["move"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
The above example will set the ``move`` policy property to ``true``.
|
||||||
|
|
||||||
|
The list of supported properties are:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["move"] = true
|
||||||
|
|
||||||
|
Moves session items when metadata ``target.node`` changes.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["follow"] = true
|
||||||
|
|
||||||
|
Moves session items to the default device when it has changed.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["audio.no-dsp"] = false
|
||||||
|
|
||||||
|
Set to ``true`` to disable channel splitting & merging on nodes and enable
|
||||||
|
passthrough of audio in the same format as the format of the device. Note that
|
||||||
|
this breaks JACK support; it is generally not recommended.
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
["duck.level"] = 0.3
|
||||||
|
|
||||||
|
How much to lower the volume of lower priority streams when ducking. Note that
|
||||||
|
this is a linear volume modifier (not cubic as in PulseAudio).
|
||||||
|
|
||||||
|
policy.lua.d/50-endpoints-config.lua
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Endpoints are objects that can group multiple clients into different groups or
|
||||||
|
roles. This is useful if a user wants to apply specific actions when a client
|
||||||
|
is connected to a particular role/endpoint. This configuration file allows
|
||||||
|
users to configure those endpoints and their actions.
|
||||||
|
|
||||||
|
* *default_policy.policy.roles*
|
||||||
|
|
||||||
|
This is a Lua array with objects defining the actions of each role.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
default_policy.policy.roles = {
|
||||||
|
["Multimedia"] = {
|
||||||
|
["alias"] = { "Movie", "Music", "Game" },
|
||||||
|
["priority"] = 10,
|
||||||
|
["action.default"] = "mix",
|
||||||
|
}
|
||||||
|
["Notification"] = {
|
||||||
|
["priority"] = 20,
|
||||||
|
["action.default"] = "duck",
|
||||||
|
["action.Notification"] = "mix",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The above example defines actions for both ``Multimedia`` and ``Notification``
|
||||||
|
roles. Since the Notification role has more priority than the Multimedia
|
||||||
|
role, when a client connects to the Notification endpoint, it will ``duck``
|
||||||
|
the volume of all Multimedia clients. If Multiple Notification clients want
|
||||||
|
to play audio, only the Notifications audio will be mixed.
|
||||||
|
|
||||||
|
Possible values of actions are: ``mix`` (Mixes audio),
|
||||||
|
``duck`` (Mixes and lowers the audio volume) or ``cork`` (Pauses audio).
|
||||||
|
|
||||||
|
* *default_policy.policy.endpoints*
|
||||||
|
|
||||||
|
This is a Lua array with objects defining the endpoints that the user wants
|
||||||
|
to create.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
default_policy.endpoints = {
|
||||||
|
["endpoint.multimedia"] = {
|
||||||
|
["media.class"] = "Audio/Sink",
|
||||||
|
["role"] = "Multimedia",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
["endpoint.notifications"] = {
|
||||||
|
["media.class"] = "Audio/Sink",
|
||||||
|
["role"] = "Notification",
|
||||||
|
}
|
||||||
|
|
||||||
|
This example creates 2 endpoints, with names ``endpoint.multimedia`` and
|
||||||
|
``endpoint.notifications``; and assigned roles ``Multimedia`` and ``Notification``
|
||||||
|
respectively. Both endpoints have ``Audio/Sink`` media class, and so are only
|
||||||
|
used for playback.
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.. _resources_contributing:
|
.. _contributing:
|
||||||
|
|
||||||
Contributing to WirePlumber
|
Contributing to WirePlumber
|
||||||
===========================
|
===========================
|
||||||
|
|
@ -25,7 +25,7 @@ the indentation settings.
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|
||||||
See :ref:`resources_testing`
|
See :ref:`testing`
|
||||||
|
|
||||||
Running in gdb / valgrind / etc...
|
Running in gdb / valgrind / etc...
|
||||||
----------------------------------
|
----------------------------------
|
||||||
139
docs/rst/daemon-logging.rst
Normal file
139
docs/rst/daemon-logging.rst
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
.. _logging:
|
||||||
|
|
||||||
|
Debug Logging
|
||||||
|
=============
|
||||||
|
|
||||||
|
Getting debug messages on the command line is a matter of setting the
|
||||||
|
``WIREPLUMBER_DEBUG`` environment variable. The generic syntax is:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
WIREPLUMBER_DEBUG=level:category1,category2,...
|
||||||
|
|
||||||
|
``level`` can be one of ``CEWMIDT`` or a numerical log level as listed below.
|
||||||
|
In either case it defines the minimum debug level to show:
|
||||||
|
|
||||||
|
0. critical warnings and fatal errors (``C`` & ``E`` in the log)
|
||||||
|
1. warnings (``W``)
|
||||||
|
2. normal messages (``M``)
|
||||||
|
3. informational messages (``I``)
|
||||||
|
4. debug messages (``D``)
|
||||||
|
5. trace messages (``T``)
|
||||||
|
|
||||||
|
``category1,category2,...`` is an *optional* comma-separated list of debug
|
||||||
|
categories to show. Any categories not listed here will *not* appear in the log.
|
||||||
|
If no categories are specified, then all messages are printed.
|
||||||
|
|
||||||
|
Categories support
|
||||||
|
`glob style patterns <https://developer.gnome.org/glib/stable/glib-Glob-style-pattern-matching.html>`_
|
||||||
|
containing ``*`` and ``?``, for convenience.
|
||||||
|
|
||||||
|
Well known categories include:
|
||||||
|
|
||||||
|
- **wireplumber**: messages from the wireplumber daemon
|
||||||
|
- **pw**: messages from libpipewire & spa plugins
|
||||||
|
- **wp-***: messages from libwireplumber
|
||||||
|
- **wp-core**: messages from *WpCore*
|
||||||
|
- **wp-proxy**: messages from *WpProxy*
|
||||||
|
- ... and so on ...
|
||||||
|
- **m-***: messages from wireplumber modules
|
||||||
|
- **m-default-profile**: messages from *libwireplumber-module-default-profile*
|
||||||
|
- **m-default-routes**: messages from *libwireplumber-module-default-routes*
|
||||||
|
- ... and so on ...
|
||||||
|
- **script/***: messages from scripts
|
||||||
|
- **script/policy-node**: messages from the *policy-node.lua* script
|
||||||
|
- ... and so on ...
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
Show all messages:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
WIREPLUMBER_DEBUG=T
|
||||||
|
|
||||||
|
Show all messages up to the *debug* level (E, C, W, M, I & D), excluding *trace*:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
WIREPLUMBER_DEBUG=D
|
||||||
|
|
||||||
|
Show all messages up to the *message* level (E, C, W & M),
|
||||||
|
excluding *info*, *debug* & *trace*
|
||||||
|
(this is also the default when ``WIREPLUMBER_DEBUG`` is omitted):
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
WIREPLUMBER_DEBUG=2
|
||||||
|
|
||||||
|
Show all messages from the wireplumber library:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
WIREPLUMBER_DEBUG=T:wp-*
|
||||||
|
|
||||||
|
Show all messages from ``wp-registry``, libpipewire and all modules:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
WIREPLUMBER_DEBUG=T:wp-registry,pw,m-*
|
||||||
|
|
||||||
|
Relationship with the GLib log handler & G_MESSAGES_DEBUG
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
Older versions of WirePlumber used to use ``G_MESSAGES_DEBUG`` to control their
|
||||||
|
log output, which is the environment variable that affects GLib's default
|
||||||
|
log handler.
|
||||||
|
|
||||||
|
As of WirePlumber 0.3, ``G_MESSAGES_DEBUG`` is no longer used, since
|
||||||
|
libwireplumber replaces the default log handler.
|
||||||
|
|
||||||
|
If you are writing your own application based on libwireplumber, you can choose
|
||||||
|
if you want to replace this log handler using the flags passed to
|
||||||
|
:c:func:`wp_init`.
|
||||||
|
|
||||||
|
Relationship with the PipeWire log handler & PIPEWIRE_DEBUG
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
libpipewire uses the ``PIPEWIRE_DEBUG`` environment variable, with a similar syntax.
|
||||||
|
WirePlumber replaces the log handler of libpipewire with its own, rendering
|
||||||
|
``PIPEWIRE_DEBUG`` useless. Instead, you should use ``WIREPLUMBER_DEBUG`` and the
|
||||||
|
``pw`` category to control log messages from libpipewire & its plugins.
|
||||||
|
|
||||||
|
If you are writing your own application based on libwireplumber, you can choose
|
||||||
|
if you want to replace this log handler using the flags passed to
|
||||||
|
:c:func:`wp_init`.
|
||||||
|
|
||||||
|
Mapping of PipeWire debug levels to WirePlumber
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Both WirePlumber and PipeWire support 6 levels of debug logging, from 0 to 5
|
||||||
|
|
||||||
|
PipeWire uses a slightly different semantic for the first 3 levels:
|
||||||
|
|
||||||
|
===== ======== ===========
|
||||||
|
Level PipeWire WirePlumber
|
||||||
|
===== ======== ===========
|
||||||
|
0 no log Critical / fatal Errors
|
||||||
|
1 Errors Warnings
|
||||||
|
2 Warnings Messages
|
||||||
|
===== ======== ===========
|
||||||
|
|
||||||
|
When PipeWire log messages are printed by the WirePlumber log handler, the
|
||||||
|
level number stays the same and the semantic changes. PipeWire's errors are
|
||||||
|
printed in the ``W`` category and PipeWire's warnings are printed in the
|
||||||
|
``M`` category.
|
||||||
|
|
||||||
|
In WirePlumber's (actually GLib's) semantics, this feels more appropriate
|
||||||
|
because:
|
||||||
|
|
||||||
|
- GLib's errors are fatal (``abort()`` is called)
|
||||||
|
- GLib's critical warnings are assertion failures (i.e. programming mistakes,
|
||||||
|
not runtime errors)
|
||||||
|
- PipeWire's errors are neither fatal, nor programming mistakes; they are
|
||||||
|
just bad situations that are not meant to happen
|
||||||
|
- GLib's warnings are exactly that: bad runtime situations that are not meant
|
||||||
|
to happen, so mapping PipeWire errors to GLib warnings makes sense
|
||||||
|
- The **Messages** log level does not exist in PipeWire, so it can be used to
|
||||||
|
fill the gap for PipeWire warnings
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
.. _daemon_configuration:
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
WirePlumber is a heavily modular daemon. By itself, it doesn't do anything
|
|
||||||
except load its configured components. The actual management logic is
|
|
||||||
implemented inside those components.
|
|
||||||
|
|
||||||
At startup, WirePlumber reads its configuration file (combined with all the
|
|
||||||
fragments it may have) and loads the components specified in the selected
|
|
||||||
profile. This configures the operation context. Then, the components take over
|
|
||||||
and drive the entirety of the daemon's operation.
|
|
||||||
|
|
||||||
The sections below describe in more detail the configuration file format and
|
|
||||||
the various options available.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
configuration/conf_file.rst
|
|
||||||
configuration/components_and_profiles.rst
|
|
||||||
configuration/configuration_option_types.rst
|
|
||||||
configuration/modifying_configuration.rst
|
|
||||||
configuration/migration.rst
|
|
||||||
configuration/features.rst
|
|
||||||
configuration/settings.rst
|
|
||||||
configuration/alsa.rst
|
|
||||||
configuration/bluetooth.rst
|
|
||||||
configuration/access.rst
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
.. _config_access:
|
|
||||||
|
|
||||||
Access configuration
|
|
||||||
====================
|
|
||||||
|
|
||||||
WirePlumber includes a "client access" policy which defines access control
|
|
||||||
rules for PipeWire clients.
|
|
||||||
|
|
||||||
Rules
|
|
||||||
-----
|
|
||||||
|
|
||||||
This policy can be configured with rules that can be used to match clients and
|
|
||||||
apply default permissions to them.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
access.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
access = "flatpak"
|
|
||||||
media.category = "Manager"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
access = "flatpak-manager"
|
|
||||||
default_permissions = "all",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
access = "flatpak"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
default_permissions = "rx"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Possible permissions are any combination of:
|
|
||||||
|
|
||||||
* ``r``: client is allowed to **read** objects, i.e. "see" them on the registry
|
|
||||||
and list their properties
|
|
||||||
* ``w``: client is allowed to **write** objects, i.e. call methods that modify
|
|
||||||
their state
|
|
||||||
* ``x``: client is allowed to **execute** methods on objects; the ``w`` flag
|
|
||||||
must also be present to call methods that modify the object
|
|
||||||
* ``m``: client is allowed to set **metadata** on objects
|
|
||||||
* ``l``: nodes of this client are allowed to **link** to other nodes that the
|
|
||||||
client can't "see" (i.e. the client doesn't have ``r`` permission on them)
|
|
||||||
|
|
||||||
The special value ``all`` is also supported and it is synonym for ``rwxm``
|
|
||||||
|
|
||||||
Permission Managers
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
For more advanced use cases, WirePlumber supports *permission managers* that can
|
|
||||||
apply per-object permissions dynamically based on rules and object interests.
|
|
||||||
Permission managers are defined in the ``access.permission-managers`` section
|
|
||||||
and then referenced by name in ``access.rules``.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
access.permission-managers = [
|
|
||||||
{
|
|
||||||
name = "custom"
|
|
||||||
default_permissions = "all"
|
|
||||||
core_permissions = "rx"
|
|
||||||
rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
media.class = "Audio/Source"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
set-permissions = "-"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
access.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
application.name = "paplay"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
permission_manager_name = "custom"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Each permission manager supports the following properties:
|
|
||||||
|
|
||||||
* ``name``: (required) a unique name used to reference the manager from
|
|
||||||
``access.rules``
|
|
||||||
* ``default_permissions``: the fallback permissions applied to all objects
|
|
||||||
that don't match any rule (applied as ``PW_ID_ANY``)
|
|
||||||
* ``core_permissions``: permissions applied specifically to the PipeWire core
|
|
||||||
object (``PW_ID_CORE``, ID 0). This is useful when you want to allow a
|
|
||||||
client to interact with the core (e.g. enumerate objects, subscribe to
|
|
||||||
events) while restricting access to individual objects. If not set, the
|
|
||||||
``default_permissions`` value is used for the core as well.
|
|
||||||
* ``rules``: a list of match rules with ``set-permissions`` actions that
|
|
||||||
grant specific permissions to objects matching the given constraints
|
|
||||||
|
|
||||||
When both ``default_permissions`` and ``permission_manager_name`` are set in
|
|
||||||
a rule's ``update-props`` action, ``default_permissions`` takes precedence and
|
|
||||||
the permission manager is ignored.
|
|
||||||
|
|
@ -1,485 +0,0 @@
|
||||||
.. _config_alsa:
|
|
||||||
|
|
||||||
ALSA configuration
|
|
||||||
==================
|
|
||||||
|
|
||||||
One of the components of WirePlumber is the ALSA monitor. This monitor is
|
|
||||||
responsible for creating PipeWire devices and nodes for all the ALSA cards that
|
|
||||||
are available on the system. It also manages the configuration of these devices.
|
|
||||||
|
|
||||||
The ALSA monitor is enabled by default and can be disabled using the
|
|
||||||
``monitor.alsa`` :ref:`feature <config_features>` in the configuration file.
|
|
||||||
|
|
||||||
The monitor, as with all device monitors, is implemented as a SPA plugin and is
|
|
||||||
part of PipeWire. WirePlumber merely loads the plugin and lets it do its work.
|
|
||||||
The plugin then monitors UDev and creates device and node objects for all the
|
|
||||||
ALSA cards that are available on the system.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
One thing worth remembering here is that in ALSA, a "card" represents a
|
|
||||||
physical sound controller device, and a "device" is a logical access point
|
|
||||||
that represents a set of inputs and/or outputs that are part of the card. In
|
|
||||||
PipeWire, a "device" is the direct equivalent of an ALSA "card" and a "node"
|
|
||||||
is almost equivalent (close, but not quite) of an ALSA "device".
|
|
||||||
|
|
||||||
Properties
|
|
||||||
----------
|
|
||||||
|
|
||||||
The ALSA monitor SPA plugin (``api.alsa.enum.udev``) supports properties that
|
|
||||||
can be used to configure it when it is loaded. These properties can be set in
|
|
||||||
the ``monitor.alsa.properties`` section of the WirePlumber configuration file.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.alsa.properties = {
|
|
||||||
alsa.use-acp = true
|
|
||||||
}
|
|
||||||
|
|
||||||
.. describe:: alsa.use-acp
|
|
||||||
|
|
||||||
A boolean that controls whether the ACP (alsa card profile) code is to be
|
|
||||||
the default manager of the device. This will probe the device and configure
|
|
||||||
the available profiles, ports and mixer settings. The code to do this is
|
|
||||||
taken directly from PulseAudio and provides devices that look and feel
|
|
||||||
exactly like the PulseAudio devices.
|
|
||||||
|
|
||||||
Rules
|
|
||||||
-----
|
|
||||||
|
|
||||||
When device and node objects are created by the ALSA monitor, they can be
|
|
||||||
configured using rules. These rules allow matching the existing properties of
|
|
||||||
these objects and updating them with new values. This is the main way of
|
|
||||||
configuring ALSA device settings.
|
|
||||||
|
|
||||||
These rules can be set in the ``monitor.alsa.rules`` section of the WirePlumber
|
|
||||||
configuration file.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.alsa.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
# This matches the value of the 'device.name' property of the device.
|
|
||||||
device.name = "~alsa_card.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
# Apply all the desired device settings here.
|
|
||||||
api.alsa.use-acp = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
# This matches the value of the 'node.name' property of the node.
|
|
||||||
{
|
|
||||||
node.name = "~alsa_output.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
# Apply all the desired node specific settings here.
|
|
||||||
update-props = {
|
|
||||||
node.nick = "My Node"
|
|
||||||
priority.driver = 100
|
|
||||||
session.suspend-timeout-seconds = 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Device properties
|
|
||||||
^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The following properties can be configured on devices created by the monitor:
|
|
||||||
|
|
||||||
.. describe:: api.alsa.use-acp
|
|
||||||
|
|
||||||
Use the ACP (alsa card profile) code to manage this device. This will probe
|
|
||||||
the device and configure the available profiles, ports and mixer settings.
|
|
||||||
The code to do this is taken directly from PulseAudio and provides devices
|
|
||||||
that look and feel exactly like the PulseAudio devices.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: api.alsa.use-ucm
|
|
||||||
|
|
||||||
When ACP is enabled and a UCM configuration is available for a device, by
|
|
||||||
default it is used instead of the ACP profiles. This option allows you to
|
|
||||||
disable this and use the ACP profiles instead.
|
|
||||||
|
|
||||||
This option does nothing if ``api.alsa.use-acp`` is set to ``false``.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: api.alsa.soft-mixer
|
|
||||||
|
|
||||||
Setting this option to ``true`` will disable the hardware mixer for volume
|
|
||||||
control and mute. All volume handling will then use software volume and mute,
|
|
||||||
leaving the hardware mixer untouched. The hardware mixer will still be used
|
|
||||||
to mute unused audio paths in the device.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: api.alsa.ignore-dB
|
|
||||||
|
|
||||||
Setting this option to ``true`` will ignore the decibel setting configured by
|
|
||||||
the driver. Use this when the driver reports wrong settings.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: device.profile-set
|
|
||||||
|
|
||||||
This option can be used to select a custom ACP profile-set name for the
|
|
||||||
device. This can be configured in UDev rules, but it can also be specified
|
|
||||||
here. The default is to use "default.conf".
|
|
||||||
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
.. describe:: device.profile
|
|
||||||
|
|
||||||
The initial active profile name. The default is to start from the "Off"
|
|
||||||
profile and then let WirePlumber select the best profile based on its
|
|
||||||
policy.
|
|
||||||
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
.. describe:: api.acp.auto-profile
|
|
||||||
|
|
||||||
Automatically select the best profile for the device. Normally this option is
|
|
||||||
disabled because WirePlumber will manage the profile of the device.
|
|
||||||
WirePlumber can save and load previously selected profiles. Enable this in
|
|
||||||
custom configurations where the relevant WirePlumber components are disabled.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: api.acp.auto-port
|
|
||||||
|
|
||||||
Automatically select the highest priority port that is available ("port" is a
|
|
||||||
PulseAudio/ACP term, the equivalent of a "Route" in PipeWire). This is by
|
|
||||||
default disabled because WirePlumber handles the task of selecting and
|
|
||||||
restoring Routes. Enable this in custom configurations where the relevant
|
|
||||||
WirePlumber components are disabled.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: api.acp.probe-rate
|
|
||||||
|
|
||||||
Sets the samplerate used for probing the ALSA devices and collecting the
|
|
||||||
profiles and ports.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: api.acp.pro-channels
|
|
||||||
|
|
||||||
Sets the number of channels to use when probing the "Pro Audio" profile.
|
|
||||||
Normally, the maximum amount of channels will be used but with this setting
|
|
||||||
this can be reduced, which can make it possible to use other samplerates on
|
|
||||||
some devices.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
Some of the other properties that can be configured on devices:
|
|
||||||
|
|
||||||
.. describe:: device.nick
|
|
||||||
|
|
||||||
A short name for the device.
|
|
||||||
|
|
||||||
.. describe:: device.description
|
|
||||||
|
|
||||||
A longer, user-friendly name of the device. This will show up in most
|
|
||||||
user interfaces as the device's name.
|
|
||||||
|
|
||||||
.. describe:: device.disabled
|
|
||||||
|
|
||||||
Disables the device. PipeWire will remove it from the list of cards or
|
|
||||||
devices.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
Node properties
|
|
||||||
^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The following properties can be configured on nodes created by the monitor:
|
|
||||||
|
|
||||||
.. describe:: priority.driver
|
|
||||||
|
|
||||||
This configures the node driver priority. Nodes with higher priority will be
|
|
||||||
used as a driver in the graph. Other nodes with lower priority will have to
|
|
||||||
resample to the driver node when they are joined in the same graph. The
|
|
||||||
default value is set based on some heuristics.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: priority.session
|
|
||||||
|
|
||||||
This configures the priority of the node when selecting a default node
|
|
||||||
(default sink/source as a link target for streams). Higher priority nodes
|
|
||||||
will be more likely candidates for becoming the default node.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
By default, sources have a ``priority.session`` value around 1600-2000 and
|
|
||||||
sinks have a value around 600-1000. If you are increasing the priority of
|
|
||||||
a sink, it is **not advised** to use a value higher than 1500, as it may
|
|
||||||
cause a sink's monitor to be selected as the default source.
|
|
||||||
|
|
||||||
.. describe:: node.pause-on-idle
|
|
||||||
|
|
||||||
Pause the node when nothing is linked to it anymore. This is by default false
|
|
||||||
because some devices make a "pop" sound when they are opened/closed.
|
|
||||||
The node will normally pause and suspend after a timeout (see below).
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: session.suspend-timeout-seconds
|
|
||||||
|
|
||||||
This option configures a different suspend timeout on the node. By default
|
|
||||||
this is ``5`` seconds. For some devices (HiFi amplifiers, for example) it
|
|
||||||
might make sense to set a higher timeout because they might require some time
|
|
||||||
to restart after being idle.
|
|
||||||
|
|
||||||
A value of ``0`` disables suspend for a node and will leave the ALSA device
|
|
||||||
busy. The device can then be manually suspended with
|
|
||||||
``pactl suspend-sink|source``.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: audio.format
|
|
||||||
|
|
||||||
The sample format of the device. By default, PipeWire will use a 32 bits
|
|
||||||
sample format but a different format can be set here.
|
|
||||||
|
|
||||||
:Type: string (``"S16LE"``, ``"S32LE"``, ``"F32LE"``, ...)
|
|
||||||
|
|
||||||
.. describe:: audio.rate
|
|
||||||
|
|
||||||
The sample rate of the device. By default, the ALSA device will be configured
|
|
||||||
with the same samplerate as the global graph. If this is not supported, or a
|
|
||||||
custom value is set here, resampling will be used to match the graph rate.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: audio.channels
|
|
||||||
|
|
||||||
The number of channels of the device. By default the channels and their
|
|
||||||
position are determined by the selected device profile. You can override
|
|
||||||
this setting here.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: audio.position
|
|
||||||
|
|
||||||
The position of the channels. By default the number of channels and their
|
|
||||||
position are determined by the selected device profile. You can override
|
|
||||||
this setting here and optionally swap or reconfigure the channel positions.
|
|
||||||
|
|
||||||
:Type: array of strings (example: ``["FL", "FR", "LFE", "FC", "RL", "RR"]``)
|
|
||||||
|
|
||||||
.. describe:: api.alsa.use-chmap
|
|
||||||
|
|
||||||
Use the channel map as reported by the driver. This is disabled by default
|
|
||||||
because it is often wrong and the ACP code handles this better.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: api.alsa.disable-mmap
|
|
||||||
|
|
||||||
Disable the use of mmap for the ALSA device. By default, PipeWire will access
|
|
||||||
the memory of the device using mmap. This can be disabled and force the usage
|
|
||||||
of the slower read and write access modes, in case the mmap support of the
|
|
||||||
device is not working properly.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: channelmix.normalize
|
|
||||||
|
|
||||||
Normalize the channel volumes when mixing & resampling, making sure that the
|
|
||||||
original 0 dB level is preserved so that nothing sounds wildly
|
|
||||||
quieter/louder. This is disabled by default.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: channelmix.mix-lfe
|
|
||||||
|
|
||||||
Creates a "center" channel for X.0 recordings from the front stereo on X.1
|
|
||||||
setups and pushes some low-frequency/bass from the "center" of X.1 recordings
|
|
||||||
into the front stereo on X.0 setups. This is disabled by default.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: monitor.channel-volumes
|
|
||||||
|
|
||||||
By default, the volume of the sink/source does not influence the volume on
|
|
||||||
the monitor ports. Set this option to true to change this. PulseAudio has
|
|
||||||
inconsistent behaviour regarding this option, it applies channel-volumes only
|
|
||||||
when the sink/source is using software volumes.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: node.disabled
|
|
||||||
|
|
||||||
Disables the node. Pipewire will remove it from the list of the nodes.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
ALSA buffer properties
|
|
||||||
......................
|
|
||||||
|
|
||||||
PipeWire by default uses a timer to consume and produce samples to/from ALSA
|
|
||||||
devices. After every timeout, it queries the hardware pointers of the device and
|
|
||||||
uses this information to set a new timeout. This works well for most devices,
|
|
||||||
but there is a class of devices, so called "batch" devices, that need extra
|
|
||||||
buffering and timing tweaks to work properly. This is because batch devices only
|
|
||||||
get their hardware pointers updated after each hardware interrupt. When the
|
|
||||||
hardware interrupt frequency and the timer frequency are aligned, it is possible
|
|
||||||
for the hardware pointers to be updated just after the timer has expired,
|
|
||||||
resulting in sometimes wrong timing information being returned by the query. In
|
|
||||||
contrast, non-batch devices get pointer updates independent of the interrupt.
|
|
||||||
|
|
||||||
This means that for batch devices we need to set the interrupt at a sufficiently
|
|
||||||
high frequency, at the cost of CPU usage, while for non-batch devices we want to
|
|
||||||
set the interrupt frequency as low as possible to save CPU. For batch devices
|
|
||||||
we also need to take the extra buffering into account caused by the delayed
|
|
||||||
updates of the hardware pointers.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Most USB devices are batch devices and will be handled as such by PipeWire by
|
|
||||||
default.
|
|
||||||
|
|
||||||
There are 2 tunable parameters to control the buffering and timeouts in a
|
|
||||||
device:
|
|
||||||
|
|
||||||
.. describe:: api.alsa.period-size
|
|
||||||
|
|
||||||
This sets the device interrupt to every period-size samples for non-batch
|
|
||||||
devices and to half of this for batch devices. For batch devices, the other
|
|
||||||
half of the period-size is used as extra buffering to compensate for the
|
|
||||||
delayed update. So, for batch devices, there is an additional period-size/2
|
|
||||||
delay. It makes sense to lower the period-size for batch devices to reduce
|
|
||||||
this delay.
|
|
||||||
|
|
||||||
:Type: integer (samples)
|
|
||||||
|
|
||||||
.. describe:: api.alsa.headroom
|
|
||||||
|
|
||||||
This adds extra delay between the hardware pointers and software pointers.
|
|
||||||
In most cases this can be set to 0. For very bad devices or emulated devices
|
|
||||||
(like in a VM) it might be necessary to increase the headroom value.
|
|
||||||
|
|
||||||
:Type: integer (samples)
|
|
||||||
|
|
||||||
.. describe:: api.alsa.period-num
|
|
||||||
|
|
||||||
This configures the number of periods in the hardware buffer, which controls
|
|
||||||
its size. Note that this is multiplied by the period of the device to
|
|
||||||
determine the size, so for batch devices, the total buffer size is
|
|
||||||
effectively period-num * period-size/2.
|
|
||||||
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
In summary, this is the overview of buffering and timings:
|
|
||||||
|
|
||||||
============== ============================================ ==========================================
|
|
||||||
Property Batch Non-Batch
|
|
||||||
============== ============================================ ==========================================
|
|
||||||
IRQ Frequency api.alsa.period-size/2 api.alsa.period-size
|
|
||||||
Extra Delay api.alsa.headroom + api.alsa.period-size/2 api.alsa.headroom
|
|
||||||
Buffer Size api.alsa.period-num * api.alsa.period-size/2 api.alsa.period-num * api.alsa.period-size
|
|
||||||
============== ============================================ ==========================================
|
|
||||||
|
|
||||||
Finally, it is possible to disable the batch device tweaks with:
|
|
||||||
|
|
||||||
.. describe:: api.alsa.disable-batch
|
|
||||||
|
|
||||||
This disables the batch device tweaks. It removes the extra delay added of
|
|
||||||
period-size/2 if the device can support this. For batch devices it is also a
|
|
||||||
good idea to lower the period-size (and increase the IRQ frequency) to get
|
|
||||||
smaller batch updates and lower latency.
|
|
||||||
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
ALSA extra latency properties
|
|
||||||
.............................
|
|
||||||
|
|
||||||
Extra internal delay in the DAC and ADC converters of the device itself can be
|
|
||||||
set with the ``latency.internal.*`` properties:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
latency.internal.rate = 256
|
|
||||||
latency.internal.ns = 0
|
|
||||||
|
|
||||||
You can configure a latency in samples (relative to rate with
|
|
||||||
``latency.internal.rate``) or in nanoseconds (``latency.internal.ns``).
|
|
||||||
This value will be added to the total reported latency by the node of the device.
|
|
||||||
|
|
||||||
You can use a tool like ``jack_iodelay`` to get the number of samples of
|
|
||||||
internal latency of your device.
|
|
||||||
|
|
||||||
This property is also adjustable at runtime with the ``ProcessLatency`` param.
|
|
||||||
You will need to find the id of the Node you want to change. For example:
|
|
||||||
Query the current internal latency of an ALSA node with id 58:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ pw-cli e 58 ProcessLatency
|
|
||||||
Object: size 80, type Spa:Pod:Object:Param:ProcessLatency (262156), id Spa:Enum:ParamId:ProcessLatency (16)
|
|
||||||
Prop: key Spa:Pod:Object:Param:ProcessLatency:quantum (1), flags 00000000
|
|
||||||
Float 0.000000
|
|
||||||
Prop: key Spa:Pod:Object:Param:ProcessLatency:rate (2), flags 00000000
|
|
||||||
Int 0
|
|
||||||
Prop: key Spa:Pod:Object:Param:ProcessLatency:ns (3), flags 00000000
|
|
||||||
Long 0
|
|
||||||
|
|
||||||
Set the internal latency to 256 samples:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ pw-cli s 58 ProcessLatency '{ rate = 256 }'
|
|
||||||
Object: size 32, type Spa:Pod:Object:Param:ProcessLatency (262156), id Spa:Enum:ParamId:ProcessLatency (16)
|
|
||||||
Prop: key Spa:Pod:Object:Param:ProcessLatency:rate (2), flags 00000000
|
|
||||||
Int 256
|
|
||||||
remote 0 node 58 changed
|
|
||||||
remote 0 port 70 changed
|
|
||||||
remote 0 port 72 changed
|
|
||||||
remote 0 port 74 changed
|
|
||||||
remote 0 port 76 changed
|
|
||||||
|
|
||||||
Startup tweaks
|
|
||||||
..............
|
|
||||||
|
|
||||||
.. describe:: api.alsa.start-delay
|
|
||||||
|
|
||||||
Some devices need some time before they can report accurate hardware pointer
|
|
||||||
positions. In those cases, an extra start delay can be added to compensate
|
|
||||||
for this startup delay. This sets the startup delay in samples. The default
|
|
||||||
is 0.
|
|
||||||
|
|
||||||
:Type: integer (samples)
|
|
||||||
|
|
||||||
IEC958 (S/PDIF) passthrough
|
|
||||||
...........................
|
|
||||||
|
|
||||||
.. describe:: iec958.codecs
|
|
||||||
|
|
||||||
S/PDIF passthrough will only be enabled when the accepted codecs are configured
|
|
||||||
on the ALSA device. This can be done by setting the list of supported codecs
|
|
||||||
on this property.
|
|
||||||
|
|
||||||
Note that it is possible to also configure this property at runtime, either
|
|
||||||
with tools like pavucontrol or with the ``pw-cli`` tool, like this:
|
|
||||||
``pw-cli s <node-id> Props '{ iec958Codecs : [ PCM ] }'``
|
|
||||||
|
|
||||||
:Type: array of strings (example: ``[ "PCM", "DTS", "AC3", "EAC3", "TrueHD", "DTS-HD" ]``)
|
|
||||||
|
|
@ -1,440 +0,0 @@
|
||||||
.. _config_bluetooth:
|
|
||||||
|
|
||||||
Bluetooth configuration
|
|
||||||
=======================
|
|
||||||
|
|
||||||
Bluetooth audio and MIDI devices are managed by the BlueZ and BlueZ-MIDI
|
|
||||||
monitors, respectively.
|
|
||||||
|
|
||||||
Both monitors are enabled by default and can be disabled using the
|
|
||||||
``monitor.bluez`` and ``monitor.bluez-midi`` :ref:`features <config_features>`
|
|
||||||
in the configuration file.
|
|
||||||
|
|
||||||
As with all device monitors, both of these monitors are implemented as SPA
|
|
||||||
plugins and are part of PipeWire. WirePlumber merely loads the plugins and lets
|
|
||||||
them do their work. These plugins then monitor the BlueZ system-wide D-Bus
|
|
||||||
service and create device and node objects for all the connected Bluetooth audio
|
|
||||||
and MIDI devices.
|
|
||||||
|
|
||||||
Logind integration
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The BlueZ monitors are integrated with logind to ensure that only one user at a
|
|
||||||
time can use the Bluetooth audio devices. This is because on most Linux desktop
|
|
||||||
systems, the graphical login manager (GDM, SDDM, etc.) is running as a separate
|
|
||||||
user and runs its own instance of PipeWire and Wireplumber. This means that if a
|
|
||||||
user logs in graphically, the Bluetooth audio devices will be automatically
|
|
||||||
grabbed by the PipeWire/WirePlumber instance of the graphical login manager,
|
|
||||||
and the user that logs in will not get access to them.
|
|
||||||
|
|
||||||
To overcome this, the BlueZ monitors are integrated with logind and are only
|
|
||||||
allowed to create device and node objects for Bluetooth audio devices if the
|
|
||||||
user is currently on the "active" logind session.
|
|
||||||
|
|
||||||
In some cases, however, this behavior is not desired. For example, if you
|
|
||||||
manually switch to a TTY and log in there, you may want to keep the Bluetooth
|
|
||||||
audio devices connected to the now inactive graphical session. Or you may want
|
|
||||||
to have a dedicated user that is always allowed to use the Bluetooth audio
|
|
||||||
devices, regardless of the active logind session, for example for a (possibly
|
|
||||||
headless) music player daemon.
|
|
||||||
|
|
||||||
To disable this behavior, you can set the ``monitor.bluez.seat-monitoring``
|
|
||||||
:ref:`feature <config_features>` to ``disabled``.
|
|
||||||
|
|
||||||
Example configuration :ref:`fragment <config_conf_file_fragments>` file:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.profiles = {
|
|
||||||
main = {
|
|
||||||
monitor.bluez.seat-monitoring = disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
If logind is not installed on the system, this functionality is disabled
|
|
||||||
automatically.
|
|
||||||
|
|
||||||
Monitor Properties
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The BlueZ monitor SPA plugin (``api.bluez5.enum.dbus``) supports properties that
|
|
||||||
can be used to configure it when it is loaded. These properties can be set in
|
|
||||||
the ``monitor.bluez.properties`` section of the WirePlumber configuration file.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.bluez.properties = {
|
|
||||||
bluez5.roles = [ a2dp_sink a2dp_source bap_sink bap_source hsp_hs hsp_ag hfp_hf hfp_ag ]
|
|
||||||
bluez5.codecs = [ sbc sbc_xq aac ]
|
|
||||||
bluez5.enable-sbc-xq = true
|
|
||||||
bluez5.hfphsp-backend = "native"
|
|
||||||
}
|
|
||||||
|
|
||||||
.. describe:: bluez5.roles
|
|
||||||
|
|
||||||
Enabled roles.
|
|
||||||
|
|
||||||
Currently some headsets (e.g. Sony WH-1000XM3) do not work with both
|
|
||||||
``hsp_ag`` and ``hfp_ag`` enabled, so by default we enable only HFP.
|
|
||||||
|
|
||||||
Supported roles:
|
|
||||||
|
|
||||||
- ``hsp_hs`` (HSP Headset)
|
|
||||||
- ``hsp_ag`` (HSP Audio Gateway),
|
|
||||||
- ``hfp_hf`` (HFP Hands-Free),
|
|
||||||
- ``hfp_ag`` (HFP Audio Gateway)
|
|
||||||
- ``a2dp_sink`` (A2DP Audio Sink)
|
|
||||||
- ``a2dp_source`` (A2DP Audio Source)
|
|
||||||
- ``bap_sink`` (LE Audio Basic Audio Profile Sink)
|
|
||||||
- ``bap_source`` (LE Audio Basic Audio Profile Source)
|
|
||||||
|
|
||||||
:Default value: ``[ a2dp_sink a2dp_source bap_sink bap_source hfp_hf hfp_ag ]``
|
|
||||||
:Type: array of strings
|
|
||||||
|
|
||||||
.. describe:: bluez5.codecs
|
|
||||||
|
|
||||||
Enabled A2DP codecs.
|
|
||||||
|
|
||||||
Supported codecs: ``sbc``, ``sbc_xq``, ``aac``, ``ldac``, ``aptx``,
|
|
||||||
``aptx_hd``, ``aptx_ll``, ``aptx_ll_duplex``, ``faststream``,
|
|
||||||
``faststream_duplex``, ``lc3plus_h3``, ``opus_05``, ``opus_05_51``,
|
|
||||||
``opus_05_71``, ``opus_05_duplex``, ``opus_05_pro``, ``lc3``.
|
|
||||||
|
|
||||||
:Default value: all available codecs
|
|
||||||
:Type: array of strings
|
|
||||||
|
|
||||||
.. describe:: bluez5.enable-msbc
|
|
||||||
|
|
||||||
Enable mSBC codec (wideband speech codec for HFP/HSP).
|
|
||||||
|
|
||||||
This does not work on all headsets, so it is enabled based on the hardware
|
|
||||||
quirks database. By explicitly setting this option you can force it to be
|
|
||||||
enabled or disabled regardless.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: bluez5.enable-sbc-xq
|
|
||||||
|
|
||||||
Enable SBC-XQ codec (high quality SBC codec for A2DP).
|
|
||||||
|
|
||||||
This does not work on all headsets, so it is enabled based on the hardware
|
|
||||||
quirks database. By explicitly setting this option you can force it to be
|
|
||||||
enabled or disabled regardless.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: bluez5.enable-hw-volume
|
|
||||||
|
|
||||||
Enable hardware volume controls.
|
|
||||||
|
|
||||||
This does not work on all headsets, so it is enabled based on the hardware
|
|
||||||
quirks database. By explicitly setting this option you can force it to be
|
|
||||||
enabled or disabled regardless.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: bluez5.hfphsp-backend
|
|
||||||
|
|
||||||
HFP/HSP backend.
|
|
||||||
|
|
||||||
Available values: ``any``, ``none``, ``hsphfpd``, ``ofono`` or ``native``.
|
|
||||||
|
|
||||||
:Default value: ``native``
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
.. describe:: bluez5.hfphsp-backend-native-modem
|
|
||||||
|
|
||||||
Modem to use for native HFP/HSP backend ModemManager support. When enabled,
|
|
||||||
PipeWire will forward HFP commands to the specified ModemManager device.
|
|
||||||
This corresponds to the 'Device' property of the
|
|
||||||
``org.freedesktop.ModemManager1.Modem`` interface. May also be ``any`` to
|
|
||||||
use any available modem device.
|
|
||||||
|
|
||||||
:Default value: ``none``
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
.. describe:: bluez5.hw-offload-sco
|
|
||||||
|
|
||||||
HFP/HSP hardware offload SCO support.
|
|
||||||
|
|
||||||
Using this feature requires a custom WirePlumber script that handles audio
|
|
||||||
routing in a platform-specific way. See ``tests/examples/bt-pinephone.lua``
|
|
||||||
for an example.
|
|
||||||
|
|
||||||
:Default value: ``false``
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: bluez5.default.rate
|
|
||||||
|
|
||||||
The default audio rate for the A2DP codec configuration.
|
|
||||||
|
|
||||||
:Default value: ``48000``
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: bluez5.default.channels
|
|
||||||
|
|
||||||
The default number of channels for the A2DP codec configuration.
|
|
||||||
|
|
||||||
:Default value: ``2``
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: bluez5.dummy-avrcp-player
|
|
||||||
|
|
||||||
Register dummy AVRCP player. Some devices have wrongly functioning volume or
|
|
||||||
playback controls if this is not enabled. Disabled by default.
|
|
||||||
|
|
||||||
:Default value: ``false``
|
|
||||||
:Type: boolean
|
|
||||||
|
|
||||||
.. describe:: Opus Pro Audio mode settings
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
bluez5.a2dp.opus.pro.channels = 3
|
|
||||||
bluez5.a2dp.opus.pro.coupled-streams = 1
|
|
||||||
bluez5.a2dp.opus.pro.locations = [ FL,FR,LFE ]
|
|
||||||
bluez5.a2dp.opus.pro.max-bitrate = 600000
|
|
||||||
bluez5.a2dp.opus.pro.frame-dms = 50
|
|
||||||
bluez5.a2dp.opus.pro.bidi.channels = 1
|
|
||||||
bluez5.a2dp.opus.pro.bidi.coupled-streams = 0
|
|
||||||
bluez5.a2dp.opus.pro.bidi.locations = [ FC ]
|
|
||||||
bluez5.a2dp.opus.pro.bidi.max-bitrate = 160000
|
|
||||||
bluez5.a2dp.opus.pro.bidi.frame-dms = 400
|
|
||||||
|
|
||||||
Options for the PipeWire-specific multichannel Opus codec, which can be used
|
|
||||||
to transport audio over Bluetooth between devices running PipeWire.
|
|
||||||
|
|
||||||
MIDI Monitor Properties
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
The BlueZ MIDI monitor SPA plugin (``api.bluez5.midi.enum``) may, in the future,
|
|
||||||
support properties that can be used to configure it when it is loaded. These
|
|
||||||
properties can be set in the ``monitor.bluez-midi.properties`` section of the
|
|
||||||
WirePlumber configuration file. At the moment of writing, there are no
|
|
||||||
properties that can be set there.
|
|
||||||
|
|
||||||
In addition, the BlueZ MIDI monitor supports a list of MIDI server node names
|
|
||||||
that can be used to create Bluetooth LE MIDI service instances. These
|
|
||||||
server node names can be set in the ``monitor.bluez-midi.servers`` section of
|
|
||||||
the WirePlumber configuration file.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.bluez-midi.servers = [ "bluez_midi.server" ]
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Typical BLE MIDI instruments have one service instance, so adding more than
|
|
||||||
one here may confuse some clients.
|
|
||||||
|
|
||||||
Rules
|
|
||||||
-----
|
|
||||||
|
|
||||||
When device and node objects are created by the BlueZ monitor, they can be
|
|
||||||
configured using rules. These rules allow matching the existing properties of
|
|
||||||
these objects and updating them with new values. This is the main way of
|
|
||||||
configuring Bluetooth device settings.
|
|
||||||
|
|
||||||
These rules can be set in the ``monitor.bluez.rules`` section of the WirePlumber
|
|
||||||
configuration file.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.bluez.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
## This matches all bluetooth devices.
|
|
||||||
device.name = "~bluez_card.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
bluez5.auto-connect = [ hfp_hf hsp_hs a2dp_sink hfp_ag hsp_ag a2dp_source ]
|
|
||||||
bluez5.hw-volume = [ hfp_hf hsp_hs a2dp_sink hfp_ag hsp_ag a2dp_source ]
|
|
||||||
bluez5.a2dp.ldac.quality = "auto"
|
|
||||||
bluez5.a2dp.aac.bitratemode = 0
|
|
||||||
bluez5.a2dp.opus.pro.application = "audio"
|
|
||||||
bluez5.a2dp.opus.pro.bidi.application = "audio"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
## Matches all sources.
|
|
||||||
node.name = "~bluez_input.*"
|
|
||||||
}
|
|
||||||
{
|
|
||||||
## Matches all sinks.
|
|
||||||
node.name = "~bluez_output.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
bluez5.media-source-role = "input"
|
|
||||||
|
|
||||||
# Common node & audio adapter properties may also be set here
|
|
||||||
node.nick = "My Node"
|
|
||||||
priority.driver = 100
|
|
||||||
priority.session = 100
|
|
||||||
node.pause-on-idle = false
|
|
||||||
resample.quality = 4
|
|
||||||
channelmix.normalize = false
|
|
||||||
channelmix.mix-lfe = false
|
|
||||||
session.suspend-timeout-seconds = 5
|
|
||||||
monitor.channel-volumes = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Device properties
|
|
||||||
^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The following properties can be set on device objects:
|
|
||||||
|
|
||||||
.. describe:: bluez5.auto-connect
|
|
||||||
|
|
||||||
Auto-connect device profiles on start up or when only partial profiles have
|
|
||||||
connected. Disabled by default if the property is not specified.
|
|
||||||
|
|
||||||
Supported values are: ``hfp_hf``, ``hsp_hs``, ``a2dp_sink``, ``hfp_ag``,
|
|
||||||
``hsp_ag`` and ``a2dp_source``.
|
|
||||||
|
|
||||||
:Default value: ``[]``
|
|
||||||
:Type: array of strings
|
|
||||||
|
|
||||||
.. describe:: bluez5.hw-volume
|
|
||||||
|
|
||||||
Enable hardware volume controls on these profiles.
|
|
||||||
|
|
||||||
Supported values are: ``hfp_hf``, ``hsp_hs``, ``a2dp_sink``, ``hfp_ag``,
|
|
||||||
``hsp_ag`` and ``a2dp_source``.
|
|
||||||
|
|
||||||
:Default value: ``[ hfp_ag hsp_ag a2dp_source ]``
|
|
||||||
:Type: array of strings
|
|
||||||
|
|
||||||
.. describe:: bluez5.a2dp.ldac.quality
|
|
||||||
|
|
||||||
LDAC encoding quality.
|
|
||||||
|
|
||||||
Available values: ``auto`` (Adaptive Bitrate, default), ``hq`` (High
|
|
||||||
Quality, 990/909kbps), ``sq`` (Standard Quality, 660/606kbps) and ``mq``
|
|
||||||
(Mobile use Quality, 330/303kbps).
|
|
||||||
|
|
||||||
:Default value: ``auto``
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
.. describe:: bluez5.a2dp.aac.bitratemode
|
|
||||||
|
|
||||||
AAC variable bitrate mode.
|
|
||||||
|
|
||||||
Available values: 0 (cbr, default), 1-5 (quality level).
|
|
||||||
|
|
||||||
:Default value: ``0``
|
|
||||||
:Type: integer
|
|
||||||
|
|
||||||
.. describe:: bluez5.a2dp.opus.pro.application
|
|
||||||
|
|
||||||
Opus Pro Audio encoding mode.
|
|
||||||
|
|
||||||
Available values: ``audio``, ``voip``, ``lowdelay``.
|
|
||||||
|
|
||||||
:Default value: ``audio``
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
.. describe:: bluez5.a2dp.opus.pro.bidi.application
|
|
||||||
|
|
||||||
Opus Pro Audio encoding mode for bidirectional audio.
|
|
||||||
|
|
||||||
Available values: ``audio``, ``voip``, ``lowdelay``.
|
|
||||||
|
|
||||||
:Default value: ``audio``
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
.. describe:: device.profile
|
|
||||||
|
|
||||||
The profile that is activated initially when the device is connected.
|
|
||||||
|
|
||||||
Available values: ``a2dp-sink`` (default) or ``headset-head-unit``.
|
|
||||||
|
|
||||||
:Default value: ``a2dp-sink``
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
Node properties
|
|
||||||
^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The following properties can be set on node objects:
|
|
||||||
|
|
||||||
.. describe:: bluez5.media-source-role
|
|
||||||
|
|
||||||
Media source role, ``input`` or ``playback``. This controls how a media
|
|
||||||
source device, such as a smartphone, is used by the system. Defaults to
|
|
||||||
``playback``, playing the incoming stream out to speakers. Set to ``input``
|
|
||||||
to use the smartphone as an input for apps (like a microphone).
|
|
||||||
|
|
||||||
:Default value: ``playback``
|
|
||||||
:Type: string
|
|
||||||
|
|
||||||
MIDI Rules
|
|
||||||
----------
|
|
||||||
|
|
||||||
Similarly to the above rules, the BlueZ MIDI monitor also supports rules that
|
|
||||||
can be used to configure MIDI nodes when they are created.
|
|
||||||
|
|
||||||
These rules can be set in the ``monitor.bluez-midi.rules`` section of the
|
|
||||||
WirePlumber configuration file.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.bluez-midi.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
node.name = "~bluez_midi.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
node.nick = "My Node"
|
|
||||||
priority.driver = 100
|
|
||||||
priority.session = 100
|
|
||||||
node.pause-on-idle = false
|
|
||||||
session.suspend-timeout-seconds = 5
|
|
||||||
node.latency-offset-msec = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
It is possible to also match MIDI server nodes by testing the ``node.name``
|
|
||||||
property against the server node names that were set in the
|
|
||||||
``monitor.bluez-midi.servers`` section of the WirePlumber configuration file.
|
|
||||||
|
|
||||||
MIDI-specific properties
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. describe:: node.latency-offset-msec
|
|
||||||
|
|
||||||
Latency adjustment to apply on the node. Larger values add a
|
|
||||||
constant latency, but reduces timing jitter caused by Bluetooth
|
|
||||||
transport.
|
|
||||||
|
|
||||||
:Default value: ``0``
|
|
||||||
:Type: integer (milliseconds)
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
.. _config_components_and_profiles:
|
|
||||||
|
|
||||||
Components & Profiles
|
|
||||||
=====================
|
|
||||||
|
|
||||||
WirePlumber is organized in components and profiles. Components are
|
|
||||||
functional parts that provide a specific feature, while profiles are
|
|
||||||
collections of components that are loaded together to offer a certain
|
|
||||||
overall experience.
|
|
||||||
|
|
||||||
Components
|
|
||||||
----------
|
|
||||||
|
|
||||||
Components are functional parts that provide a specific feature. They can be
|
|
||||||
described by a name, a type, a feature that they provide and a set of
|
|
||||||
dependencies, required and optional.
|
|
||||||
|
|
||||||
In the configuration file, a component is described as a SPA-JSON object,
|
|
||||||
in the ``wireplumber.components`` array section, like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
{
|
|
||||||
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> ]
|
|
||||||
}
|
|
||||||
|
|
||||||
Name & arguments
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The name identifies the resource that this component loads. For example,
|
|
||||||
it can be a file or a shared library. Depending on the type, the component
|
|
||||||
may also accept arguments, which are passed on to the resource when it is
|
|
||||||
loaded.
|
|
||||||
|
|
||||||
Types
|
|
||||||
~~~~~
|
|
||||||
|
|
||||||
The main types of components are:
|
|
||||||
|
|
||||||
* **script/lua**
|
|
||||||
|
|
||||||
A Lua script, which usually contains one or more event hooks and/or
|
|
||||||
other custom logic. This is the main type of component as WirePlumber's
|
|
||||||
business logic is mostly written in Lua.
|
|
||||||
|
|
||||||
* **module**
|
|
||||||
|
|
||||||
A WirePlumber module, which is a shared library that can be loaded
|
|
||||||
dynamically. Modules usually provide some bundled logic to be consumed by
|
|
||||||
scripts or some integration between WirePlumber and an external service.
|
|
||||||
|
|
||||||
* **pw-module**
|
|
||||||
|
|
||||||
A PipeWire module, which is also a shared library that can be loaded
|
|
||||||
dynamically, but extends the functionality of the underlying *libpipewire*
|
|
||||||
library. Loading PipeWire modules in the WirePlumber context can be useful
|
|
||||||
to load custom protocol extensions or to offload some functionality from
|
|
||||||
the PipeWire daemon.
|
|
||||||
|
|
||||||
* **virtual**
|
|
||||||
|
|
||||||
Virtual components are just load targets that can be used to pull in
|
|
||||||
other components by defining dependencies. They do not provide any
|
|
||||||
functionality by themselves. Note that such components do not have a "name".
|
|
||||||
|
|
||||||
* **built-in**
|
|
||||||
|
|
||||||
These components are functional parts that are already built into the
|
|
||||||
WirePlumber library. They provide mostly internal support elements and checks.
|
|
||||||
|
|
||||||
Features
|
|
||||||
~~~~~~~~
|
|
||||||
|
|
||||||
A "feature" is a name that we can use to refer to what is being provided
|
|
||||||
by a component. For example, the ``monitors/alsa.lua`` script provides the
|
|
||||||
``monitor.alsa`` feature. The feature name is used to refer to the component
|
|
||||||
when defining dependencies between components and also when defining profiles.
|
|
||||||
|
|
||||||
When a component loads successfully, its feature is marked as provided,
|
|
||||||
otherwise it is not. Whether a feature is provided or not can be checked at
|
|
||||||
runtime in Lua scripts using the :func:`Core.test_feature` function and in C code
|
|
||||||
using the :c:func:`wp_core_test_feature` function.
|
|
||||||
|
|
||||||
For a list of well-known features, see :ref:`config_features`.
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Each component can "provide" a feature. When the component is loaded, the
|
|
||||||
feature is marked as provided. Other components can either "require"
|
|
||||||
or "want" a feature.
|
|
||||||
|
|
||||||
If a component "requires" a feature, that means that this feature **must** be
|
|
||||||
provided before this component is loaded and WirePlumber will try to load the
|
|
||||||
relevant component that provides that feature if it is not already loaded
|
|
||||||
(i.e. it will pull in the component). If that other component fails to load,
|
|
||||||
hence the feature is not provided, the component that requires it will fail
|
|
||||||
to load as well.
|
|
||||||
|
|
||||||
If a component "wants" a feature, that means that this feature would be nice
|
|
||||||
to have, in the sense that it would offer additional functionality if it
|
|
||||||
was provided, but it's not strictly needed. WirePlumber will also try to load
|
|
||||||
the relevant component that provides that feature if it is not already loaded,
|
|
||||||
meaning that it will also pull in the component. However, if that other
|
|
||||||
component fails to load, the component that wants it will still be loaded
|
|
||||||
without error.
|
|
||||||
|
|
||||||
Profiles
|
|
||||||
--------
|
|
||||||
|
|
||||||
A profile is a collection of components that are loaded together to offer
|
|
||||||
a certain overall experience.
|
|
||||||
|
|
||||||
Profiles are defined in the configuration file as a SPA-JSON object,
|
|
||||||
in the ``wireplumber.profiles`` section, like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
<profile> = {
|
|
||||||
<feature name> = [ required | optional | disabled ]
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
Each feature can be marked as *required*, *optional* or *disabled*.
|
|
||||||
|
|
||||||
* **required**: Loading this profile will pull in the component that can
|
|
||||||
provide this feature in and if it fails to load, the profile will fail to
|
|
||||||
load as well.
|
|
||||||
* **optional**: Loading this profile does not pull in the component that
|
|
||||||
can provide this feature. If any of the required components either
|
|
||||||
*requires* or *wants* this feature, then WirePlumber will try to load it.
|
|
||||||
If it fails to load, the error condition depends on whether this feature was
|
|
||||||
required or wanted by the component that pulled it in.
|
|
||||||
* **disabled**: This feature will **not** be loaded, even if it is *wanted*
|
|
||||||
by some component. If any required component *requires* this feature, then
|
|
||||||
the profile will fail to load.
|
|
||||||
|
|
||||||
By default, all the features provided by all the components in the
|
|
||||||
``wireplumber.components`` section are considered to be *optional*.
|
|
||||||
That means that no component will be loaded on an empty profile, since optional
|
|
||||||
components are not pulled in automatically.
|
|
||||||
|
|
||||||
If a feature is marked as *required* in a profile, then the component that
|
|
||||||
provides that feature will be pulled in, together with all its dependencies,
|
|
||||||
both required and optional.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
In essence, all optional features are opt-in by default. To opt out,
|
|
||||||
you need to mark the feature as *disabled*.
|
|
||||||
|
|
||||||
Dependency chain example
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Consider the following configuration file:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.components = [
|
|
||||||
{
|
|
||||||
name = libwireplumber-module-dbus-connection, type = module
|
|
||||||
provides = support.dbus
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = libwireplumber-module-reserve-device, type = module
|
|
||||||
provides = support.reserve-device
|
|
||||||
requires = [ support.dbus ]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = monitors/alsa.lua, type = script/lua
|
|
||||||
provides = monitor.alsa
|
|
||||||
wants = [ support.reserve-device ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
wireplumber.profiles = {
|
|
||||||
main = {
|
|
||||||
monitor.alsa = required
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
In this example, the ``main`` profile requires the ``monitor.alsa`` feature.
|
|
||||||
This will cause the ``monitors/alsa.lua`` script to be loaded. Now, since the
|
|
||||||
``monitors/alsa.lua`` script *wants* the ``support.reserve-device`` feature,
|
|
||||||
the ``libwireplumber-module-reserve-device`` module will also be pulled in.
|
|
||||||
And since that one *requires* the ``support.dbus`` feature, the
|
|
||||||
``libwireplumber-module-dbus-connection`` module will also be pulled in.
|
|
||||||
|
|
||||||
However, on a system without D-Bus, a user may want to opt out of the
|
|
||||||
``libwireplumber-module-dbus-connection`` module. This can be done by marking
|
|
||||||
the ``support.dbus`` feature as disabled in the profile:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.profiles = {
|
|
||||||
main = {
|
|
||||||
monitor.alsa = required
|
|
||||||
support.dbus = disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Upon doing that, the ``libwireplumber-module-dbus-connection`` module will
|
|
||||||
not be loaded, causing the ``libwireplumber-module-reserve-device`` module
|
|
||||||
to not be loaded as well, since it requires the ``support.dbus`` feature.
|
|
||||||
The ``monitors/alsa.lua`` script will still be loaded, since it only *wants*
|
|
||||||
the ``support.reserve-device`` feature.
|
|
||||||
|
|
@ -1,244 +0,0 @@
|
||||||
.. _config_conf_file:
|
|
||||||
|
|
||||||
The configuration file
|
|
||||||
======================
|
|
||||||
|
|
||||||
WirePlumber's configuration file is by default ``wireplumber.conf`` and resides
|
|
||||||
in one of the WirePlumber specific
|
|
||||||
:ref:`configuration file search locations <config_locations>`.
|
|
||||||
|
|
||||||
The default configuration file can be changed on the command line by passing
|
|
||||||
the ``--config-file`` or ``-c`` option:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wireplumber --config-file=custom.conf
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
|
|
||||||
Starting with WirePlumber 0.5, this is the only file that WirePlumber reads
|
|
||||||
to load configuration (together with its fragments - see below). In the past,
|
|
||||||
WirePlumber also used to read Lua configuration files that were referenced
|
|
||||||
from ``wireplumber.conf`` and all the heavy lifting was done in Lua. This is
|
|
||||||
no longer the case, and the **Lua configuration files are no longer supported.**
|
|
||||||
See :ref:`config_migration`.
|
|
||||||
|
|
||||||
Note that Lua is still the scripting language for WirePlumber, but it is only
|
|
||||||
used for actual scripting and not for configuration.
|
|
||||||
|
|
||||||
The SPA-JSON Format
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The format of this configuration file is a variant of JSON that is also
|
|
||||||
used in PipeWire configuration files (also known as SPA-JSON). The file consists
|
|
||||||
of a global JSON object that is not explicitly typed, and a list of sections
|
|
||||||
which are essentially key-value pairs of that global JSON object. Each section
|
|
||||||
is usually a JSON object, but it can also be a JSON array.
|
|
||||||
|
|
||||||
SPA-JSON is a superset of standard JSON, so any valid JSON file is also a valid
|
|
||||||
SPA-JSON file. However, it is more permissive than standard JSON. First of all,
|
|
||||||
it allows strings to be typed without quotes (``"``), and it also allows the
|
|
||||||
character ``=`` as a separator between keys and values in addition to the
|
|
||||||
standard ``:``. This can make it look similar to INI files or other custom
|
|
||||||
configuration formats that people are familiar with, which makes it easier for
|
|
||||||
users to read and edit.
|
|
||||||
|
|
||||||
Other deviations from standard JSON include allowing comments (lines starting
|
|
||||||
with ``#`` are treated as comments) and allowing the separator characters
|
|
||||||
(``:``, ``=``, ``,``) to appear in excess or abundance. That means that you can
|
|
||||||
write ``key = value`` or ``key: value`` or ``key value`` and it will be
|
|
||||||
interpreted the same way. You may also write ``[val1, val2, val3]`` or
|
|
||||||
``[val1, val2, val3, ]`` or ``[val1 val2 val3]`` and it will be interpreted
|
|
||||||
the same way. This is allowed because the SPA-JSON parser in fact ignores all
|
|
||||||
the separator characters (the real separator is the space character).
|
|
||||||
|
|
||||||
Examples of valid SPA-JSON files:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
# This is the most common syntax
|
|
||||||
section1 = {
|
|
||||||
string-key = value1
|
|
||||||
number-key = 123
|
|
||||||
boolean-key = true
|
|
||||||
}
|
|
||||||
section2 = [
|
|
||||||
val1, val2, val3
|
|
||||||
]
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
# Mixed syntax
|
|
||||||
section1 {
|
|
||||||
"string-key" = "value1"
|
|
||||||
number-key: 123
|
|
||||||
boolean-key true
|
|
||||||
}
|
|
||||||
section2 = [
|
|
||||||
val1, val2 val3,
|
|
||||||
]
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
# Standard JSON (albeit this comment line)
|
|
||||||
"section1": {
|
|
||||||
"string-key": "value1",
|
|
||||||
"number-key": 123,
|
|
||||||
"boolean-key": true
|
|
||||||
}
|
|
||||||
"section2": [
|
|
||||||
"val1", "val2", "val3"
|
|
||||||
]
|
|
||||||
|
|
||||||
.. _config_conf_file_fragments:
|
|
||||||
|
|
||||||
Fragments
|
|
||||||
---------
|
|
||||||
|
|
||||||
Just like PipeWire, WirePlumber supports configuration fragments. This means
|
|
||||||
that the main configuration file can be split into multiple files, and all of
|
|
||||||
them will be loaded and merged together. This is mostly useful to allow users
|
|
||||||
to customize their configuration without having to modify the main file.
|
|
||||||
|
|
||||||
When loading the configuration file, WirePlumber will also look for
|
|
||||||
additional files in the directory that has the same name as the configuration
|
|
||||||
file suffixed with ``.d`` and will load all of them as well. For example,
|
|
||||||
loading ``wireplumber.conf`` will also load any ``.conf`` files under
|
|
||||||
``wireplumber.conf.d/``. This directory is searched in all the configuration
|
|
||||||
search locations and the fragments are loaded from *all* of them, starting
|
|
||||||
from the most system-wide locations and moving towards the most user-specific
|
|
||||||
locations, in alphanumerical order within each location (see also
|
|
||||||
:ref:`config_locations_fragments`).
|
|
||||||
|
|
||||||
When a JSON object appears in multiple files, the properties of the objects are
|
|
||||||
merged together. When a JSON array appears in multiple files, the arrays are
|
|
||||||
concatenated together. When merging objects, if specific properties appear in
|
|
||||||
many of those objects, the last one to be parsed always overwrites previous
|
|
||||||
ones, unless the value is also an object or array; if it is, then the value is
|
|
||||||
recursively merged using the same rules.
|
|
||||||
|
|
||||||
Sections
|
|
||||||
--------
|
|
||||||
|
|
||||||
WirePlumber reads the following standard sections from the configuration
|
|
||||||
file:
|
|
||||||
|
|
||||||
* *wireplumber.components*
|
|
||||||
|
|
||||||
This section is an array that lists components that can be loaded by
|
|
||||||
WirePlumber. For more information, see :ref:`config_components_and_profiles`.
|
|
||||||
|
|
||||||
* *wireplumber.components.rules*
|
|
||||||
|
|
||||||
This section is an array containing rules that can be used to modify entries
|
|
||||||
of the *wireplumber.components* array. This is useful to inject changes
|
|
||||||
to the components list without having to modify the main configuration file.
|
|
||||||
|
|
||||||
* *wireplumber.profiles*
|
|
||||||
|
|
||||||
This section is an object that defines profiles that can be loaded by
|
|
||||||
WirePlumber. For more information, see :ref:`config_components_and_profiles`.
|
|
||||||
|
|
||||||
* *wireplumber.settings*
|
|
||||||
|
|
||||||
This section is an object that defines settings that can be used to
|
|
||||||
alter WirePlumber's behavior. For more information, see :ref:`config_settings`.
|
|
||||||
|
|
||||||
* *wireplumber.settings.schema*
|
|
||||||
|
|
||||||
This section is an object that defines the schema for the settings that
|
|
||||||
can be listed in *wireplumber.settings*. This is used to validate the
|
|
||||||
settings when they are modified at runtime. For more information, see
|
|
||||||
:ref:`config_configuration_option_types`.
|
|
||||||
|
|
||||||
In addition, there are many sections that are specific to certain components,
|
|
||||||
mostly hardware monitors, such as *monitor.alsa.properties*,
|
|
||||||
*monitor.alsa.rules*, etc. These are documented further on, in the respective
|
|
||||||
sections of this documentation that describe the configuration options of
|
|
||||||
these components.
|
|
||||||
|
|
||||||
Finally, WirePlumber also reads the following sections, which are parsed
|
|
||||||
by libpipewire to configure the PipeWire context:
|
|
||||||
|
|
||||||
* *context.properties*
|
|
||||||
|
|
||||||
Used to define properties to configure the PipeWire context and some modules.
|
|
||||||
|
|
||||||
* *context.spa-libs*
|
|
||||||
|
|
||||||
Used to find SPA factory names. It maps a SPA factory name regular expression
|
|
||||||
to a library name that should contain that factory. The object property names
|
|
||||||
are the regular expressions, and the object property values are the actual
|
|
||||||
library names:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
<factory-name regex> = <library-name>
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
context.spa-libs = {
|
|
||||||
api.alsa.* = alsa/libspa-alsa
|
|
||||||
audio.convert.* = audioconvert/libspa-audioconvert
|
|
||||||
}
|
|
||||||
|
|
||||||
In this example, we instruct wireplumber to lookup any *api.alsa.** factory
|
|
||||||
in the *libspa-alsa* library, and any *audio.convert.** factory
|
|
||||||
in the *libspa-audioconvert* library.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The default configuration file already contains a list of well-known
|
|
||||||
factory names and their corresponding libraries. You should only
|
|
||||||
need to add entries to this section if you are using custom SPA plugins.
|
|
||||||
|
|
||||||
* *context.modules*
|
|
||||||
|
|
||||||
Used to load PipeWire modules. This does not affect the PipeWire daemon by any
|
|
||||||
means. It exists simply to allow loading *libpipewire* modules inside
|
|
||||||
WirePlumber. This is usually useful to load PipeWire protocol extensions,
|
|
||||||
so that you can export custom objects to PipeWire and other clients.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
PipeWire modules can also be loaded as :ref:`components <config_components_and_profiles>`,
|
|
||||||
which may be preferable since it allows you to load them conditionally
|
|
||||||
based on the profile and component dependencies.
|
|
||||||
|
|
||||||
.. admonition:: Remember
|
|
||||||
|
|
||||||
Modules listed in *context.modules* are always loaded before attempting a
|
|
||||||
connection to the PipeWire daemon, while modules listed in
|
|
||||||
*wireplumber.components* are always loaded after the connection is
|
|
||||||
established. It is important to load the PipeWire protocol-native module
|
|
||||||
and any extensions (such as module-metadata) in the *context.modules*
|
|
||||||
section, so that the connection can be done properly.
|
|
||||||
|
|
||||||
Each module is described by a JSON object containing the module's *name*,
|
|
||||||
its arguments (*args*) and a combination of *flags*, which can be ``ifexists``
|
|
||||||
and ``nofail``.
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
{
|
|
||||||
name = <module-name>
|
|
||||||
[ args = { <key> = <value> ... } ]
|
|
||||||
[ flags = [ [ ifexists ] [ nofail ] ]
|
|
||||||
}
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
context.modules = [
|
|
||||||
{ name = libpipewire-module-adapter }
|
|
||||||
{
|
|
||||||
name = libpipewire-module-metadata,
|
|
||||||
flags = [ ifexists ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
The above example loads both PipeWire adapter and metadata modules. The
|
|
||||||
metadata module will be ignored if not found because of its ``ifexists`` flag.
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
.. _config_configuration_option_types:
|
|
||||||
|
|
||||||
Configuration option types
|
|
||||||
==========================
|
|
||||||
|
|
||||||
As seen in the previous sections, WirePlumber can be partly configured by
|
|
||||||
enabling or disabling features, which affect which components are getting
|
|
||||||
loaded. These components, however, can be further configured to fine-tune their
|
|
||||||
behavior. This section describes the different types of configuration options
|
|
||||||
that can be used to configure WirePlumber components.
|
|
||||||
|
|
||||||
Dynamic options ("Settings")
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Dynamic options (also simply referred to as "settings") are configuration
|
|
||||||
options that can be changed at runtime. They are typically simple values like
|
|
||||||
booleans, integers, strings, etc. and are all located under the
|
|
||||||
``wireplumber.settings`` section in the configuration file. Their purpose is to
|
|
||||||
allow the user to change simple behavioral aspects of WirePlumber.
|
|
||||||
|
|
||||||
As the name suggests, these options are dynamic and can be changed at runtime
|
|
||||||
using ``wpctl`` or the :ref:`settings_api` API. For example, setting the
|
|
||||||
``device.routes.default-sink-volume`` setting to ``0.5`` can be done like this:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings device.routes.default-sink-volume 0.5
|
|
||||||
|
|
||||||
Under the hood, when WirePlumber starts, the ``metadata.sm-settings`` component
|
|
||||||
(provided by ``libwireplumber-module-settings``) reads this section from the
|
|
||||||
configuration file and populates the ``sm-settings`` metadata object, which is
|
|
||||||
exported to PipeWire. In addition, it reads the ``wireplumber.settings.schema``
|
|
||||||
section and populates the ``schema-sm-settings`` metadata object, which is used
|
|
||||||
by the API to validate the settings. Any options that are missing from
|
|
||||||
``wireplumber.settings`` are also populated in ``sm-settings`` from their
|
|
||||||
default values in the schema. Then the rest of the components read their
|
|
||||||
configuration options from this metadata object via the :ref:`settings_api` API.
|
|
||||||
|
|
||||||
Most of the components that use such dynamic options make sure to listen
|
|
||||||
to changes in the metadata object so that they can immediately adapt their
|
|
||||||
behavior. Other components, however, do not react immediately and the changes
|
|
||||||
only take effect the next time the option is needed. For instance, some options
|
|
||||||
affect created objects in a way that cannot be changed after the object has been
|
|
||||||
created, so when the option is changed it applies only to new objects and not
|
|
||||||
existing ones.
|
|
||||||
|
|
||||||
Changing the settings at runtime in the ``sm-settings`` metadata object is
|
|
||||||
a non-persistent change. The changes will be lost when WirePlumber is
|
|
||||||
restarted. However, the :ref:`settings_api` API also supports saving settings
|
|
||||||
to a state file, which will be loaded again when WirePlumber starts and
|
|
||||||
override the settings from the configuration file. This is done by using yet
|
|
||||||
another metadata object called ``persistent-sm-settings``. When a setting is
|
|
||||||
changed in the ``persistent-sm-settings`` metadata object, WirePlumber
|
|
||||||
automatically saves the change to the state file and also changes the value in
|
|
||||||
the ``sm-settings`` metadata object immediately.
|
|
||||||
|
|
||||||
To make such a persistent change using ``wpctl``, the ``--save`` option can be
|
|
||||||
used. For example, to set the ``device.routes.default-sink-volume`` setting to
|
|
||||||
``0.5`` and save it to the state file:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings --save device.routes.default-sink-volume 0.5
|
|
||||||
|
|
||||||
With ``wpctl``, it is also possible to restore a setting to its default value
|
|
||||||
(taken from the schema), by using the ``--reset`` option. For example, to reset
|
|
||||||
the ``device.routes.default-sink-volume`` setting, the following command can be
|
|
||||||
used:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings --reset device.routes.default-sink-volume
|
|
||||||
|
|
||||||
In addition, the ``--delete`` option can be used to delete a setting from the
|
|
||||||
``persistent-sm-settings`` metadata object, which will also remove it from the
|
|
||||||
state file. After deleting, the value from the ``wireplumber.settings`` section
|
|
||||||
of the configuration file will be used again. For example, to delete the
|
|
||||||
``device.routes.default-sink-volume`` setting, the following command can be
|
|
||||||
used:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings --delete device.routes.default-sink-volume
|
|
||||||
|
|
||||||
A list of all the available settings can be found in the :ref:`config_settings`
|
|
||||||
section.
|
|
||||||
|
|
||||||
Static options
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Static options are more complex configuration structures that reside only in the
|
|
||||||
configuration file and cannot be changed at runtime. They are typically used to
|
|
||||||
configure device monitors and provide rules that match objects and perform
|
|
||||||
actions such as update their properties.
|
|
||||||
|
|
||||||
While these options could also in theory be stored in the metadata object and
|
|
||||||
be made dynamic, this is not supported because these options are both complex
|
|
||||||
and therefore hard to change on the command line, but also because they are
|
|
||||||
typically used to configure objects that are created at startup and cannot be
|
|
||||||
changed later.
|
|
||||||
|
|
||||||
Static options are located in their own top-level sections. Examples of such
|
|
||||||
sections are ``monitor.alsa.properties`` and ``monitor.alsa.rules`` that are
|
|
||||||
used to configure the ``monitor.alsa`` component. The next sections of this
|
|
||||||
documentation describe in detail all the available static options.
|
|
||||||
|
|
||||||
Component arguments
|
|
||||||
~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Components can also be configured statically by passing arguments to them when
|
|
||||||
they are loaded. This is done by adding an ``arguments`` key to the component
|
|
||||||
description in the ``wireplumber.components`` section (see
|
|
||||||
:ref:`config_components_and_profiles`).
|
|
||||||
|
|
||||||
The arguments are mostly meant as a way to instantiate multiple instances of the
|
|
||||||
same module or script with slightly different configuration to create a new
|
|
||||||
unique component. For example, the ``metadata.lua`` script can be instantiated
|
|
||||||
multiple times to create multiple metadata objects, each with a different name.
|
|
||||||
The name of the metadata object is passed as an argument to the script.
|
|
||||||
|
|
||||||
While many more static options could be passed as arguments, this is not
|
|
||||||
recommended because it is not possible to override the arguments by adding
|
|
||||||
:ref:`fragment<config_conf_file_fragments>` configuration files. Therefore, it
|
|
||||||
is recommended to use component-specific top-level sections, unless the option
|
|
||||||
is not meant to be changed by the user.
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
.. _config_features:
|
|
||||||
|
|
||||||
Well-known features
|
|
||||||
===================
|
|
||||||
|
|
||||||
This is a list of some well-known features that can be enabled or
|
|
||||||
disabled accordingly.
|
|
||||||
|
|
||||||
There are many more features actually defined in the configuration file, and it
|
|
||||||
can be confusing to go through them. This list here is meant to be a quick
|
|
||||||
reference for the most common ones that actually make sense to be toggled in
|
|
||||||
a configuration file in order to customize WirePlumber's behavior.
|
|
||||||
|
|
||||||
For more information on what features are and how they work, refer to the
|
|
||||||
previous section: :ref:`config_components_and_profiles`.
|
|
||||||
|
|
||||||
Hardware monitors
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Audio
|
|
||||||
~~~~~
|
|
||||||
|
|
||||||
.. describe:: hardware.audio
|
|
||||||
|
|
||||||
Enables bringing up audio hardware.
|
|
||||||
|
|
||||||
:wants: ``monitor.alsa``, ``monitor.alsa-midi``
|
|
||||||
|
|
||||||
.. describe:: monitor.alsa
|
|
||||||
|
|
||||||
Enables the ALSA device monitor.
|
|
||||||
|
|
||||||
:wants: ``monitor.alsa.reserve-device``
|
|
||||||
|
|
||||||
.. describe:: monitor.alsa.reserve-device
|
|
||||||
|
|
||||||
Enables D-Bus device reservation API for ALSA devices.
|
|
||||||
|
|
||||||
:requires: ``support.reserve-device``
|
|
||||||
|
|
||||||
.. describe:: monitor.alsa-midi
|
|
||||||
|
|
||||||
Enables the ALSA MIDI device monitor.
|
|
||||||
|
|
||||||
.. describe:: node.software-dsp
|
|
||||||
|
|
||||||
Enables software DSP based on pre-configured hardware rules.
|
|
||||||
|
|
||||||
See :ref:`policies_software_dsp` for more information.
|
|
||||||
|
|
||||||
Bluetooth
|
|
||||||
~~~~~~~~~
|
|
||||||
|
|
||||||
.. describe:: hardware.bluetooth
|
|
||||||
|
|
||||||
Enables bringing up bluetooth hardware.
|
|
||||||
|
|
||||||
:wants: ``monitor.bluez``, ``monitor.bluez-midi``
|
|
||||||
|
|
||||||
.. describe:: monitor.bluez
|
|
||||||
|
|
||||||
Enables the BlueZ device monitor.
|
|
||||||
|
|
||||||
:wants: ``monitor.bluez.seat-monitoring``
|
|
||||||
|
|
||||||
.. describe:: monitor.bluez.seat-monitoring
|
|
||||||
|
|
||||||
Enables seat monitoring on the bluetooth monitor.
|
|
||||||
|
|
||||||
When enabled, this will make sure that the bluetooth devices are only
|
|
||||||
enabled on the active seat.
|
|
||||||
|
|
||||||
:requires: ``support.logind``
|
|
||||||
|
|
||||||
.. describe:: monitor.bluez-midi
|
|
||||||
|
|
||||||
Enables the BlueZ MIDI device monitor.
|
|
||||||
|
|
||||||
:wants: ``monitor.bluez.seat-monitoring``
|
|
||||||
|
|
||||||
Video
|
|
||||||
~~~~~
|
|
||||||
|
|
||||||
.. describe:: hardware.video-capture
|
|
||||||
|
|
||||||
Enables bringing up video capture hardware (cameras, hdmi capture devices,
|
|
||||||
etc.)
|
|
||||||
|
|
||||||
:wants: ``monitor.v4l2``, ``monitor.libcamera``
|
|
||||||
|
|
||||||
.. describe:: monitor.v4l2
|
|
||||||
|
|
||||||
Enables the V4L2 device monitor.
|
|
||||||
|
|
||||||
.. describe:: monitor.libcamera
|
|
||||||
|
|
||||||
Enables the libcamera device monitor.
|
|
||||||
|
|
||||||
Support components
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. describe:: support.dbus
|
|
||||||
|
|
||||||
Provides a D-Bus connection to the session bus. This is needed by some other
|
|
||||||
support features (see below) but it is generally optional. WirePlumber does
|
|
||||||
not require a D-Bus connection to work.
|
|
||||||
|
|
||||||
On a system where WirePlumber is configured to run system-wide (headless,
|
|
||||||
embedded, etc), this will most likely fail to load and thus disable all the
|
|
||||||
other support features that require it. On such systems it makes sense to
|
|
||||||
disable this feature explicitly, to avoid the overhead of trying to connect
|
|
||||||
to the session bus.
|
|
||||||
|
|
||||||
.. describe:: support.reserve-device
|
|
||||||
|
|
||||||
Provides support for the
|
|
||||||
`D-Bus device reservation API <http://git.0pointer.net/reserve.git/tree/reserve.txt>`_,
|
|
||||||
allowing the device monitors to reserve devices for exclusive access.
|
|
||||||
|
|
||||||
:requires: ``support.dbus``
|
|
||||||
|
|
||||||
.. describe:: support.portal-permissionstore
|
|
||||||
|
|
||||||
Integrates with the flatpak portal permission store to give appropriate
|
|
||||||
access permissions to flatpak applications.
|
|
||||||
|
|
||||||
:requires: ``support.dbus``
|
|
||||||
|
|
||||||
.. describe:: support.logind
|
|
||||||
|
|
||||||
Integrates with systemd-logind to enable specific functionality only on the
|
|
||||||
active seat.
|
|
||||||
|
|
||||||
Policies
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. describe:: policy.standard
|
|
||||||
|
|
||||||
Enables the standard WirePlumber policy. This includes all the logic
|
|
||||||
for enabling devices, linking streams, granting permissions to clients,
|
|
||||||
etc, as appropriate for a desktop system.
|
|
||||||
|
|
||||||
.. describe:: policy.role-based
|
|
||||||
|
|
||||||
Enables the role based priority system policy. This system creates virtual sinks
|
|
||||||
that group streams based on their ``media.role`` property, and assigns a
|
|
||||||
priority to each role. Depending on the priority configuration, lower
|
|
||||||
priority roles may be corked or ducked when a higher priority role stream
|
|
||||||
is active.
|
|
||||||
|
|
||||||
This policy was designed for automotive and mobile systems and may not work
|
|
||||||
as expected on desktop systems.
|
|
||||||
|
|
||||||
Note that this policy is implemented as a superset of ``policy.standard``,
|
|
||||||
so ``policy.standard`` should not be disabled when enabling this policy.
|
|
||||||
|
|
||||||
:requires: ``policy.standard``
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
# you need to add here any files you add to the toc directory as well
|
|
||||||
sphinx_files += files(
|
|
||||||
'conf_file.rst',
|
|
||||||
'components_and_profiles.rst',
|
|
||||||
'configuration_option_types.rst',
|
|
||||||
'modifying_configuration.rst',
|
|
||||||
'migration.rst',
|
|
||||||
'features.rst',
|
|
||||||
'settings.rst',
|
|
||||||
'alsa.rst',
|
|
||||||
'bluetooth.rst',
|
|
||||||
'access.rst',
|
|
||||||
)
|
|
||||||
|
|
@ -1,305 +0,0 @@
|
||||||
.. _config_migration:
|
|
||||||
|
|
||||||
Migrating configuration from 0.4
|
|
||||||
================================
|
|
||||||
|
|
||||||
The configuration file format has changed in version 0.5. No automatic migration
|
|
||||||
of old configuration files is performed, so you will have to manually update
|
|
||||||
them. This document describes the changes and how to update your configuration.
|
|
||||||
|
|
||||||
wireplumber.conf
|
|
||||||
----------------
|
|
||||||
|
|
||||||
In WirePlumber 0.4, there used to be a ``.conf`` file, typically
|
|
||||||
``wireplumber.conf``, using the SPA-JSON format, that would list some Lua
|
|
||||||
scripts in the ``wireplumber.components`` section. These scripts were of type
|
|
||||||
``config/lua`` and they were called by default ``main.lua``, ``policy.lua`` and
|
|
||||||
``bluetooth.lua``.
|
|
||||||
|
|
||||||
Typical ``wireplumber.components`` section of a ``wireplumber.conf`` file in 0.4
|
|
||||||
would look like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.components = [
|
|
||||||
#{ name = <component-name>, type = <component-type> }
|
|
||||||
#
|
|
||||||
# WirePlumber components to load
|
|
||||||
#
|
|
||||||
|
|
||||||
# The lua scripting engine
|
|
||||||
{ name = libwireplumber-module-lua-scripting, type = module }
|
|
||||||
|
|
||||||
# The lua configuration file(s)
|
|
||||||
# Other components are loaded from there
|
|
||||||
{ name = main.lua, type = config/lua }
|
|
||||||
{ name = policy.lua, type = config/lua }
|
|
||||||
{ name = bluetooth.lua, type = config/lua }
|
|
||||||
]
|
|
||||||
|
|
||||||
These Lua "configuration" scripts were then looked up in the standard
|
|
||||||
configuration directories (``/usr/share/wireplumber``, ``/etc/wireplumber`` and
|
|
||||||
``~/.config/wireplumber``). The system also supported fragments of these scripts
|
|
||||||
to be placed in directories called ``main.lua.d``, ``policy.lua.d`` and
|
|
||||||
``bluetooth.lua.d`` respectively, in the same locations.
|
|
||||||
|
|
||||||
.. attention::
|
|
||||||
|
|
||||||
Starting with WirePlumber 0.5, Lua "configuration" files are **no longer
|
|
||||||
supported**.
|
|
||||||
|
|
||||||
If you attempt to start it with a ``wireplumber.conf`` that still
|
|
||||||
lists ``config/lua`` components in its ``wireplumber.components`` section, you
|
|
||||||
will see the following error message on the output:
|
|
||||||
|
|
||||||
Failed to load configuration: The configuration file at '...' is likely an
|
|
||||||
old WirePlumber 0.4 config and is not supported anymore. Try removing it.
|
|
||||||
|
|
||||||
As the message says, to resolve this you should remove the old
|
|
||||||
``wireplumber.conf`` file from the designated location. This should allow the
|
|
||||||
new WirePlumber to start using the default configuration that it ships with.
|
|
||||||
|
|
||||||
Lua configuration scripts
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
If you had custom Lua configuration scripts in the standard configuration
|
|
||||||
directories, such as *"main.lua.d"*, *"policy.lua.d"* or *"bluetooth.lua.d"*,
|
|
||||||
**you need to port them**.
|
|
||||||
|
|
||||||
Locations of files
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The first thing you need to know is that the new files should be placed in the
|
|
||||||
``~/.config/wireplumber/wireplumber.conf.d/`` directory instead of
|
|
||||||
``~/.config/wireplumber/main.lua.d/`` and such ...
|
|
||||||
|
|
||||||
In addition, since the new files are in the SPA-JSON format, they should have
|
|
||||||
the ``.conf`` extension instead of ``.lua``.
|
|
||||||
|
|
||||||
See also :ref:`config_locations`.
|
|
||||||
|
|
||||||
Porting device/node rules
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
One of the most common use-cases for these scripts was to set up properties
|
|
||||||
for devices and nodes using rules. Here is an example of an old rules script:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
:caption: ~/.config/wireplumber/main.lua.d/51-alsa-pro-audio.lua
|
|
||||||
|
|
||||||
local rule = {
|
|
||||||
matches = {
|
|
||||||
{
|
|
||||||
{ "device.name", "matches", "alsa_card.*" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
apply_properties = {
|
|
||||||
["api.alsa.use-acp"] = false,
|
|
||||||
["device.profile"] = "pro-audio",
|
|
||||||
["api.acp.auto-profile"] = false,
|
|
||||||
["api.acp.auto-port"] = false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
table.insert(alsa_monitor.rules, rule)
|
|
||||||
|
|
||||||
This equivalent of this script in the new configuration format would look like
|
|
||||||
this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:caption: ~/.config/wireplumber/wireplumber.conf.d/51-alsa-pro-audio.conf
|
|
||||||
|
|
||||||
monitor.alsa.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
device.name = "~alsa_card.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
api.alsa.use-acp = false,
|
|
||||||
device.profile = "pro-audio"
|
|
||||||
api.acp.auto-profile = false
|
|
||||||
api.acp.auto-port = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Another example of Bluetooth node rules:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
:caption: ~/.config/wireplumber/bluetooth.lua.d/51-headphones.lua
|
|
||||||
|
|
||||||
local rule = {
|
|
||||||
matches = {
|
|
||||||
{
|
|
||||||
{ "node.name", "equals", "bluez_output.02_11_45_A0_B3_27.a2dp-sink" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
apply_properties = {
|
|
||||||
["node.nick"] = "Headphones",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
table.insert(bluez_monitor.rules, rule)
|
|
||||||
|
|
||||||
This equivalent of this script in the new configuration format would look like:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:caption: ~/.config/wireplumber/wireplumber.conf.d/51-headphones.conf
|
|
||||||
|
|
||||||
monitor.bluez.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
node.name = "bluez_output.02_11_45_A0_B3_27.a2dp-sink"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
node.nick = "Headphones"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
See also :ref:`config_modifying_configuration_rules`.
|
|
||||||
|
|
||||||
Porting properties configuration
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you had configuration scripts that were setting properties in tables such
|
|
||||||
as ``alsa_monitor.properties`` or ``bluez_monitor.properties``, then in many
|
|
||||||
cases porting to the new format can be done as follows:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
:caption: ~/.config/wireplumber/bluetooth.lua.d/80-bluez-properties.lua
|
|
||||||
|
|
||||||
bluez_monitor.properties["bluez5.roles"] = "[ a2dp_sink a2dp_source bap_sink bap_source hsp_hs hsp_ag hfp_hf hfp_ag ]"
|
|
||||||
bluez_monitor.properties["bluez5.hfphsp-backend"] = "native"
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:caption: ~/.config/wireplumber/wireplumber.conf.d/80-bluez-properties.conf
|
|
||||||
|
|
||||||
monitor.bluez.properties = {
|
|
||||||
bluez5.roles = [ a2dp_sink a2dp_source bap_sink bap_source hsp_hs hsp_ag hfp_hf hfp_ag ]
|
|
||||||
bluez5.hfphsp-backend = "native"
|
|
||||||
}
|
|
||||||
|
|
||||||
See also :ref:`config_modifying_configuration_static`.
|
|
||||||
|
|
||||||
In a lot of cases, however, these properties have been promoted to become either
|
|
||||||
:ref:`Settings <config_modifying_configuration_settings>` or
|
|
||||||
:ref:`Features <config_modifying_configuration_features>`.
|
|
||||||
Here are some common examples:
|
|
||||||
|
|
||||||
Disabling the D-Bus device reservation API in the ALSA monitor:
|
|
||||||
|
|
||||||
* Old format:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
:caption: ~/.config/wireplumber/main.lua.d/80-disable-alsa-reserve.lua
|
|
||||||
|
|
||||||
alsa_monitor.properties["alsa.reserve"] = false
|
|
||||||
|
|
||||||
* New format:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:caption: ~/.config/wireplumber/wireplumber.conf.d/80-disable-alsa-reserve.conf
|
|
||||||
|
|
||||||
wireplumber.profiles = {
|
|
||||||
main = {
|
|
||||||
monitor.alsa.reserve-device = disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Disabling seat monitoring via logind in the BlueZ monitor:
|
|
||||||
|
|
||||||
* Old format:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
:caption: ~/.config/wireplumber/bluetooth.lua.d/80-disable-logind.lua
|
|
||||||
|
|
||||||
bluez_monitor.properties["with-logind"] = false
|
|
||||||
|
|
||||||
* New format:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:caption: ~/.config/wireplumber/wireplumber.conf.d/80-disable-logind.conf
|
|
||||||
|
|
||||||
wireplumber.profiles = {
|
|
||||||
main = {
|
|
||||||
monitor.bluez.seat-monitoring = disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
See also :ref:`config_modifying_configuration_features`.
|
|
||||||
|
|
||||||
Linking policy configuration (moved to settings and renamed):
|
|
||||||
|
|
||||||
* Old format:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
:caption: ~/.config/wireplumber/policy.lua.d/80-policy.lua
|
|
||||||
|
|
||||||
default_policy.policy = {
|
|
||||||
["move"] = false,
|
|
||||||
["follow"] = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
* New format:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:caption: ~/.config/wireplumber/wireplumber.conf.d/80-policy.conf
|
|
||||||
|
|
||||||
wireplumber.settings = {
|
|
||||||
linking.allow-moving-streams = false
|
|
||||||
linking.follow-default-target = false
|
|
||||||
}
|
|
||||||
|
|
||||||
See also :ref:`config_modifying_configuration_settings` and remember that
|
|
||||||
settings can also be changed at runtime via :command:`wpctl`.
|
|
||||||
|
|
||||||
Loading custom scripts
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you had custom Lua scripts that were loaded by the old configuration file,
|
|
||||||
you need to port the old ``load_script()`` commands into component descriptions.
|
|
||||||
|
|
||||||
For example, if you had a script that was loaded like this:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
:caption: ~/.config/wireplumber/main.lua.d/99-my-script.lua
|
|
||||||
|
|
||||||
load_script("my-script.lua")
|
|
||||||
|
|
||||||
You should now create a new component description in the configuration file
|
|
||||||
and also make sure to require it in the profile:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:caption: ~/.config/wireplumber/wireplumber.conf.d/99-my-script.conf
|
|
||||||
|
|
||||||
wireplumber.components = [
|
|
||||||
{
|
|
||||||
name = my-script.lua, type = script/lua
|
|
||||||
provides = custom.my-script
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
wireplumber.profiles = {
|
|
||||||
main = {
|
|
||||||
custom.my-script = required
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.. attention::
|
|
||||||
|
|
||||||
Another important thing to mention here is the location of custom scripts. In
|
|
||||||
0.4, scripts could be loaded in configuration locations such as
|
|
||||||
``~/.config/wireplumber/scripts/`` and ``/etc/wireplumber/scripts/``. In 0.5,
|
|
||||||
the XDG base directory specification for data files is honored, so the new
|
|
||||||
location for custom scripts is ``~/.local/share/wireplumber/scripts/`` and
|
|
||||||
anything else specified in ``$XDG_DATA_HOME`` and ``$XDG_DATA_DIRS``. See
|
|
||||||
:ref:`daemon_file_locations` for more information.
|
|
||||||
|
|
@ -1,365 +0,0 @@
|
||||||
.. _config_modifying_configuration:
|
|
||||||
|
|
||||||
Modifying configuration
|
|
||||||
=======================
|
|
||||||
|
|
||||||
WirePlumber is a heavily modular daemon that depends on its configuration
|
|
||||||
file to operate. If you were to start WirePlumber with an empty configuration
|
|
||||||
file, it would fail to start. This is why the default configuration file is
|
|
||||||
installed in the system-wide application data directory, which prevents it from
|
|
||||||
being modified by the user.
|
|
||||||
|
|
||||||
It is technically possible, if you wish, to copy the default configuration
|
|
||||||
file in one of the other :ref:`configuration search locations <config_locations>`
|
|
||||||
and modify it. However, this is **not recommended**, as it may lead to issues
|
|
||||||
when upgrading WirePlumber.
|
|
||||||
|
|
||||||
In the :ref:`Configuration file <config_conf_file>` section, we saw that
|
|
||||||
configuration files support fragments, which allow you to override or extend the
|
|
||||||
default configuration. This is the recommended way to modify the configuration.
|
|
||||||
|
|
||||||
Working with fragments
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
The easiest way to add :ref:`fragments <config_conf_file_fragments>` to
|
|
||||||
modify the default configuration is to create a directory called
|
|
||||||
``~/.config/wireplumber/wireplumber.conf.d`` and place your fragments there.
|
|
||||||
|
|
||||||
All fragment files need to have the ``.conf`` extension and must be valid
|
|
||||||
SPA-JSON files. The fragments are loaded in alphanumerical order, so you can
|
|
||||||
control the order in which they are loaded by naming them accordingly. It is
|
|
||||||
recommended to use a numeric prefix for the file names, e.g.
|
|
||||||
``10-my-fragment.conf``, ``20-my-other-fragment.conf``, etc., so that you can
|
|
||||||
easily control the order in which they are loaded.
|
|
||||||
|
|
||||||
.. _config_modifying_configuration_features:
|
|
||||||
|
|
||||||
Customizing the loaded features
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
As seen in the :ref:`Components & Profiles <config_components_and_profiles>`
|
|
||||||
section, the list of components that are loaded can be customized by enabling or
|
|
||||||
disabling :ref:`well-known features <config_features>` in the profile that is
|
|
||||||
in use by WirePlumber.
|
|
||||||
|
|
||||||
The default profile of WirePlumber is called ``main``, so a fragment that
|
|
||||||
enables or disables a specific feature in the default configuration should look
|
|
||||||
like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.profiles = {
|
|
||||||
main = {
|
|
||||||
some.feature.name = disabled
|
|
||||||
some.other.feature.name = required
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Remember that features can be ``required``, ``optional`` or ``disabled``. See
|
|
||||||
the :ref:`Components & Profiles <config_components_and_profiles>` for details.
|
|
||||||
|
|
||||||
.. _config_modifying_configuration_settings:
|
|
||||||
|
|
||||||
Modifying dynamic options ("settings")
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
As seen in the :ref:`Configuration option types <config_configuration_option_types>`
|
|
||||||
section, WirePlumber components can be partly configured with dynamic options
|
|
||||||
(referred to as "settings"). These settings can either be modified permanently
|
|
||||||
in the configuration file, or they can be modified at runtime using the
|
|
||||||
``wpctl`` command-line tool.
|
|
||||||
|
|
||||||
To modify a setting in the configuration file, you can use a fragment like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.settings = {
|
|
||||||
some.setting.name = value
|
|
||||||
}
|
|
||||||
|
|
||||||
For example, setting the ``device.routes.default-sink-volume`` setting to
|
|
||||||
``0.5`` can be done like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.settings = {
|
|
||||||
device.routes.default-sink-volume = 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Since the configuration file is only read at startup, this will only take
|
|
||||||
effect after restarting WirePlumber.
|
|
||||||
|
|
||||||
If you would prefer to change the setting at runtime, you can use ``wpctl`` as
|
|
||||||
follows:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings device.routes.default-sink-volume 0.5
|
|
||||||
Updated setting 'device.routes.default-sink-volume' to: 0.5
|
|
||||||
|
|
||||||
The above command changes the setting immediately, but for the current
|
|
||||||
WirePlumber instance only. If you want the setting to be applied every time
|
|
||||||
WirePlumber is started, you may also use the ``--save`` option:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings --save device.routes.default-sink-volume 0.5
|
|
||||||
Updated and saved setting 'device.routes.default-sink-volume' to: 0.5
|
|
||||||
|
|
||||||
This will save the setting persistently in WirePlumber's state storage.
|
|
||||||
Even though it is not in the configuration file, this saved value will be
|
|
||||||
applied automatically when WirePlumber is started.
|
|
||||||
|
|
||||||
.. attention::
|
|
||||||
|
|
||||||
When a setting's value is saved, it will override the value from the
|
|
||||||
configuration file. Changing the value in the configuration file will
|
|
||||||
have no effect until the saved value is removed. Use the ``--delete``
|
|
||||||
switch in ``wpctl`` to remove a saved value (see below).
|
|
||||||
|
|
||||||
With ``wpctl``, it is also possible to restore a setting to its default value
|
|
||||||
(taken from the schema), by using the ``--reset`` option. For example, to reset
|
|
||||||
the ``device.routes.default-sink-volume`` setting, the following command can be
|
|
||||||
used:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings --reset device.routes.default-sink-volume
|
|
||||||
Reset setting 'device.routes.default-sink-volume' successfully
|
|
||||||
$ wpctl settings device.routes.default-sink-volume
|
|
||||||
Value: 0.064 (Saved: 0.5)
|
|
||||||
|
|
||||||
Note that the ``--reset`` option will only reset the setting to its default
|
|
||||||
value, but it will not remove the saved value from the state file. If you want
|
|
||||||
to remove the saved value, you can use the ``--delete`` option:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ wpctl settings --delete device.routes.default-sink-volume
|
|
||||||
Deleted setting 'device.routes.default-sink-volume' successfully
|
|
||||||
$ wpctl settings device.routes.default-sink-volume
|
|
||||||
Value: 0.064
|
|
||||||
|
|
||||||
A list of all the available settings can be found in the :ref:`config_settings`
|
|
||||||
section.
|
|
||||||
|
|
||||||
.. _config_modifying_configuration_static:
|
|
||||||
|
|
||||||
Modifying static options
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Static options always live in their own section of the configuration file.
|
|
||||||
Sections can be of two types: either a JSON object or a JSON array.
|
|
||||||
|
|
||||||
When dealing with a **JSON object**, you can add or modify a key-value pair by
|
|
||||||
creating a fragment like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
wireplumber.some-section = {
|
|
||||||
some.option = new_value
|
|
||||||
}
|
|
||||||
|
|
||||||
This is similar to what we have seen also above for modifying profile features
|
|
||||||
and settings (because both are JSON objects).
|
|
||||||
|
|
||||||
When dealing with a **JSON array**, any values that you define in a fragment
|
|
||||||
will be appended to the array. For example, to add a new rule to the
|
|
||||||
``monitor.alsa.rules`` array, you can create a fragment like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.alsa.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
device.name = "~alsa_card.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
api.alsa.use-ucm = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
This will add a new rule to the ``monitor.alsa.rules`` array, which will
|
|
||||||
be evaluated **after** all other rules that were parsed before. This is where
|
|
||||||
the order in which fragments are loaded actually matters.
|
|
||||||
|
|
||||||
If you don't want to append a new rule, but rather override the entire array
|
|
||||||
with a new one, you can do so by using the ``override.`` prefix on the array
|
|
||||||
name:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
override.monitor.alsa.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
device.name = "~alsa_card.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
api.alsa.use-ucm = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
This will now replace the entire ``monitor.alsa.rules`` array with this new one.
|
|
||||||
|
|
||||||
.. attention::
|
|
||||||
|
|
||||||
If you want to remove a rule from the array, you will need to override the
|
|
||||||
whole array with a new one that does not contain the rule you want to remove.
|
|
||||||
There is no way to remove a specific element from an array using fragments.
|
|
||||||
|
|
||||||
Another thing worth remembering here is that this behavior of appending values
|
|
||||||
to arrays also works in arrays that are nested inside other arrays or objects.
|
|
||||||
For example, consider this fragment:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.bluez.properties = {
|
|
||||||
bluez5.codecs = [ sbc_xq aac ldac ]
|
|
||||||
}
|
|
||||||
|
|
||||||
If this is the first time that the ``bluez5.codecs`` array is being defined, it
|
|
||||||
will be created with the given values. If it already exists, the given values
|
|
||||||
will be appended to the existing array. If you want to make sure that this
|
|
||||||
fragment will override the existing array, you need to use the ``override.``
|
|
||||||
prefix on the array name:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
monitor.bluez.properties = {
|
|
||||||
override.bluez5.codecs = [ sbc_xq aac ldac ]
|
|
||||||
}
|
|
||||||
|
|
||||||
The ``override.`` prefix may also be used in JSON object keys, to override the
|
|
||||||
entire object with a new one. For example, to override the entire
|
|
||||||
``monitor.bluez.properties`` object, you can use a fragment like this:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
override.monitor.bluez.properties = {
|
|
||||||
bluez5.codecs = [ sbc_xq aac ldac ]
|
|
||||||
}
|
|
||||||
|
|
||||||
Here, the entire ``monitor.bluez.properties`` object will be replaced with the
|
|
||||||
new one, and all previous key-value pairs configured will be discarded. This
|
|
||||||
also means that the ``bluez5.codecs`` array will be replaced with the new one
|
|
||||||
and does not require the ``override.`` prefix.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Even though WirePlumber uses PipeWire's syntax for configuration files, the
|
|
||||||
``override.`` prefix is a WirePlumber extension and does not work in
|
|
||||||
PipeWire.
|
|
||||||
|
|
||||||
.. _config_modifying_configuration_rules:
|
|
||||||
|
|
||||||
Working with rules
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Some of the static option sections in the configuration file are used to define
|
|
||||||
rules that are evaluated by WirePlumber at runtime. These rules are typically
|
|
||||||
used to match objects and perform actions on them. For example, the
|
|
||||||
``monitor.alsa.rules`` section is used to define rules that are evaluated by
|
|
||||||
the ALSA monitor to match ALSA devices and update their properties.
|
|
||||||
|
|
||||||
The syntax of these rules is the same as the syntax of
|
|
||||||
`PipeWire's rules <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire#rules>`_.
|
|
||||||
|
|
||||||
A rule is always a JSON object with two keys: ``matches`` and ``actions``. The
|
|
||||||
``matches`` key is used to define the conditions that need to be met for the
|
|
||||||
rule to be evaluated as true, and the ``actions`` key is used to define the
|
|
||||||
actions that are performed when the rule is evaluated as true.
|
|
||||||
|
|
||||||
The ``matches`` key is always a JSON array of objects, where each object
|
|
||||||
defines a condition that needs to be met. Each condition is a list of key-value
|
|
||||||
pairs, where the key is the name of the property that is being matched, and the
|
|
||||||
value is the value that the property needs to have. Within a condition, all
|
|
||||||
the key-value pairs are combined with a logical AND, and all the conditions in
|
|
||||||
the ``matches`` array are combined with a logical OR.
|
|
||||||
|
|
||||||
The ``actions`` key is always a JSON object, where each key-value pair defines
|
|
||||||
an action that is performed when the rule is evaluated as true. The action
|
|
||||||
name is specific to the rule and is defined by the rule's documentation, but
|
|
||||||
most frequently you will see the ``update-props`` action, which is used to
|
|
||||||
update the properties of the matched object.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
some.theoretical.rules = [
|
|
||||||
{
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
object.name = "my_object"
|
|
||||||
object.profile.name = "my_profile"
|
|
||||||
}
|
|
||||||
{
|
|
||||||
object.name = "other_object"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
actions = {
|
|
||||||
update-props = {
|
|
||||||
object.tag = "matched_by_my_rule"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
This rule is equivalent to the following expression:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
if (properties["object.name"] == "my_object" and properties["object.profile.name"] == "my_profile") or (properties["object.name"] == "other_object"):
|
|
||||||
properties["object.tag"] = "matched_by_my_rule"
|
|
||||||
|
|
||||||
In the ``matches`` array, it is also possible to use regular expressions to match
|
|
||||||
property values. For example, to match all nodes with a name that starts with
|
|
||||||
``my_``, you can use the following condition:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
node.name = "~my_.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
The ``~`` character signifies that the value is a regular expression. The exact
|
|
||||||
syntax of the regular expressions is the POSIX extended regex syntax, as
|
|
||||||
described in the `regex (7)` man page.
|
|
||||||
|
|
||||||
In addition to regular expressions, you may also use the ``!`` character to
|
|
||||||
negate a condition. For example, to match all nodes with a name that does not
|
|
||||||
start with ``my_``, you can use the following condition:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
node.name = "!~my_.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
The ``!`` character can be used with or without a regular expression. For
|
|
||||||
example, to match all nodes with a name that is not equal to ``my_node``,
|
|
||||||
you can use the following condition:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
matches = [
|
|
||||||
{
|
|
||||||
node.name = "!my_node"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
.. _config_settings:
|
|
||||||
|
|
||||||
Well-known settings
|
|
||||||
===================
|
|
||||||
|
|
||||||
This section describes the settings that can be configured on WirePlumber.
|
|
||||||
|
|
||||||
Settings can be either configured statically in the configuration file
|
|
||||||
by setting them under the ``wireplumber.settings`` section, or they can be
|
|
||||||
configured dynamically at runtime by using metadata.
|
|
||||||
|
|
||||||
For more information on what "settings" are and how they work, refer to the
|
|
||||||
previous section: :ref:`config_configuration_option_types`.
|
|
||||||
|
|
||||||
.. describe:: device.restore-profile
|
|
||||||
|
|
||||||
When a device profile is changed manually (e.g. via pavucontrol), WirePlumber
|
|
||||||
stores the selected profile and restores it when the device appears again
|
|
||||||
(e.g. after a reboot). If this setting is disabled, WirePlumber will always
|
|
||||||
pick the best profile for the device based on profile priorities and
|
|
||||||
availability (or custom rules, if any).
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
|
|
||||||
.. describe:: device.restore-routes
|
|
||||||
|
|
||||||
When a device route is changed manually (e.g. via pavucontrol), WirePlumber
|
|
||||||
stores the selected route and restores it when the same profile is
|
|
||||||
selected for this device. If this setting is disabled, WirePlumber will
|
|
||||||
always pick the best route for this device profile based on route priorities
|
|
||||||
and availability (or custom rules, if any).
|
|
||||||
|
|
||||||
This setting also enables WirePlumber to restore properties of the device
|
|
||||||
route when the route is restored. This includes the volume levels of sources
|
|
||||||
and sinks, as well as the IEC958 codecs selected (for routes that support
|
|
||||||
encoded streams, such as HDMI).
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
|
|
||||||
.. describe:: device.routes.default-sink-volume
|
|
||||||
|
|
||||||
This option allows to set the default volume for sinks that are part of a
|
|
||||||
device route (e.g. ALSA PCM sinks). This is used when the route is restored
|
|
||||||
and the sink does not have a previously stored volume.
|
|
||||||
|
|
||||||
It is possible to override the value on a per-device basis with a property
|
|
||||||
(*not* a setting, so this would go into a configuration file) on the device
|
|
||||||
named ``device.routes.default-sink-volume``.
|
|
||||||
|
|
||||||
:Default value: ``0.4 ^ 3`` (40% on the cubic scale)
|
|
||||||
|
|
||||||
.. describe:: device.routes.default-source-volume
|
|
||||||
|
|
||||||
This option allows to set the default volume for sources that are part of a
|
|
||||||
device route (e.g. ALSA PCM sources). This is used when the route is restored
|
|
||||||
and the source does not have a previously stored volume.
|
|
||||||
|
|
||||||
It is possible to override the value on a per-device basis with a property
|
|
||||||
(*not* a setting, so this would go into a configuration file) on the device
|
|
||||||
named ``device.routes.default-source-volume``.
|
|
||||||
|
|
||||||
:Default value: ``1.0`` (100%)
|
|
||||||
|
|
||||||
.. describe:: linking.allow-moving-streams
|
|
||||||
|
|
||||||
This option allows moving streams by overriding their target via metadata.
|
|
||||||
When enabled, WirePlumber monitors the "default" metadata for changes in the
|
|
||||||
``target.object`` key of streams and if this key is set to a valid node name
|
|
||||||
(``node.name``) or serial (``object.serial``), the stream is moved to that
|
|
||||||
target node.
|
|
||||||
|
|
||||||
This is used by applications such as pavucontrol and is recommended for
|
|
||||||
compatibility with PulseAudio.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
On the metadata, the ``target.node`` key is also supported for
|
|
||||||
compatibility with older versions of PipeWire, but it is deprecated.
|
|
||||||
Please use the ``target.object`` key instead.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
:See also: ``node.stream.restore-target``
|
|
||||||
|
|
||||||
.. describe:: linking.follow-default-target
|
|
||||||
|
|
||||||
When a stream was started with the ``target.object`` property, WirePlumber
|
|
||||||
normally links that stream to that target node and ignores the "default"
|
|
||||||
target for that direction. However, if this option is enabled, WirePlumber
|
|
||||||
will check if the designated target node *is* the "default" target and if so,
|
|
||||||
it will act as if the stream did not have that property.
|
|
||||||
|
|
||||||
In practice, this means that if the "default" target changes at runtime,
|
|
||||||
the stream will be moved to the new "default" target.
|
|
||||||
|
|
||||||
This is what Pulseaudio does and is implemented here for compatibility
|
|
||||||
with some applications that do start with a ``target.object`` property
|
|
||||||
set to the "default" target and expect the stream to be moved when the
|
|
||||||
"default" target changes.
|
|
||||||
|
|
||||||
Note that this logic is only applied on client (i.e. application) streams
|
|
||||||
and *not* on filters.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
|
|
||||||
.. describe:: linking.pause-playback
|
|
||||||
|
|
||||||
When an audio sink is removed, pause media players that have streams
|
|
||||||
playing to it. Pausing is done via MPRIS interface.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
|
|
||||||
.. describe:: node.features.audio.no-dsp
|
|
||||||
|
|
||||||
When this option is set to ``true``, audio nodes will not be configured
|
|
||||||
in dsp mode, meaning that their channels will *not* be split into separate
|
|
||||||
ports and that the audio data will *not* be converted to the float 32 format
|
|
||||||
(F32P). Instead, devices will be configured in passthrough mode and streams
|
|
||||||
will be configured in convert mode, so that their audio data is converted
|
|
||||||
directly to the format that the device is expecting.
|
|
||||||
|
|
||||||
This may be useful if you are trying to minimize audio processing for an
|
|
||||||
embedded system, but it is not recommended for general use.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
This option **will break** compatibility with JACK applications
|
|
||||||
and may also break certain patchbay applications. Do not enable, unless
|
|
||||||
you understand what you are doing.
|
|
||||||
|
|
||||||
:Default value: ``false``
|
|
||||||
|
|
||||||
.. describe:: node.features.audio.monitor-ports
|
|
||||||
|
|
||||||
This enables the creation of "monitor" ports for audio nodes. Monitor ports
|
|
||||||
are created on nodes that have input ports (i.e. sinks and capture streams)
|
|
||||||
and allow monitoring of the audio data that is being sent to the node.
|
|
||||||
|
|
||||||
This is mostly used by monitoring applications, such as pavucontrol.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
|
|
||||||
.. describe:: node.features.audio.control-port
|
|
||||||
|
|
||||||
This enables the creation of a "control" port for audio nodes. Control ports
|
|
||||||
allow sending MIDI data to the node, allowing for control of certain node's
|
|
||||||
parameters (such as volume) via external controllers.
|
|
||||||
|
|
||||||
:Default value: ``false``
|
|
||||||
|
|
||||||
.. describe:: node.stream.restore-props
|
|
||||||
|
|
||||||
WirePlumber stores stream parameters such as volume and mute status for each
|
|
||||||
client (i.e. application) stream. If this setting is enabled, WirePlumber
|
|
||||||
will restore the previously stored stream parameters when the stream is
|
|
||||||
activated. If it is disabled, stream parameters will be initialized to their
|
|
||||||
default values.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
|
|
||||||
.. describe:: node.stream.restore-target
|
|
||||||
|
|
||||||
When a client (i.e. application) stream is manually moved to a different
|
|
||||||
target node (e.g. via pavucontrol), the target node is stored by WirePlumber.
|
|
||||||
If this setting is enabled, WirePlumber will restore the previously stored
|
|
||||||
target node when the stream is activated.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
This does not restore manual links made by patchbay applications. This
|
|
||||||
is only meant to restore the ``target.object`` property in the "default"
|
|
||||||
metadata, which is manipulated by applications such as pavucontrol when
|
|
||||||
a stream is moved to a different target.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
:See also: ``linking.allow-moving-streams``
|
|
||||||
|
|
||||||
.. describe:: node.stream.default-playback-volume
|
|
||||||
|
|
||||||
The default volume for playback streams to be applied when the stream is
|
|
||||||
activated. This is only applied when ``node.stream.restore-props`` is
|
|
||||||
``true`` and the stream does not have a previously stored volume.
|
|
||||||
|
|
||||||
:Default value: ``1.0``
|
|
||||||
:Range: ``0.0`` to ``1.0``
|
|
||||||
|
|
||||||
.. describe:: node.stream.default-capture-volume
|
|
||||||
|
|
||||||
The default volume for capture streams to be applied when the stream is
|
|
||||||
activated. This is only applied when ``node.stream.restore-props`` is
|
|
||||||
``true`` and the stream does not have a previously stored volume.
|
|
||||||
|
|
||||||
:Default value: ``1.0``
|
|
||||||
:Range: ``0.0`` to ``1.0``
|
|
||||||
|
|
||||||
.. describe:: node.filter.forward-format
|
|
||||||
|
|
||||||
When a "filter" pair of nodes (such as echo-cancel or filter-chain) is
|
|
||||||
linked to a device node that has a different channel map than the filter
|
|
||||||
nodes, this option allows the channel map of the filter nodes to be changed
|
|
||||||
to match the channel map of the device node. The change is applied to both
|
|
||||||
ends of the "filter", so that any streams linked to the filter are also
|
|
||||||
reconfigured to match the target channel map.
|
|
||||||
|
|
||||||
This is useful, for instance, to make sure that an application will be
|
|
||||||
properly configured to output surround audio to a surround device, even
|
|
||||||
when going through a filter that was not explicitly configured to have
|
|
||||||
a surround channel map.
|
|
||||||
|
|
||||||
:Default value: ``false``
|
|
||||||
|
|
||||||
.. describe:: node.restore-default-targets
|
|
||||||
|
|
||||||
This setting enables WirePlumber to store and restore the "default" source
|
|
||||||
and sink targets of the graph. In PulseAudio terminology, this is also known
|
|
||||||
as the "fallback" source and sink.
|
|
||||||
|
|
||||||
When this setting is enabled, WirePlumber will store the "default" source
|
|
||||||
and sink targets when they are changed manually (e.g. via pavucontrol) and
|
|
||||||
restore them when the available nodes change or after a reload/restart.
|
|
||||||
It will also store a history of past selected "default" targets and restore
|
|
||||||
previously selected ones if the currently selected are not available.
|
|
||||||
|
|
||||||
If this is disabled, WirePlumber will pick the best available source
|
|
||||||
and sink targets based on their priorities, but it will also respect
|
|
||||||
manual user selections that are done at runtime - it will just not remember
|
|
||||||
them so that it can restore them at a later time.
|
|
||||||
|
|
||||||
:Default value: ``true``
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
.. _daemon_file_locations:
|
|
||||||
|
|
||||||
Locations of WirePlumber's files
|
|
||||||
================================
|
|
||||||
|
|
||||||
.. _config_locations:
|
|
||||||
|
|
||||||
Location of configuration files
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
WirePlumber's default locations of its configuration files are the following,
|
|
||||||
in order of priority:
|
|
||||||
|
|
||||||
1. ``$XDG_CONFIG_HOME/wireplumber``
|
|
||||||
2. ``$XDG_CONFIG_DIRS/wireplumber``
|
|
||||||
3. ``$sysconfdir/wireplumber``
|
|
||||||
4. ``$XDG_DATA_DIRS/wireplumber``
|
|
||||||
5. ``$datadir/wireplumber``
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
* ``$syscondir`` and ``$datadir`` refer to
|
|
||||||
`meson's directory options <https://mesonbuild.com/Builtin-options.html#directories>`_
|
|
||||||
and are hardcoded at build time
|
|
||||||
* ``$XDG_`` variables refer to the
|
|
||||||
`XDG Base Directory Specification <https://specifications.freedesktop.org/basedir-spec/latest/index.html>`_
|
|
||||||
|
|
||||||
It is recommended that user specific overrides are placed in
|
|
||||||
``$XDG_CONFIG_HOME/wireplumber``, while host-specific configuration is placed in
|
|
||||||
``$XDG_CONFIG_DIRS/wireplumber`` or ``$sysconfdir/wireplumber`` and
|
|
||||||
distribution-provided configuration is placed in ``$XDG_DATA_DIRS/wireplumber``
|
|
||||||
or ``$datadir/wireplumber``.
|
|
||||||
|
|
||||||
At runtime, WirePlumber will seek out the directory with the highest priority
|
|
||||||
that contains the required configuration file. This setup allows a user or
|
|
||||||
system administrator to effortlessly override the configuration files provided
|
|
||||||
by the distribution. They can achieve this by placing a file with an identical
|
|
||||||
name in a higher priority directory.
|
|
||||||
|
|
||||||
It is also possible to override the configuration directory by setting the
|
|
||||||
``WIREPLUMBER_CONFIG_DIR`` environment variable:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
WIREPLUMBER_CONFIG_DIR=src/config wireplumber
|
|
||||||
|
|
||||||
``WIREPLUMBER_CONFIG_DIR`` supports listing multiple directories, using the
|
|
||||||
standard path list separator ``:``. If multiple directories are specified,
|
|
||||||
the first one has the highest priority and the last one has the lowest.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
When the configuration directory is overridden with
|
|
||||||
``WIREPLUMBER_CONFIG_DIR``, the default locations are ignored and
|
|
||||||
configuration files are *only* looked up in the directories specified by this
|
|
||||||
variable.
|
|
||||||
|
|
||||||
.. _config_locations_fragments:
|
|
||||||
|
|
||||||
Configuration fragments
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
WirePlumber also supports configuration fragments. These are configuration files
|
|
||||||
that are loaded in addition to the main configuration file, allowing to
|
|
||||||
override or extend the configuration without having to copy the whole file.
|
|
||||||
See also the :ref:`config_conf_file_fragments` section for semantics.
|
|
||||||
|
|
||||||
Configuration fragments are always loaded from subdirectories of the main search
|
|
||||||
directories that have the same name as the configuration file, with the ``.d``
|
|
||||||
suffix appended. For example, if WirePlumber loads ``wireplumber.conf``, it will
|
|
||||||
also load ``wireplumber.conf.d/*.conf``. Note also that the fragment files need
|
|
||||||
to have the ``.conf`` suffix.
|
|
||||||
|
|
||||||
When WirePlumber loads a configuration file from the default locations, it will
|
|
||||||
also load all configuration fragments that are present in all of the default
|
|
||||||
locations, but following the reverse order of priority. This allows
|
|
||||||
configuration fragments that are installed in more system-wide locations to be
|
|
||||||
overridden by the system administrator or the users.
|
|
||||||
|
|
||||||
For example, assuming WirePlumber loads ``wireplumber.conf``, from any of the
|
|
||||||
search locations, it will also locate and load the following fragments, in this
|
|
||||||
order:
|
|
||||||
|
|
||||||
1. ``$datadir/wireplumber/wireplumber.conf.d/*.conf``
|
|
||||||
2. ``$XDG_DATA_DIRS/wireplumber/wireplumber.conf.d/*.conf``
|
|
||||||
3. ``$sysconfdir/wireplumber/wireplumber.conf.d/*.conf``
|
|
||||||
4. ``$XDG_CONFIG_DIRS/wireplumber/wireplumber.conf.d/*.conf``
|
|
||||||
5. ``$XDG_CONFIG_HOME/wireplumber/wireplumber.conf.d/*.conf``
|
|
||||||
|
|
||||||
Within each search location that contains fragments, the individual fragment
|
|
||||||
files are opened in alphanumerical order. This can be important to know, because
|
|
||||||
the parsing order matters in merging. See :ref:`config_conf_file_fragments`
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
When ``WIREPLUMBER_CONFIG_DIR`` is set, the default locations are ignored and
|
|
||||||
fragment files are *only* looked up in the directories specified by this
|
|
||||||
variable.
|
|
||||||
|
|
||||||
.. _config_locations_scripts:
|
|
||||||
|
|
||||||
Location of scripts
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
WirePlumber's default locations of its data files are the following,
|
|
||||||
in order of priority:
|
|
||||||
|
|
||||||
1. ``$XDG_DATA_HOME/wireplumber``
|
|
||||||
2. ``$XDG_DATA_DIRS/wireplumber``
|
|
||||||
3. ``$datadir/wireplumber``
|
|
||||||
|
|
||||||
At runtime, WirePlumber will search the directories for the highest-priority
|
|
||||||
directory to contain the needed data file.
|
|
||||||
|
|
||||||
Scripts are a specific kind of "data" files and are expected to be located
|
|
||||||
within a ``scripts`` subdirectory in the above data search locations. The "data"
|
|
||||||
directory is a somewhat more generic path that may be used for other kinds of
|
|
||||||
data files in the future.
|
|
||||||
|
|
||||||
It is also possible to override the data directory by setting the
|
|
||||||
``WIREPLUMBER_DATA_DIR`` environment variable:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
WIREPLUMBER_DATA_DIR=src wireplumber
|
|
||||||
|
|
||||||
As with the default data directories, script files in particular are expected
|
|
||||||
to be located within a ``scripts`` subdirectory, so in the above example the
|
|
||||||
scripts would actually reside in ``src/scripts``.
|
|
||||||
|
|
||||||
``WIREPLUMBER_DATA_DIR`` supports listing multiple directories, using the
|
|
||||||
standard path list separator ``:``. If multiple directories are specified,
|
|
||||||
the first one has the highest priority and the last one has the lowest.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
When ``WIREPLUMBER_DATA_DIR`` is set, the default locations are ignored and
|
|
||||||
scripts are *only* looked up in the directories specified by this variable.
|
|
||||||
|
|
||||||
Location of modules
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
WirePlumber modules
|
|
||||||
^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
WirePlumber's default location of its modules is
|
|
||||||
``$libdir/wireplumber-$api_version``, where ``$libdir`` is set at compile time
|
|
||||||
by the build system. Typically, it ends up being ``/usr/lib/wireplumber-0.5``
|
|
||||||
(or ``/usr/lib/<arch-triplet>/wireplumber-0.5`` on multiarch systems)
|
|
||||||
|
|
||||||
It is possible to override this directory at runtime by setting the
|
|
||||||
``WIREPLUMBER_MODULE_DIR`` environment variable:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
WIREPLUMBER_MODULE_DIR=build/modules wireplumber
|
|
||||||
|
|
||||||
``WIREPLUMBER_MODULE_DIR`` supports listing multiple directories, using the
|
|
||||||
standard path list separator ``:``. If multiple directories are specified, the
|
|
||||||
first one has the highest priority and the last one has the lowest.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
When ``WIREPLUMBER_MODULE_DIR`` is set, the default locations are ignored and
|
|
||||||
scripts are *only* looked up in the directories specified by this variable.
|
|
||||||
|
|
||||||
PipeWire and SPA modules
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
PipeWire and SPA modules are not loaded from the same location as WirePlumber's
|
|
||||||
modules. They are loaded from the location that PipeWire loads them.
|
|
||||||
|
|
||||||
It is also possible to override these locations by using environment variables:
|
|
||||||
``SPA_PLUGIN_DIR`` and ``PIPEWIRE_MODULE_DIR``. For more details, refer to
|
|
||||||
PipeWire's documentation.
|
|
||||||
|
|
@ -1,248 +0,0 @@
|
||||||
.. _daemon_logging:
|
|
||||||
|
|
||||||
Debug Logging
|
|
||||||
=============
|
|
||||||
|
|
||||||
WirePlumber is instrumented with log messages in its entire codebase. These
|
|
||||||
messages are categorized based on two heuristics: the log topic and the log
|
|
||||||
level.
|
|
||||||
|
|
||||||
The log topic is a string that identifies which component of the code this
|
|
||||||
message is coming from. Well-known topics include:
|
|
||||||
|
|
||||||
- **wireplumber**: messages from the wireplumber daemon
|
|
||||||
- **wp-***: messages from libwireplumber
|
|
||||||
|
|
||||||
- **wp-core**: messages from *WpCore*
|
|
||||||
- **wp-proxy**: messages from *WpProxy*
|
|
||||||
- ... and so on ...
|
|
||||||
|
|
||||||
- **m-***: messages from wireplumber modules
|
|
||||||
|
|
||||||
- **m-default-profile**: messages from *libwireplumber-module-default-profile*
|
|
||||||
- **m-default-routes**: messages from *libwireplumber-module-default-routes*
|
|
||||||
- ... and so on ...
|
|
||||||
|
|
||||||
- **s-***: messages from scripts
|
|
||||||
|
|
||||||
- **s-linking**: messages from the *linking/\*.lua* scripts
|
|
||||||
- **s-default-nodes**: messages from the *default-nodes/\*.lua* scripts
|
|
||||||
- ... and so on ...
|
|
||||||
|
|
||||||
- **pw.***: messages from libpipewire
|
|
||||||
- **spa.***: messages from spa plugins
|
|
||||||
- **mod.***: messages from libpipewire modules
|
|
||||||
- **conn.***: messages to debug the pipewire socket connection
|
|
||||||
|
|
||||||
The log level is a value that designates the importance of the message.
|
|
||||||
The levels that exist in WirePlumber are the following:
|
|
||||||
|
|
||||||
- ``F``: *Fatal errors*. These messages represent situations where execution
|
|
||||||
of the program cannot continue. In the extremely unlikely case that
|
|
||||||
they appear, these messages also cause the process to be terminated.
|
|
||||||
- ``E``: *Critical warnings* (or "errors" in the PipeWire terminology).
|
|
||||||
These messages represent situations where something unexpected has happened
|
|
||||||
and someone with understanding of the code should probably take a look at it.
|
|
||||||
These situations are usually programming mistakes or omissions.
|
|
||||||
This does not necessarily mean that the program is not functioning correctly.
|
|
||||||
It may mean, though, that the specific part of the program that logged the
|
|
||||||
message (the plugin, subsystem, ...) may not work optimally.
|
|
||||||
- ``W``: *Warnings*. These messages represent situations where something has
|
|
||||||
gone unintentionally wrong, but it was not totally unexpected. The situation
|
|
||||||
is recovered and the program can continue. In many cases, this warning may
|
|
||||||
mean that there is something wrong with the configuration or the environment
|
|
||||||
and may need attention from the user.
|
|
||||||
- ``N``: *Notices*. These are important messages that the user should notice,
|
|
||||||
like warnings, but they do not necessarily mean a bad situation.
|
|
||||||
- ``I``: *Informational messages*. These messages provide information about
|
|
||||||
the internal operations of the program.
|
|
||||||
- ``D``: *Debug messages*. These messages provide details about the
|
|
||||||
internal operations of the program, which can be useful for debugging.
|
|
||||||
- ``T``: *Traces*. These messages provide very verbose printouts of internal
|
|
||||||
operations and data that affects these operations. These can be useful for
|
|
||||||
debugging as well, but it may be best to be enabled only for the topic(s)
|
|
||||||
that are intended to be debugged, as they can be very big in volume.
|
|
||||||
|
|
||||||
By default, WirePlumber logs only messages from levels ``F``, ``E``, ``W``
|
|
||||||
and ``N``. These messages are printed on the standard error (``stderr``) stream,
|
|
||||||
or they are logged to the systemd journal, if WirePlumber was started as a
|
|
||||||
systemd service.
|
|
||||||
|
|
||||||
The ``WIREPLUMBER_DEBUG`` environment variable can be used to change which
|
|
||||||
topics and levels are enabled. The generic syntax is:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
WIREPLUMBER_DEBUG=[<topic pattern>:]<level>,...,
|
|
||||||
|
|
||||||
This is a comma-separated list of topics to enable, paired with a level for
|
|
||||||
each topic.
|
|
||||||
|
|
||||||
``<level>`` can be one of ``FEWNIDT`` or a numerical log level as listed below.
|
|
||||||
|
|
||||||
0. fatal errors (``F``)
|
|
||||||
1. critical warnings (``E``)
|
|
||||||
2. warnings and notices (``W`` & ``N``)
|
|
||||||
3. informational messages (``I``)
|
|
||||||
4. debug messages (``D``)
|
|
||||||
5. trace messages (``T``)
|
|
||||||
|
|
||||||
Each level always includes messages from the previous levels, so for instance
|
|
||||||
enabling level ``3`` (or ``I``) will also enable messages from levels ``2``
|
|
||||||
and ``1`` (``N``, ``W``, ``E`` and ``F``)
|
|
||||||
|
|
||||||
``<topic pattern>`` is an *optional* description of one or more topics.
|
|
||||||
This supports
|
|
||||||
`glob style patterns <https://developer-old.gnome.org/glib/stable/glib-Glob-style-pattern-matching.html>`_
|
|
||||||
containing ``*`` and ``?``.
|
|
||||||
|
|
||||||
If a ``<topic pattern>`` is not specified, then the given ``<level>`` is
|
|
||||||
considered to be the global log level, which applies to all topics that have
|
|
||||||
no explicit level specified.
|
|
||||||
|
|
||||||
Changing log level at runtime
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
The debug log level can be changed at runtime using ``wpctl``:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
wpctl set-log-level D # enable debug logging for Wireplumber
|
|
||||||
wpctl set-log-level - # restore default logging for Wireplumber
|
|
||||||
|
|
||||||
wpctl set-log-level 0 4 # enable debug logging for Pipewire daemon
|
|
||||||
wpctl set-log-level 0 - # restore default logging for Pipewire daemon
|
|
||||||
|
|
||||||
Equivalently, it is also possible to adjust the logging by setting
|
|
||||||
``log.level`` in the ``settings`` metadata:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
pw-metadata -n settings <ID> log.level "D" # WirePlumber logging
|
|
||||||
|
|
||||||
pw-metadata -n settings 0 log.level 4 # PipeWire daemon logging
|
|
||||||
|
|
||||||
Above, ``<ID>`` should be replaced by the WirePlumber daemon client ID.
|
|
||||||
|
|
||||||
Note that PipeWire daemon log levels must be specified by numbers, not
|
|
||||||
letter codes.
|
|
||||||
|
|
||||||
Changing log level via static configuration
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
If you need to capture logs from WirePlumber at startup or in other circumstances
|
|
||||||
where changing the level at runtime or setting an environment variable is not
|
|
||||||
feasible, then you may also set the log level in the configuration file.
|
|
||||||
|
|
||||||
The log level changes via the ``log.level`` key in the ``context.properties``
|
|
||||||
section:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
context.properties = {
|
|
||||||
log.level = "D"
|
|
||||||
}
|
|
||||||
|
|
||||||
You may use the same syntax as in ``WIREPLUMBER_DEBUG`` to describe the exact
|
|
||||||
logging you want to achieve. For instance, to log debug messages from all
|
|
||||||
scripts and informational messages from everywhere else:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
context.properties = {
|
|
||||||
log.level = "I,s-*:D"
|
|
||||||
}
|
|
||||||
|
|
||||||
The easiest way to configure this is to drop a
|
|
||||||
:ref:`fragment file <config_conf_file_fragments>` that contains just this.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ mkdir -p ~/.config/wireplumber/wireplumber.conf.d
|
|
||||||
$ echo 'context.properties = { log.level = "D" }' > ~/.config/wireplumber/wireplumber.conf.d/log.conf
|
|
||||||
|
|
||||||
See also :ref:`config_modifying_configuration`
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
|
|
||||||
Show *all* messages:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
WIREPLUMBER_DEBUG=T
|
|
||||||
|
|
||||||
Show all messages up to the *debug* level (F, E, W, N, I & D), excluding *trace*:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
WIREPLUMBER_DEBUG=D
|
|
||||||
|
|
||||||
Show all messages up to the *notice* level (F, E, W & N),
|
|
||||||
excluding *info*, *debug* & *trace*
|
|
||||||
(this is also the default when ``WIREPLUMBER_DEBUG`` is omitted):
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
WIREPLUMBER_DEBUG=2
|
|
||||||
|
|
||||||
Show all messages from the wireplumber library (including traces), but only
|
|
||||||
up to informational messages from other topics:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
WIREPLUMBER_DEBUG=I,wp-*:T
|
|
||||||
|
|
||||||
Show debug messages from ``wp-registry``, libpipewire and all modules, keeping
|
|
||||||
all other topics up to the *notice* level.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
WIREPLUMBER_DEBUG=2,wp-registry:4,pw.*:4,m-*:4
|
|
||||||
|
|
||||||
Relationship with the GLib log handler & G_MESSAGES_DEBUG
|
|
||||||
---------------------------------------------------------
|
|
||||||
|
|
||||||
Older versions of WirePlumber used to use ``G_MESSAGES_DEBUG`` to control their
|
|
||||||
log output, which is the environment variable that affects GLib's default
|
|
||||||
log handler.
|
|
||||||
|
|
||||||
As of WirePlumber 0.3, ``G_MESSAGES_DEBUG`` is no longer used, since
|
|
||||||
libwireplumber replaces the default log handler.
|
|
||||||
|
|
||||||
If you are writing your own application based on libwireplumber, you can choose
|
|
||||||
if you want to replace this log handler using the flags passed to
|
|
||||||
:c:func:`wp_init`.
|
|
||||||
|
|
||||||
Relationship with the PipeWire log handler & PIPEWIRE_DEBUG
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
libpipewire uses the ``PIPEWIRE_DEBUG`` environment variable, with a similar syntax.
|
|
||||||
WirePlumber replaces the log handler of libpipewire with its own, rendering
|
|
||||||
``PIPEWIRE_DEBUG`` useless. Instead, you should use ``WIREPLUMBER_DEBUG``.
|
|
||||||
All the log topics that apply to libpipewire and its modules / plugins work
|
|
||||||
the same in ``WIREPLUMBER_DEBUG``.
|
|
||||||
|
|
||||||
If you are writing your own application based on libwireplumber, you can choose
|
|
||||||
if you want to replace this log handler using the flags passed to
|
|
||||||
:c:func:`wp_init`.
|
|
||||||
|
|
||||||
Mapping of PipeWire debug levels to WirePlumber
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
PipeWire supports 5 levels of debug logging. WirePlumber, on the other hand,
|
|
||||||
supports 7 levels. Some levels seem common, but the terminology and the
|
|
||||||
semantics are slightly different. The following table shows how the various
|
|
||||||
levels are mapped:
|
|
||||||
|
|
||||||
============= =============== ========================
|
|
||||||
Numeric Level PipeWire WirePlumber
|
|
||||||
============= =============== ========================
|
|
||||||
0 no log ``F`` - Fatal Error
|
|
||||||
1 ``E`` - Error ``E`` - Critical Warning
|
|
||||||
2 ``W`` - Warning ``W`` - Warning,
|
|
||||||
``N`` - Notice
|
|
||||||
3 ``I`` - Info ``I`` - Info
|
|
||||||
4 ``D`` - Debug ``D`` - Debug
|
|
||||||
5 ``T`` - Trace ``T`` - Trace
|
|
||||||
============= =============== ========================
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
# you need to add here any files you add to the toc directory as well
|
|
||||||
sphinx_files += files(
|
|
||||||
'installing.rst',
|
|
||||||
'running.rst',
|
|
||||||
'configuration.rst',
|
|
||||||
'locations.rst',
|
|
||||||
'logging.rst',
|
|
||||||
'multi_instance.rst',
|
|
||||||
)
|
|
||||||
|
|
||||||
subdir('configuration')
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
.. _daemon_multi_instance:
|
|
||||||
|
|
||||||
Running multiple instances
|
|
||||||
==========================
|
|
||||||
|
|
||||||
WirePlumber has the ability to run either as a single instance daemon or as
|
|
||||||
multiple instances, meaning that there can be multiple processes, each one
|
|
||||||
doing a different task.
|
|
||||||
|
|
||||||
The most common use case for such a setup is to separate the graph orchestration
|
|
||||||
tasks from the device monitoring and object creation ones. This can be useful
|
|
||||||
for robustness and security reasons, as it allows restarting the device monitors
|
|
||||||
or running them in different security contexts without affecting the rest of the
|
|
||||||
session management functionality.
|
|
||||||
|
|
||||||
To achieve a multi-instance setup, WirePlumber can be started multiple times
|
|
||||||
with a different :ref:`profile<config_components_and_profiles>` loaded in each
|
|
||||||
instance. This can be achieved using the ``--profile`` command line option to
|
|
||||||
select the profile to load:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ wireplumber --profile=custom
|
|
||||||
|
|
||||||
When no particular profile is specified, the ``main`` profile is loaded.
|
|
||||||
|
|
||||||
For multi-instance configuration, the default ``wireplumber.conf`` specifies 4
|
|
||||||
profiles:
|
|
||||||
|
|
||||||
.. describe:: policy
|
|
||||||
|
|
||||||
This profile runs all the policy scripts, i.e. ones that monitor changes
|
|
||||||
in the graph and execute actions to link nodes, select default devices,
|
|
||||||
create new nodes or configure existing ones differently.
|
|
||||||
|
|
||||||
.. describe:: audio
|
|
||||||
|
|
||||||
The audio profile runs the ALSA and ALSA MIDI monitors, which make audio &
|
|
||||||
MIDI devices available to PipeWire.
|
|
||||||
|
|
||||||
.. describe:: bluetooth
|
|
||||||
|
|
||||||
The bluetooth profile runs the BlueZ and BlueZ MIDI monitors, which enable
|
|
||||||
Bluetooth audio & MIDI devices and other Bluetooth functionality tied to the
|
|
||||||
A2DP, HSP, HFP and BAP profiles, using BlueZ.
|
|
||||||
|
|
||||||
.. describe:: video-capture
|
|
||||||
|
|
||||||
The video-capture profile runs the V4L2 and libcamera monitors, which make
|
|
||||||
video capture devices, such as cameras and HDMI capture cards, available
|
|
||||||
to PipeWire.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The ``main`` profile includes all the functionality of the ``policy``,
|
|
||||||
``audio``, ``video-capture`` and ``bluetooth`` profiles combined (i.e. it is
|
|
||||||
the default for a standard single instance configuration). You should never
|
|
||||||
load the ``main`` profile alongside these other 4 profiles, as their
|
|
||||||
functionality will conflict.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Always ensure that the instances you load serve a different purpose and they
|
|
||||||
do not conflict with each other. Conflicting components executed in parallel
|
|
||||||
will have undefined behavior.
|
|
||||||
|
|
||||||
Systemd integration
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
To make this easier to work with, a template systemd unit is provided, which is
|
|
||||||
meant to be started with the name of the profile as a template argument:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ systemctl --user disable wireplumber # disable the "main" profile instance
|
|
||||||
$ systemctl --user enable wireplumber@policy
|
|
||||||
$ systemctl --user enable wireplumber@audio
|
|
||||||
$ systemctl --user enable wireplumber@video-capture
|
|
||||||
$ systemctl --user enable wireplumber@bluetooth
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
In WirePlumber 0.4, the template argument was the name of the configuration
|
|
||||||
file to load, since profiles did not exist. In WirePlumber 0.5, the template
|
|
||||||
argument is the name of the profile and the configuration file is always
|
|
||||||
``wireplumber.conf``. To change the name of the configuration file you need
|
|
||||||
to craft custom systemd unit files and use the ``--config-file`` command line
|
|
||||||
option as needed.
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
.. _design_events_and_hooks:
|
|
||||||
|
|
||||||
Events and Hooks
|
|
||||||
================
|
|
||||||
|
|
||||||
Session management is all about reacting to events and taking necessary
|
|
||||||
actions. This is why WirePlumber's logic is all built on events and hooks.
|
|
||||||
|
|
||||||
Events
|
|
||||||
------
|
|
||||||
|
|
||||||
Events are objects that represent a change that has just happened on a PipeWire
|
|
||||||
object, or just a trigger for making a decision and potentially taking some
|
|
||||||
action.
|
|
||||||
|
|
||||||
Every event has a source, a subject and some properties, which include the
|
|
||||||
event type.
|
|
||||||
|
|
||||||
* The ``source`` is a reference to the GObject that created this event.
|
|
||||||
Typically, this is the ``WpStandardEventSource`` plugin.
|
|
||||||
* The ``subject`` is an *optional* reference to the object that this event
|
|
||||||
is about. For example, in a ``node-added`` event, the ``subject`` would be
|
|
||||||
a reference to the ``WpNode`` object that was just added. Some events,
|
|
||||||
especially those which are used only to trigger actions, do not have a
|
|
||||||
subject.
|
|
||||||
* The ``properties`` is a dictionary that contains information about the event,
|
|
||||||
including the event type, and also includes all the PipeWire properties of the
|
|
||||||
``subject``, if there is one.
|
|
||||||
* The ``event.type`` property describes the nature of the event, for example
|
|
||||||
``node-added`` or ``metadata-changed`` are some valid event types.
|
|
||||||
|
|
||||||
Every event also has a priority. Events with a higher priority are processed
|
|
||||||
before events with a lower priority. When two or more events have the same
|
|
||||||
priority, they are processed in a first-in-first-out manner. This logic
|
|
||||||
is defined in the *event dispatcher*.
|
|
||||||
|
|
||||||
Events are short-lived objects. They are created at the time that something is
|
|
||||||
happening and they are destroyed after they get processed. Processing an event
|
|
||||||
means executing all the hooks that are associated with it. The next section
|
|
||||||
explains what hooks are and how they are associated with events.
|
|
||||||
|
|
||||||
Hooks
|
|
||||||
-----
|
|
||||||
|
|
||||||
Hooks are objects that represent a runnable action that needs to be executed
|
|
||||||
when a certain event is processed. Every hook, therefore, consists of a
|
|
||||||
function - synchronous or asynchronous - that can be executed. Additionally,
|
|
||||||
every hook has a means to associate itself with specific events. This is
|
|
||||||
normally done by declaring *interest* to specific event properties or
|
|
||||||
combinations of them.
|
|
||||||
|
|
||||||
There are two main types of hooks: ``SimpleEventHook`` and ``AsyncEventHook``.
|
|
||||||
|
|
||||||
* ``SimpleEventHook`` contains a single, synchronous function. As soon as this
|
|
||||||
function is executed, the hook is completed.
|
|
||||||
* ``AsyncEventHook`` contains multiple functions, combined together in a state
|
|
||||||
machine using ``WpTransition`` underneath. The hook is completed only after
|
|
||||||
the state machine reaches its final state and this can take any amount of time
|
|
||||||
necessary.
|
|
||||||
|
|
||||||
Every hook also has a name, which can be an arbitrary string of characters.
|
|
||||||
Additionally, it has two arrays of names, which declare dependencies between
|
|
||||||
this hook and others. One array is called ``before`` and the other is called
|
|
||||||
``after``. The hook names in the ``before`` array specify that this hook must
|
|
||||||
be executed *before* those other hooks. Similarly, the hook names in the
|
|
||||||
``after`` array specify that this hook must be executed *after* those other
|
|
||||||
hooks. Using this mechanism, it is possible to define the order in which
|
|
||||||
hooks will be executed, for a specific event.
|
|
||||||
|
|
||||||
Hooks are long-lived objects. They are created once, registered in the
|
|
||||||
*event dispatcher*, they are attached on events and detached after their
|
|
||||||
execution. They don't maintain any internal state, so the actions of the hook
|
|
||||||
depend solely on the event itself.
|
|
||||||
|
|
||||||
The Event Dispatcher
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
The event dispatcher is a (per core) singleton object that processes all events
|
|
||||||
and also maintains a list of all the registered hooks. It has a method to
|
|
||||||
*push* events on it, which causes them to be scheduled for processing.
|
|
||||||
|
|
||||||
Scheduling of events and hooks
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
The main idea and reasoning behind this architecture is to have everything
|
|
||||||
execute in a predefined order and always wait for an action to finish before
|
|
||||||
executing the next one.
|
|
||||||
|
|
||||||
Every event has a *priority* and every hook also has an order of execution that
|
|
||||||
derives from the inter-dependencies between hooks, which are defined with
|
|
||||||
``before`` and ``after`` (see above). When an event is pushed on the dispatcher,
|
|
||||||
the dispatcher goes through all the registered hooks and checks which hooks are
|
|
||||||
configured to run on this event (their event interest matches the event).
|
|
||||||
It then makes a list of them, sorted by their order of execution, and stores it
|
|
||||||
on the event. The event is then added on the dispatcher's list of events, which
|
|
||||||
is sorted by priority.
|
|
||||||
|
|
||||||
For example::
|
|
||||||
|
|
||||||
List of events
|
|
||||||
| event1 (prio 99) -> hook1, hook2, hook3
|
|
||||||
| event2 (prio 50) -> hook5, hook2, hook4
|
|
||||||
v
|
|
||||||
|
|
||||||
The dispatcher has an internal ``GSource`` that is registered with
|
|
||||||
``G_PRIORITY_HIGH_IDLE`` priority. When there is at least one event in the
|
|
||||||
list of events, the source is dispatched. Every time it gets dispatched,
|
|
||||||
it takes the top-most event (the highest priority one) and executes the highest
|
|
||||||
priority hook in that event. If the hook executes synchronously, it then takes
|
|
||||||
the next hook and continues until there are no more hooks on this event;
|
|
||||||
then it goes to the next event, and so on. If the hook, however, executes
|
|
||||||
asynchronously, processing stops until the hook finishes; after finishing,
|
|
||||||
processing resumes like before.
|
|
||||||
|
|
||||||
It is important to notice here that the list of events may be modified while
|
|
||||||
events are getting processed. For example, a device is added; that's a
|
|
||||||
``device-added`` event. Then a hook is executed to set the profile. That creates
|
|
||||||
nodes, so a couple of ``node-added`` events... But there is also another hook to
|
|
||||||
set the route, which was attached on the ``device-added`` event for the device.
|
|
||||||
Suppose that we give the ``node-added`` events lower priority than the
|
|
||||||
``device-added`` events, then the ``set-route`` hook will execute right after
|
|
||||||
the ``set-profile`` and before any ``node-added`` events are processed.
|
|
||||||
|
|
||||||
Visually, with sample priorities::
|
|
||||||
|
|
||||||
List of events
|
|
||||||
| "device-added" (prio 20) -> set-profile, set-route
|
|
||||||
| "node-added" (prio 10) -> restore-stream, create-session-item
|
|
||||||
v
|
|
||||||
|
|
||||||
Obviously, there can also be a case where a newly added event has higher
|
|
||||||
priority than the event that was being processed before. In that case,
|
|
||||||
processing the hooks of the original event is stopped until all the hooks from
|
|
||||||
the higher priority event have been processed. For example, a capture stream
|
|
||||||
node being added may trigger the "bluetooth autoswitch" hook, which will then
|
|
||||||
change the profile of a device. Changing the profile also has to trigger setting
|
|
||||||
a new route and also handling the new device nodes, creating session items for
|
|
||||||
them... After all this is done, processing the original capture stream
|
|
||||||
``node-added`` event can continue.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# you need to add here any files you add to the toc directory as well
|
|
||||||
sphinx_files += files(
|
|
||||||
'understanding_session_management.rst',
|
|
||||||
'understanding_wireplumber.rst',
|
|
||||||
'events_and_hooks.rst',
|
|
||||||
)
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
.. _design_understanding_session_management:
|
|
||||||
|
|
||||||
Understanding Session Management
|
|
||||||
================================
|
|
||||||
|
|
||||||
The PipeWire session manager is a tool that is tasked to do a lot of things.
|
|
||||||
Many people understand the term "session manager" as a tool that is responsible
|
|
||||||
for managing links between nodes, but that is only one of many tasks. To
|
|
||||||
understand the entirety of its operation, we need to discuss how PipeWire works,
|
|
||||||
first.
|
|
||||||
|
|
||||||
When PipeWire starts, it loads a set of modules that are defined in its
|
|
||||||
configuration file. These modules provide functionality to PipeWire, otherwise
|
|
||||||
it is just an empty process that does nothing. Under normal circumstances,
|
|
||||||
the modules that are loaded on PipeWire's startup contain object *factories*,
|
|
||||||
plus the native protocol module that allows inter-process communication.
|
|
||||||
Other than that, PipeWire does not really load or do anything else. This is
|
|
||||||
where session management begins.
|
|
||||||
|
|
||||||
Session management is basically about setting up PipeWire to do something
|
|
||||||
useful. This is achieved by utilizing PipeWire's exposed object factories to
|
|
||||||
create some useful objects, then work with their methods to modify and later
|
|
||||||
destroy them. Such objects include devices, nodes, ports, links and others.
|
|
||||||
This is a task that requires continuous monitoring and action taking, reacting
|
|
||||||
on a large number of different events that happen as the system is being used.
|
|
||||||
|
|
||||||
High-level areas of operation
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
The session management logic, in WirePlumber, is divided into 6 different areas
|
|
||||||
of operation:
|
|
||||||
|
|
||||||
1. Device Enablement
|
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Enabling devices is a fundamental area of operation. It is achieved by using
|
|
||||||
the device monitor objects (or just "monitors"), which are typically
|
|
||||||
implemented as SPA plugins in PipeWire, but they are loaded by WirePlumber.
|
|
||||||
Their task is to discover available media devices and create objects in PipeWire
|
|
||||||
that offer a way to interact with them.
|
|
||||||
|
|
||||||
Well-known monitors include:
|
|
||||||
|
|
||||||
- The ALSA monitor, which enables audio devices
|
|
||||||
- The ALSA MIDI monitor, which enables MIDI devices
|
|
||||||
- The libcamera monitor, which enables cameras
|
|
||||||
- The Video4Linux2 (V4L2) monitor, which also enables cameras, but also
|
|
||||||
other video capture devices through the V4L2 Linux API
|
|
||||||
- The BlueZ monitor, which enables bluetooth audio devices
|
|
||||||
|
|
||||||
2. Device Configuration
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Most devices expose complex functionality, from the computer's perspective, that
|
|
||||||
needs to be managed in order to provide a simple and smooth user experience.
|
|
||||||
For that reason, for example, audio devices are organized into *profiles* and
|
|
||||||
*routes*, which allow setting them up to serve a specific use case. These
|
|
||||||
need to be configured and managed by the session manager.
|
|
||||||
|
|
||||||
3. Client Access Control
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
When client applications connect to PipeWire, they need to obtain permissions
|
|
||||||
in order to be able to access the objects exposed by PipeWire and interact
|
|
||||||
with them. In some circumstances and configurations, the session manager is also
|
|
||||||
tasked with deciding which permissions should be granted to each client.
|
|
||||||
|
|
||||||
4. Node Configuration
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Nodes are the fundamental elements of media processing. They are typically
|
|
||||||
created either by the device monitors or by client applications. When they are
|
|
||||||
created, they are in a state where they cannot be linked. Linking them requires
|
|
||||||
some configuration, such as configuring the media format and subsequently
|
|
||||||
the number and the type of ports that should be exposed. Additionally, some
|
|
||||||
properties and metadata related to the node might need to be set according to
|
|
||||||
user preferences. All of this is taken care of by the session manager.
|
|
||||||
|
|
||||||
5. Link Management
|
|
||||||
^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
When nodes are finally ready to use, the session manager is also tasked to
|
|
||||||
decide how they should be linked together in order for media to flow though.
|
|
||||||
For instance, an audio playback stream node most likely needs to be linked to
|
|
||||||
the default audio output device node. The session manager then also needs to
|
|
||||||
create all these links and monitor all conditions that may affect them so that
|
|
||||||
dynamic re-linking is possible in case something changes
|
|
||||||
(ex. if a device disconnects). In some cases, device and node configuration
|
|
||||||
may also need to change as a result of links being created or destroyed.
|
|
||||||
|
|
||||||
6. Metadata Management
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
While in operation, PipeWire and WirePlumber both store some additional
|
|
||||||
properties about objects and their operation in storage that lives outside
|
|
||||||
these objects. These properties are referred to as "metadata" and they are
|
|
||||||
stored in "metadata objects". This metadata can be changed externally by tools
|
|
||||||
such as `pw-metadata`, but also others.
|
|
||||||
|
|
||||||
In some circumstances, this metadata needs to interact with logic inside
|
|
||||||
the session manager. Most notably, selecting the default audio and video inputs
|
|
||||||
and outputs is done by setting metadata. The session manager then needs to
|
|
||||||
validate this information, store it and restore it on the next restart, but also
|
|
||||||
ensure that the default inputs and outputs stay valid and reasonable when
|
|
||||||
devices are plugged and unplugged dynamically.
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
.. _design_understanding_wireplumber:
|
|
||||||
|
|
||||||
Understanding WirePlumber
|
|
||||||
=========================
|
|
||||||
|
|
||||||
Knowing the fundamentals of session management, let's see here how WirePlumber
|
|
||||||
is structured.
|
|
||||||
|
|
||||||
The library
|
|
||||||
-----------
|
|
||||||
|
|
||||||
WirePlumber is built on top of the `libwireplumber` library, which provides
|
|
||||||
fundamental building blocks for expressing all the session management logic.
|
|
||||||
Libwireplumber, which is written in C and based on GObject, wraps the PipeWire
|
|
||||||
API and offers a higher level and more convenient API. While the WirePlumber
|
|
||||||
daemon implements the session management logic, the underlying library can also
|
|
||||||
be utilized outside the scope of the WirePlumber daemon. This allows for the
|
|
||||||
creation of external tools and GUIs that interact with PipeWire.
|
|
||||||
|
|
||||||
Being GObject based, the library is introspectable and can be used from any
|
|
||||||
language that supports `GObject Introspection <https://gi.readthedocs.io/en/latest/>`_.
|
|
||||||
The library is also available as a C API.
|
|
||||||
|
|
||||||
The object model
|
|
||||||
^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The most fundamental code contained in the WirePlumber library is the object
|
|
||||||
model, i.e. its representation of PipeWire's objects.
|
|
||||||
|
|
||||||
PipeWire exposes several objects, such nodes and ports, via the IPC protocol
|
|
||||||
in a manner that is hard to interact with using standard object-oriented
|
|
||||||
principles, because it is asynchronous. For example, when an object is created,
|
|
||||||
its existence is announced over the protocol, but its properties are announced
|
|
||||||
later, on a secondary message. If something needs to react on this object
|
|
||||||
creation event, it typically needs to access the object's properties, so it
|
|
||||||
must wait until the properties have been sent. Doing this might sound simple,
|
|
||||||
and it is, but it becomes a tedious repetitive process to be doing this
|
|
||||||
everywhere instead of focusing on writing the actual event handling logic.
|
|
||||||
|
|
||||||
WirePlumber's library solves this by creating proxy objects that cache all the
|
|
||||||
information and updates received from PipeWire throughout each object's lifetime.
|
|
||||||
Then, it makes them available via the :ref:`WpObjectManager <obj_manager_api>`
|
|
||||||
API, which has the ability to wait until certain information (ex, the
|
|
||||||
properties) has been cached on each object before announcing it.
|
|
||||||
|
|
||||||
Session management utilities
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The library also provides a set of utilities that are useful for session
|
|
||||||
management. For example, it provides the :ref:`WpSessionItem <session_item_api>`
|
|
||||||
class that can be used to abstract a part of the graph with some logic attached
|
|
||||||
to it. It also provides the :ref:`Events & Hooks API <design_events_and_hooks>`,
|
|
||||||
which is a way to express event handling logic in a declarative way.
|
|
||||||
|
|
||||||
Misc utilities
|
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The library also provides a set of miscellaneous utilities that bridge the
|
|
||||||
PipeWire API with the GObject API, such as :ref:`WpProperties <properties_api>`,
|
|
||||||
:ref:`WpSpaPod <spa_pod_api>`, :ref:`WpSpaJson <spa_json_api>` and others.
|
|
||||||
These complement the object model and make it easier to interact with PipeWire
|
|
||||||
objects.
|
|
||||||
|
|
||||||
The daemon
|
|
||||||
----------
|
|
||||||
|
|
||||||
The WirePlumber daemon is implemented on top of the library API and its job is
|
|
||||||
to host components that implement the session management logic. By itself, it
|
|
||||||
doesn't do anything except load and activate these components. The actual logic
|
|
||||||
is implemented inside them.
|
|
||||||
|
|
||||||
Modular design ensures that it is possible to swap the implementation of
|
|
||||||
specific functionality without having to re-implement the rest of it, allowing
|
|
||||||
flexibility on target-sensitive parts, such as policy management and
|
|
||||||
making use of non-standard hardware.
|
|
||||||
|
|
||||||
There are several kinds of components, each with a different purpose. The two
|
|
||||||
main kinds are `modules` and `Lua scripts`. See also
|
|
||||||
:ref:`config_components_and_profiles`.
|
|
||||||
|
|
||||||
Modules
|
|
||||||
^^^^^^^
|
|
||||||
|
|
||||||
Modules extend the libwireplumber API for specific purposes, usually to provide
|
|
||||||
support code or to allow WirePlumber to communicate with external services (ex
|
|
||||||
D-Bus APIs). They either expose their own APIs via GObject signals (i.e. dynamic
|
|
||||||
APIs that do not require linking at compile time) or implement certain objects
|
|
||||||
through interfaces. They are written in C.
|
|
||||||
|
|
||||||
Lua scripts
|
|
||||||
^^^^^^^^^^^
|
|
||||||
|
|
||||||
Lua scripts implement most of the session management. The libwireplumber API is
|
|
||||||
made available in Lua with idiomatic bindings that make it very easy to write
|
|
||||||
session management logic.
|
|
||||||
|
|
||||||
Lua has been chosen because it is a very lightweight scripting language that is
|
|
||||||
suitable for embedding. It is also very easy to learn and use, as well as bind
|
|
||||||
it to C code. However, WirePlumber can be easily extended to support scripting
|
|
||||||
languages other than Lua. The entire Lua scripting system is implemented as a
|
|
||||||
module.
|
|
||||||
|
|
@ -7,54 +7,23 @@ Table of Contents
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: The WirePlumber Daemon
|
:caption: The WirePlumber Daemon
|
||||||
|
|
||||||
daemon/installing.rst
|
installing-wireplumber.rst
|
||||||
daemon/running.rst
|
running-wireplumber-daemon.rst
|
||||||
daemon/configuration.rst
|
configuration.rst
|
||||||
daemon/locations.rst
|
daemon-logging.rst
|
||||||
daemon/logging.rst
|
|
||||||
daemon/multi_instance.rst
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:caption: WirePlumber's Design
|
|
||||||
|
|
||||||
design/understanding_session_management.rst
|
|
||||||
design/understanding_wireplumber.rst
|
|
||||||
design/events_and_hooks.rst
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:caption: WirePlumber's Policies
|
|
||||||
|
|
||||||
policies/linking.rst
|
|
||||||
policies/smart_filters.rst
|
|
||||||
policies/software_dsp.rst
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: The WirePlumber Library
|
:caption: The WirePlumber Library
|
||||||
|
|
||||||
library/c_api.rst
|
c_api.rst
|
||||||
|
lua_api.rst
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:caption: Scripting
|
|
||||||
|
|
||||||
scripting/lua_api.rst
|
|
||||||
scripting/existing_scripts.rst
|
|
||||||
scripting/custom_scripts.rst
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:caption: Tools
|
|
||||||
|
|
||||||
tools/wpctl.rst
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Resources
|
:caption: Resources
|
||||||
|
|
||||||
resources/contributing.rst
|
contributing.rst
|
||||||
resources/testing.rst
|
testing.rst
|
||||||
resources/community.rst
|
community.rst
|
||||||
resources/releases.rst
|
releases.rst
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.. _daemon_installing:
|
.. _installing-wireplumber:
|
||||||
|
|
||||||
Installing WirePlumber
|
Installing WirePlumber
|
||||||
======================
|
======================
|
||||||
|
|
@ -8,8 +8,8 @@ Dependencies
|
||||||
|
|
||||||
In order to compile WirePlumber you will need:
|
In order to compile WirePlumber you will need:
|
||||||
|
|
||||||
* GLib >= 2.68
|
* GLib >= 2.62
|
||||||
* PipeWire >= 1.0
|
* PipeWire 0.3 (>= 0.3.43)
|
||||||
* Lua 5.3 or 5.4
|
* Lua 5.3 or 5.4
|
||||||
|
|
||||||
Lua is optional in the sense that if it is not found in the system, a bundled
|
Lua is optional in the sense that if it is not found in the system, a bundled
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
.. _base_dirs_api:
|
|
||||||
|
|
||||||
Base Directories File Lookup
|
|
||||||
============================
|
|
||||||
.. doxygengroup:: wpbasedirs
|
|
||||||
:content-only:
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
.. _conf_api:
|
|
||||||
|
|
||||||
Static Configuration
|
|
||||||
====================
|
|
||||||
.. graphviz::
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
digraph inheritance {
|
|
||||||
rankdir=LR;
|
|
||||||
GObject -> WpConf;
|
|
||||||
}
|
|
||||||
|
|
||||||
.. doxygenstruct:: WpConf
|
|
||||||
|
|
||||||
.. doxygengroup:: wpconf
|
|
||||||
:content-only:
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
.. _permission_manager_api:
|
|
||||||
|
|
||||||
WpPermissionManager
|
|
||||||
===================
|
|
||||||
.. graphviz::
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
digraph inheritance {
|
|
||||||
rankdir=LR;
|
|
||||||
GObject -> WpObject;
|
|
||||||
WpObject -> WpPermissionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
.. doxygenstruct:: WpPermissionManager
|
|
||||||
|
|
||||||
.. doxygengroup:: wppermissionmanager
|
|
||||||
:content-only:
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
.. _settings_api:
|
|
||||||
|
|
||||||
Settings
|
|
||||||
========
|
|
||||||
.. graphviz::
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
digraph inheritance {
|
|
||||||
rankdir=LR;
|
|
||||||
GBoxed -> WpSettingsSpec;
|
|
||||||
GBoxed -> WpSettingsItem;
|
|
||||||
GObject -> WpObject;
|
|
||||||
WpObject -> WpSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
.. doxygenstruct:: WpSettingsSpec
|
|
||||||
|
|
||||||
.. doxygenstruct:: WpSettingsItem
|
|
||||||
|
|
||||||
.. doxygenstruct:: WpSettings
|
|
||||||
|
|
||||||
.. doxygengroup:: wpsettings
|
|
||||||
:content-only:
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# you need to add here any files you add to the toc directory as well
|
|
||||||
sphinx_files += files(
|
|
||||||
'c_api.rst',
|
|
||||||
)
|
|
||||||
|
|
||||||
subdir('c_api')
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.. _scripting_lua_api:
|
.. _lua_api:
|
||||||
|
|
||||||
Lua API Documentation
|
Lua API Documentation
|
||||||
=====================
|
=====================
|
||||||
|
|
@ -8,17 +8,6 @@ object is not exposed to the scripts.
|
||||||
|
|
||||||
For some functionality, though, the following static functions are exposed.
|
For some functionality, though, the following static functions are exposed.
|
||||||
|
|
||||||
.. function:: Core.get_properties()
|
|
||||||
|
|
||||||
Binds :c:func:`wp_core_get_properties`
|
|
||||||
|
|
||||||
Returns a table with the properties of the core. These are the same
|
|
||||||
properties that appear on WirePlumber's *client* object in the PipeWire
|
|
||||||
global registry.
|
|
||||||
|
|
||||||
:returns: the properties of the core
|
|
||||||
:rtype: table
|
|
||||||
|
|
||||||
.. function:: Core.get_info()
|
.. function:: Core.get_info()
|
||||||
|
|
||||||
Returns a table with information about the core. The table contains
|
Returns a table with information about the core. The table contains
|
||||||
|
|
@ -131,14 +120,3 @@ For some functionality, though, the following static functions are exposed.
|
||||||
the api plugins to load, if they are not already loaded
|
the api plugins to load, if they are not already loaded
|
||||||
:param callback: the function to call after the plugins have been loaded;
|
:param callback: the function to call after the plugins have been loaded;
|
||||||
this function takes references to the plugins as parameters
|
this function takes references to the plugins as parameters
|
||||||
|
|
||||||
.. function:: Core.test_feature()
|
|
||||||
|
|
||||||
Binds :c:func:`wp_core_test_feature`
|
|
||||||
|
|
||||||
Tests if the specified feature is provided in the current WirePlumber
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
:param string feature: the name of the feature to test
|
|
||||||
:returns: true if the feature is provided, false otherwise
|
|
||||||
:rtype: boolean
|
|
||||||
|
|
@ -9,7 +9,12 @@ embeddable scripting language.
|
||||||
WirePlumber uses `Lua version 5.4 <https://www.lua.org/versions.html>`_ to
|
WirePlumber uses `Lua version 5.4 <https://www.lua.org/versions.html>`_ to
|
||||||
implement its engine. For older systems, Lua 5.3 is also supported.
|
implement its engine. For older systems, Lua 5.3 is also supported.
|
||||||
|
|
||||||
Scripts can be ran with the ``wpexec`` tool.
|
There are currently two uses for Lua in WirePlumber:
|
||||||
|
|
||||||
|
- To implement the scripting engine
|
||||||
|
- To implement lua-based :ref:`config files <config_lua>`
|
||||||
|
|
||||||
|
This section is only documenting the API of the **scripting engine**. Scripts can be ran with the ``wpexec`` tool.
|
||||||
|
|
||||||
Example scripts can be found in the `tests/examples` directory of the wireplumber source tree.
|
Example scripts can be found in the `tests/examples` directory of the wireplumber source tree.
|
||||||
|
|
||||||
|
|
@ -40,7 +45,7 @@ In this environment, the following rules apply:
|
||||||
|
|
||||||
Here is a full list of Lua functions (and API tables) that are exposed:
|
Here is a full list of Lua functions (and API tables) that are exposed:
|
||||||
|
|
||||||
.. literalinclude:: ../../../../modules/module-lua-scripting/wplua/sandbox.lua
|
.. literalinclude:: ../../../modules/module-lua-scripting/wplua/sandbox.lua
|
||||||
:language: lua
|
:language: lua
|
||||||
:lines: 27-30
|
:lines: 27-30
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ dropped, the module is unloaded.
|
||||||
Constructors
|
Constructors
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
.. function:: LocalModule(name, arguments, properties)
|
.. function:: LocalModule(name, arguments, properties, [load_args_from_file])
|
||||||
|
|
||||||
Loads the named module with the provided arguments and properties (either of
|
Loads the named module with the provided arguments and properties (either of
|
||||||
which can be ``nil``).
|
which can be ``nil``).
|
||||||
|
|
@ -21,6 +21,7 @@ Constructors
|
||||||
module arguments
|
module arguments
|
||||||
:param table properties: can be ``nil`` or a table that can be
|
:param table properties: can be ``nil`` or a table that can be
|
||||||
:ref:`converted <lua_gobject_lua_to_c>` to :c:struct:`WpProperties`
|
:ref:`converted <lua_gobject_lua_to_c>` to :c:struct:`WpProperties`
|
||||||
|
:param load_args_from_file: (since 0.4.15) optional. if true, arguments param is treated as a file path to load args from
|
||||||
:returns: a new LocalModule
|
:returns: a new LocalModule
|
||||||
:rtype: LocalModule (:c:struct:`WpImplModule`)
|
:rtype: LocalModule (:c:struct:`WpImplModule`)
|
||||||
:since: 0.4.2
|
:since: 0.4.2
|
||||||
|
|
@ -3,33 +3,6 @@
|
||||||
Debug Logging
|
Debug Logging
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Constructors
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. function:: Log.open_topic(topic)
|
|
||||||
|
|
||||||
Opens a LogTopic with the given topic name. Well known script topics are
|
|
||||||
described in :ref:`daemon_logging`, and messages from scripts shall use
|
|
||||||
**s-***.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block:: lua
|
|
||||||
|
|
||||||
local obj
|
|
||||||
log = Log.open_topic ("s-linking")
|
|
||||||
log:info (obj, "an info message on obj")
|
|
||||||
log:debug ("a debug message")
|
|
||||||
|
|
||||||
Above example shows how to output debug logs.
|
|
||||||
|
|
||||||
:param string topic: The log topic to open
|
|
||||||
:returns: the log topic object
|
|
||||||
:rtype: Log (:c:struct:`WpLogTopic`)
|
|
||||||
|
|
||||||
Methods
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
.. function:: Log.warning(object, message)
|
.. function:: Log.warning(object, message)
|
||||||
|
|
||||||
Logs a warning message, like :c:macro:`wp_warning_object`
|
Logs a warning message, like :c:macro:`wp_warning_object`
|
||||||
|
|
@ -39,9 +12,9 @@ Methods
|
||||||
:type object: GObject or GBoxed
|
:type object: GObject or GBoxed
|
||||||
:param string message: the warning message to log
|
:param string message: the warning message to log
|
||||||
|
|
||||||
.. function:: Log.notice(object, message)
|
.. function:: Log.message(object, message)
|
||||||
|
|
||||||
Logs a notice message, like :c:macro:`wp_notice_object`
|
Logs a normal message, like :c:macro:`wp_message_object`
|
||||||
|
|
||||||
:param object: optional object to associate the message with; you
|
:param object: optional object to associate the message with; you
|
||||||
may skip this and just start with the *message* as the first parameter
|
may skip this and just start with the *message* as the first parameter
|
||||||
|
|
@ -30,7 +30,7 @@ contain the following methods:
|
||||||
:param string param_name: the PipeWire param name to enumerate,
|
:param string param_name: the PipeWire param name to enumerate,
|
||||||
ex "Props", "Route"
|
ex "Props", "Route"
|
||||||
:returns: the available parameters
|
:returns: the available parameters
|
||||||
:rtype: Iterator; the iteration items are Spa Pod objects
|
:rtype: Iterator; the iteration items are :ref:`Spa Pod <lua_spa_pod>` objects
|
||||||
|
|
||||||
.. function:: PipewireObject.set_param(self, param_name, pod)
|
.. function:: PipewireObject.set_param(self, param_name, pod)
|
||||||
|
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
# you need to add here any files you add to the toc directory as well
|
# you need to add here any files you add to the toc directory as well
|
||||||
sphinx_files += files(
|
sphinx_files += files(
|
||||||
'index.rst',
|
'index.rst',
|
||||||
|
'installing-wireplumber.rst',
|
||||||
|
'running-wireplumber-daemon.rst',
|
||||||
|
'configuration.rst',
|
||||||
|
'daemon-logging.rst',
|
||||||
|
'contributing.rst',
|
||||||
|
'community.rst',
|
||||||
|
'testing.rst',
|
||||||
|
'releases.rst',
|
||||||
|
'c_api.rst',
|
||||||
|
'lua_api.rst',
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir('daemon')
|
subdir('c_api')
|
||||||
subdir('design')
|
subdir('lua_api')
|
||||||
subdir('policies')
|
subdir('configuration')
|
||||||
subdir('library')
|
|
||||||
subdir('scripting')
|
|
||||||
subdir('tools')
|
|
||||||
subdir('resources')
|
|
||||||
|
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
.. _policies_linking:
|
|
||||||
|
|
||||||
Linking Policy
|
|
||||||
==============
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
|
|
||||||
The linking policy in WirePlumber is the logic charged to link a PipeWire stream
|
|
||||||
node with a PipeWire device node (most cases), or with another PipeWire stream
|
|
||||||
node (monitoring applications).
|
|
||||||
|
|
||||||
PipeWire stream nodes always have one of the following media classes:
|
|
||||||
|
|
||||||
- Stream/Output/Audio: For audio playback applications (Eg pw-play).
|
|
||||||
- Stream/Input/Audio: For audio capture applications (Eg pw-record).
|
|
||||||
- Stream/Input/Video: For video capture applications (Eg cheese).
|
|
||||||
|
|
||||||
And Pipewire device nodes always have one of the following media classes:
|
|
||||||
|
|
||||||
- Audio/Sink: For audio playback devices (Eg Speakers).
|
|
||||||
- Audio/Source: For audio capture devices (Eg Microphones).
|
|
||||||
- Video/Source: For video capture devices (Eg Cameras).
|
|
||||||
|
|
||||||
By default, since in most cases we want to link a stream node with a device
|
|
||||||
node, the linking policy logic when linking 2 nodes always follows the following
|
|
||||||
assignments:
|
|
||||||
|
|
||||||
.. graphviz::
|
|
||||||
|
|
||||||
digraph nodes {
|
|
||||||
rankdir=LR;
|
|
||||||
APS [shape=box label=<audio playback stream<BR/>(Stream/Output/Audio)>];
|
|
||||||
APD [shape=box label=<audio playback device<BR/>(Audio/Sink)>];
|
|
||||||
ACS [shape=box label=<audio capture stream<BR/>(Stream/Input/Audio)>];
|
|
||||||
ACD [shape=box label=<audio capture device<BR/>(Audio/Source)>];
|
|
||||||
VCS [shape=box label=<video capture stream<BR/>(Stream/Input/Video)>];
|
|
||||||
VCD [shape=box label=<video capture device<BR/>(Video/Source)>];
|
|
||||||
APS -> APD;
|
|
||||||
ACD -> ACS;
|
|
||||||
VCD -> VCS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
After that, once the media class of a device node has been selected for a
|
|
||||||
particular stream node, and there are more than 1 device node matching such
|
|
||||||
media class, WirePlumber will select one based on a set of priorities:
|
|
||||||
|
|
||||||
First, it will check if there is a default configured device node for the
|
|
||||||
selected device media class. If there is one, and the node exists, it will link
|
|
||||||
the stream node with such configured default node. Users can easily configure
|
|
||||||
default device nodes for all the 3 different device media classes using tools
|
|
||||||
such as ``pavucontrol`` or ``wpctl``. The logic is implemented in the
|
|
||||||
``linking/find-default-target.lua`` Lua script.
|
|
||||||
|
|
||||||
If there isn't any default node configured, or there is a default node
|
|
||||||
configured but the node does not exist, WirePlumber will instead select the
|
|
||||||
best device node available. The best device node is the node with highest
|
|
||||||
session priority and available routes to the physical device. The logic is
|
|
||||||
implemented in the ``linking/find-best-target.lua`` Lua script.
|
|
||||||
|
|
||||||
If the best node could not be found because the system does not have any,
|
|
||||||
WirePlumber won't link the stream and will send a "no target node available"
|
|
||||||
error to the client.
|
|
||||||
|
|
||||||
|
|
||||||
Stream node linking properties
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
The above default linking logic behavior can be changed by setting specific
|
|
||||||
properties on the nodes.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
These properties must be set in the **stream** nodes (not the device nodes),
|
|
||||||
otherwise they won't have any effect.
|
|
||||||
|
|
||||||
- **target.object**:
|
|
||||||
|
|
||||||
The name of the desired node for this stream to be linked with.
|
|
||||||
If this property is present, WirePlumber will try to find such node, see if it
|
|
||||||
can be linked with the stream, and if so, will use it instead of the default
|
|
||||||
node or best node. The logic is implemented in the ``linking/find-defined-target.lua``
|
|
||||||
Lua script. Since this property is not set by default, WirePlumber will always
|
|
||||||
link stream nodes to the default or best device node found. This property can be
|
|
||||||
easily set using tools such as ``pw-play`` with the ``--target`` flag.
|
|
||||||
|
|
||||||
Note that any node name can be specified there, even if the name is not a device
|
|
||||||
node name, but another stream node name. If this is the case, WirePlumber will
|
|
||||||
link 2 stream nodes together. An example of this case is the monitoring nodes
|
|
||||||
created by ``pavucontrol`` to monitor audio of all audio devices and streams.
|
|
||||||
|
|
||||||
- **node.dont-reconnect**:
|
|
||||||
|
|
||||||
Boolean indicating whether the stream node should not be reconnected to a new
|
|
||||||
node if its current linked node (target) was destroyed or not. By default it
|
|
||||||
is set to ``false``, so if the property is not present in the stream node, WirePlumber
|
|
||||||
will always try to reconnect the stream node to a new target instead of sending
|
|
||||||
an error to the client. The logic is implemented in the ``linking/prepare-link.lua``
|
|
||||||
Lua script.
|
|
||||||
|
|
||||||
- **node.dont-move**:
|
|
||||||
|
|
||||||
Boolean indicating whether the stream node should not be movable or not at runtime
|
|
||||||
using the metadata. If a stream node is not movable, it means that users cannot
|
|
||||||
relink the stream node to a new target at runtime (using tools such as ``pavucontrol``
|
|
||||||
or ``pw-metadata``) when the stream node is already linked to a different node. By
|
|
||||||
default it is set to ``false``, so if the property is not present, WirePlumber will
|
|
||||||
always move, and therefore link the stream node to a new target if it is defined and
|
|
||||||
updated in the ``target.object`` metadata key.
|
|
||||||
|
|
||||||
- **node.dont-fallback**:
|
|
||||||
|
|
||||||
Boolean indicating whether the stream node should not fallback to a different
|
|
||||||
target if its defined target does not exist (the one defined with the ``target.object``
|
|
||||||
property) or not. Therefore, if this property is set to ``true``, WirePlumber sends
|
|
||||||
a "defined target not found" error to the client and will also destroy the stream
|
|
||||||
node. By default it is set to ``false``, so if the property is not present in the
|
|
||||||
stream node, WirePlumber will always fallback to the default or best target if
|
|
||||||
the defined target was not found.
|
|
||||||
|
|
||||||
- **node.linger**:
|
|
||||||
|
|
||||||
Boolean indicating whether the stream node should linger or not if its defined
|
|
||||||
target was not found and the ``node.dont-fallback`` is set to true. Therefore, if
|
|
||||||
this property is set to ``true``, the defined target was not found, and the
|
|
||||||
``node.dont-fallback`` is set to true, WirePlumber won't send a "defined target not found"
|
|
||||||
error to the client, and won't destroy the stream node. This is useful if we want
|
|
||||||
the stream to wait (without processing any data) until its defined target becomes
|
|
||||||
available. By default it is set to ``false``, so if the property is not present in the
|
|
||||||
stream node, WirePlumber will always destroy the node and send an error to the client
|
|
||||||
if its target was not found and ``node.dont-fallback`` was set to true.
|
|
||||||
|
|
||||||
|
|
||||||
Linking settings
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Apart from the above properties, there are also global settings for the linking
|
|
||||||
policy. See :ref:`config_settings` for more information, the linking settings
|
|
||||||
are prefixed with ``linking.``.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# you need to add here any files you add to the toc directory as well
|
|
||||||
sphinx_files += files(
|
|
||||||
'linking.rst',
|
|
||||||
'smart_filters.rst',
|
|
||||||
'software_dsp.rst',
|
|
||||||
)
|
|
||||||
|
|
@ -1,380 +0,0 @@
|
||||||
.. _policies_smart_filters:
|
|
||||||
|
|
||||||
Smart Filters
|
|
||||||
=============
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
|
|
||||||
The smart filters policy allows automatically linking filters together, in a
|
|
||||||
chain, and tied to a specific target node. This is useful when we want to apply
|
|
||||||
a specific processing chain to a specific device, for example. When a stream is
|
|
||||||
about to be linked to a target node that is associated with a smart filter
|
|
||||||
chain, the policy will automatically link the stream with the first filter in
|
|
||||||
the chain, and the last filter in the chain with the target node. This is done
|
|
||||||
transparently to the client, allowing users to define a specific processing
|
|
||||||
chain for a specific device without having to create setups with virtual sinks
|
|
||||||
(or sources) that must be explicitly targeted by the clients.
|
|
||||||
|
|
||||||
Filters, in general, are nodes that are placed in the middle of the graph and
|
|
||||||
are used to modify the data that passes through them. For example, the
|
|
||||||
*echo-cancel*, the *filter-chain*, or the *loopback* nodes are filters.
|
|
||||||
Filters can be implemented either as a single node or as a pair of nodes with
|
|
||||||
opposite directions. For example, the *null-audio-sink* node can be configured
|
|
||||||
to be a single-node filter. On the other hand, the *filter-chain* is a pair of
|
|
||||||
nodes with opposite directions, where one node captures the audio from the graph
|
|
||||||
and the other node sends the modified audio back to the graph.
|
|
||||||
|
|
||||||
For the purpose of the **smart filters** policy, WirePlumber will only consider
|
|
||||||
pairs of nodes as filters, not single-node ones. More specifically, a pair of
|
|
||||||
nodes will be considered to be a filter by WirePlumber if they have the
|
|
||||||
``node.link-group`` property set to a common value. This property is always set
|
|
||||||
on pairs of nodes that are internally linked together and is a good indicator
|
|
||||||
that the nodes are implementing a filter.
|
|
||||||
|
|
||||||
That pair of nodes **must** always consist of a *stream* node and a *main* node.
|
|
||||||
The main node acts as a virtual device, where the data is sent or captured
|
|
||||||
to/from, and the stream node acts as a regular stream, where the data is sent
|
|
||||||
or received to/from the next node in the graph. This is designated by their
|
|
||||||
media class, as shown in the table below:
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:widths: 30 35 35
|
|
||||||
:header-rows: 1
|
|
||||||
:stub-columns: 1
|
|
||||||
|
|
||||||
* -
|
|
||||||
- Input filter (virtual sink)
|
|
||||||
- Output filter (virtual source)
|
|
||||||
* - Main node
|
|
||||||
- ``Audio/Sink`` (capture)
|
|
||||||
- ``Audio/Source`` (playback)
|
|
||||||
* - Stream node
|
|
||||||
- ``Stream/Output/Audio`` (playback)
|
|
||||||
- ``Stream/Input/Audio`` (capture)
|
|
||||||
|
|
||||||
For instance, if a smart filter is used between an application playback stream
|
|
||||||
and the default audio sink, the graph would look like this:
|
|
||||||
|
|
||||||
.. graphviz::
|
|
||||||
|
|
||||||
digraph nodes {
|
|
||||||
rankdir=LR;
|
|
||||||
A [shape=box label=<application stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
FM [shape=box label=<filter main node<BR/>(Audio/Sink)>];
|
|
||||||
FS [shape=box label=<filter stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
D [shape=box label=<default device node<BR/>(Audio/Sink)>];
|
|
||||||
A -> FM;
|
|
||||||
FS -> D;
|
|
||||||
subgraph cluster_filter {
|
|
||||||
style="dotted";
|
|
||||||
FM; FS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The same logic is applied if the smart filter is used between an application
|
|
||||||
capture stream and the default audio source, it is just all in the opposite
|
|
||||||
direction. This is how the graph would look like in this case:
|
|
||||||
|
|
||||||
.. graphviz::
|
|
||||||
|
|
||||||
digraph nodes {
|
|
||||||
rankdir=LR;
|
|
||||||
A [shape=box label=<application stream node<BR/>(Stream/Input/Audio)>];
|
|
||||||
FM [shape=box label=<filter main node<BR/>(Audio/Source)>];
|
|
||||||
FS [shape=box label=<filter stream node<BR/>(Stream/Input/Audio)>];
|
|
||||||
D [shape=box label=<default device node<BR/>(Audio/Source)>];
|
|
||||||
D -> FS;
|
|
||||||
FM -> A;
|
|
||||||
subgraph cluster_filter {
|
|
||||||
style="dotted";
|
|
||||||
FM; FS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
When multiple filters have the same direction, they can also be chained together
|
|
||||||
so that the output of one filter is sent to the input of the next filter. The
|
|
||||||
next section describes how these chains can be described with properties so that
|
|
||||||
they are automatically linked by WirePlumber in any way we want.
|
|
||||||
|
|
||||||
Filter properties
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
When a filter node is created, WirePlumber will check for the presence of the
|
|
||||||
following optional node properties on the **main** node:
|
|
||||||
|
|
||||||
- **filter.smart**
|
|
||||||
|
|
||||||
Boolean indicating whether smart policy will be used for these filter nodes or
|
|
||||||
not. This is disabled by default, therefore filter nodes will be treated as
|
|
||||||
regular nodes, without applying any kind of extra logic. On the other hand, if
|
|
||||||
this property is set to ``true``, automatic (smart) filter policy will be used
|
|
||||||
when linking them. The properties below will then also apply, providing
|
|
||||||
further instructions.
|
|
||||||
|
|
||||||
- **filter.smart.name**
|
|
||||||
|
|
||||||
The unique name of the filter. WirePlumber will use the value of the
|
|
||||||
``node.link-group`` property as the filter name if this property is not set.
|
|
||||||
|
|
||||||
- **filter.smart.disabled**
|
|
||||||
|
|
||||||
Boolean indicating whether the filter should be disabled or not. A disabled
|
|
||||||
filter will never be used under any circumstances. If the property is not set,
|
|
||||||
WirePlumber will consider the filter as enabled (i.e. disabled = false).
|
|
||||||
|
|
||||||
- **filter.smart.targetable**
|
|
||||||
|
|
||||||
Boolean indicating whether the filter can be directly linked with clients that
|
|
||||||
have it defined as a target (Eg: ``pw-play --target <filter-name>``) or not.
|
|
||||||
This can be useful when a client wants to be linked with a filter that is in
|
|
||||||
the middle of the chain in order to bypass the filters that are placed before
|
|
||||||
the selected one. If the property is not set, WirePlumber will consider the
|
|
||||||
filter not targetable by default, meaning filters will never by bypassed by
|
|
||||||
clients, and clients will always be linked with the first filter in the chain.
|
|
||||||
|
|
||||||
- **filter.smart.target**
|
|
||||||
|
|
||||||
A JSON object that defines the matching properties of the filter's target
|
|
||||||
node. A filter target can never be another filter node (WirePlumber will
|
|
||||||
ignore it), it must be a device or virtual sink (or source, depending on the
|
|
||||||
direction of the filter). If this property is not set, WirePlumber will use
|
|
||||||
the default sink/source as the target.
|
|
||||||
|
|
||||||
- **filter.smart.before**
|
|
||||||
|
|
||||||
A JSON array containing the names of the filters that are supposed to be
|
|
||||||
chained after this filter (i.e. this filter here should be chained *before*
|
|
||||||
those). If not set, WirePlumber will link the filters by order of creation.
|
|
||||||
|
|
||||||
- **filter.smart.after**
|
|
||||||
|
|
||||||
A JSON array containing the names of the filters that are supposed to be
|
|
||||||
chained before this filter (i.e. this filter here should be chained *after*
|
|
||||||
those). If not set, WirePlumber will link the filters by order of creation.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
These properties must be set on the filter's **main** node, not the stream
|
|
||||||
node.
|
|
||||||
|
|
||||||
As an example, we will describe here how to create 2 loopback filters in
|
|
||||||
PipeWire's configuration, with names loopback-1 and loopback-2, that will be
|
|
||||||
linked with the default audio device, and use loopback-2 filter as the last
|
|
||||||
filter in the chain.
|
|
||||||
|
|
||||||
The PipeWire configuration files for the 2 filters should be like this:
|
|
||||||
|
|
||||||
- ~/.config/pipewire/pipewire.conf.d/loopback-1.conf:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:emphasize-lines: 8-11
|
|
||||||
|
|
||||||
context.modules = [
|
|
||||||
{ name = libpipewire-module-loopback
|
|
||||||
args = {
|
|
||||||
node.name = loopback-1-sink
|
|
||||||
node.description = "Loopback 1 Sink"
|
|
||||||
capture.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
media.class = Audio/Sink
|
|
||||||
filter.smart = true
|
|
||||||
filter.smart.name = loopback-1
|
|
||||||
filter.smart.before = [ loopback-2 ]
|
|
||||||
}
|
|
||||||
playback.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
node.passive = true
|
|
||||||
stream.dont-remix = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
- ~/.config/pipewire/pipewire.conf.d/loopback-2.conf:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:emphasize-lines: 8-10
|
|
||||||
|
|
||||||
context.modules = [
|
|
||||||
{ name = libpipewire-module-loopback
|
|
||||||
args = {
|
|
||||||
node.name = loopback-2-sink
|
|
||||||
node.description = "Loopback 2 Sink"
|
|
||||||
capture.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
media.class = Audio/Sink
|
|
||||||
filter.smart = true
|
|
||||||
filter.smart.name = loopback-2
|
|
||||||
}
|
|
||||||
playback.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
node.passive = true
|
|
||||||
stream.dont-remix = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
After restarting PipeWire to apply the configuration changes, playing a test
|
|
||||||
wave audio file with paplay to the default device should result in the following
|
|
||||||
graph:
|
|
||||||
|
|
||||||
.. graphviz::
|
|
||||||
|
|
||||||
digraph nodes {
|
|
||||||
rankdir=LR;
|
|
||||||
paplay [shape=box label=<paplay node<BR/>(Stream/Output/Audio)>];
|
|
||||||
L1M [shape=box label=<loopback-1 main node<BR/>(Audio/Sink)>];
|
|
||||||
L1S [shape=box label=<loopback-1 stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
L2M [shape=box label=<loopback-2 main node<BR/>(Audio/Sink)>];
|
|
||||||
L2S [shape=box label=<loopback-2 stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
device [shape=box label=<default device node<BR/>(Audio/Sink)>];
|
|
||||||
paplay -> L1M;
|
|
||||||
L1S -> L2M;
|
|
||||||
L2S -> device;
|
|
||||||
subgraph cluster_filter1 {
|
|
||||||
style="dotted";
|
|
||||||
L1M; L1S;
|
|
||||||
}
|
|
||||||
subgraph cluster_filter2 {
|
|
||||||
style="dotted";
|
|
||||||
L2M; L2S;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Now, if we remove the ``filter.smart.before = [ loopback-2 ]`` property from the
|
|
||||||
loopback-1 filter, and add a ``filter.smart.before = [ loopback-1 ]`` property
|
|
||||||
in the loopback-2 filter configuration file, WirePlumber should link the
|
|
||||||
loopback-1 filter as the last filter in the chain, like this:
|
|
||||||
|
|
||||||
.. graphviz::
|
|
||||||
|
|
||||||
digraph nodes {
|
|
||||||
rankdir=LR;
|
|
||||||
paplay [shape=box label=<paplay node<BR/>(Stream/Output/Audio)>];
|
|
||||||
L1M [shape=box label=<loopback-1 main node<BR/>(Audio/Sink)>];
|
|
||||||
L1S [shape=box label=<loopback-1 stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
L2M [shape=box label=<loopback-2 main node<BR/>(Audio/Sink)>];
|
|
||||||
L2S [shape=box label=<loopback-2 stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
device [shape=box label=<default device node<BR/>(Audio/Sink)>];
|
|
||||||
paplay -> L2M;
|
|
||||||
L2S -> L1M;
|
|
||||||
L1S -> device;
|
|
||||||
subgraph cluster_filter1 {
|
|
||||||
style="dotted";
|
|
||||||
L1M; L1S;
|
|
||||||
}
|
|
||||||
subgraph cluster_filter2 {
|
|
||||||
style="dotted";
|
|
||||||
L2M; L2S;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
In addition, the filters can have different targets. For example, we can define
|
|
||||||
the filters like this:
|
|
||||||
|
|
||||||
- ~/.config/pipewire/pipewire.conf.d/loopback-1.conf:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
:emphasize-lines: 12
|
|
||||||
|
|
||||||
context.modules = [
|
|
||||||
{ name = libpipewire-module-loopback
|
|
||||||
args = {
|
|
||||||
node.name = loopback-1-sink
|
|
||||||
node.description = "Loopback 1 Sink"
|
|
||||||
capture.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
media.class = Audio/Sink
|
|
||||||
filter.smart = true
|
|
||||||
filter.smart.name = loopback-1
|
|
||||||
filter.smart.after = [ loopback-2 ]
|
|
||||||
filter.smart.target = { node.name = "not-default-audio-device" }
|
|
||||||
}
|
|
||||||
playback.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
node.passive = true
|
|
||||||
stream.dont-remix = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
- ~/.config/pipewire/pipewire.conf.d/loopback-2.conf:
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
context.modules = [
|
|
||||||
{ name = libpipewire-module-loopback
|
|
||||||
args = {
|
|
||||||
node.name = loopback-2-sink
|
|
||||||
node.description = "Loopback 2 Sink"
|
|
||||||
capture.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
media.class = Audio/Sink
|
|
||||||
filter.smart = true
|
|
||||||
filter.smart.name = loopback-2
|
|
||||||
}
|
|
||||||
playback.props = {
|
|
||||||
audio.position = [ FL FR ]
|
|
||||||
node.passive = true
|
|
||||||
stream.dont-remix = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
In this case, playing a test wave audio file with paplay to the
|
|
||||||
``not-default-audio-device`` device should result in the following graph:
|
|
||||||
|
|
||||||
.. graphviz::
|
|
||||||
|
|
||||||
digraph nodes {
|
|
||||||
rankdir=LR;
|
|
||||||
paplay [shape=box label=<paplay node<BR/>(Stream/Output/Audio)>];
|
|
||||||
L1M [shape=box label=<loopback-1 main node<BR/>(Audio/Sink)>];
|
|
||||||
L1S [shape=box label=<loopback-1 stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
L2M [shape=box label=<loopback-2 main node<BR/>(Audio/Sink)>];
|
|
||||||
L2S [shape=box label=<loopback-2 stream node<BR/>(Stream/Output/Audio)>];
|
|
||||||
device [shape=box label=<not-default-audio-device node<BR/>(Audio/Sink)>];
|
|
||||||
paplay -> L2M;
|
|
||||||
L2S -> L1M;
|
|
||||||
L1S -> device;
|
|
||||||
subgraph cluster_filter1 {
|
|
||||||
style="dotted";
|
|
||||||
L1M; L1S;
|
|
||||||
}
|
|
||||||
subgraph cluster_filter2 {
|
|
||||||
style="dotted";
|
|
||||||
L2M; L2S;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
In this configuration, the loopback-1 filter will only be linked if the
|
|
||||||
application stream is targeting the device node called
|
|
||||||
"not-default-audio-device".
|
|
||||||
|
|
||||||
Filters metadata
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Similar to the default metadata, it is also possible to override the filter
|
|
||||||
properties using the "filters" metadata object. This allow users to change the
|
|
||||||
filters policy at runtime.
|
|
||||||
|
|
||||||
For example, assuming the id of the *loopback-1* main node is ``40``, we can
|
|
||||||
disable the filter by setting its ``filter.smart.disabled`` metadata key to
|
|
||||||
``true`` using the ``pw-metadata`` tool like this:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ pw-metadata -n filters 40 "filter.smart.disabled" true Spa:String:JSON
|
|
||||||
|
|
||||||
We can also change the target of a filter at runtime:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ pw-metadata -n filters 40 "filter.smart.target" "{ node.name = new-target-node-name }" Spa:String:JSON
|
|
||||||
|
|
||||||
Every time a key in the filters metadata changes, all filters are unlinked and
|
|
||||||
re-linked properly, following the new policy.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue