python312Packages.publicsuffixlist: 1.0.2.20241207 -> 1.0.2.20241213 (#365192)
[NixPkgs.git] / nixos / modules / services / audio / jack.nix
blob476bd40c10e18edc7ad73f60da804b8b9bb12feb
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
7 let
8   cfg = config.services.jack;
10   pcmPlugin = cfg.jackd.enable && cfg.alsa.enable;
11   loopback = cfg.jackd.enable && cfg.loopback.enable;
13   enable32BitAlsaPlugins =
14     cfg.alsa.support32Bit && pkgs.stdenv.hostPlatform.isx86_64 && pkgs.pkgsi686Linux.alsa-lib != null;
16   umaskNeeded = lib.versionOlder cfg.jackd.package.version "1.9.12";
17   bridgeNeeded = lib.versionAtLeast cfg.jackd.package.version "1.9.12";
20   options = {
21     services.jack = {
22       jackd = {
23         enable = lib.mkEnableOption ''
24           JACK Audio Connection Kit. You need to add yourself to the "jackaudio" group
25         '';
27         package =
28           lib.mkPackageOption pkgs "jack2" {
29             example = "jack1";
30           }
31           // {
32             # until jack1 promiscuous mode is fixed
33             internal = true;
34           };
36         extraOptions = lib.mkOption {
37           type = lib.types.listOf lib.types.str;
38           default = [
39             "-dalsa"
40           ];
41           example = lib.literalExpression ''
42             [ "-dalsa" "--device" "hw:1" ];
43           '';
44           description = ''
45             Specifies startup command line arguments to pass to JACK server.
46           '';
47         };
49         session = lib.mkOption {
50           type = lib.types.lines;
51           description = ''
52             Commands to run after JACK is started.
53           '';
54         };
56       };
58       alsa = {
59         enable = lib.mkOption {
60           type = lib.types.bool;
61           default = true;
62           description = ''
63             Route audio to/from generic ALSA-using applications using ALSA JACK PCM plugin.
64           '';
65         };
67         support32Bit = lib.mkOption {
68           type = lib.types.bool;
69           default = false;
70           description = ''
71             Whether to support sound for 32-bit ALSA applications on 64-bit system.
72           '';
73         };
74       };
76       loopback = {
77         enable = lib.mkOption {
78           type = lib.types.bool;
79           default = false;
80           description = ''
81             Create ALSA loopback device, instead of using PCM plugin. Has broader
82             application support (things like Steam will work), but may need fine-tuning
83             for concrete hardware.
84           '';
85         };
87         index = lib.mkOption {
88           type = lib.types.int;
89           default = 10;
90           description = ''
91             Index of an ALSA loopback device.
92           '';
93         };
95         config = lib.mkOption {
96           type = lib.types.lines;
97           description = ''
98             ALSA config for loopback device.
99           '';
100         };
102         dmixConfig = lib.mkOption {
103           type = lib.types.lines;
104           default = "";
105           example = ''
106             period_size 2048
107             periods 2
108           '';
109           description = ''
110             For music production software that still doesn't support JACK natively you
111             would like to put buffer/period adjustments here
112             to decrease dmix device latency.
113           '';
114         };
116         session = lib.mkOption {
117           type = lib.types.lines;
118           description = ''
119             Additional commands to run to setup loopback device.
120           '';
121         };
122       };
124     };
126   };
128   config = lib.mkMerge [
130     (lib.mkIf pcmPlugin {
131       environment.etc."alsa/conf.d/98-jack.conf".text = ''
132         pcm_type.jack {
133           libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;
134           ${lib.optionalString enable32BitAlsaPlugins "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     })
144     (lib.mkIf loopback {
145       boot.kernelModules = [ "snd-aloop" ];
146       boot.kernelParams = [ "snd-aloop.index=${toString cfg.loopback.index}" ];
147       environment.etc."alsa/conf.d/99-jack-loopback.conf".text = cfg.loopback.config;
148     })
150     (lib.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       '';
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       ];
222       users.users.jackaudio = {
223         group = "jackaudio";
224         extraGroups = [ "audio" ];
225         description = "JACK Audio system service user";
226         isSystemUser = true;
227       };
228       # https://jackaudio.org/faq/linux_rt_config.html
229       security.pam.loginLimits = [
230         {
231           domain = "@jackaudio";
232           type = "-";
233           item = "rtprio";
234           value = "99";
235         }
236         {
237           domain = "@jackaudio";
238           type = "-";
239           item = "memlock";
240           value = "unlimited";
241         }
242       ];
243       users.groups.jackaudio = { };
245       environment = {
246         systemPackages = [ cfg.jackd.package ];
247         etc."alsa/conf.d/50-jack.conf".source = "${pkgs.alsa-plugins}/etc/alsa/conf.d/50-jack.conf";
248         variables.JACK_PROMISCUOUS_SERVER = "jackaudio";
249       };
251       services.udev.extraRules = ''
252         ACTION=="add", SUBSYSTEM=="sound", ATTRS{id}!="Loopback", TAG+="systemd", ENV{SYSTEMD_WANTS}="jack.service"
253       '';
255       systemd.services.jack = {
256         description = "JACK Audio Connection Kit";
257         serviceConfig =
258           {
259             User = "jackaudio";
260             SupplementaryGroups = lib.optional (
261               config.hardware.pulseaudio.enable && !config.hardware.pulseaudio.systemWide
262             ) "users";
263             ExecStart = "${cfg.jackd.package}/bin/jackd ${lib.escapeShellArgs cfg.jackd.extraOptions}";
264             LimitRTPRIO = 99;
265             LimitMEMLOCK = "infinity";
266           }
267           // lib.optionalAttrs umaskNeeded {
268             UMask = "007";
269           };
270         path = [ cfg.jackd.package ];
271         environment = {
272           JACK_PROMISCUOUS_SERVER = "jackaudio";
273           JACK_NO_AUDIO_RESERVATION = "1";
274         };
275         restartIfChanged = false;
276       };
277       systemd.services.jack-session = {
278         description = "JACK session";
279         script = ''
280           ${pkgs.jack-example-tools}/bin/jack_wait -w
281           ${cfg.jackd.session}
282           ${lib.optionalString cfg.loopback.enable cfg.loopback.session}
283         '';
284         serviceConfig = {
285           RemainAfterExit = true;
286           User = "jackaudio";
287           StateDirectory = "jack";
288           LimitRTPRIO = 99;
289           LimitMEMLOCK = "infinity";
290         };
291         path = [ cfg.jackd.package ];
292         environment = {
293           JACK_PROMISCUOUS_SERVER = "jackaudio";
294           HOME = "/var/lib/jack";
295         };
296         wantedBy = [ "jack.service" ];
297         partOf = [ "jack.service" ];
298         after = [ "jack.service" ];
299         restartIfChanged = false;
300       };
301     })
303   ];
305   meta.maintainers = [ ];