PipeWire: Difference between revisions

Frontear (talk | contribs)
m some small changes to formatting and grammar
Jopejoe1 (talk | contribs)
format code with nixfmt
Line 10: Line 10:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
# On NixOS 24.05 or older, this option must be set:
  # On NixOS 24.05 or older, this option must be set:
#sound.enable = false;
  #sound.enable = false;


# rtkit is optional but recommended
  # rtkit is optional but recommended
security.rtkit.enable = true;
  security.rtkit.enable = true;
services.pipewire = {
  services.pipewire = {
  enable = true;
    enable = true;
  alsa.enable = true;
    alsa.enable = true;
  alsa.support32Bit = true;
    alsa.support32Bit = true;
  pulse.enable = true;
    pulse.enable = true;
  # If you want to use JACK applications, uncomment this
    # If you want to use JACK applications, uncomment this
  #jack.enable = true;
    #jack.enable = true;
};
  };
</syntaxhighlight>
</syntaxhighlight>


Line 33: Line 33:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
services.pipewire.wireplumber.extraConfig."10-bluez.conf" = {
  services.pipewire.wireplumber.extraConfig."10-bluez.conf" = {
"monitor.bluez.properties" = {
    "monitor.bluez.properties" = {
"bluez5.enable-sbc-xq" = true;
      "bluez5.enable-sbc-xq" = true;
"bluez5.enable-msbc" = true;
      "bluez5.enable-msbc" = true;
"bluez5.enable-hw-volume" = true;
      "bluez5.enable-hw-volume" = true;
"bluez5.headset-roles" = ["hsp_hs" "hsp_ag" "hfp_hf" "hfp_ag"];
      "bluez5.headset-roles" = [
};
        "hsp_hs"
};
        "hsp_ag"
        "hfp_hf"
        "hfp_ag"
      ];
    };
  };
</syntaxhighlight>
</syntaxhighlight>


Line 46: Line 51:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
services.pipewire.wireplumber.configPackages = [
  services.pipewire.wireplumber.configPackages = [
(pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/10-bluez.conf" ''
    (pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/10-bluez.conf" ''
monitor.bluez.properties = {
      monitor.bluez.properties = {
bluez5.enable-sbc-xq = true
        bluez5.enable-sbc-xq = true
bluez5.enable-msbc = true
        bluez5.enable-msbc = true
bluez5.enable-hw-volume = true
        bluez5.enable-hw-volume = true
bluez5.headset-roles = [hsp_hs hsp_ag hfp_hf hfp_ag]
        bluez5.headset-roles = [hsp_hs hsp_ag hfp_hf hfp_ag]
}
      }
'')
    '')
];
  ];
</syntaxhighlight>
</syntaxhighlight>


Line 63: Line 68:


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
services.pipewire = {
  services.pipewire = {
  media-session.config.bluez-monitor.rules = [
    media-session.config.bluez-monitor.rules = [
    {
      {
      # Matches all cards
        # Matches all cards
      matches = [ { "device.name" = "~bluez_card.*"; } ];
        matches = [ { "device.name" = "~bluez_card.*"; } ];
      actions = {
        actions = {
        "update-props" = {
          "update-props" = {
          "bluez5.reconnect-profiles" = [ "hfp_hf" "hsp_hs" "a2dp_sink" ];
            "bluez5.reconnect-profiles" = [
          # mSBC is not expected to work on all headset + adapter combinations.
              "hfp_hf"
          "bluez5.msbc-support" = true;
              "hsp_hs"
          # SBC-XQ is not expected to work on all headset + adapter combinations.
              "a2dp_sink"
          "bluez5.sbc-xq-support" = true;
            ];
            # mSBC is not expected to work on all headset + adapter combinations.
            "bluez5.msbc-support" = true;
            # SBC-XQ is not expected to work on all headset + adapter combinations.
            "bluez5.sbc-xq-support" = true;
          };
         };
         };
       };
       }
    }
      {
    {
        matches = [
      matches = [
          # Matches all sources
        # Matches all sources
          { "node.name" = "~bluez_input.*"; }
        { "node.name" = "~bluez_input.*"; }
          # Matches all outputs
        # Matches all outputs
          { "node.name" = "~bluez_output.*"; }
        { "node.name" = "~bluez_output.*"; }
        ];
      ];
      }
    }
    ];
  ];
  };
};
</syntaxHighlight>
</syntaxHighlight>


Line 110: Line 119:
For NixOS 24.05 and newer:
For NixOS 24.05 and newer:
<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
services.pipewire.extraConfig.pipewire."91-null-sinks" = {
  services.pipewire.extraConfig.pipewire."91-null-sinks" = {
  "context.objects" = [
    {
      # A default dummy driver. This handles nodes marked with the "node.always-driver"
      # properyty when no other driver is currently active. JACK clients need this.
      factory = "spa-node-factory";
      args = {
        "factory.name"    = "support.node.driver";
        "node.name"        = "Dummy-Driver";
        "priority.driver"  = 8000;
      };
    }
    {
      factory = "adapter";
      args = {
        "factory.name"    = "support.null-audio-sink";
        "node.name"        = "Microphone-Proxy";
        "node.description" = "Microphone";
        "media.class"      = "Audio/Source/Virtual";
        "audio.position"  = "MONO";
      };
    }
    {
      factory = "adapter";
      args = {
        "factory.name"    = "support.null-audio-sink";
        "node.name"        = "Main-Output-Proxy";
        "node.description" = "Main Output";
        "media.class"      = "Audio/Sink";
        "audio.position"  = "FL,FR";
      };
    }
  ];
};
</syntaxHighlight>
 
If you're still using 23.11 or earlier, you can use <code>environment.etc</code> and <code>pkgs.formats.json</code>:
<syntaxHighlight lang=nix>
environment.etc = let
  json = pkgs.formats.json {};
in {
  "pipewire/pipewire.d/91-null-sinks.conf".source = json.generate "91-null-sinks.conf" {
     "context.objects" = [
     "context.objects" = [
       {
       {
Line 158: Line 126:
         factory = "spa-node-factory";
         factory = "spa-node-factory";
         args = {
         args = {
           "factory.name"     = "support.node.driver";
           "factory.name" = "support.node.driver";
           "node.name"       = "Dummy-Driver";
           "node.name" = "Dummy-Driver";
           "priority.driver" = 8000;
           "priority.driver" = 8000;
         };
         };
       }
       }
Line 166: Line 134:
         factory = "adapter";
         factory = "adapter";
         args = {
         args = {
           "factory.name"     = "support.null-audio-sink";
           "factory.name" = "support.null-audio-sink";
           "node.name"       = "Microphone-Proxy";
           "node.name" = "Microphone-Proxy";
           "node.description" = "Microphone";
           "node.description" = "Microphone";
           "media.class"     = "Audio/Source/Virtual";
           "media.class" = "Audio/Source/Virtual";
           "audio.position"   = "MONO";
           "audio.position" = "MONO";
         };
         };
       }
       }
Line 176: Line 144:
         factory = "adapter";
         factory = "adapter";
         args = {
         args = {
           "factory.name"     = "support.null-audio-sink";
           "factory.name" = "support.null-audio-sink";
           "node.name"       = "Main-Output-Proxy";
           "node.name" = "Main-Output-Proxy";
           "node.description" = "Main Output";
           "node.description" = "Main Output";
           "media.class"     = "Audio/Sink";
           "media.class" = "Audio/Sink";
           "audio.position"   = "FL,FR";
           "audio.position" = "FL,FR";
         };
         };
       }
       }
     ];
     ];
   };
   };
};
</syntaxHighlight>
 
If you're still using 23.11 or earlier, you can use <code>environment.etc</code> and <code>pkgs.formats.json</code>:
<syntaxHighlight lang=nix>
  environment.etc =
    let
      json = pkgs.formats.json { };
    in
    {
      "pipewire/pipewire.d/91-null-sinks.conf".source = json.generate "91-null-sinks.conf" {
        "context.objects" = [
          {
            # A default dummy driver. This handles nodes marked with the "node.always-driver"
            # properyty when no other driver is currently active. JACK clients need this.
            factory = "spa-node-factory";
            args = {
              "factory.name" = "support.node.driver";
              "node.name" = "Dummy-Driver";
              "priority.driver" = 8000;
            };
          }
          {
            factory = "adapter";
            args = {
              "factory.name" = "support.null-audio-sink";
              "node.name" = "Microphone-Proxy";
              "node.description" = "Microphone";
              "media.class" = "Audio/Source/Virtual";
              "audio.position" = "MONO";
            };
          }
          {
            factory = "adapter";
            args = {
              "factory.name" = "support.null-audio-sink";
              "node.name" = "Main-Output-Proxy";
              "node.description" = "Main Output";
              "media.class" = "Audio/Sink";
              "audio.position" = "FL,FR";
            };
          }
        ];
      };
    };
</syntaxHighlight>
</syntaxHighlight>


Line 208: Line 219:
For 24.05 and newer:
For 24.05 and newer:
<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
services.pipewire.extraConfig.pipewire."92-low-latency" = {
  services.pipewire.extraConfig.pipewire."92-low-latency" = {
  "context.properties" = {
    "context.properties" = {
    "default.clock.rate" = 48000;
      "default.clock.rate" = 48000;
    "default.clock.quantum" = 32;
      "default.clock.quantum" = 32;
    "default.clock.min-quantum" = 32;
      "default.clock.min-quantum" = 32;
    "default.clock.max-quantum" = 32;
      "default.clock.max-quantum" = 32;
    };
   };
   };
};
</syntaxHighlight>
</syntaxHighlight>


Line 226: Line 237:
For 24.05 or newer:
For 24.05 or newer:
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
services.pipewire.extraConfig.pipewire-pulse."92-low-latency" = {
  services.pipewire.extraConfig.pipewire-pulse."92-low-latency" = {
  "context.properties" = [
    "context.properties" = [
    {
      {
      name = "libpipewire-module-protocol-pulse";
        name = "libpipewire-module-protocol-pulse";
      args = { };
        args = { };
    }
      }
  ];
    ];
  "pulse.properties" = {
    "pulse.properties" = {
    "pulse.min.req" = "32/48000";
      "pulse.min.req" = "32/48000";
    "pulse.default.req" = "32/48000";
      "pulse.default.req" = "32/48000";
    "pulse.max.req" = "32/48000";
      "pulse.max.req" = "32/48000";
    "pulse.min.quantum" = "32/48000";
      "pulse.min.quantum" = "32/48000";
    "pulse.max.quantum" = "32/48000";
      "pulse.max.quantum" = "32/48000";
  };
    };
  "stream.properties" = {
    "stream.properties" = {
    "node.latency" = "32/48000";
      "node.latency" = "32/48000";
    "resample.quality" = 1;
      "resample.quality" = 1;
    };
   };
   };
};
</syntaxhighlight>
</syntaxhighlight>
If you're still using 23.11 or earlier, you can use <code>environment.etc</code> and <code>pkgs.formats.json</code> like in [[PipeWire#Advanced_Configuration|Advanced Configuration]].
If you're still using 23.11 or earlier, you can use <code>environment.etc</code> and <code>pkgs.formats.json</code> like in [[PipeWire#Advanced_Configuration|Advanced Configuration]].
Line 254: Line 265:
For 24.05 and newer:
For 24.05 and newer:
<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
services.pipewire.wireplumber.configPackages = [
  services.pipewire.wireplumber.configPackages = [
  (pkgs.writeTextDir "share/wireplumber/main.lua.d/99-alsa-lowlatency.lua" ''
    (pkgs.writeTextDir "share/wireplumber/main.lua.d/99-alsa-lowlatency.lua" ''
      alsa_monitor.rules = {
        {
          matches = {{{ "node.name", "matches", "alsa_output.*" }}};
          apply_properties = {
            ["audio.format"] = "S32LE",
            ["audio.rate"] = "96000", -- for USB soundcards it should be twice your desired rate
            ["api.alsa.period-size"] = 2, -- defaults to 1024, tweak by trial-and-error
            -- ["api.alsa.disable-batch"] = true, -- generally, USB soundcards use the batch mode
          },
        },
      }
    '')
  ];
</syntaxHighlight>
 
Once [https://github.com/NixOS/nixpkgs/pull/292115 #292115] is merged and has reached nixos-unstable, you'll be able to use <code>services.pipewire.wireplumber.extraLuaConfig</code> as well:
<syntaxHighlight lang=nix>
  services.pipewire.wireplumber.extraLuaConfig.main."99-alsa-lowlatency" = ''
     alsa_monitor.rules = {
     alsa_monitor.rules = {
       {
       {
Line 267: Line 296:
       },
       },
     }
     }
   '')
   '';
];
</syntaxHighlight>
</syntaxHighlight>


Once [https://github.com/NixOS/nixpkgs/pull/292115 #292115] is merged and has reached nixos-unstable, you'll be able to use <code>services.pipewire.wireplumber.extraLuaConfig</code> as well:
If you're still using 23.11 or earlier, you can use <code>environment.etc</code>:
<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
services.pipewire.wireplumber.extraLuaConfig.main."99-alsa-lowlatency" = ''
  environment.etc."wireplumber/main.lua.d/99-alsa-lowlatency.lua".text = ''
  alsa_monitor.rules = {
    alsa_monitor.rules = {
    {
      {
      matches = {{{ "node.name", "matches", "alsa_output.*" }}};
        matches = {{{ "node.name", "matches", "alsa_output.*" }}};
      apply_properties = {
        apply_properties = {
        ["audio.format"] = "S32LE",
          ["audio.format"] = "S32LE",
        ["audio.rate"] = "96000", -- for USB soundcards it should be twice your desired rate
          ["audio.rate"] = "96000", -- for USB soundcards it should be twice your desired rate
        ["api.alsa.period-size"] = 2, -- defaults to 1024, tweak by trial-and-error
          ["api.alsa.period-size"] = 2, -- defaults to 1024, tweak by trial-and-error
        -- ["api.alsa.disable-batch"] = true, -- generally, USB soundcards use the batch mode
          -- ["api.alsa.disable-batch"] = true, -- generally, USB soundcards use the batch mode
        },
       },
       },
     },
     }
   }
   '';
'';
</syntaxHighlight>
</syntaxHighlight>
If you're still using 23.11 or earlier, you can use <code>environment.etc</code>:
<syntaxHighlight lang=nix>
environment.etc.
"wireplumber/main.lua.d/99-alsa-lowlatency.lua".text = ''
  alsa_monitor.rules = {
    {
      matches = {{{ "node.name", "matches", "alsa_output.*" }}};
      apply_properties = {
        ["audio.format"] = "S32LE",
        ["audio.rate"] = "96000", -- for USB soundcards it should be twice your desired rate
        ["api.alsa.period-size"] = 2, -- defaults to 1024, tweak by trial-and-error
        -- ["api.alsa.disable-batch"] = true, -- generally, USB soundcards use the batch mode
      },
    },
  }
'';</syntaxHighlight>


The <code>matches</code> attribute applies the <code>actions</code> to the devices/properties listed there. It is usually used with soundcard names, like shown in the config above. <code><matches></code> can match any of the outputs of
The <code>matches</code> attribute applies the <code>actions</code> to the devices/properties listed there. It is usually used with soundcard names, like shown in the config above. <code><matches></code> can match any of the outputs of