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.

Changed files
+154
nixos
doc
manual
release-notes
modules
services
audio
+2
nixos/doc/manual/release-notes/rl-2305.section.md
···
- [goeland](https://github.com/slurdge/goeland), an alternative to rss2email written in golang with many filters. Available as [services.goeland](#opt-services.goeland.enable).
+
- [tts](https://github.com/coqui-ai/TTS), a battle-tested deep learning toolkit for Text-to-Speech. Mutiple servers may be configured below [services.tts.servers](#opt-services.tts.servers).
+
- [atuin](https://github.com/ellie/atuin), a sync server for shell history. Available as [services.atuin](#opt-services.atuin.enable).
- [networkd-dispatcher](https://gitlab.com/craftyguy/networkd-dispatcher), a dispatcher service for systemd-networkd connection status changes. Available as [services.networkd-dispatcher](#opt-services.networkd-dispatcher.enable).
+1
nixos/modules/module-list.nix
···
./services/audio/snapserver.nix
./services/audio/spotifyd.nix
./services/audio/squeezelite.nix
+
./services/audio/tts.nix
./services/audio/ympd.nix
./services/backup/automysqlbackup.nix
./services/backup/bacula.nix
+151
nixos/modules/services/audio/tts.nix
···
+
{ 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;
+
};
+
}