Last modified: Wed Oct 28 09:01:51 EDT 2020
This page documents what I did to destroy dynamic range on a system burdened with the evil daemon PulseAudio. The first two pieces are based on the advice in ArchWiki; the last step (fixing things in pavucontrol) came from vaguely related troubleshooting Q&A elsewhere.
PulseAudio supports only LADSPA filters. Non-support of libavfilter is a serious disappointment, but for this simple task, a sufficient LADSPA filter is gettable (almost). I used the "Fast Lookahead limiter" from Steve Harris, but with the following modification to allow greater compression:
diff -ur ladspa-0.4.17/fast_lookahead_limiter_1913.xml ladspa-0.4.17-dwf/fast_lookahead_limiter_1913.xml --- ladspa-0.4.17/fast_lookahead_limiter_1913.xml 2016-10-17 05:05:54.000000000 -0400 +++ ladspa-0.4.17-dwf/fast_lookahead_limiter_1913.xml 2020-10-22 17:04:00.886286144 -0400 @@ -193,7 +193,7 @@ <name>Input gain (dB)</name> <p>Gain that is applied to the input stage. Can be used to trim gain to bring it roughly under the limit or to push the signal against the limit.</p> - <range min="-20" max="20"/> + <range min="-70" max="70"/> </port> <port label="limit" dir="input" type="control" hint="default_0">
Build the plugin for the architecture of the target machine and install fast_lookahead_limiter_1913.so in /usr/local/lib/ladspa. Nothing else from the plugins distribution is required on the target machine.
For figuring out how to invoke this plugin and others, the analyseplugin program of the LADSPA SDK was very useful:
bash-4.3$ ladspa/bin/analyseplugin fast_lookahead_limiter_1913 Plugin Name: "Fast Lookahead limiter" Plugin Label: "fastLookaheadLimiter" Plugin Unique ID: 1913 Maker: "Steve Harris <email redacted>" Copyright: "GPL" Must Run Real-Time: No Has activate() Function: Yes Has deactivate() Function: No Has run_adding() Function: Yes Environment: Normal or Hard Real-Time Ports: "Input gain (dB)" input, control, -20 to 20, default 0 "Limit (dB)" input, control, -20 to 0, default 0 "Release time (s)" input, control, 0.01 to 2, default 0.5075 "Attenuation (dB)" output, control, 0 to 70 "Input 1" input, audio "Input 2" input, audio "Output 1" output, audio "Output 2" output, audio "latency" output, control
As described by ArchWiki, use
pacmd list-sinks to identify the existing output sink. Mine was called alsa_output.pci-0000_00_0e.0.hdmi-stereo.
The following goes in ~/.config/pulse/default.pa:
.include /etc/pulse/default.pa set-default-sink alsa_output.pci-0000_00_0e.0.hdmi-stereo load-module module-ladspa-sink sink_name=squash sink_master=alsa_output.pci-0000_00_0e.0.hdmi-stereo plugin=fast_lookahead_limiter_1913 label=fastLookaheadLimiter control=30,0,0.5 set-default-sink squash
Then restart the daemon or reboot.
The parameters to be provided after control= are those described as "input, control" in the ports listing of analyseplugin, in the order listed. The parameters that I use smash the input range of −30 dB to 0 dB up to maximum volume while inputs below −30 dB drop off linearly:
The set-default-sink command sets the "fallback" sink, which supposedly somehow acts as some sort of default sometimes. But in fact, it had no effect on anything as far as I could tell.
There are at least three different ways that apps decide which output sink to use, none of which are determined by set-default-sink: the other default, an app setting in pavucontrol, or an app-internal setting.
sudo apt install pavucontrol
On the Output Devices tab of pavucontrol, there appears to be a default output selection that is independent of set-default-sink. Clicking the check mark next to the LADSPA plugin caused GNOME Videos to switch its output.
This setting is hidden like the Doors of Durin. If you look in Firefox, you find nothing relevant. If you look in pavucontrol, you also find nothing relevant. But if you launch Firefox and start it playing some audio and then look in the Playback tab of pavucontrol while Firefox is running, you can switch Firefox's output to the LADSPA plugin. Once done, it sticks for future Firefox processes.
Firefox will probably implement its own app-internal setting in the future, but this will serve as an example for other apps that do not.
VLC has its own setting to explicitly choose the output.
In theory, you can get the log from a PulseAudio daemon with
journalctl -b -t pulseaudio -p 7. In reality, the logging level
is such that nothing useful is normally preserved.
Prevent automatic respawning of PulseAudio by putting
no in ~/.config/pulse/client.conf. Do
pulseaudio -k to
kill the PulseAudio daemon if it is running, then run it in a terminal with
These low-priority log messages might have something to do with why set-default-sink had no effect:
I: [pulseaudio] core.c: configured_default_sink: (unset) -> squash I: [pulseaudio] core.c: configured_default_sink: squash -> alsa_output.pci-0000_00_0e.0.hdmi-stereo I: [pulseaudio] core.c: configured_default_sink: alsa_output.pci-0000_00_0e.0.hdmi-stereo -> squash I: [pulseaudio] core.c: default_sink: alsa_output.pci-0000_00_0e.0.hdmi-stereo -> squash D: [pulseaudio] sink-input.c: Can't connect input to squash, as that would create a cycle. I: [pulseaudio] module-switch-on-connect.c: Failed to move sink input 0 "(null)" to squash.
I've no idea why it would think it had a cycle.
If you exceed the range of an input control for the LADSPA filter, PulseAudio logs the following and dies:
W: [pulseaudio] module-ladspa-sink.c: Control value 0 over upper bound: 30.000000 (upper bound: 20.000000) E: [pulseaudio] module-ladspa-sink.c: Failed to parse, validate or set control parameters E: [pulseaudio] module.c: Failed to load module "module-ladspa-sink" (argument: "sink_name=squash sink_master=alsa_output.pci-0000_00_0e.0.hdmi-stereo plugin=fast_lookahead_limiter_1913 label=fastLookaheadLimiter control=30,0,0.5"): initialization failed. E: [pulseaudio] main.c: Module load failed. E: [pulseaudio] main.c: Failed to initialize daemon.
The applyplugin program of the LADSPA SDK is useful for testing plugins on wav files, but it's picky about the format and chokes on the metadata that ffmpeg automatically adds. To remove the metadata from a wav file:
ffmpeg -i orig.wav -fflags +bitexact -flags:a +bitexact -codec copy clean.wav
Then this works:
ladspa/bin/applyplugin clean.wav out.wav \ fast_lookahead_limiter_1913 fastLookaheadLimiter \ 30 0 0.5 Peak output: 32767.5
The limits on control input parameters are not enforced when the plugin is used this way.