NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
17.17k stars 13.45k forks source link

Pipewire, unable to set up filter chain via configuration.nix #193176

Closed mabequinho closed 1 year ago

mabequinho commented 1 year ago

Describe the bug

Unable to set filter chain via configuration.nix although it works if i put on ~/.config/pipewire/

Steps To Reproduce

Steps to reproduce the behavior:

  1. add a libpipewire-module-filter-chain block inside context.modules via option services.pipewire.config.pipewire.

pipewireconfnix.txt

Expected behavior

Add a extra sound imput(Noise Canceling source).

Additional context

could be related to upgrade from pipewire 0.3.54 to 0.3.55 breaks rnnoise filter

× pipewire.service - PipeWire Multimedia Service
     Loaded: loaded (/etc/systemd/user/pipewire.service; linked-runtime; preset: enabled)
    Drop-In: /nix/store/khly40xfd1j3kdq3hj1s728sbxnx4xn1-user-units/pipewire.service.d
             └─overrides.conf
     Active: failed (Result: exit-code) since Tue 2022-09-27 09:58:18 -03; 15s ago
   Duration: 18ms
TriggeredBy: × pipewire.socket
    Process: 21658 ExecStart=/nix/store/85bkqah1kxikn0cqmxs3d0q4n527b9g5-pipewire-0.3.56/bin/pipewire (code=exited, status=161)
   Main PID: 21658 (code=exited, status=161)
        CPU: 18ms

set 27 09:58:18 inferno systemd[1142]: pipewire.service: Scheduled restart job, restart counter is at 5.
set 27 09:58:18 inferno systemd[1142]: Stopped PipeWire Multimedia Service.
set 27 09:58:18 inferno systemd[1142]: pipewire.service: Start request repeated too quickly.
set 27 09:58:18 inferno systemd[1142]: pipewire.service: Failed with result 'exit-code'.
set 27 09:58:18 inferno systemd[1142]: Failed to start PipeWire Multimedia Service.
[E][02881.509507] pw.core      | [          core.c:  382 core_new()] 0x557bbb8330e0: can't find protocol 'PipeWire:Protocol:Native': Operation not supported
[E][02881.509582] mod.filter-chain | [module-filter-ch: 2235 pipewire__module_init()] can't connect: Operation not supported
[E][02881.509791] pw.conf      | [          conf.c:  560 load_module()] 0x557bbb7cd710: could not load mandatory module "libpipewire-module-filter-chain": Operation not supported
[E][02881.509864] default      | [      pipewire.c:  125 main()] failed to create context: Operation not supported

Notify maintainers

Metadata

 - system: `"x86_64-linux"`
 - host os: `Linux 5.19.9, NixOS, 22.11 (Raccoon), 22.11pre411253.79d3ca08920`
 - multi-user?: `yes`
 - sandbox: `yes`
 - version: `nix-env (Nix) 2.11.0`
 - channels(lucio): `""`
 - channels(root): `"nixos"`
 - nixpkgs: `/nix/var/nix/profiles/per-user/root/channels/nixos`
jansol commented 1 year ago

I have used this for quite a while without problems:

{ config, pkgs, ... }:
let
  json = pkgs.formats.json {};
  pw_rnnoise_config = {
    "context.properties" = {
      "log.level" = 0;
    };
    "context.spa-libs" = {
      "audio.convert.*" = "audioconvert/libspa-audioconvert";
      "support.*" = "support/libspa-support";
    };
    "context.modules" = [
      {
        name = "libpipewire-module-rtkit";
        args = {
          #"nice.level"   = -11;
          #"rt.prio"      = 88;
          #"rt.time.soft" = 2000000;
          #"rt.time.hard" = 2000000;
        };
        flags = [ "ifexists" "nofail" ];
      }
      { name = "libpipewire-module-protocol-native"; }
      { name = "libpipewire-module-client-node"; }
      { name = "libpipewire-module-adapter"; }

      {
        name = "libpipewire-module-filter-chain";
        args = {
          "node.name"        = "effect_input.rnnoise";
          "node.description" = "Noise Canceling source";
          "media.name"       = "Noise Canceling source";
          "filter.graph" = {
            nodes = [
              {
                type   = "ladspa";
                name   = "rnnoise";
                plugin = "librnnoise_ladspa";
                label  = "noise_suppressor_mono";
                control = {
                  "VAD Threshold (%)" = 50.0;
                 };
              }
            ];
          };
          "capture.props" = {
            "node.passive" = true;
        "node.target" = "alsa_input.usb-M-Audio_Fast_Track-00.pro-input-0";
        "audio.position" = [ "AUX0" ];
          };
          "playback.props" = {
        "audio.position" = [ "MONO" ];
            "media.class" = "Audio/Source";
          };
        };
      }
    ];
  };
in
{
  environment.etc."pipewire/source-rnnoise.conf" = {
      source = json.generate "source-rnnoise.conf" pw_rnnoise_config;
  };
  systemd.user.services."pipewire-source-rnnoise" = {
    environment = { LADSPA_PATH = "${pkgs.rnnoise-plugin}/lib/ladspa"; };
    description = "Noise canceling source for pipewire";
    wantedBy = ["pipewire.service"];
    script = "${pkgs.pipewire}/bin/pipewire -c source-rnnoise.conf";
    enable = true;
    path = with pkgs; [pipewire rnnoise-plugin];
  };
}
mabequinho commented 1 year ago

You know if something like this would be enough? Since pipewire now can use split conf files I think it could work if there´s a way to generate the /etc/pipewire/pipewire.conf.d/99-input-denoising.conf without the leading and trailing brackets.

I´m trying to make something like this https://github.com/werman/noise-suppression-for-voice

let
  json = pkgs.formats.json {};
  pw_rnnoise_config = {

  "context.modules"= [

###MICFILTER
    { "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_stereo";
                        "control" = {
                            "VAD Threshold (%)" = 50.0;
                        };
                    }
                ];
            };
            "audio.position" = [ "FL" "FR" ];
            "capture.props" = {
                "node.name" = "effect_input.rnnoise";
                "node.passive" = true;
            };
            "playback.props" = {
                "node.name" = "effect_output.rnnoise";
                "media.class" = "Audio/Source";
            };
        };
    }
];

  };
in
{
  environment.etc."pipewire/pipewire.conf.d/99-input-denoising.conf" = {
      source = json.generate "99-input-denoising.conf" pw_rnnoise_config;
  };
}
mabequinho commented 1 year ago

nvmd, its working this way, you you enlightened me, thnx.

{ config, pkgs, ... }:
{
  sound.enable = true;
  hardware.pulseaudio.enable = false;
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    };
}
{ config, pkgs, ... }:

let
  json = pkgs.formats.json {};
  pw_rnnoise_config = {
  "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_stereo";
                        "control" = {
                            "VAD Threshold (%)" = 50.0;
                        };
                    }
                ];
            };
            "audio.position" = [ "FL" "FR" ];
            "capture.props" = {
                "node.name" = "effect_input.rnnoise";
                "node.passive" = true;
            };
            "playback.props" = {
                "node.name" = "effect_output.rnnoise";
                "media.class" = "Audio/Source";
            };
        };
    }
];
};
in
{
  environment.etc."pipewire/pipewire.conf.d/99-input-denoising.conf" = {
      source = json.generate "99-input-denoising.conf" pw_rnnoise_config;
        };
}
nixos-discourse commented 1 year ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/pipewire-unable-to-set-filter-chain-via-configuration-nix/21972/4

jansol commented 1 year ago

@wnxkiv85 That log fragment you posted says that it hasn't loaded libpipewire-module-protocol-native yet, and filter-chain depends on that. It used to be a very common issue that you had to have the modules listed in your config in a specific order but I got the impression that they had removed that limitation.

Maybe that is what the regression is about. The separate config fragments presumably get processed only after the main config file so the dependencies are already loaded.

unlux commented 1 month ago
error: The option `services.pipewire.extraConfig."pipewire/pipewire.conf.d/99-input-denoising.conf"' does not exist. Definition values:
       - In `/nix/store/318v6ykv94kgh9hwzgpc81zz0vmbds9a-source/modules/system/noisecancel.nix':
           {
             source = <derivation 99-input-denoising.conf>;
           }

:cry:

jansol commented 1 month ago

Nowadays you have to put it in services.pipewire.extraConfig.pipewire:

{ config, pkgs, ... }:
let
  pw_voice_dsp_config = {
    // <snip, this looks the same as before>
  };
in
{
  hardware.pulseaudio.enable = false;
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    # compatibility layers
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    jack.enable = true;
    // if your filters depend on LV2 plugins you can list their nix packages here if you don't want to fiddle with global `*_PLUGIN_PATH` env variables. not sure if it works for other plugin formats though
    extraLv2Packages = with pkgs; [ lsp-plugins ];

    // Custom config files need to be listed here now:
    extraConfig.pipewire = {
      "99-voice-dsp-source" = pw_voice_dsp_config;
    };
  };
}

I've switched away from rnnoise/deepfilternet to a simple gate/compressor/eq setup, but the overall idea is similar.

unlux commented 1 month ago

got it working, thanks king!