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