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