fix(pipewire): denoising through configPackages, desktop gets steam and bitwig
The RNNoise config was deployed to /etc/xdg/pipewire, which PipeWire never reads: it resolves $PIPEWIRE_CONFIG_DIR, then the home config, then its compiled-in /etc/pipewire, and knows nothing of XDG_CONFIG_DIRS. An installed system therefore had no denoising while carrying the file. It now goes through services.pipewire.configPackages, with the LADSPA plugin substituted to its store path since LADSPA_PATH has no /usr here. Alongside, on the desktop host only: steam via its module (which also turns on 32-bit graphics), bitwig-studio, and rtkit so a DAW can ask for realtime scheduling at all.
This commit is contained in:
@@ -1,5 +1,63 @@
|
|||||||
# Decisions
|
# Decisions
|
||||||
|
|
||||||
|
## 2026-08-20 – Input denoising was deployed to a path PipeWire never reads
|
||||||
|
|
||||||
|
- **Who**: Dominik, ClaudeCode
|
||||||
|
- **Why**: The Arch machine runs RNNoise input denoising, and the config for it
|
||||||
|
lives in the shared tree as
|
||||||
|
`defaults/xdg/pipewire/pipewire.conf.d/99-input-denoising.conf`. It was in
|
||||||
|
`modules/desktop.nix` too, in the generic sweep to `/etc/xdg/` — and inert
|
||||||
|
there. PipeWire resolves its config directory from `$PIPEWIRE_CONFIG_DIR`,
|
||||||
|
then `$XDG_CONFIG_HOME/pipewire` or `$HOME/.config/pipewire`, then the
|
||||||
|
compiled-in `/etc/pipewire`; `XDG_CONFIG_DIRS` appears nowhere in
|
||||||
|
`src/pipewire/conf.c`. On Arch the file works because `post-install.sh` seeds
|
||||||
|
it into the home directory. Same class as the swaylock config on 2026-08-18: a
|
||||||
|
file installed to a plausible path that the program does not read.
|
||||||
|
- **Tradeoffs**: (1) **Which option** — the module accepts both
|
||||||
|
`services.pipewire.extraConfig.pipewire.<name>` (Nix attributes, rendered to
|
||||||
|
JSON) and `services.pipewire.configPackages` (a package carrying
|
||||||
|
`share/pipewire/pipewire.conf.d/*.conf`); both end up in the same merged
|
||||||
|
directory (`pipewire.nix:59-75`). The NixOS wiki uses `extraConfig` for its
|
||||||
|
examples, which are written by hand and have no file behind them. Taking that
|
||||||
|
route here would mean translating the shared file into Nix and maintaining the
|
||||||
|
VAD values in two places, so `configPackages` was chosen — which is also what
|
||||||
|
the module's own option documentation shows for a conf.d file
|
||||||
|
(`pipewire.nix:258-279`). (2) **The plugin path** — the file names the plugin
|
||||||
|
as `librnnoise_ladspa`, resolved through `LADSPA_PATH`, which is
|
||||||
|
`/usr/lib/ladspa` on Arch and does not exist here. There is no LADSPA
|
||||||
|
counterpart to `services.pipewire.extraLv2Packages`, so the absolute store
|
||||||
|
path is substituted into the file, the same treatment the wallpaper paths get.
|
||||||
|
- **How**: `denoisingConfig` via `runCommand` + `substitute`, handed to
|
||||||
|
`services.pipewire.configPackages`. The `xdg/pipewire/` entry in
|
||||||
|
`environment.etc` is removed rather than kept for layout parity — after the
|
||||||
|
swaylock case, an inert copy is a liability. Verified by building the package:
|
||||||
|
the conf.d file is there, the plugin line points at
|
||||||
|
`rnnoise-plugin-1.10/lib/ladspa/librnnoise_ladspa.so`, and that file exists.
|
||||||
|
- **Note**: the filter chain shows up as a separate "Noise Canceling source"
|
||||||
|
input. It does not replace the raw microphone; applications have to select it.
|
||||||
|
|
||||||
|
## 2026-08-20 – Steam, Bitwig and rtkit belong to the desktop alone
|
||||||
|
|
||||||
|
- **Who**: Dominik, ClaudeCode
|
||||||
|
- **Why**: Both programs are wanted on the desktop and on no other machine, so
|
||||||
|
they go into `hosts/desktop/default.nix` rather than the shared module every
|
||||||
|
host imports.
|
||||||
|
- **Tradeoffs**: (1) **Steam** — `pkgs.steam` in `systemPackages` is the wrong
|
||||||
|
handle: the module wraps the binary in an FHS environment and turns on
|
||||||
|
`hardware.graphics.enable32Bit` itself (`nixos/modules/programs/steam.nix:215-218`),
|
||||||
|
which the bare package would leave off. (2) **Bitwig** — the `bitwig-studio`
|
||||||
|
attribute currently resolves to `bitwig-studio6-6.0.11`; `bitwig-studio5`
|
||||||
|
(5.3.13) exists as a separate attribute if a downgrade is ever needed.
|
||||||
|
Dominik chose 6. (3) **Realtime** — `security.rtkit.enable` was off. The
|
||||||
|
rlimits the PipeWire module installs (`rtprio` 95, `nice` -19, `memlock`)
|
||||||
|
apply to members of the `pipewire` group, and the account is in
|
||||||
|
`networkmanager wheel video input plugdev docker` only, so a DAW had no path
|
||||||
|
to realtime scheduling at all. Enabled on this host, where the DAW is.
|
||||||
|
- **How**: `programs.steam.enable`, `security.rtkit.enable` and
|
||||||
|
`environment.systemPackages = [ pkgs.bitwig-studio ]` in the host file.
|
||||||
|
Verified by evaluating both hosts: on `desktop` all three are set and 32-bit
|
||||||
|
graphics came along; on `thinkpad` Steam and rtkit stay `false`.
|
||||||
|
|
||||||
## 2026-08-20 – The browser is LibreWolf from nixpkgs, not a self-packaged Waterfox
|
## 2026-08-20 – The browser is LibreWolf from nixpkgs, not a self-packaged Waterfox
|
||||||
|
|
||||||
- **Who**: Dominik, ClaudeCode
|
- **Who**: Dominik, ClaudeCode
|
||||||
|
|||||||
@@ -109,6 +109,20 @@
|
|||||||
# the shared module sets it unconditionally.
|
# the shared module sets it unconditionally.
|
||||||
services.fwupd.enable = lib.mkForce false;
|
services.fwupd.enable = lib.mkForce false;
|
||||||
|
|
||||||
|
# --- Applications, this machine only ---
|
||||||
|
#
|
||||||
|
# Steam comes as a module rather than a package: it wraps the binary in an FHS
|
||||||
|
# environment and turns on 32-bit graphics for the drivers, which the package
|
||||||
|
# alone would not do.
|
||||||
|
programs.steam.enable = true;
|
||||||
|
|
||||||
|
# Lets audio applications ask for realtime scheduling, which Bitwig does. The
|
||||||
|
# rlimits the PipeWire module sets (rtprio 95, memlock) apply to members of
|
||||||
|
# the pipewire group only, and this account is not one.
|
||||||
|
security.rtkit.enable = true;
|
||||||
|
|
||||||
|
environment.systemPackages = [ pkgs.bitwig-studio ];
|
||||||
|
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|
||||||
|
|||||||
+28
-2
@@ -36,6 +36,24 @@ let
|
|||||||
--replace-fail /usr/share/moonarch/wallpaper-blur.jpg ${wallpaperBlur}
|
--replace-fail /usr/share/moonarch/wallpaper-blur.jpg ${wallpaperBlur}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# PipeWire searches $PIPEWIRE_CONFIG_DIR, then $XDG_CONFIG_HOME/pipewire, then
|
||||||
|
# its compiled-in /etc/pipewire — never /etc/xdg (verified in PipeWire's
|
||||||
|
# src/pipewire/conf.c). The shared file therefore cannot be deployed the way
|
||||||
|
# the other XDG configs are; it goes through the module's configPackages,
|
||||||
|
# which is what its own option documentation shows for a conf.d file.
|
||||||
|
#
|
||||||
|
# The file names the plugin as `librnnoise_ladspa`, resolved through
|
||||||
|
# LADSPA_PATH — /usr/lib/ladspa on Arch, nothing here. Substituted to the
|
||||||
|
# store path, like the wallpaper paths above. LADSPA has no counterpart to
|
||||||
|
# services.pipewire.extraLv2Packages, so the path belongs in the file.
|
||||||
|
denoisingConfig = pkgs.runCommand "moonarch-pipewire-denoising" { } ''
|
||||||
|
mkdir -p "$out/share/pipewire/pipewire.conf.d"
|
||||||
|
substitute ${xdg}/pipewire/pipewire.conf.d/99-input-denoising.conf \
|
||||||
|
"$out/share/pipewire/pipewire.conf.d/99-input-denoising.conf" \
|
||||||
|
--replace-fail "plugin = librnnoise_ladspa" \
|
||||||
|
"plugin = ${pkgs.rnnoise-plugin}/lib/ladspa/librnnoise_ladspa.so"
|
||||||
|
'';
|
||||||
|
|
||||||
# zsh/newuser runs its setup wizard on every interactive login for as long as
|
# zsh/newuser runs its setup wizard on every interactive login for as long as
|
||||||
# the user has none of .zshenv, .zprofile, .zshrc or .zlogin — the whole
|
# the user has none of .zshenv, .zprofile, .zshrc or .zlogin — the whole
|
||||||
# configuration living in /etc/zshrc does not count. Seeded below to stop
|
# configuration living in /etc/zshrc does not count. Seeded below to stop
|
||||||
@@ -74,8 +92,9 @@ in
|
|||||||
"xdg/qt6ct/qt6ct.conf".source = "${xdg}/qt6ct/qt6ct.conf";
|
"xdg/qt6ct/qt6ct.conf".source = "${xdg}/qt6ct/qt6ct.conf";
|
||||||
"xdg/gtk-3.0/settings.ini".source = "${xdg}/gtk-3.0/settings.ini";
|
"xdg/gtk-3.0/settings.ini".source = "${xdg}/gtk-3.0/settings.ini";
|
||||||
"xdg/gtk-4.0/settings.ini".source = "${xdg}/gtk-4.0/settings.ini";
|
"xdg/gtk-4.0/settings.ini".source = "${xdg}/gtk-4.0/settings.ini";
|
||||||
"xdg/pipewire/pipewire.conf.d/99-input-denoising.conf".source =
|
# No xdg/pipewire/ entry: PipeWire does not read /etc/xdg, so the file was
|
||||||
"${xdg}/pipewire/pipewire.conf.d/99-input-denoising.conf";
|
# deployed and inert. It goes through services.pipewire.configPackages
|
||||||
|
# below instead.
|
||||||
# Kept for layout parity with the Arch package; waypaper itself only reads
|
# Kept for layout parity with the Arch package; waypaper itself only reads
|
||||||
# the copy in the home directory, seeded below.
|
# the copy in the home directory, seeded below.
|
||||||
"xdg/waypaper/config.ini".source = waypaperConfig;
|
"xdg/waypaper/config.ini".source = waypaperConfig;
|
||||||
@@ -202,6 +221,13 @@ in
|
|||||||
openmoji-color
|
openmoji-color
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# --- Audio ---
|
||||||
|
#
|
||||||
|
# RNNoise input denoising, from the shared config. The filter chain appears as
|
||||||
|
# a separate "Noise Canceling source" input; it does not replace the raw
|
||||||
|
# microphone, so applications have to select it.
|
||||||
|
services.pipewire.configPackages = [ denoisingConfig ];
|
||||||
|
|
||||||
# --- Printing ---
|
# --- Printing ---
|
||||||
#
|
#
|
||||||
# Driverless only, and no printer is declared here. Asked over IPP, the
|
# Driverless only, and no printer is declared here. Asked over IPP, the
|
||||||
|
|||||||
Reference in New Issue
Block a user