about summary refs log tree commit diff
path: root/nixos/modules/services/audio
diff options
context:
space:
mode:
authorMartin Weinelt <hexa@darmstadt.ccc.de>2023-01-28 22:25:26 +0100
committerMartin Weinelt <hexa@darmstadt.ccc.de>2023-02-21 11:59:17 +0100
commit198713cf8229eaaa7f41d94b06014c756ee5fa4f (patch)
tree90d5c0f6a379ce01639c0fbaefba31eefe4ffab4 /nixos/modules/services/audio
parent2d84e9d64687110e21b6867ac7a058155f6f9a9e (diff)
downloadnixlib-198713cf8229eaaa7f41d94b06014c756ee5fa4f.tar
nixlib-198713cf8229eaaa7f41d94b06014c756ee5fa4f.tar.gz
nixlib-198713cf8229eaaa7f41d94b06014c756ee5fa4f.tar.bz2
nixlib-198713cf8229eaaa7f41d94b06014c756ee5fa4f.tar.lz
nixlib-198713cf8229eaaa7f41d94b06014c756ee5fa4f.tar.xz
nixlib-198713cf8229eaaa7f41d94b06014c756ee5fa4f.tar.zst
nixlib-198713cf8229eaaa7f41d94b06014c756ee5fa4f.zip
nixos/tts: init
Provide a module to configure Coqui TTS, available as `tts` in nixpkgs
for a few releases already.

The module supports multiple servers in parallel, so multiple languages
and testing scenarios can be covered, without affecting any production
usage.
Diffstat (limited to 'nixos/modules/services/audio')
-rw-r--r--nixos/modules/services/audio/tts.nix151
1 files changed, 151 insertions, 0 deletions
diff --git a/nixos/modules/services/audio/tts.nix b/nixos/modules/services/audio/tts.nix
new file mode 100644
index 000000000000..1a355c8ee39f
--- /dev/null
+++ b/nixos/modules/services/audio/tts.nix
@@ -0,0 +1,151 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+let
+  cfg = config.services.tts;
+in
+
+{
+  options.services.tts = let
+    inherit (lib) literalExpression mkOption mdDoc mkEnableOption types;
+  in  {
+    servers = mkOption {
+      type = types.attrsOf (types.submodule (
+        { ... }: {
+          options = {
+            enable = mkEnableOption (mdDoc "Coqui TTS server");
+
+            port = mkOption {
+              type = types.port;
+              example = 5000;
+              description = mdDoc ''
+                Port to bind the TTS server to.
+              '';
+            };
+
+            model = mkOption {
+              type = types.nullOr types.str;
+              default = "tts_models/en/ljspeech/tacotron2-DDC";
+              example = null;
+              description = mdDoc ''
+                Name of the model to download and use for speech synthesis.
+
+                Check `tts-server --list_models` for possible values.
+
+                Set to `null` to use a custom model.
+              '';
+            };
+
+            useCuda = mkOption {
+              type = types.bool;
+              default = false;
+              example = true;
+              description = mdDoc ''
+                Whether to offload computation onto a CUDA compatible GPU.
+              '';
+            };
+
+            extraArgs = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = mdDoc ''
+                Extra arguments to pass to the server commandline.
+              '';
+            };
+          };
+        }
+      ));
+      default = {};
+      example = literalExpression ''
+        {
+          english = {
+            port = 5300;
+            model = "tts_models/en/ljspeech/tacotron2-DDC";
+          };
+          german = {
+            port = 5301;
+            model = "tts_models/de/thorsten/tacotron2-DDC";
+          };
+          dutch = {
+            port = 5302;
+            model = "tts_models/nl/mai/tacotron2-DDC";
+          };
+        }
+      '';
+      description = mdDoc ''
+        TTS server instances.
+      '';
+    };
+  };
+
+  config = let
+    inherit (lib) mkIf mapAttrs' nameValuePair optionalString concatMapStringsSep escapeShellArgs;
+  in mkIf (cfg.servers != {}) {
+    systemd.services = mapAttrs' (server: options:
+      nameValuePair "tts-${server}" {
+        description = "Coqui TTS server instance ${server}";
+        after = [
+          "network-online.target"
+        ];
+        wantedBy = [
+          "multi-user.target"
+        ];
+        path = with pkgs; [
+          espeak-ng
+        ];
+        environment.HOME = "/var/lib/tts";
+        serviceConfig = {
+          DynamicUser = true;
+          User = "tts";
+          StateDirectory = "tts";
+          ExecStart = "${pkgs.tts}/bin/tts-server --port ${toString options.port}"
+            + optionalString (options.model != null) " --model_name ${options.model}"
+            + optionalString (options.useCuda) " --use_cuda"
+            + (concatMapStringsSep " " escapeShellArgs options.extraArgs);
+          CapabilityBoundingSet = "";
+          DeviceAllow = if options.useCuda then [
+            # https://docs.nvidia.com/dgx/pdf/dgx-os-5-user-guide.pdf
+            "/dev/nvidia1"
+            "/dev/nvidia2"
+            "/dev/nvidia3"
+            "/dev/nvidia4"
+            "/dev/nvidia-caps/nvidia-cap1"
+            "/dev/nvidia-caps/nvidia-cap2"
+            "/dev/nvidiactl"
+            "/dev/nvidia-modeset"
+            "/dev/nvidia-uvm"
+            "/dev/nvidia-uvm-tools"
+          ] else "";
+          DevicePolicy = "closed";
+          LockPersonality = true;
+          # jit via numba->llvmpipe
+          MemoryDenyWriteExecute = false;
+          PrivateDevices = true;
+          PrivateUsers = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectControlGroups = true;
+          ProtectProc = "invisible";
+          ProcSubset = "pid";
+          RestrictAddressFamilies = [
+            "AF_INET"
+            "AF_INET6"
+          ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = [
+            "@system-service"
+            "~@privileged"
+          ];
+          UMask = "0077";
+        };
+      }) cfg.servers;
+  };
+}