mirror of
https://github.com/zephrynis/nix-flake.git
synced 2026-08-18 05:55:53 +00:00
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>
75 lines
2.8 KiB
Nix
75 lines
2.8 KiB
Nix
{ 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 90–95 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;
|
||
};
|
||
};
|
||
}
|
||
];
|
||
};
|
||
}
|