{ config, lib, pkgs, ... }: with lib; let cfg = config.services.trivmix; mixerModule = { connectIn = mkOption { type = with types; either str (listOf str); default = []; description = "Ports to connect mixer input to"; }; connectOut = mkOption { type = with types; either str (listOf str); default = []; description = "Ports to connect mixer output to"; }; onConnect = mkOption { type = types.lines; default = ""; description = "Additional shell commands to run after connections are set up"; }; adjustable = mkOption { type = types.bool; default = true; description = "Should the volume on this mixer be adjustable?"; }; group = mkOption { type = types.nullOr types.str; default = null; description = "Volumes of mixers that share a group name are synchronised"; }; initial = mkOption { type = types.str; default = "1"; description = "Initial volume"; }; }; service = name: mixerCfg: let connectIn = if isList mixerCfg.connectIn then mixerCfg.connectIn else singleton mixerCfg.connectIn; connectOut = if isList mixerCfg.connectOut then mixerCfg.connectOut else singleton mixerCfg.connectOut; inherit (mixerCfg) onConnect adjustable group initial; connectScript = '' #!${pkgs.stdenv.shell} ${concatMapStringsSep "\n" (port: "jack_connect ${port} $1") connectIn} ${concatMapStringsSep "\n" (port: "jack_connect $2 ${port}") connectOut} ${onConnect} ''; connect = (connectOut != []) || (connectIn != []) || (onConnect != []); mixerDeps = filter (x: any (hasPrefix (x + ":")) connectIn || any (hasPrefix (x + ":")) connectOut) (attrNames cfg); in { wantedBy = [ "sound.target" ]; requires = [ "jack.service" ] ++ map (n: n + ".service") mixerDeps; path = with pkgs; [ jack2Full ]; serviceConfig = { Type = "simple"; ExecStart = ''${pkgs.trivmix}/bin/trivmix --client ${name} \ ${optionalString connect "--run ${connectScript}"} \ "--level ${initial}" \ ${optionalString (adjustable && isNull group) "/dev/shm/mix/${name}/level"} \ ${optionalString (! isNull group) "/dev/shm/mix/${group}/level"} ''; User = "jack"; Group = "audio"; Nice = "-10"; LimitRTPRIO = "95:95"; LimitMEMLOCK = "infinity"; }; in { options.services.trivmix = mkOption { type = types.attrsOf (types.submodule mixerModule); default = []; description = "Definition of mixers"; }; config.systemd.services = mapAttrs service cfg; }