1 { config, lib, pkgs, ... }:
6 cfg = config.services.jack;
8 pcmPlugin = cfg.jackd.enable && cfg.alsa.enable;
9 loopback = cfg.jackd.enable && cfg.loopback.enable;
11 enable32BitAlsaPlugins = cfg.alsa.support32Bit && pkgs.stdenv.hostPlatform.isx86_64 && pkgs.pkgsi686Linux.alsa-lib != null;
13 umaskNeeded = versionOlder cfg.jackd.package.version "1.9.12";
14 bridgeNeeded = versionAtLeast cfg.jackd.package.version "1.9.12";
19 enable = mkEnableOption ''
20 JACK Audio Connection Kit. You need to add yourself to the "jackaudio" group
23 package = mkPackageOption pkgs "jack2" {
26 # until jack1 promiscuous mode is fixed
30 extraOptions = mkOption {
31 type = types.listOf types.str;
35 example = literalExpression ''
36 [ "-dalsa" "--device" "hw:1" ];
39 Specifies startup command line arguments to pass to JACK server.
46 Commands to run after JACK is started.
57 Route audio to/from generic ALSA-using applications using ALSA JACK PCM plugin.
61 support32Bit = mkOption {
65 Whether to support sound for 32-bit ALSA applications on 64-bit system.
75 Create ALSA loopback device, instead of using PCM plugin. Has broader
76 application support (things like Steam will work), but may need fine-tuning
77 for concrete hardware.
85 Index of an ALSA loopback device.
92 ALSA config for loopback device.
96 dmixConfig = mkOption {
104 For music production software that still doesn't support JACK natively you
105 would like to put buffer/period adjustments here
106 to decrease dmix device latency.
113 Additional commands to run to setup loopback device.
125 environment.etc."alsa/conf.d/98-jack.conf".text = ''
127 libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;
128 ${lib.optionalString enable32BitAlsaPlugins
129 "libs.32Bit = ${pkgs.pkgsi686Linux.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;"}
140 boot.kernelModules = [ "snd-aloop" ];
141 boot.kernelParams = [ "snd-aloop.index=${toString cfg.loopback.index}" ];
142 environment.etc."alsa/conf.d/99-jack-loopback.conf".text = cfg.loopback.config;
145 (mkIf cfg.jackd.enable {
146 services.jack.jackd.session = ''
147 ${lib.optionalString bridgeNeeded "${pkgs.a2jmidid}/bin/a2jmidid -e &"}
149 # https://alsa.opensrc.org/Jack_and_Loopback_device_as_Alsa-to-Jack_bridge#id06
150 services.jack.loopback.config = ''
153 card ${toString cfg.loopback.index}
162 ${cfg.loopback.dmixConfig}
168 control { name Master }
172 card ${toString cfg.loopback.index}
179 card ${toString cfg.loopback.index}
185 card ${toString cfg.loopback.index}
192 playback.pcm "asoftvol"
193 capture.pcm "loophw01"
200 services.jack.loopback.session = ''
201 alsa_in -j cloop -dcloop &
202 alsa_out -j ploop -dploop &
203 while [ "$(jack_lsp cloop)" == "" ] || [ "$(jack_lsp ploop)" == "" ]; do sleep 1; done
204 jack_connect cloop:capture_1 system:playback_1
205 jack_connect cloop:capture_2 system:playback_2
206 jack_connect system:capture_1 ploop:playback_1
207 jack_connect system:capture_2 ploop:playback_2
212 assertion = !(cfg.alsa.enable && cfg.loopback.enable);
213 message = "For JACK both alsa and loopback options shouldn't be used at the same time.";
217 users.users.jackaudio = {
219 extraGroups = [ "audio" ];
220 description = "JACK Audio system service user";
223 # https://jackaudio.org/faq/linux_rt_config.html
224 security.pam.loginLimits = [
225 { domain = "@jackaudio"; type = "-"; item = "rtprio"; value = "99"; }
226 { domain = "@jackaudio"; type = "-"; item = "memlock"; value = "unlimited"; }
228 users.groups.jackaudio = {};
231 systemPackages = [ cfg.jackd.package ];
232 etc."alsa/conf.d/50-jack.conf".source = "${pkgs.alsa-plugins}/etc/alsa/conf.d/50-jack.conf";
233 variables.JACK_PROMISCUOUS_SERVER = "jackaudio";
236 services.udev.extraRules = ''
237 ACTION=="add", SUBSYSTEM=="sound", ATTRS{id}!="Loopback", TAG+="systemd", ENV{SYSTEMD_WANTS}="jack.service"
240 systemd.services.jack = {
241 description = "JACK Audio Connection Kit";
244 SupplementaryGroups = lib.optional
245 (config.hardware.pulseaudio.enable
246 && !config.hardware.pulseaudio.systemWide) "users";
247 ExecStart = "${cfg.jackd.package}/bin/jackd ${lib.escapeShellArgs cfg.jackd.extraOptions}";
249 LimitMEMLOCK = "infinity";
250 } // optionalAttrs umaskNeeded {
253 path = [ cfg.jackd.package ];
255 JACK_PROMISCUOUS_SERVER = "jackaudio";
256 JACK_NO_AUDIO_RESERVATION = "1";
258 restartIfChanged = false;
260 systemd.services.jack-session = {
261 description = "JACK session";
265 ${lib.optionalString cfg.loopback.enable cfg.loopback.session}
268 RemainAfterExit = true;
270 StateDirectory = "jack";
272 LimitMEMLOCK = "infinity";
274 path = [ cfg.jackd.package ];
276 JACK_PROMISCUOUS_SERVER = "jackaudio";
277 HOME = "/var/lib/jack";
279 wantedBy = [ "jack.service" ];
280 partOf = [ "jack.service" ];
281 after = [ "jack.service" ];
282 restartIfChanged = false;
288 meta.maintainers = [ ];