Files
nix-flake/modules/noise-suppression.nix
zephrynis 91053f9693 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>
2026-07-28 00:51:47 +01:00

75 lines
2.8 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ 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;
};
};
}
];
};
}