at 23.11-pre 8.6 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.jack; 7 8 pcmPlugin = cfg.jackd.enable && cfg.alsa.enable; 9 loopback = cfg.jackd.enable && cfg.loopback.enable; 10 11 enable32BitAlsaPlugins = cfg.alsa.support32Bit && pkgs.stdenv.isx86_64 && pkgs.pkgsi686Linux.alsa-lib != null; 12 13 umaskNeeded = versionOlder cfg.jackd.package.version "1.9.12"; 14 bridgeNeeded = versionAtLeast cfg.jackd.package.version "1.9.12"; 15in { 16 options = { 17 services.jack = { 18 jackd = { 19 enable = mkEnableOption (lib.mdDoc '' 20 JACK Audio Connection Kit. You need to add yourself to the "jackaudio" group 21 ''); 22 23 package = mkOption { 24 # until jack1 promiscuous mode is fixed 25 internal = true; 26 type = types.package; 27 default = pkgs.jack2; 28 defaultText = literalExpression "pkgs.jack2"; 29 example = literalExpression "pkgs.jack1"; 30 description = lib.mdDoc '' 31 The JACK package to use. 32 ''; 33 }; 34 35 extraOptions = mkOption { 36 type = types.listOf types.str; 37 default = [ 38 "-dalsa" 39 ]; 40 example = literalExpression '' 41 [ "-dalsa" "--device" "hw:1" ]; 42 ''; 43 description = lib.mdDoc '' 44 Specifies startup command line arguments to pass to JACK server. 45 ''; 46 }; 47 48 session = mkOption { 49 type = types.lines; 50 description = lib.mdDoc '' 51 Commands to run after JACK is started. 52 ''; 53 }; 54 55 }; 56 57 alsa = { 58 enable = mkOption { 59 type = types.bool; 60 default = true; 61 description = lib.mdDoc '' 62 Route audio to/from generic ALSA-using applications using ALSA JACK PCM plugin. 63 ''; 64 }; 65 66 support32Bit = mkOption { 67 type = types.bool; 68 default = false; 69 description = lib.mdDoc '' 70 Whether to support sound for 32-bit ALSA applications on 64-bit system. 71 ''; 72 }; 73 }; 74 75 loopback = { 76 enable = mkOption { 77 type = types.bool; 78 default = false; 79 description = lib.mdDoc '' 80 Create ALSA loopback device, instead of using PCM plugin. Has broader 81 application support (things like Steam will work), but may need fine-tuning 82 for concrete hardware. 83 ''; 84 }; 85 86 index = mkOption { 87 type = types.int; 88 default = 10; 89 description = lib.mdDoc '' 90 Index of an ALSA loopback device. 91 ''; 92 }; 93 94 config = mkOption { 95 type = types.lines; 96 description = lib.mdDoc '' 97 ALSA config for loopback device. 98 ''; 99 }; 100 101 dmixConfig = mkOption { 102 type = types.lines; 103 default = ""; 104 example = '' 105 period_size 2048 106 periods 2 107 ''; 108 description = lib.mdDoc '' 109 For music production software that still doesn't support JACK natively you 110 would like to put buffer/period adjustments here 111 to decrease dmix device latency. 112 ''; 113 }; 114 115 session = mkOption { 116 type = types.lines; 117 description = lib.mdDoc '' 118 Additional commands to run to setup loopback device. 119 ''; 120 }; 121 }; 122 123 }; 124 125 }; 126 127 config = mkMerge [ 128 129 (mkIf pcmPlugin { 130 sound.extraConfig = '' 131 pcm_type.jack { 132 libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_jack.so ; 133 ${lib.optionalString enable32BitAlsaPlugins 134 "libs.32Bit = ${pkgs.pkgsi686Linux.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;"} 135 } 136 pcm.!default { 137 @func getenv 138 vars [ PCM ] 139 default "plug:jack" 140 } 141 ''; 142 }) 143 144 (mkIf loopback { 145 boot.kernelModules = [ "snd-aloop" ]; 146 boot.kernelParams = [ "snd-aloop.index=${toString cfg.loopback.index}" ]; 147 sound.extraConfig = cfg.loopback.config; 148 }) 149 150 (mkIf cfg.jackd.enable { 151 services.jack.jackd.session = '' 152 ${lib.optionalString bridgeNeeded "${pkgs.a2jmidid}/bin/a2jmidid -e &"} 153 ''; 154 # https://alsa.opensrc.org/Jack_and_Loopback_device_as_Alsa-to-Jack_bridge#id06 155 services.jack.loopback.config = '' 156 pcm.loophw00 { 157 type hw 158 card ${toString cfg.loopback.index} 159 device 0 160 subdevice 0 161 } 162 pcm.amix { 163 type dmix 164 ipc_key 219345 165 slave { 166 pcm loophw00 167 ${cfg.loopback.dmixConfig} 168 } 169 } 170 pcm.asoftvol { 171 type softvol 172 slave.pcm "amix" 173 control { name Master } 174 } 175 pcm.cloop { 176 type hw 177 card ${toString cfg.loopback.index} 178 device 1 179 subdevice 0 180 format S32_LE 181 } 182 pcm.loophw01 { 183 type hw 184 card ${toString cfg.loopback.index} 185 device 0 186 subdevice 1 187 } 188 pcm.ploop { 189 type hw 190 card ${toString cfg.loopback.index} 191 device 1 192 subdevice 1 193 format S32_LE 194 } 195 pcm.aduplex { 196 type asym 197 playback.pcm "asoftvol" 198 capture.pcm "loophw01" 199 } 200 pcm.!default { 201 type plug 202 slave.pcm aduplex 203 } 204 ''; 205 services.jack.loopback.session = '' 206 alsa_in -j cloop -dcloop & 207 alsa_out -j ploop -dploop & 208 while [ "$(jack_lsp cloop)" == "" ] || [ "$(jack_lsp ploop)" == "" ]; do sleep 1; done 209 jack_connect cloop:capture_1 system:playback_1 210 jack_connect cloop:capture_2 system:playback_2 211 jack_connect system:capture_1 ploop:playback_1 212 jack_connect system:capture_2 ploop:playback_2 213 ''; 214 215 assertions = [ 216 { 217 assertion = !(cfg.alsa.enable && cfg.loopback.enable); 218 message = "For JACK both alsa and loopback options shouldn't be used at the same time."; 219 } 220 ]; 221 222 users.users.jackaudio = { 223 group = "jackaudio"; 224 extraGroups = [ "audio" ]; 225 description = "JACK Audio system service user"; 226 isSystemUser = true; 227 }; 228 # http://jackaudio.org/faq/linux_rt_config.html 229 security.pam.loginLimits = [ 230 { domain = "@jackaudio"; type = "-"; item = "rtprio"; value = "99"; } 231 { domain = "@jackaudio"; type = "-"; item = "memlock"; value = "unlimited"; } 232 ]; 233 users.groups.jackaudio = {}; 234 235 environment = { 236 systemPackages = [ cfg.jackd.package ]; 237 etc."alsa/conf.d/50-jack.conf".source = "${pkgs.alsa-plugins}/etc/alsa/conf.d/50-jack.conf"; 238 variables.JACK_PROMISCUOUS_SERVER = "jackaudio"; 239 }; 240 241 services.udev.extraRules = '' 242 ACTION=="add", SUBSYSTEM=="sound", ATTRS{id}!="Loopback", TAG+="systemd", ENV{SYSTEMD_WANTS}="jack.service" 243 ''; 244 245 systemd.services.jack = { 246 description = "JACK Audio Connection Kit"; 247 serviceConfig = { 248 User = "jackaudio"; 249 SupplementaryGroups = lib.optional 250 (config.hardware.pulseaudio.enable 251 && !config.hardware.pulseaudio.systemWide) "users"; 252 ExecStart = "${cfg.jackd.package}/bin/jackd ${lib.escapeShellArgs cfg.jackd.extraOptions}"; 253 LimitRTPRIO = 99; 254 LimitMEMLOCK = "infinity"; 255 } // optionalAttrs umaskNeeded { 256 UMask = "007"; 257 }; 258 path = [ cfg.jackd.package ]; 259 environment = { 260 JACK_PROMISCUOUS_SERVER = "jackaudio"; 261 JACK_NO_AUDIO_RESERVATION = "1"; 262 }; 263 restartIfChanged = false; 264 }; 265 systemd.services.jack-session = { 266 description = "JACK session"; 267 script = '' 268 jack_wait -w 269 ${cfg.jackd.session} 270 ${lib.optionalString cfg.loopback.enable cfg.loopback.session} 271 ''; 272 serviceConfig = { 273 RemainAfterExit = true; 274 User = "jackaudio"; 275 StateDirectory = "jack"; 276 LimitRTPRIO = 99; 277 LimitMEMLOCK = "infinity"; 278 }; 279 path = [ cfg.jackd.package ]; 280 environment = { 281 JACK_PROMISCUOUS_SERVER = "jackaudio"; 282 HOME = "/var/lib/jack"; 283 }; 284 wantedBy = [ "jack.service" ]; 285 partOf = [ "jack.service" ]; 286 after = [ "jack.service" ]; 287 restartIfChanged = false; 288 }; 289 }) 290 291 ]; 292 293 meta.maintainers = [ ]; 294}