at 17.09-beta 8.8 kB view raw
1{ config, lib, pkgs, pkgs_i686, ... }: 2 3with pkgs; 4with lib; 5 6let 7 8 cfg = config.hardware.pulseaudio; 9 alsaCfg = config.sound; 10 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; 14 15 overriddenPackage = cfg.package.override 16 (optionalAttrs hasZeroconf { zeroconfSupport = true; }); 17 binary = "${getBin overriddenPackage}/bin/pulseaudio"; 18 binaryNoDaemon = "${binary} --daemonize=no"; 19 20 # Forces 32bit pulseaudio and alsaPlugins to be built/supported for apps 21 # using 32bit alsa on 64bit linux. 22 enable32BitAlsaPlugins = cfg.support32Bit && stdenv.isx86_64 && (pkgs_i686.alsaLib != null && pkgs_i686.libpulseaudio != null); 23 24 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 ${cfg.extraConfig} 40 ''; 41 }; 42 43 ids = config.ids; 44 45 uid = ids.uids.pulseaudio; 46 gid = ids.gids.pulseaudio; 47 48 stateDir = "/var/run/pulse"; 49 50 # Create pulse/client.conf even if PulseAudio is disabled so 51 # that we can disable the autospawn feature in programs that 52 # are built with PulseAudio support (like KDE). 53 clientConf = writeText "client.conf" '' 54 autospawn=${if nonSystemWide then "yes" else "no"} 55 ${optionalString nonSystemWide "daemon-binary=${binary}"} 56 ${cfg.extraClientConf} 57 ''; 58 59 # Write an /etc/asound.conf that causes all ALSA applications to 60 # be re-routed to the PulseAudio server through ALSA's Pulse 61 # plugin. 62 alsaConf = writeText "asound.conf" ('' 63 pcm_type.pulse { 64 libs.native = ${pkgs.alsaPlugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ; 65 ${lib.optionalString enable32BitAlsaPlugins 66 "libs.32Bit = ${pkgs_i686.alsaPlugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ;"} 67 } 68 pcm.!default { 69 type pulse 70 hint.description "Default Audio Device (via PulseAudio)" 71 } 72 ctl_type.pulse { 73 libs.native = ${pkgs.alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ; 74 ${lib.optionalString enable32BitAlsaPlugins 75 "libs.32Bit = ${pkgs_i686.alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;"} 76 } 77 ctl.!default { 78 type pulse 79 } 80 ${alsaCfg.extraConfig} 81 ''); 82 83in { 84 85 options = { 86 87 hardware.pulseaudio = { 88 enable = mkOption { 89 type = types.bool; 90 default = false; 91 description = '' 92 Whether to enable the PulseAudio sound server. 93 ''; 94 }; 95 96 systemWide = mkOption { 97 type = types.bool; 98 default = false; 99 description = '' 100 If false, a PulseAudio server is launched automatically for 101 each user that tries to use the sound system. The server runs 102 with user privileges. This is the recommended and most secure 103 way to use PulseAudio. If true, one system-wide PulseAudio 104 server is launched on boot, running as the user "pulse". 105 Please read the PulseAudio documentation for more details. 106 ''; 107 }; 108 109 support32Bit = mkOption { 110 type = types.bool; 111 default = false; 112 description = '' 113 Whether to include the 32-bit pulseaudio libraries in the system or not. 114 This is only useful on 64-bit systems and currently limited to x86_64-linux. 115 ''; 116 }; 117 118 configFile = mkOption { 119 type = types.nullOr types.path; 120 description = '' 121 The path to the default configuration options the PulseAudio server 122 should use. By default, the "default.pa" configuration 123 from the PulseAudio distribution is used. 124 ''; 125 }; 126 127 extraConfig = mkOption { 128 type = types.lines; 129 default = ""; 130 description = '' 131 Literal string to append to <literal>configFile</literal> 132 and the config file generated by the pulseaudio module. 133 ''; 134 }; 135 136 extraClientConf = mkOption { 137 type = types.lines; 138 default = ""; 139 description = '' 140 Extra configuration appended to pulse/client.conf file. 141 ''; 142 }; 143 144 package = mkOption { 145 type = types.package; 146 default = pulseaudioLight; 147 defaultText = "pkgs.pulseaudioLight"; 148 example = literalExample "pkgs.pulseaudioFull"; 149 description = '' 150 The PulseAudio derivation to use. This can be used to enable 151 features (such as JACK support, Bluetooth) via the 152 <literal>pulseaudioFull</literal> package. 153 ''; 154 }; 155 156 daemon = { 157 logLevel = mkOption { 158 type = types.str; 159 default = "notice"; 160 description = '' 161 The log level that the system-wide pulseaudio daemon should use, 162 if activated. 163 ''; 164 }; 165 166 config = mkOption { 167 type = types.attrsOf types.unspecified; 168 default = {}; 169 description = ''Config of the pulse daemon. See <literal>man pulse-daemon.conf</literal>.''; 170 example = literalExample ''{ flat-volumes = "no"; }''; 171 }; 172 }; 173 174 zeroconf = { 175 discovery.enable = 176 mkEnableOption "discovery of pulseaudio sinks in the local network"; 177 publish.enable = 178 mkEnableOption "publishing the pulseaudio sink in the local network"; 179 }; 180 181 # TODO: enable by default? 182 tcp = { 183 enable = mkEnableOption "tcp streaming support"; 184 185 anonymousClients = { 186 allowAll = mkEnableOption "all anonymous clients to stream to the server"; 187 allowedIpRanges = mkOption { 188 type = types.listOf types.str; 189 default = []; 190 example = literalExample ''[ "127.0.0.1" "192.168.1.0/24" ]''; 191 description = '' 192 A list of IP subnets that are allowed to stream to the server. 193 ''; 194 }; 195 }; 196 }; 197 198 }; 199 200 }; 201 202 203 config = mkMerge [ 204 { 205 environment.etc = singleton { 206 target = "pulse/client.conf"; 207 source = clientConf; 208 }; 209 210 hardware.pulseaudio.configFile = mkDefault "${getBin overriddenPackage}/etc/pulse/default.pa"; 211 } 212 213 (mkIf cfg.enable { 214 environment.systemPackages = [ overriddenPackage ]; 215 216 environment.etc = [ 217 { target = "asound.conf"; 218 source = alsaConf; } 219 220 { target = "pulse/daemon.conf"; 221 source = writeText "daemon.conf" (lib.generators.toKeyValue {} cfg.daemon.config); } 222 ]; 223 224 # Allow PulseAudio to get realtime priority using rtkit. 225 security.rtkit.enable = true; 226 227 systemd.packages = [ overriddenPackage ]; 228 }) 229 230 (mkIf hasZeroconf { 231 services.avahi.enable = true; 232 }) 233 (mkIf cfg.zeroconf.publish.enable { 234 services.avahi.publish.enable = true; 235 services.avahi.publish.userServices = true; 236 }) 237 238 (mkIf nonSystemWide { 239 environment.etc = singleton { 240 target = "pulse/default.pa"; 241 source = myConfigFile; 242 }; 243 systemd.user = { 244 services.pulseaudio = { 245 restartIfChanged = true; 246 serviceConfig = { 247 RestartSec = "500ms"; 248 PassEnvironment = "DISPLAY"; 249 }; 250 }; 251 sockets.pulseaudio = { 252 wantedBy = [ "sockets.target" ]; 253 }; 254 }; 255 }) 256 257 (mkIf systemWide { 258 users.extraUsers.pulse = { 259 # For some reason, PulseAudio wants UID == GID. 260 uid = assert uid == gid; uid; 261 group = "pulse"; 262 extraGroups = [ "audio" ]; 263 description = "PulseAudio system service user"; 264 home = stateDir; 265 createHome = true; 266 }; 267 268 users.extraGroups.pulse.gid = gid; 269 270 systemd.services.pulseaudio = { 271 description = "PulseAudio System-Wide Server"; 272 wantedBy = [ "sound.target" ]; 273 before = [ "sound.target" ]; 274 environment.PULSE_RUNTIME_PATH = stateDir; 275 serviceConfig = { 276 Type = "notify"; 277 ExecStart = "${binaryNoDaemon} --log-level=${cfg.daemon.logLevel} --system -n --file=${myConfigFile}"; 278 Restart = "on-failure"; 279 RestartSec = "500ms"; 280 }; 281 }; 282 283 environment.variables.PULSE_COOKIE = "${stateDir}/.config/pulse/cookie"; 284 }) 285 ]; 286 287}