Skip to content

Instantly share code, notes, and snippets.

@chadmed

chadmed/pw.md Secret

Last active March 15, 2022 11:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chadmed/51428670bb7fda92a2ef9c879ac1c678 to your computer and use it in GitHub Desktop.
Save chadmed/51428670bb7fda92a2ef9c879ac1c678 to your computer and use it in GitHub Desktop.
pw-feature req

Feature proposal: Automatic DSP and routing for complex audio devices

In the 21st century, DSP has become absolutely essential for high-quality audio on virtually all classes of output device, even in a lot of high-end HiFi and professional grade speakers and headphones. Unfortunately, it is 2022 and Linux userspace still has no sane way of applying this neccessary DSP in a way that is transparent and seamless. The result is that virtually all devices that rely on some form of DSP or another sound atrocious in Linux, with many examples being totally unusable.

Likewise, many integrated audio devices (laptop speakers, active loudspeakers with USB input, etc.) are being released now with elaborate driver arrays and require custom handling to be routed correctly. As one of the pages in the PipeWire wiki claims, "channel routing [on these devices] will almost certainly be wrong." A user should not and cannot be expected to rummage through various configuration files setting up virtual sinks and WirePlumber rules just so their speakers sound as they are intended to.

Additionally, many of these devices are also dumb or have misconfigured firmware and have no "safe" volume limits. A good example of this is certain Chromebooks, which users are able to blow up just by cranking the volume in alsamixer.

These DSP tricks are not only something used for gimmicks like virtual surround, they're a vital component of the audio chain. Without a sane and seamless way to handle it for users, Linux will never even be on par with Windows or macOS, let alone surpass them (as it should) on this front.

I am aware that some of this can be achieved with various configuration changes in /etc/asound.conf, /usr/share/alsa/alsa-card-profile/, BYO PipeWire configuration fragments, EasyEffects, Carla, etc. Quite frankly, this state of affairs is just not even remotely acceptable for an end user to have to contend with, and as more devices like laptops and tablets are released with these sorts of audio processing requirements this situation will only get worse.

To address these problems, I propose a mechanism by which PipeWire can automatically set up the routing and DSP required by these devices.

How it should work

  1. A device is discovered.

  2. PipeWire gets the name of that device via some mechanism (ALSA, USB props, etc.).

  3. PipeWire looks in a directory structure and finds a PipeWire configuration fragment matching the device name if one exists.

  4. PipeWire loads that configuration, which sets up one or more virtual sinks. The sinks consist of a filter chain of convolvers that apply FIRs/IIRs to the audio signal. Multiple can be defined for virtual surround, etc. since the file is just a PipeWire configuration fragment. If a volume limit has been specified, PipeWire should transparently scale the volume curve so that this limit is presented as "100%" to the user.

  5. The hardware device is kicked into the Pro Audio profile

  6. Any node.targets specified in the virtual sinks are hidden from the rest of the graph so that they cannot be accessed by any other userspace software.

If no configuration fragment exists in this directory structure, the existing behaviour should be fallen back on.

Only specified node.targets should be hidden from userspace, everything else on the hardware device should behave as normal.

What already exists

  • The builtin convolver is more than adequate for applying DSP in the form of 32-bit float FIRs/IIRs in the hardware's native/max sample rate

  • The existing parsing of config fragments is also more than adequate for describing how to handle these types of devices

What needs to be implemented

  • PipeWire needs to be able to hide the node.target from the graph. This is absolutely essential for a seamless user experience and really the main reason for making this feature request.

  • A standard directory structure for discovering and loading community-provided profiles, similar to how CUPS handles PPDs.

It would be wholly infeasible to expect PipeWire to provide and maintain thousands of device profiles in the default PipeWire install, so leaving it to the community to provide and package these profiles means that users can choose to install whichever package is relevant to their setup and leave out untold gigabytes of irrelevant IRs and configuration fragments that will never be loaded on their machines. For this to be workable however, we must have a standard directory structure.

For this, I suggest /usr/share/pipewire/profiles/DRIVER/DEVICE-NAME, with DRIVER and DEVICE-NAME being the ALSA driver name and reported device name respectively. Anything below the DEVICE-NAME directory can be named and structured however the provider of that profile sees fit, except for the PipeWire configuration fragment, which should be DEVICE-NAME.conf.

An example of the behaviour

This example uses a 14-inch MacBook Pro with M1 Pro SoC (model J314s). This machine has 2x 3-way stereo speakers consisting of a tweeter and two woofers each. One of the woofers is a "helper" woofer for bass and sub-bass, as is the case in many 3-way loudspeaker designs. To utilise this array properly, we have to set up a virtual stereo sink that clones the FL and FR signals, puts them through a convolver for each driver with an FIR that processes the signal, then route the outputs of those convolvers directly to each driver. The user should not be able to select the actual hardware device as a sink, as this results in incorrect/undefined behaviour.

  1. PipeWire starts, probes ALSA and finds the MacBook Pro J314/6 Integrated Audio device provided by the snd-soc-applesilicon ALSA driver.

  2. PipeWire looks for the directory /usr/share/pipewire/profiles/soc-applesilicon/macbook-pro-j314-6-integrated-audio/ and parses a file called macbook-pro-j314-6-integrated-audio.conf as though it were a configuration fragment stored in /etc/pipewire/pipewire.conf.d/.

  3. This configuration fragment describes a virtual sink called MacBook Pro Integrated Speakers that captures a stereo stream, feeds it through a series of convolutions, then upmixes it to six channels based on the physical configuration of the stereo array. It also sets a volume limit of -12dBFS on the virtual sink.

  4. The hardware device is put into the Pro Audio profile which exposes all six of its channels.

  5. The node.target specified in the virtual sink is hidden from the graph so that it is inaccessible to the rest of userspace. Other programs can only see MacBook Pro Integrated Speakers as the sink, effectively an alias for the hardware device.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment