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 defaultText = "pkgs.pulseaudioLight"; 103 example = literalExample "pkgs.pulseaudioFull"; 104 description = '' 105 The PulseAudio derivation to use. This can be used to enable 106 features (such as JACK support, Bluetooth) via the 107 <literal>pulseaudioFull</literal> package. 108 ''; 109 }; 110 111 daemon = { 112 logLevel = mkOption { 113 type = types.str; 114 default = "notice"; 115 description = '' 116 The log level that the system-wide pulseaudio daemon should use, 117 if activated. 118 ''; 119 }; 120 }; 121 }; 122 123 }; 124 125 126 config = mkMerge [ 127 { 128 environment.etc = singleton { 129 target = "pulse/client.conf"; 130 source = clientConf; 131 }; 132 133 hardware.pulseaudio.configFile = mkDefault "${cfg.package}/etc/pulse/default.pa"; 134 } 135 136 (mkIf cfg.enable { 137 environment.systemPackages = [ cfg.package ]; 138 139 environment.etc = singleton { 140 target = "asound.conf"; 141 source = alsaConf; 142 }; 143 144 # Allow PulseAudio to get realtime priority using rtkit. 145 security.rtkit.enable = true; 146 }) 147 148 (mkIf nonSystemWide { 149 environment.etc = singleton { 150 target = "pulse/default.pa"; 151 source = cfg.configFile; 152 }; 153 }) 154 155 (mkIf systemWide { 156 users.extraUsers.pulse = { 157 # For some reason, PulseAudio wants UID == GID. 158 uid = assert uid == gid; uid; 159 group = "pulse"; 160 extraGroups = [ "audio" ]; 161 description = "PulseAudio system service user"; 162 home = stateDir; 163 createHome = true; 164 }; 165 166 users.extraGroups.pulse.gid = gid; 167 168 systemd.services.pulseaudio = { 169 description = "PulseAudio System-Wide Server"; 170 wantedBy = [ "sound.target" ]; 171 before = [ "sound.target" ]; 172 environment.PULSE_RUNTIME_PATH = stateDir; 173 serviceConfig = { 174 ExecStart = "${cfg.package}/bin/pulseaudio -D --log-level=${cfg.daemon.logLevel} --system --use-pid-file -n --file=${cfg.configFile}"; 175 PIDFile = "${stateDir}/pid"; 176 }; 177 }; 178 }) 179 ]; 180 181}