1{ config, lib, pkgs, pkgs_i686, ... }:
2
3with pkgs;
4with lib;
5
6let
7
8 cfg = config.hardware.pulseaudio;
9
10 systemWide = cfg.enable && cfg.systemWide;
11 nonSystemWide = cfg.enable && !cfg.systemWide;
12
13 # Forces 32bit pulseaudio and alsaPlugins to be built/supported for apps
14 # using 32bit alsa on 64bit linux.
15 enable32BitAlsaPlugins = cfg.support32Bit && stdenv.isx86_64 && (pkgs_i686.alsaLib != null && pkgs_i686.libpulseaudio != null);
16
17 ids = config.ids;
18
19 uid = ids.uids.pulseaudio;
20 gid = ids.gids.pulseaudio;
21
22 stateDir = "/var/run/pulse";
23
24 # Create pulse/client.conf even if PulseAudio is disabled so
25 # that we can disable the autospawn feature in programs that
26 # are built with PulseAudio support (like KDE).
27 clientConf = writeText "client.conf" ''
28 autospawn=${if nonSystemWide then "yes" else "no"}
29 ${optionalString nonSystemWide "daemon-binary=${cfg.package}/bin/pulseaudio"}
30 '';
31
32 # Write an /etc/asound.conf that causes all ALSA applications to
33 # be re-routed to the PulseAudio server through ALSA's Pulse
34 # plugin.
35 alsaConf = writeText "asound.conf" (''
36 pcm_type.pulse {
37 libs.native = ${pkgs.alsaPlugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ;
38 ${lib.optionalString enable32BitAlsaPlugins
39 "libs.32Bit = ${pkgs_i686.alsaPlugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ;"}
40 }
41 pcm.!default {
42 type pulse
43 hint.description "Default Audio Device (via PulseAudio)"
44 }
45 ctl_type.pulse {
46 libs.native = ${alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;
47 ${lib.optionalString enable32BitAlsaPlugins
48 "libs.32Bit = ${pkgs_i686.alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;"}
49 }
50 ctl.!default {
51 type pulse
52 }
53 '');
54
55in {
56
57 options = {
58
59 hardware.pulseaudio = {
60 enable = mkOption {
61 type = types.bool;
62 default = false;
63 description = ''
64 Whether to enable the PulseAudio sound server.
65 '';
66 };
67
68 systemWide = mkOption {
69 type = types.bool;
70 default = false;
71 description = ''
72 If false, a PulseAudio server is launched automatically for
73 each user that tries to use the sound system. The server runs
74 with user privileges. This is the recommended and most secure
75 way to use PulseAudio. If true, one system-wide PulseAudio
76 server is launched on boot, running as the user "pulse".
77 Please read the PulseAudio documentation for more details.
78 '';
79 };
80
81 support32Bit = mkOption {
82 type = types.bool;
83 default = false;
84 description = ''
85 Whether to include the 32-bit pulseaudio libraries in the systemn or not.
86 This is only useful on 64-bit systems and currently limited to x86_64-linux.
87 '';
88 };
89
90 configFile = mkOption {
91 type = types.path;
92 description = ''
93 The path to the configuration the PulseAudio server
94 should use. By default, the "default.pa" configuration
95 from the PulseAudio distribution is used.
96 '';
97 };
98
99 package = mkOption {
100 type = types.package;
101 default = pulseaudioLight;
102 example = literalExample "pkgs.pulseaudioFull";
103 description = ''
104 The PulseAudio derivation to use. This can be used to enable
105 features (such as JACK support, Bluetooth) via the
106 <literal>pulseaudioFull</literal> package.
107 '';
108 };
109
110 daemon = {
111 logLevel = mkOption {
112 type = types.str;
113 default = "notice";
114 description = ''
115 The log level that the system-wide pulseaudio daemon should use,
116 if activated.
117 '';
118 };
119 };
120 };
121
122 };
123
124
125 config = mkMerge [
126 {
127 environment.etc = singleton {
128 target = "pulse/client.conf";
129 source = clientConf;
130 };
131
132 hardware.pulseaudio.configFile = mkDefault "${cfg.package}/etc/pulse/default.pa";
133 }
134
135 (mkIf cfg.enable {
136 environment.systemPackages = [ cfg.package ];
137
138 environment.etc = singleton {
139 target = "asound.conf";
140 source = alsaConf;
141 };
142
143 # Allow PulseAudio to get realtime priority using rtkit.
144 security.rtkit.enable = true;
145 })
146
147 (mkIf nonSystemWide {
148 environment.etc = singleton {
149 target = "pulse/default.pa";
150 source = cfg.configFile;
151 };
152 })
153
154 (mkIf systemWide {
155 users.extraUsers.pulse = {
156 # For some reason, PulseAudio wants UID == GID.
157 uid = assert uid == gid; uid;
158 group = "pulse";
159 extraGroups = [ "audio" ];
160 description = "PulseAudio system service user";
161 home = stateDir;
162 createHome = true;
163 };
164
165 users.extraGroups.pulse.gid = gid;
166
167 systemd.services.pulseaudio = {
168 description = "PulseAudio System-Wide Server";
169 wantedBy = [ "sound.target" ];
170 before = [ "sound.target" ];
171 environment.PULSE_RUNTIME_PATH = stateDir;
172 serviceConfig = {
173 ExecStart = "${cfg.package}/bin/pulseaudio -D --log-level=${cfg.daemon.logLevel} --system --use-pid-file -n --file=${cfg.configFile}";
174 PIDFile = "${stateDir}/pid";
175 };
176 };
177 })
178 ];
179
180}