vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / audio / jack.nix
blob7344fdf4a218643937f92df312cf62dc86725dd1
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
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";
15 in {
16   options = {
17     services.jack = {
18       jackd = {
19         enable = mkEnableOption ''
20           JACK Audio Connection Kit. You need to add yourself to the "jackaudio" group
21         '';
23         package = mkPackageOption pkgs "jack2" {
24           example = "jack1";
25         } // {
26           # until jack1 promiscuous mode is fixed
27           internal = true;
28         };
30         extraOptions = mkOption {
31           type = types.listOf types.str;
32           default = [
33             "-dalsa"
34           ];
35           example = literalExpression ''
36             [ "-dalsa" "--device" "hw:1" ];
37           '';
38           description = ''
39             Specifies startup command line arguments to pass to JACK server.
40           '';
41         };
43         session = mkOption {
44           type = types.lines;
45           description = ''
46             Commands to run after JACK is started.
47           '';
48         };
50       };
52       alsa = {
53         enable = mkOption {
54           type = types.bool;
55           default = true;
56           description = ''
57             Route audio to/from generic ALSA-using applications using ALSA JACK PCM plugin.
58           '';
59         };
61         support32Bit = mkOption {
62           type = types.bool;
63           default = false;
64           description = ''
65             Whether to support sound for 32-bit ALSA applications on 64-bit system.
66           '';
67         };
68       };
70       loopback = {
71         enable = mkOption {
72           type = types.bool;
73           default = false;
74           description = ''
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.
78           '';
79         };
81         index = mkOption {
82           type = types.int;
83           default = 10;
84           description = ''
85             Index of an ALSA loopback device.
86           '';
87         };
89         config = mkOption {
90           type = types.lines;
91           description = ''
92             ALSA config for loopback device.
93           '';
94         };
96         dmixConfig = mkOption {
97           type = types.lines;
98           default = "";
99           example = ''
100             period_size 2048
101             periods 2
102           '';
103           description = ''
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.
107           '';
108         };
110         session = mkOption {
111           type = types.lines;
112           description = ''
113             Additional commands to run to setup loopback device.
114           '';
115         };
116       };
118     };
120   };
122   config = mkMerge [
124     (mkIf pcmPlugin {
125       environment.etc."alsa/conf.d/98-jack.conf".text = ''
126         pcm_type.jack {
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 ;"}
130         }
131         pcm.!default {
132           @func getenv
133           vars [ PCM ]
134           default "plug:jack"
135         }
136       '';
137     })
139     (mkIf loopback {
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;
143     })
145     (mkIf cfg.jackd.enable {
146       services.jack.jackd.session = ''
147         ${lib.optionalString bridgeNeeded "${pkgs.a2jmidid}/bin/a2jmidid -e &"}
148       '';
149       # https://alsa.opensrc.org/Jack_and_Loopback_device_as_Alsa-to-Jack_bridge#id06
150       services.jack.loopback.config = ''
151         pcm.loophw00 {
152           type hw
153           card ${toString cfg.loopback.index}
154           device 0
155           subdevice 0
156         }
157         pcm.amix {
158           type dmix
159           ipc_key 219345
160           slave {
161             pcm loophw00
162             ${cfg.loopback.dmixConfig}
163           }
164         }
165         pcm.asoftvol {
166           type softvol
167           slave.pcm "amix"
168           control { name Master }
169         }
170         pcm.cloop {
171           type hw
172           card ${toString cfg.loopback.index}
173           device 1
174           subdevice 0
175           format S32_LE
176         }
177         pcm.loophw01 {
178           type hw
179           card ${toString cfg.loopback.index}
180           device 0
181           subdevice 1
182         }
183         pcm.ploop {
184           type hw
185           card ${toString cfg.loopback.index}
186           device 1
187           subdevice 1
188           format S32_LE
189         }
190         pcm.aduplex {
191           type asym
192           playback.pcm "asoftvol"
193           capture.pcm "loophw01"
194         }
195         pcm.!default {
196           type plug
197           slave.pcm aduplex
198         }
199       '';
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
208       '';
210       assertions = [
211         {
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.";
214         }
215       ];
217       users.users.jackaudio = {
218         group = "jackaudio";
219         extraGroups = [ "audio" ];
220         description = "JACK Audio system service user";
221         isSystemUser = true;
222       };
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"; }
227       ];
228       users.groups.jackaudio = {};
230       environment = {
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";
234       };
236       services.udev.extraRules = ''
237         ACTION=="add", SUBSYSTEM=="sound", ATTRS{id}!="Loopback", TAG+="systemd", ENV{SYSTEMD_WANTS}="jack.service"
238       '';
240       systemd.services.jack = {
241         description = "JACK Audio Connection Kit";
242         serviceConfig = {
243           User = "jackaudio";
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}";
248           LimitRTPRIO = 99;
249           LimitMEMLOCK = "infinity";
250         } // optionalAttrs umaskNeeded {
251           UMask = "007";
252         };
253         path = [ cfg.jackd.package ];
254         environment = {
255           JACK_PROMISCUOUS_SERVER = "jackaudio";
256           JACK_NO_AUDIO_RESERVATION = "1";
257         };
258         restartIfChanged = false;
259       };
260       systemd.services.jack-session = {
261         description = "JACK session";
262         script = ''
263           jack_wait -w
264           ${cfg.jackd.session}
265           ${lib.optionalString cfg.loopback.enable cfg.loopback.session}
266         '';
267         serviceConfig = {
268           RemainAfterExit = true;
269           User = "jackaudio";
270           StateDirectory = "jack";
271           LimitRTPRIO = 99;
272           LimitMEMLOCK = "infinity";
273         };
274         path = [ cfg.jackd.package ];
275         environment = {
276           JACK_PROMISCUOUS_SERVER = "jackaudio";
277           HOME = "/var/lib/jack";
278         };
279         wantedBy = [ "jack.service" ];
280         partOf = [ "jack.service" ];
281         after = [ "jack.service" ];
282         restartIfChanged = false;
283       };
284     })
286   ];
288   meta.maintainers = [ ];