1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8 9let 10 cfg = config.services.wyoming.piper; 11 12 inherit (lib) 13 literalExpression 14 mkOption 15 mkEnableOption 16 mkPackageOption 17 types 18 ; 19 20 inherit (builtins) 21 toString 22 ; 23 24 inherit (utils) 25 escapeSystemdExecArgs 26 ; 27in 28 29{ 30 options.services.wyoming.piper = with types; { 31 package = mkPackageOption pkgs "wyoming-piper" { }; 32 33 servers = mkOption { 34 default = { }; 35 description = '' 36 Attribute set of wyoming-piper instances to spawn. 37 ''; 38 type = types.attrsOf ( 39 types.submodule ( 40 { ... }: 41 { 42 options = { 43 enable = mkEnableOption "Wyoming Piper server"; 44 45 voice = mkOption { 46 type = str; 47 example = "en-us-ryan-medium"; 48 description = '' 49 Name of the voice model to use. See the following website for samples: 50 https://rhasspy.github.io/piper-samples/ 51 ''; 52 }; 53 54 uri = mkOption { 55 type = strMatching "^(tcp|unix)://.*$"; 56 example = "tcp://0.0.0.0:10200"; 57 description = '' 58 URI to bind the wyoming server to. 59 ''; 60 }; 61 62 speaker = mkOption { 63 type = ints.unsigned; 64 default = 0; 65 description = '' 66 ID of a specific speaker in a multi-speaker model. 67 ''; 68 apply = toString; 69 }; 70 71 noiseScale = mkOption { 72 type = float; 73 default = 0.667; 74 description = '' 75 Generator noise value. 76 ''; 77 apply = toString; 78 }; 79 80 noiseWidth = mkOption { 81 type = float; 82 default = 0.333; 83 description = '' 84 Phoneme width noise value. 85 ''; 86 apply = toString; 87 }; 88 89 lengthScale = mkOption { 90 type = float; 91 default = 1.0; 92 description = '' 93 Phoneme length value. 94 ''; 95 apply = toString; 96 }; 97 98 streaming = mkEnableOption "audio streaming on sentence boundaries" // { 99 default = true; 100 }; 101 102 useCUDA = mkOption { 103 type = bool; 104 default = pkgs.config.cudaSupport; 105 defaultText = literalExpression "pkgs.config.cudaSupport"; 106 description = '' 107 Whether to accelerate the underlying onnxruntime library with CUDA. 108 ''; 109 }; 110 111 extraArgs = mkOption { 112 type = listOf str; 113 default = [ ]; 114 description = '' 115 Extra arguments to pass to the server commandline. 116 ''; 117 }; 118 }; 119 } 120 ) 121 ); 122 }; 123 }; 124 125 config = 126 let 127 inherit (lib) 128 mapAttrs' 129 mkIf 130 nameValuePair 131 ; 132 in 133 mkIf (cfg.servers != { }) { 134 systemd.services = mapAttrs' ( 135 server: options: 136 nameValuePair "wyoming-piper-${server}" { 137 inherit (options) enable; 138 description = "Wyoming Piper server instance ${server}"; 139 wants = [ 140 "network-online.target" 141 ]; 142 after = [ 143 "network-online.target" 144 ]; 145 wantedBy = [ 146 "multi-user.target" 147 ]; 148 serviceConfig = { 149 DynamicUser = true; 150 User = "wyoming-piper"; 151 StateDirectory = [ "wyoming/piper" ]; 152 # https://github.com/home-assistant/addons/blob/master/piper/rootfs/etc/s6-overlay/s6-rc.d/piper/run 153 ExecStart = escapeSystemdExecArgs ( 154 [ 155 (lib.getExe cfg.package) 156 "--data-dir" 157 "/var/lib/wyoming/piper" 158 "--uri" 159 options.uri 160 "--voice" 161 options.voice 162 "--speaker" 163 options.speaker 164 "--length-scale" 165 options.lengthScale 166 "--noise-scale" 167 options.noiseScale 168 "--noise-w-scale" 169 options.noiseWidth 170 ] 171 ++ lib.optionals options.streaming [ 172 "--streaming" 173 ] 174 ++ lib.optionals options.useCUDA [ 175 "--use-cuda" 176 ] 177 ++ options.extraArgs 178 ); 179 CapabilityBoundingSet = ""; 180 DeviceAllow = ""; 181 DevicePolicy = "closed"; 182 LockPersonality = true; 183 MemoryDenyWriteExecute = false; # required for onnxruntime 184 PrivateDevices = true; 185 PrivateUsers = true; 186 ProtectHome = true; 187 ProtectHostname = true; 188 ProtectKernelLogs = true; 189 ProtectKernelModules = true; 190 ProtectKernelTunables = true; 191 ProtectControlGroups = true; 192 ProtectProc = "invisible"; 193 ProcSubset = "all"; # for onnxruntime, which queries cpuinfo 194 RestrictAddressFamilies = [ 195 "AF_INET" 196 "AF_INET6" 197 "AF_UNIX" 198 ]; 199 RestrictNamespaces = true; 200 RestrictRealtime = true; 201 SystemCallArchitectures = "native"; 202 SystemCallFilter = [ 203 "@system-service" 204 "~@privileged" 205 ]; 206 UMask = "0077"; 207 }; 208 } 209 ) cfg.servers; 210 }; 211}