python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / config / pulseaudio.nix
blob80ff6c1aabf74af78829c885cde654c87dc375c8
1 { config, lib, pkgs, ... }:
3 with pkgs;
4 with lib;
6 let
8   cfg = config.hardware.pulseaudio;
9   alsaCfg = config.sound;
11   systemWide = cfg.enable && cfg.systemWide;
12   nonSystemWide = cfg.enable && !cfg.systemWide;
13   hasZeroconf = let z = cfg.zeroconf; in z.publish.enable || z.discovery.enable;
15   overriddenPackage = cfg.package.override
16     (optionalAttrs hasZeroconf { zeroconfSupport = true; });
17   binary = "${getBin overriddenPackage}/bin/pulseaudio";
18   binaryNoDaemon = "${binary} --daemonize=no";
20   # Forces 32bit pulseaudio and alsa-plugins to be built/supported for apps
21   # using 32bit alsa on 64bit linux.
22   enable32BitAlsaPlugins = cfg.support32Bit && stdenv.isx86_64 && (pkgs.pkgsi686Linux.alsa-lib != null && pkgs.pkgsi686Linux.libpulseaudio != null);
25   myConfigFile =
26     let
27       addModuleIf = cond: mod: optionalString cond "load-module ${mod}";
28       allAnon = optional cfg.tcp.anonymousClients.allowAll "auth-anonymous=1";
29       ipAnon =  let a = cfg.tcp.anonymousClients.allowedIpRanges;
30                 in optional (a != []) ''auth-ip-acl=${concatStringsSep ";" a}'';
31     in writeTextFile {
32       name = "default.pa";
33         text = ''
34         .include ${cfg.configFile}
35         ${addModuleIf cfg.zeroconf.publish.enable "module-zeroconf-publish"}
36         ${addModuleIf cfg.zeroconf.discovery.enable "module-zeroconf-discover"}
37         ${addModuleIf cfg.tcp.enable (concatStringsSep " "
38            ([ "module-native-protocol-tcp" ] ++ allAnon ++ ipAnon))}
39         ${addModuleIf config.services.jack.jackd.enable "module-jack-sink"}
40         ${addModuleIf config.services.jack.jackd.enable "module-jack-source"}
41         ${cfg.extraConfig}
42       '';
43     };
45   ids = config.ids;
47   uid = ids.uids.pulseaudio;
48   gid = ids.gids.pulseaudio;
50   stateDir = "/run/pulse";
52   # Create pulse/client.conf even if PulseAudio is disabled so
53   # that we can disable the autospawn feature in programs that
54   # are built with PulseAudio support (like KDE).
55   clientConf = writeText "client.conf" ''
56     autospawn=no
57     ${cfg.extraClientConf}
58   '';
60   # Write an /etc/asound.conf that causes all ALSA applications to
61   # be re-routed to the PulseAudio server through ALSA's Pulse
62   # plugin.
63   alsaConf = writeText "asound.conf" (''
64     pcm_type.pulse {
65       libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ;
66       ${lib.optionalString enable32BitAlsaPlugins
67      "libs.32Bit = ${pkgs.pkgsi686Linux.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ;"}
68     }
69     pcm.!default {
70       type pulse
71       hint.description "Default Audio Device (via PulseAudio)"
72     }
73     ctl_type.pulse {
74       libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;
75       ${lib.optionalString enable32BitAlsaPlugins
76      "libs.32Bit = ${pkgs.pkgsi686Linux.alsa-plugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;"}
77     }
78     ctl.!default {
79       type pulse
80     }
81     ${alsaCfg.extraConfig}
82   '');
84 in {
86   options = {
88     hardware.pulseaudio = {
89       enable = mkOption {
90         type = types.bool;
91         default = false;
92         description = lib.mdDoc ''
93           Whether to enable the PulseAudio sound server.
94         '';
95       };
97       systemWide = mkOption {
98         type = types.bool;
99         default = false;
100         description = lib.mdDoc ''
101           If false, a PulseAudio server is launched automatically for
102           each user that tries to use the sound system. The server runs
103           with user privileges. If true, one system-wide PulseAudio
104           server is launched on boot, running as the user "pulse", and
105           only users in the "pulse-access" group will have access to the server.
106           Please read the PulseAudio documentation for more details.
108           Don't enable this option unless you know what you are doing.
109         '';
110       };
112       support32Bit = mkOption {
113         type = types.bool;
114         default = false;
115         description = lib.mdDoc ''
116           Whether to include the 32-bit pulseaudio libraries in the system or not.
117           This is only useful on 64-bit systems and currently limited to x86_64-linux.
118         '';
119       };
121       configFile = mkOption {
122         type = types.nullOr types.path;
123         description = lib.mdDoc ''
124           The path to the default configuration options the PulseAudio server
125           should use. By default, the "default.pa" configuration
126           from the PulseAudio distribution is used.
127         '';
128       };
130       extraConfig = mkOption {
131         type = types.lines;
132         default = "";
133         description = lib.mdDoc ''
134           Literal string to append to `configFile`
135           and the config file generated by the pulseaudio module.
136         '';
137       };
139       extraClientConf = mkOption {
140         type = types.lines;
141         default = "";
142         description = lib.mdDoc ''
143           Extra configuration appended to pulse/client.conf file.
144         '';
145       };
147       package = mkOption {
148         type = types.package;
149         default = if config.services.jack.jackd.enable
150                   then pkgs.pulseaudioFull
151                   else pkgs.pulseaudio;
152         defaultText = literalExpression "pkgs.pulseaudio";
153         example = literalExpression "pkgs.pulseaudioFull";
154         description = lib.mdDoc ''
155           The PulseAudio derivation to use.  This can be used to enable
156           features (such as JACK support, Bluetooth) via the
157           `pulseaudioFull` package.
158         '';
159       };
161       extraModules = mkOption {
162         type = types.listOf types.package;
163         default = [];
164         example = literalExpression "[ pkgs.pulseaudio-modules-bt ]";
165         description = lib.mdDoc ''
166           Extra pulseaudio modules to use. This is intended for out-of-tree
167           pulseaudio modules like extra bluetooth codecs.
169           Extra modules take precedence over built-in pulseaudio modules.
170         '';
171       };
173       daemon = {
174         logLevel = mkOption {
175           type = types.str;
176           default = "notice";
177           description = lib.mdDoc ''
178             The log level that the system-wide pulseaudio daemon should use,
179             if activated.
180           '';
181         };
183         config = mkOption {
184           type = types.attrsOf types.unspecified;
185           default = {};
186           description = lib.mdDoc "Config of the pulse daemon. See `man pulse-daemon.conf`.";
187           example = literalExpression ''{ realtime-scheduling = "yes"; }'';
188         };
189       };
191       zeroconf = {
192         discovery.enable =
193           mkEnableOption (lib.mdDoc "discovery of pulseaudio sinks in the local network");
194         publish.enable =
195           mkEnableOption (lib.mdDoc "publishing the pulseaudio sink in the local network");
196       };
198       # TODO: enable by default?
199       tcp = {
200         enable = mkEnableOption (lib.mdDoc "tcp streaming support");
202         anonymousClients = {
203           allowAll = mkEnableOption (lib.mdDoc "all anonymous clients to stream to the server");
204           allowedIpRanges = mkOption {
205             type = types.listOf types.str;
206             default = [];
207             example = literalExpression ''[ "127.0.0.1" "192.168.1.0/24" ]'';
208             description = lib.mdDoc ''
209               A list of IP subnets that are allowed to stream to the server.
210             '';
211           };
212         };
213       };
215     };
217   };
220   config = mkMerge [
221     {
222       environment.etc = {
223         "pulse/client.conf".source = clientConf;
224       };
226       hardware.pulseaudio.configFile = mkDefault "${getBin overriddenPackage}/etc/pulse/default.pa";
227     }
229     (mkIf cfg.enable {
230       environment.systemPackages = [ overriddenPackage ];
232       sound.enable = true;
234       environment.etc = {
235         "asound.conf".source = alsaConf;
237         "pulse/daemon.conf".source = writeText "daemon.conf"
238           (lib.generators.toKeyValue {} cfg.daemon.config);
240         "openal/alsoft.conf".source = writeText "alsoft.conf" "drivers=pulse";
242         "libao.conf".source = writeText "libao.conf" "default_driver=pulse";
243       };
245       # Disable flat volumes to enable relative ones
246       hardware.pulseaudio.daemon.config.flat-volumes = mkDefault "no";
248       # Upstream defaults to speex-float-1 which results in audible artifacts
249       hardware.pulseaudio.daemon.config.resample-method = mkDefault "speex-float-5";
251       # Allow PulseAudio to get realtime priority using rtkit.
252       security.rtkit.enable = true;
254       systemd.packages = [ overriddenPackage ];
256       # PulseAudio is packaged with udev rules to handle various audio device quirks
257       services.udev.packages = [ overriddenPackage ];
258     })
260     (mkIf (cfg.extraModules != []) {
261       hardware.pulseaudio.daemon.config.dl-search-path = let
262         overriddenModules = builtins.map
263           (drv: drv.override { pulseaudio = overriddenPackage; })
264           cfg.extraModules;
265         modulePaths = builtins.map
266           (drv: "${drv}/lib/pulseaudio/modules")
267           # User-provided extra modules take precedence
268           (overriddenModules ++ [ overriddenPackage ]);
269       in lib.concatStringsSep ":" modulePaths;
270     })
272     (mkIf hasZeroconf {
273       services.avahi.enable = true;
274     })
275     (mkIf cfg.zeroconf.publish.enable {
276       services.avahi.publish.enable = true;
277       services.avahi.publish.userServices = true;
278     })
280     (mkIf nonSystemWide {
281       environment.etc = {
282         "pulse/default.pa".source = myConfigFile;
283       };
284       systemd.user = {
285         services.pulseaudio = {
286           restartIfChanged = true;
287           serviceConfig = {
288             RestartSec = "500ms";
289             PassEnvironment = "DISPLAY";
290           };
291         } // optionalAttrs config.services.jack.jackd.enable {
292           environment.JACK_PROMISCUOUS_SERVER = "jackaudio";
293         };
294         sockets.pulseaudio = {
295           wantedBy = [ "sockets.target" ];
296         };
297       };
298     })
300     (mkIf systemWide {
301       users.users.pulse = {
302         # For some reason, PulseAudio wants UID == GID.
303         uid = assert uid == gid; uid;
304         group = "pulse";
305         extraGroups = [ "audio" ];
306         description = "PulseAudio system service user";
307         home = stateDir;
308         createHome = true;
309         isSystemUser = true;
310       };
312       users.groups.pulse.gid = gid;
313       users.groups.pulse-access = {};
315       systemd.services.pulseaudio = {
316         description = "PulseAudio System-Wide Server";
317         wantedBy = [ "sound.target" ];
318         before = [ "sound.target" ];
319         environment.PULSE_RUNTIME_PATH = stateDir;
320         serviceConfig = {
321           Type = "notify";
322           ExecStart = "${binaryNoDaemon} --log-level=${cfg.daemon.logLevel} --system -n --file=${myConfigFile}";
323           Restart = "on-failure";
324           RestartSec = "500ms";
325         };
326       };
328       environment.variables.PULSE_COOKIE = "${stateDir}/.config/pulse/cookie";
329     })
330   ];