295 lines
8.3 KiB
Nix
295 lines
8.3 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.jack;
|
|
|
|
pcmPlugin = cfg.jackd.enable && cfg.alsa.enable;
|
|
loopback = cfg.jackd.enable && cfg.loopback.enable;
|
|
|
|
enable32BitAlsaPlugins = cfg.alsa.support32Bit && pkgs.stdenv.isx86_64 && pkgs.pkgsi686Linux.alsa-lib != null;
|
|
|
|
umaskNeeded = versionOlder cfg.jackd.package.version "1.9.12";
|
|
bridgeNeeded = versionAtLeast cfg.jackd.package.version "1.9.12";
|
|
in {
|
|
options = {
|
|
services.jack = {
|
|
jackd = {
|
|
enable = mkEnableOption ''
|
|
JACK Audio Connection Kit. You need to add yourself to the "jackaudio" group
|
|
'';
|
|
|
|
package = mkOption {
|
|
# until jack1 promiscuous mode is fixed
|
|
internal = true;
|
|
type = types.package;
|
|
default = pkgs.jack2;
|
|
defaultText = literalExpression "pkgs.jack2";
|
|
example = literalExpression "pkgs.jack1";
|
|
description = ''
|
|
The JACK package to use.
|
|
'';
|
|
};
|
|
|
|
extraOptions = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [
|
|
"-dalsa"
|
|
];
|
|
example = literalExpression ''
|
|
[ "-dalsa" "--device" "hw:1" ];
|
|
'';
|
|
description = ''
|
|
Specifies startup command line arguments to pass to JACK server.
|
|
'';
|
|
};
|
|
|
|
session = mkOption {
|
|
type = types.lines;
|
|
description = ''
|
|
Commands to run after JACK is started.
|
|
'';
|
|
};
|
|
|
|
};
|
|
|
|
alsa = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = ''
|
|
Route audio to/from generic ALSA-using applications using ALSA JACK PCM plugin.
|
|
'';
|
|
};
|
|
|
|
support32Bit = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to support sound for 32-bit ALSA applications on 64-bit system.
|
|
'';
|
|
};
|
|
};
|
|
|
|
loopback = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Create ALSA loopback device, instead of using PCM plugin. Has broader
|
|
application support (things like Steam will work), but may need fine-tuning
|
|
for concrete hardware.
|
|
'';
|
|
};
|
|
|
|
index = mkOption {
|
|
type = types.int;
|
|
default = 10;
|
|
description = ''
|
|
Index of an ALSA loopback device.
|
|
'';
|
|
};
|
|
|
|
config = mkOption {
|
|
type = types.lines;
|
|
description = ''
|
|
ALSA config for loopback device.
|
|
'';
|
|
};
|
|
|
|
dmixConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
example = ''
|
|
period_size 2048
|
|
periods 2
|
|
'';
|
|
description = ''
|
|
For music production software that still doesn't support JACK natively you
|
|
would like to put buffer/period adjustments here
|
|
to decrease dmix device latency.
|
|
'';
|
|
};
|
|
|
|
session = mkOption {
|
|
type = types.lines;
|
|
description = ''
|
|
Additional commands to run to setup loopback device.
|
|
'';
|
|
};
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
config = mkMerge [
|
|
|
|
(mkIf pcmPlugin {
|
|
sound.extraConfig = ''
|
|
pcm_type.jack {
|
|
libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;
|
|
${lib.optionalString enable32BitAlsaPlugins
|
|
"libs.32Bit = ${pkgs.pkgsi686Linux.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;"}
|
|
}
|
|
pcm.!default {
|
|
@func getenv
|
|
vars [ PCM ]
|
|
default "plug:jack"
|
|
}
|
|
'';
|
|
})
|
|
|
|
(mkIf loopback {
|
|
boot.kernelModules = [ "snd-aloop" ];
|
|
boot.kernelParams = [ "snd-aloop.index=${toString cfg.loopback.index}" ];
|
|
sound.extraConfig = cfg.loopback.config;
|
|
})
|
|
|
|
(mkIf cfg.jackd.enable {
|
|
services.jack.jackd.session = ''
|
|
${lib.optionalString bridgeNeeded "${pkgs.a2jmidid}/bin/a2jmidid -e &"}
|
|
'';
|
|
# https://alsa.opensrc.org/Jack_and_Loopback_device_as_Alsa-to-Jack_bridge#id06
|
|
services.jack.loopback.config = ''
|
|
pcm.loophw00 {
|
|
type hw
|
|
card ${toString cfg.loopback.index}
|
|
device 0
|
|
subdevice 0
|
|
}
|
|
pcm.amix {
|
|
type dmix
|
|
ipc_key 219345
|
|
slave {
|
|
pcm loophw00
|
|
${cfg.loopback.dmixConfig}
|
|
}
|
|
}
|
|
pcm.asoftvol {
|
|
type softvol
|
|
slave.pcm "amix"
|
|
control { name Master }
|
|
}
|
|
pcm.cloop {
|
|
type hw
|
|
card ${toString cfg.loopback.index}
|
|
device 1
|
|
subdevice 0
|
|
format S32_LE
|
|
}
|
|
pcm.loophw01 {
|
|
type hw
|
|
card ${toString cfg.loopback.index}
|
|
device 0
|
|
subdevice 1
|
|
}
|
|
pcm.ploop {
|
|
type hw
|
|
card ${toString cfg.loopback.index}
|
|
device 1
|
|
subdevice 1
|
|
format S32_LE
|
|
}
|
|
pcm.aduplex {
|
|
type asym
|
|
playback.pcm "asoftvol"
|
|
capture.pcm "loophw01"
|
|
}
|
|
pcm.!default {
|
|
type plug
|
|
slave.pcm aduplex
|
|
}
|
|
'';
|
|
services.jack.loopback.session = ''
|
|
alsa_in -j cloop -dcloop &
|
|
alsa_out -j ploop -dploop &
|
|
while [ "$(jack_lsp cloop)" == "" ] || [ "$(jack_lsp ploop)" == "" ]; do sleep 1; done
|
|
jack_connect cloop:capture_1 system:playback_1
|
|
jack_connect cloop:capture_2 system:playback_2
|
|
jack_connect system:capture_1 ploop:playback_1
|
|
jack_connect system:capture_2 ploop:playback_2
|
|
'';
|
|
|
|
assertions = [
|
|
{
|
|
assertion = !(cfg.alsa.enable && cfg.loopback.enable);
|
|
message = "For JACK both alsa and loopback options shouldn't be used at the same time.";
|
|
}
|
|
];
|
|
|
|
users.users.jackaudio = {
|
|
group = "jackaudio";
|
|
extraGroups = [ "audio" ];
|
|
description = "JACK Audio system service user";
|
|
isSystemUser = true;
|
|
};
|
|
# http://jackaudio.org/faq/linux_rt_config.html
|
|
security.pam.loginLimits = [
|
|
{ domain = "@jackaudio"; type = "-"; item = "rtprio"; value = "99"; }
|
|
{ domain = "@jackaudio"; type = "-"; item = "memlock"; value = "unlimited"; }
|
|
];
|
|
users.groups.jackaudio = {};
|
|
|
|
environment = {
|
|
systemPackages = [ cfg.jackd.package ];
|
|
etc."alsa/conf.d/50-jack.conf".source = "${pkgs.alsa-plugins}/etc/alsa/conf.d/50-jack.conf";
|
|
variables.JACK_PROMISCUOUS_SERVER = "jackaudio";
|
|
};
|
|
|
|
services.udev.extraRules = ''
|
|
ACTION=="add", SUBSYSTEM=="sound", ATTRS{id}!="Loopback", TAG+="systemd", ENV{SYSTEMD_WANTS}="jack.service"
|
|
'';
|
|
|
|
systemd.services.jack = {
|
|
description = "JACK Audio Connection Kit";
|
|
serviceConfig = {
|
|
User = "jackaudio";
|
|
SupplementaryGroups = lib.optional
|
|
(config.hardware.pulseaudio.enable
|
|
&& !config.hardware.pulseaudio.systemWide) "users";
|
|
ExecStart = "${cfg.jackd.package}/bin/jackd ${lib.escapeShellArgs cfg.jackd.extraOptions}";
|
|
LimitRTPRIO = 99;
|
|
LimitMEMLOCK = "infinity";
|
|
} // optionalAttrs umaskNeeded {
|
|
UMask = "007";
|
|
};
|
|
path = [ cfg.jackd.package ];
|
|
environment = {
|
|
JACK_PROMISCUOUS_SERVER = "jackaudio";
|
|
JACK_NO_AUDIO_RESERVATION = "1";
|
|
};
|
|
restartIfChanged = false;
|
|
};
|
|
systemd.services.jack-session = {
|
|
description = "JACK session";
|
|
script = ''
|
|
jack_wait -w
|
|
${cfg.jackd.session}
|
|
${lib.optionalString cfg.loopback.enable cfg.loopback.session}
|
|
'';
|
|
serviceConfig = {
|
|
RemainAfterExit = true;
|
|
User = "jackaudio";
|
|
StateDirectory = "jack";
|
|
LimitRTPRIO = 99;
|
|
LimitMEMLOCK = "infinity";
|
|
};
|
|
path = [ cfg.jackd.package ];
|
|
environment = {
|
|
JACK_PROMISCUOUS_SERVER = "jackaudio";
|
|
HOME = "/var/lib/jack";
|
|
};
|
|
wantedBy = [ "jack.service" ];
|
|
partOf = [ "jack.service" ];
|
|
after = [ "jack.service" ];
|
|
restartIfChanged = false;
|
|
};
|
|
})
|
|
|
|
];
|
|
|
|
meta.maintainers = [ ];
|
|
}
|