This document contains only my personal opinions and calls of judgement, and where any comment is made as to the quality of anybody's work, the comment is an opinion, in my judgement.
[file this blog page at: dig del.icio.us Technorati]
I have switched to Pipewire a while ago as it seems rather more efficient than PulseAudio and I have been fairly happy, even if its configuration is poorly documented and seems rather complicated.
To make the configuration easier PipeWire has two dynamic configuration systems called pipewire-media-session and wireplumber which is the new nicer thing that "dioscovers" audio devices and adds them to the PipeWire configuration, plus some graphical tools called Helvum and qpwgraph to adjust dynamically the audio flow graph.
wireplumber comes with a large set of Lua script to discover and configure devices (version 0.4 does, version 0.5 switched to JSON based configuration). The discovery process is done by monitor entities triggered by DBUS messages from various OS subsystems.
I have recently got a BlueTooth headset and every time I tried to connect to it I got two related error messages:
blueman.bluez.errors.DBusFailedError: Protocol not available.
a2dp-sink profile connect failed
There are many, many web pages about this issue, but they never explain what is really going:
The error I got was that even if the headset BlueTooth connection worked, the BlueTooth daemon could not find a handler over DBUS for its type. The most common reason why that does not happen is that wireplumber does not have the relevant plugin that is in the libspa-0.2-bluetooth package (Debian/Ubuntu) but I had installed it so it ought to have worked, but then I discovered that the wireplumber BlueTooth did not invoke it. The reason is that /usr/share/wireplumber/scripts/monitors/bluez.lua and /usr/share/wireplumber/bluetooth.lua.d/30-bluez-monitor.lua (Debian/Ubuntu) have these lines:
-- if logind support is enabled, activate -- the monitor only when the seat is active function startStopMonitor(seat_state) Log.info(logind_plugin, "Seat state changed: " .. seat_state) if seat_state == "active" then monitor = createMonitor() elseif monitor then monitor:deactivate(Feature.SpaDevice.ENABLED) monitor = nil end end
if bluez_monitor.properties["with-logind"] then load_optional_module("logind") end
while I have systemd-logind installed I do not use it for X11 logins, so the "seat" seems inactive, and then wireplumber ignores BlueTooth devices. The relevant parameter is in /usr/share/wireplumber/bluetooth.lua.d/50-bluez-config.lua (Debian/Ubuntu):
-- Enable the logind module, which arbitrates which user will be allowed -- to have bluetooth audio enabled at any given time (particularly useful -- if you are using GDM as a display manager, as the gdm user also launches -- pipewire and wireplumber). -- This requires access to the D-Bus user session; disable if you are running -- a system-wide instance of wireplumber. ["with-logind"] = true,
Setting it to false on my laptop allowed wireplumber to handle BlueTooth audio devices and thus the use of the headset, by putting in $XDG_CONFIG_DIR/wireplumber/bluetooth.lua.d/99-bluez-config.lua:
-- 'logind' is present but it is unused bluez_monitor.properties = { ["with-logind"] = false, }
A practice that has become more common for configuration (and
other) files is to replace them with directories is to rteplace them
with directories (usually with a name ending in .d
)
containing several files that are read in sequence. Related to that
is the practice to have a parallel directory of possible files to
link into the configuration directory, for example
avail.d/settings2.js linked as
conf.d/settings2.js.
The aim of these practices is to allow creating virtual configuration files made of multiple sections installed by different packages or stakeholders without having to edit a single file by in effect editing a directory instead.
These practices make it far more difficult to maintain systems because they store configuration state as absence or presence of directory entries and comparing, saving, restoring them is not as simple as for lines in a file.
Files can be easily compared, copied to be saved and copied to be restored, and the whole state of the file is simply and wholly compared, saved, restored, but that does not happen for directories as simply as that.
Another issue is that the order of files in a directory is not well defined, so if the convention is to read the file in alphabetical order the common practice is to prefix them with a number, which creates the issue of changing file names to update the number prefix when adding new files in the middle of the desired order. In a file the ordering of lines or records is well defined by position.
Consider a directory with configuration sections conf.d/item1.xml and conf.d/item2.xml, which is then saved, they are modified, then a third section conf.d/item3.xml gets added, then it is decided to restore them as they were when saved: that restores conf.d/item1.xml and conf.d/item2.xml as they were, but also leaves present conf.d/item3.xml because most usual save formats do not record the absence of a file in a directory.
The alternative is first to write as file conf.xml with lines:
item1: true item2: 5
Which is then saved, then modified to have content:
item1: false item2: 5 item3: "/etc"
When the saved version is restored the line item3: "/etc/" is not left around.
A technique that has most of the advantages of both and used to be common is instead to list in a file which sections to include, for example in main.cf:
include "item1.cf" include "item2.cf"
Then the directory containing the three files is saved, item1.cf is modified and item3.cf is added and accordingly include "item3.cf" is added to main.cf. On restore item3.cf continues to exist but there is no more the line include "item3.cf" in main.cf do it does not matter.
This still means that an file that includes other files needs to be edited but since that file can be just a sequence of lines containing file names that can be done quite simply even by scripts.