noise-suppression: RNNoise virtual mic via PipeWire, enable on host

App-independent mic denoising as a PipeWire filter-chain exposing a
"Noise Canceling source" — replaces Discord's unusable Krisp and works
in every app. Imported into the nixos-pc host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-28 00:51:47 +01:00
parent 4b3983d895
commit 91053f9693
2 changed files with 75 additions and 0 deletions

View File

@@ -8,6 +8,7 @@
../../modules/nvidia.nix ../../modules/nvidia.nix
../../modules/gaming.nix ../../modules/gaming.nix
../../modules/tailscale.nix ../../modules/tailscale.nix
../../modules/noise-suppression.nix
]; ];
networking.hostName = "nixos-pc"; networking.hostName = "nixos-pc";

View File

@@ -0,0 +1,74 @@
{ pkgs, ... }:
# App-independent microphone noise suppression via PipeWire + RNNoise.
#
# Replaces Discord's Krisp (which can't be enabled on current nixpkgs Discord:
# the binary is patchelf'd, and even after bypassing Krisp's signature check the
# newer "THz SDK" stage fails with KRISP_INIT_ERROR_GLOBAL_INIT). Instead we run
# RNNoise as a PipeWire filter-chain that exposes a cleaned virtual microphone.
#
# This works in EVERY app (Discord, browser, OBS, games), survives Discord
# updates, and touches no DRM. In each app's input settings, pick the source
# named "Noise Canceling source" instead of the raw mic.
{
# LADSPA plugin providing noise_suppressor_mono/stereo (werman RNNoise).
environment.systemPackages = [ pkgs.rnnoise-plugin ];
services.pipewire.extraConfig.pipewire."99-input-denoising" = {
"context.modules" = [
{
name = "libpipewire-module-filter-chain";
args = {
"node.description" = "Noise Canceling source";
"media.name" = "Noise Canceling source";
"filter.graph" = {
nodes = [
{
type = "ladspa";
name = "rnnoise";
plugin = "${pkgs.rnnoise-plugin}/lib/ladspa/librnnoise_ladspa.so";
label = "noise_suppressor_mono";
control = {
# Higher threshold = more aggressive gating of non-voice.
# 50% is a balanced starting point; raise toward 9095 if a
# noisy background still leaks through, lower if speech clips.
"VAD Threshold (%)" = 50.0;
"VAD Grace Period (ms)" = 200.0;
"Retroactive VAD Grace (ms)" = 0.0;
};
}
{
# Makeup gain baked into the graph so the source volume is
# reproducible instead of living only in WirePlumber's runtime
# state. Mult = 1.53 matches the 153% level set in the mixer.
type = "builtin";
name = "gain";
label = "linear";
control = {
"Mult" = 1.53;
"Add" = 0.0;
};
}
];
links = [
{ output = "rnnoise:Output"; input = "gain:In"; }
];
};
# The virtual mic apps select. media.class = Audio/Source makes it
# appear as a capture device throughout the desktop.
"capture.props" = {
"node.name" = "capture.rnnoise_source";
"node.passive" = true;
"audio.rate" = 48000;
};
"playback.props" = {
"node.name" = "rnnoise_source";
"media.class" = "Audio/Source";
"audio.rate" = 48000;
};
};
}
];
};
}