@qvac/tts-ggml 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +51 -10
  2. package/docs/pocket-tts.md +12 -10
  3. package/index.d.ts +43 -1130
  4. package/index.js +127 -3042
  5. package/lib/addonEvents.d.ts +26 -0
  6. package/lib/addonEvents.js +51 -0
  7. package/lib/conditioning.d.ts +50 -0
  8. package/lib/conditioning.js +184 -0
  9. package/lib/engineDetection.d.ts +14 -0
  10. package/lib/engineDetection.js +110 -0
  11. package/lib/engineIds.d.ts +15 -0
  12. package/lib/engineIds.js +39 -0
  13. package/lib/engineParams.d.ts +3 -0
  14. package/lib/engineParams.js +34 -0
  15. package/lib/engineSetup.d.ts +153 -0
  16. package/lib/engineSetup.js +2 -0
  17. package/lib/engines/audio8.d.ts +83 -0
  18. package/lib/engines/audio8.js +262 -0
  19. package/lib/engines/chatterbox.d.ts +10 -0
  20. package/lib/engines/chatterbox.js +112 -0
  21. package/lib/engines/cosyvoice.d.ts +87 -0
  22. package/lib/engines/cosyvoice.js +267 -0
  23. package/lib/engines/moss.d.ts +26 -0
  24. package/lib/engines/moss.js +177 -0
  25. package/lib/engines/mossSoundEffect.d.ts +41 -0
  26. package/lib/engines/mossSoundEffect.js +117 -0
  27. package/lib/engines/mossSpeech.d.ts +86 -0
  28. package/lib/engines/mossSpeech.js +228 -0
  29. package/lib/engines/parler.d.ts +71 -0
  30. package/lib/engines/parler.js +287 -0
  31. package/lib/engines/supertonic.d.ts +11 -0
  32. package/lib/engines/supertonic.js +115 -0
  33. package/lib/exclusiveQueue.d.ts +16 -0
  34. package/lib/exclusiveQueue.js +45 -0
  35. package/lib/jobFields.d.ts +20 -0
  36. package/lib/jobFields.js +73 -0
  37. package/lib/modelFiles.d.ts +207 -0
  38. package/lib/modelFiles.js +117 -0
  39. package/lib/nativeParams.d.ts +12 -0
  40. package/lib/nativeParams.js +73 -0
  41. package/lib/optionValidation.d.ts +2 -0
  42. package/lib/optionValidation.js +103 -0
  43. package/lib/options.d.ts +4 -0
  44. package/lib/options.js +17 -0
  45. package/lib/pocketLifecycle.d.ts +67 -0
  46. package/lib/pocketLifecycle.js +170 -0
  47. package/lib/reload.d.ts +12 -0
  48. package/lib/reload.js +76 -0
  49. package/lib/sentenceStream.d.ts +85 -0
  50. package/lib/sentenceStream.js +367 -0
  51. package/lib/setup.d.ts +7 -0
  52. package/lib/setup.js +169 -0
  53. package/lib/streamingSupport.d.ts +8 -0
  54. package/lib/streamingSupport.js +77 -0
  55. package/lib/types.d.ts +465 -0
  56. package/lib/types.js +2 -0
  57. package/lib/voiceControls.d.ts +12 -0
  58. package/lib/voiceControls.js +23 -0
  59. package/package.json +11 -10
  60. package/test/integration/audio8.test.js +6 -12
  61. package/test/integration/gpu-smoke.test.js +2 -11
  62. package/test/integration/lavasr-enhancer.test.js +2 -15
  63. package/test/integration/moss-sfx.test.js +29 -4
  64. package/test/integration/moss.test.js +1 -1
  65. package/test/utils/desktopGpuBackend.js +21 -0
  66. package/test/utils/runTTS.js +1 -1
@@ -0,0 +1,87 @@
1
+ import type { TTSConfigurationParams } from "../../tts";
2
+ import type { EngineSetup, ModelPaths } from "../engineSetup";
3
+ import { type NormalizedFiles } from "../modelFiles";
4
+ export declare const COSYVOICE3_LLM_RE: RegExp;
5
+ export declare const COSYVOICE_DIALECTS: {
6
+ readonly cantonese: "广东话";
7
+ readonly northeastern: "东北话";
8
+ readonly gansu: "甘肃话";
9
+ readonly guizhou: "贵州话";
10
+ readonly henan: "河南话";
11
+ readonly hubei: "湖北话";
12
+ readonly hunan: "湖南话";
13
+ readonly jiangxi: "江西话";
14
+ readonly minnan: "闽南话";
15
+ readonly ningxia: "宁夏话";
16
+ readonly shanxi: "山西话";
17
+ readonly shaanxi: "陕西话";
18
+ readonly shandong: "山东话";
19
+ readonly shanghai: "上海话";
20
+ readonly sichuan: "四川话";
21
+ readonly tianjin: "天津话";
22
+ readonly yunnan: "云南话";
23
+ };
24
+ export declare const COSYVOICE_VOLUMES: {
25
+ readonly loud: "Please say a sentence as loudly as possible.";
26
+ readonly soft: "Please say a sentence in a very soft voice.";
27
+ };
28
+ export declare const COSYVOICE_STYLES: {
29
+ readonly peppa: "我想体验一下小猪佩奇风格,可以吗?";
30
+ readonly robot: "你可以尝试用机器人的方式解答吗?";
31
+ };
32
+ /**
33
+ * CosyVoice3 controls that have no canonical cross-engine vocabulary yet.
34
+ * Emotion and speaking rate are NOT here -- use the top-level `emotion` /
35
+ * `pace` options, which work the same way on every engine that supports them.
36
+ * Exactly one field takes effect per synthesis, resolved by precedence
37
+ * dialect > volume > style. Pass a raw string instead for an arbitrary
38
+ * instruction (advanced escape hatch).
39
+ */
40
+ export interface CosyvoiceInstruct {
41
+ /** Chinese dialect; renders "请用{dialect}表达。". */
42
+ dialect?: keyof typeof COSYVOICE_DIALECTS;
43
+ /** Loudness. */
44
+ volume?: keyof typeof COSYVOICE_VOLUMES;
45
+ /** Playful style preset. */
46
+ style?: keyof typeof COSYVOICE_STYLES;
47
+ }
48
+ /**
49
+ * Render a CosyVoice3 `instruct` option to the trained instruction string.
50
+ * A raw string passes through (trimmed); the structured form emits exactly one
51
+ * control by precedence dialect > volume > style. Returns "" for no
52
+ * instruction. An invalid structured key throws.
53
+ */
54
+ export declare function renderCosyvoiceInstruct(instruct: string | CosyvoiceInstruct | undefined): string;
55
+ /**
56
+ * CosyVoice3 per-call instruction. Same values as the constructor's
57
+ * `instruct`; a per-call conditioning control replaces the configured one for
58
+ * that synthesis, and one control takes effect per synthesis.
59
+ */
60
+ export interface CosyvoiceJobFields {
61
+ instruct?: string | CosyvoiceInstruct;
62
+ }
63
+ /**
64
+ * True when `modelDir` contains a CosyVoice3 LLM GGUF (the sub-model that
65
+ * unambiguously identifies a CosyVoice3 model directory).
66
+ */
67
+ export declare function dirHasCosyvoice3(modelDir?: string): boolean;
68
+ /**
69
+ * CosyVoice3 discovers its sub-model GGUFs from a model directory; the native
70
+ * engine resolves the individual components. Explicit per-component paths win
71
+ * over the directory.
72
+ */
73
+ export declare function resolveCosyvoiceModelPaths(files: NormalizedFiles): Pick<ModelPaths, "cosyvoiceModelDir" | "cosyvoiceLlmModelPath" | "cosyvoiceFlowModelPath" | "cosyvoiceHiftModelPath" | "cosyvoiceS3tokModelPath" | "cosyvoiceCampplusModelPath" | "cosyvoiceVocabPath" | "cosyvoiceMergesPath" | "cosyvoiceVoiceModelPath">;
74
+ export declare function assertCosyvoiceOptionConsistency(setup: EngineSetup): void;
75
+ /**
76
+ * `promptText` is deliberately not required: its absence selects
77
+ * cross-lingual mode. With a model dir present but no cloning GGUFs in it,
78
+ * the native load fail-closes instead.
79
+ */
80
+ export declare function assertCosyvoiceCloneConsistent(setup: EngineSetup): void;
81
+ /**
82
+ * CosyVoice3's per-call instruction, rendered like the constructor's. The
83
+ * native layer reads it next to emotion/pace, and any of the three replaces
84
+ * the configured conditioning for that synthesis.
85
+ */
86
+ export declare function resolveJobInstruct(setup: EngineSetup, source: CosyvoiceJobFields | null | undefined, where: string): string | undefined;
87
+ export declare function buildCosyvoiceParams(setup: EngineSetup): TTSConfigurationParams;
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.COSYVOICE_STYLES = exports.COSYVOICE_VOLUMES = exports.COSYVOICE_DIALECTS = exports.COSYVOICE3_LLM_RE = void 0;
4
+ exports.renderCosyvoiceInstruct = renderCosyvoiceInstruct;
5
+ exports.dirHasCosyvoice3 = dirHasCosyvoice3;
6
+ exports.resolveCosyvoiceModelPaths = resolveCosyvoiceModelPaths;
7
+ exports.assertCosyvoiceOptionConsistency = assertCosyvoiceOptionConsistency;
8
+ exports.assertCosyvoiceCloneConsistent = assertCosyvoiceCloneConsistent;
9
+ exports.resolveJobInstruct = resolveJobInstruct;
10
+ exports.buildCosyvoiceParams = buildCosyvoiceParams;
11
+ /* eslint-disable @typescript-eslint/no-require-imports -- Bare modules expose CommonJS export shapes. */
12
+ const fs = require("bare-fs");
13
+ const engineIds_1 = require("../engineIds");
14
+ const modelFiles_1 = require("../modelFiles");
15
+ const nativeParams_1 = require("../nativeParams");
16
+ // The LLM sub-model is the tell for CosyVoice3 modelDir auto-detection.
17
+ exports.COSYVOICE3_LLM_RE = /^cosyvoice3-llm(-[a-z0-9_]+)?\.gguf$/i;
18
+ // CosyVoice3 instruct2 control vocabulary (cosyvoice/utils/common.py
19
+ // instruct_list). The structured `instruct` option renders to the exact
20
+ // trained instruction string; the native engine wraps it as
21
+ // "You are a helpful assistant. " + <instruction> + "<|endofprompt|>" and
22
+ // drops the LM prompt speech tokens. One control applies per synthesis
23
+ // (the model is trained on single instructions).
24
+ exports.COSYVOICE_DIALECTS = {
25
+ cantonese: "广东话",
26
+ northeastern: "东北话",
27
+ gansu: "甘肃话",
28
+ guizhou: "贵州话",
29
+ henan: "河南话",
30
+ hubei: "湖北话",
31
+ hunan: "湖南话",
32
+ jiangxi: "江西话",
33
+ minnan: "闽南话",
34
+ ningxia: "宁夏话",
35
+ shanxi: "山西话",
36
+ shaanxi: "陕西话",
37
+ shandong: "山东话",
38
+ shanghai: "上海话",
39
+ sichuan: "四川话",
40
+ tianjin: "天津话",
41
+ yunnan: "云南话",
42
+ };
43
+ exports.COSYVOICE_VOLUMES = {
44
+ loud: "Please say a sentence as loudly as possible.",
45
+ soft: "Please say a sentence in a very soft voice.",
46
+ };
47
+ exports.COSYVOICE_STYLES = {
48
+ peppa: "我想体验一下小猪佩奇风格,可以吗?",
49
+ robot: "你可以尝试用机器人的方式解答吗?",
50
+ };
51
+ /**
52
+ * Look up a structured-instruct control value, throwing a clear error for an
53
+ * invalid key instead of letting an `undefined` render into the instruction
54
+ * string.
55
+ */
56
+ function cosyvoiceInstructValue(map, key, kind) {
57
+ const value = map[key];
58
+ if (value == null) {
59
+ throw new Error(`Invalid CosyVoice instruct ${kind} "${key}". Valid ${kind}s: ${Object.keys(map).join(", ")}.`);
60
+ }
61
+ return value;
62
+ }
63
+ /**
64
+ * Render a CosyVoice3 `instruct` option to the trained instruction string.
65
+ * A raw string passes through (trimmed); the structured form emits exactly one
66
+ * control by precedence dialect > volume > style. Returns "" for no
67
+ * instruction. An invalid structured key throws.
68
+ */
69
+ function renderCosyvoiceInstruct(instruct) {
70
+ // Only `undefined` means "omitted" (zero-shot). An explicit `null` is a
71
+ // malformed value, not an omission, so it is rejected below like any other
72
+ // non-object.
73
+ if (instruct === undefined)
74
+ return "";
75
+ if (typeof instruct === "string")
76
+ return instruct.trim();
77
+ // A JS caller can pass values the type forbids (null, a number, an array, a
78
+ // Date, a class instance). Check defensively as `unknown`: require an ordinary
79
+ // object literal so exotic objects can't slip past the presence checks below
80
+ // and degrade to zero-shot, and so control fields are read from the object's
81
+ // own properties rather than an inherited prototype.
82
+ const control = instruct;
83
+ if (control === null || typeof control !== "object" || Array.isArray(control)) {
84
+ throw new Error("Invalid CosyVoice instruct: expected a string or a plain control " +
85
+ "object such as { dialect: 'cantonese' }.");
86
+ }
87
+ const proto = Object.getPrototypeOf(control);
88
+ if (proto !== Object.prototype && proto !== null) {
89
+ throw new Error("Invalid CosyVoice instruct: expected a plain control object such as " +
90
+ "{ dialect: 'cantonese' }, not an instance of another type.");
91
+ }
92
+ // Reject unknown structured keys (typos like `{ dialekt: 'cantonese' }`)
93
+ // before the precedence chain, which would otherwise fall through to "".
94
+ const supportedControls = ["dialect", "volume", "style"];
95
+ const unknownKeys = Object.keys(instruct).filter((key) => !supportedControls.includes(key));
96
+ if (unknownKeys.length > 0) {
97
+ const hint = unknownKeys.some((key) => key === "emotion" || key === "speed")
98
+ ? " Emotion and speaking rate moved to the top-level `emotion` / `pace` options."
99
+ : "";
100
+ throw new Error(`Invalid CosyVoice instruct key(s): ${unknownKeys.join(", ")}. ` +
101
+ `Valid keys: dialect, volume, style.${hint}`);
102
+ }
103
+ // Presence, not truthiness: a control that is set to a malformed value
104
+ // (`{ dialect: '' }`, `{ dialect: null }`) must raise via
105
+ // cosyvoiceInstructValue rather than fall through to zero-shot. `undefined`
106
+ // means the field was not set, so it is skipped. Precedence: dialect >
107
+ // volume > style.
108
+ if (instruct.dialect !== undefined) {
109
+ return `请用${cosyvoiceInstructValue(exports.COSYVOICE_DIALECTS, instruct.dialect, "dialect")}表达。`;
110
+ }
111
+ if (instruct.volume !== undefined) {
112
+ return cosyvoiceInstructValue(exports.COSYVOICE_VOLUMES, instruct.volume, "volume");
113
+ }
114
+ if (instruct.style !== undefined) {
115
+ return cosyvoiceInstructValue(exports.COSYVOICE_STYLES, instruct.style, "style");
116
+ }
117
+ return "";
118
+ }
119
+ /**
120
+ * True when `modelDir` contains a CosyVoice3 LLM GGUF (the sub-model that
121
+ * unambiguously identifies a CosyVoice3 model directory).
122
+ */
123
+ function dirHasCosyvoice3(modelDir) {
124
+ if (!modelDir)
125
+ return false;
126
+ let entries;
127
+ try {
128
+ entries = fs.readdirSync(modelDir);
129
+ }
130
+ catch {
131
+ return false;
132
+ }
133
+ return entries.some((name) => exports.COSYVOICE3_LLM_RE.test(name));
134
+ }
135
+ /**
136
+ * CosyVoice3 discovers its sub-model GGUFs from a model directory; the native
137
+ * engine resolves the individual components. Explicit per-component paths win
138
+ * over the directory.
139
+ */
140
+ function resolveCosyvoiceModelPaths(files) {
141
+ return {
142
+ cosyvoiceModelDir: (0, modelFiles_1.firstNonEmpty)(files.cosyvoiceModelDir, files.modelDir),
143
+ cosyvoiceLlmModelPath: files.cosyvoiceLlmModel,
144
+ cosyvoiceFlowModelPath: files.cosyvoiceFlowModel,
145
+ cosyvoiceHiftModelPath: files.cosyvoiceHiftModel,
146
+ cosyvoiceS3tokModelPath: files.cosyvoiceS3tokModel,
147
+ cosyvoiceCampplusModelPath: files.cosyvoiceCampplusModel,
148
+ cosyvoiceVocabPath: files.cosyvoiceVocab,
149
+ cosyvoiceMergesPath: files.cosyvoiceMerges,
150
+ cosyvoiceVoiceModelPath: files.cosyvoiceVoiceModel,
151
+ };
152
+ }
153
+ function assertCosyvoiceOptionConsistency(setup) {
154
+ if (setup.engineType === engineIds_1.ENGINE_COSYVOICE3)
155
+ return;
156
+ const cosyvoiceOnly = [];
157
+ const cosyvoiceOnlyFields = {
158
+ instruct: setup.settings.instruct,
159
+ promptText: setup.settings.promptText,
160
+ };
161
+ for (const [key, value] of Object.entries(cosyvoiceOnlyFields)) {
162
+ if (value != null)
163
+ cosyvoiceOnly.push(key);
164
+ }
165
+ if (cosyvoiceOnly.length > 0) {
166
+ throw new Error(`tts-ggml: ${cosyvoiceOnly.join(", ")} are cosyvoice3-only options ` +
167
+ `(engine is ${setup.engineType})`);
168
+ }
169
+ }
170
+ /**
171
+ * `promptText` is deliberately not required: its absence selects
172
+ * cross-lingual mode. With a model dir present but no cloning GGUFs in it,
173
+ * the native load fail-closes instead.
174
+ */
175
+ function assertCosyvoiceCloneConsistent(setup) {
176
+ if (setup.engineType !== engineIds_1.ENGINE_COSYVOICE3)
177
+ return;
178
+ if (!setup.settings.referenceAudio)
179
+ return;
180
+ const haveExplicit = setup.paths.cosyvoiceS3tokModelPath && setup.paths.cosyvoiceCampplusModelPath;
181
+ if (!haveExplicit && !setup.paths.cosyvoiceModelDir) {
182
+ throw new Error("tts-ggml: referenceAudio (CosyVoice3 voice cloning) needs the " +
183
+ "speech-tokenizer and speaker-encoder GGUFs: set " +
184
+ "files.cosyvoiceS3tokModel + files.cosyvoiceCampplusModel, or a " +
185
+ "files.cosyvoiceModelDir containing cosyvoice3-s3tok*.gguf and " +
186
+ "cosyvoice3-campplus*.gguf");
187
+ }
188
+ }
189
+ /**
190
+ * CosyVoice3's per-call instruction, rendered like the constructor's. The
191
+ * native layer reads it next to emotion/pace, and any of the three replaces
192
+ * the configured conditioning for that synthesis.
193
+ */
194
+ function resolveJobInstruct(setup, source, where) {
195
+ if (source?.instruct === undefined)
196
+ return undefined;
197
+ if (setup.engineType !== engineIds_1.ENGINE_COSYVOICE3) {
198
+ throw new Error(`tts-ggml: ${where}: per-call instruct is cosyvoice3-only ` +
199
+ `(engine is ${setup.engineType})`);
200
+ }
201
+ return renderCosyvoiceInstruct(source.instruct) || undefined;
202
+ }
203
+ function buildCosyvoiceParams(setup) {
204
+ const parameters = {
205
+ engineType: engineIds_1.ENGINE_COSYVOICE3,
206
+ cosyvoiceModelDir: setup.paths.cosyvoiceModelDir || "",
207
+ language: setup.config.language || "en",
208
+ };
209
+ if (setup.paths.cosyvoiceLlmModelPath) {
210
+ parameters.cosyvoiceLlmModelPath = setup.paths.cosyvoiceLlmModelPath;
211
+ }
212
+ if (setup.paths.cosyvoiceFlowModelPath) {
213
+ parameters.cosyvoiceFlowModelPath = setup.paths.cosyvoiceFlowModelPath;
214
+ }
215
+ if (setup.paths.cosyvoiceHiftModelPath) {
216
+ parameters.cosyvoiceHiftModelPath = setup.paths.cosyvoiceHiftModelPath;
217
+ }
218
+ if (setup.paths.cosyvoiceS3tokModelPath) {
219
+ parameters.cosyvoiceS3tokModelPath = setup.paths.cosyvoiceS3tokModelPath;
220
+ }
221
+ if (setup.paths.cosyvoiceCampplusModelPath) {
222
+ parameters.cosyvoiceCampplusModelPath = setup.paths.cosyvoiceCampplusModelPath;
223
+ }
224
+ if (setup.paths.cosyvoiceVocabPath) {
225
+ parameters.cosyvoiceVocabPath = setup.paths.cosyvoiceVocabPath;
226
+ }
227
+ if (setup.paths.cosyvoiceMergesPath) {
228
+ parameters.cosyvoiceMergesPath = setup.paths.cosyvoiceMergesPath;
229
+ }
230
+ if (setup.paths.cosyvoiceVoiceModelPath) {
231
+ parameters.cosyvoiceVoiceModelPath = setup.paths.cosyvoiceVoiceModelPath;
232
+ }
233
+ if (setup.settings.vulkanDevice != null) {
234
+ parameters.vulkanDevice = setup.settings.vulkanDevice | 0;
235
+ }
236
+ if (setup.settings.flowCutPrompt != null) {
237
+ parameters.flowCutPrompt = !!setup.settings.flowCutPrompt;
238
+ }
239
+ if (setup.settings.referenceAudio != null) {
240
+ parameters.referenceAudio = setup.settings.referenceAudio;
241
+ }
242
+ if (setup.settings.promptText != null) {
243
+ parameters.promptText = String(setup.settings.promptText);
244
+ }
245
+ if (setup.settings.instruct)
246
+ parameters.instruct = setup.settings.instruct;
247
+ // Canonical values; the native layer maps them to the trained instructions.
248
+ if (setup.settings.emotion != null)
249
+ parameters.emotion = String(setup.settings.emotion);
250
+ if (setup.settings.pace != null)
251
+ parameters.pace = String(setup.settings.pace);
252
+ if (setup.settings.voice)
253
+ parameters.voice = setup.settings.voice;
254
+ (0, nativeParams_1.assignCommonNativeParams)(setup, parameters);
255
+ if (setup.settings.cfmSteps != null)
256
+ parameters.cfmSteps = setup.settings.cfmSteps | 0;
257
+ if (setup.settings.streamChunkTokens != null) {
258
+ parameters.streamChunkTokens = setup.settings.streamChunkTokens | 0;
259
+ }
260
+ if (setup.settings.streamFirstChunkTokens != null) {
261
+ parameters.streamFirstChunkTokens = setup.settings.streamFirstChunkTokens | 0;
262
+ }
263
+ if (setup.settings.streamLeftContextTokens != null) {
264
+ parameters.streamLeftContextTokens = setup.settings.streamLeftContextTokens | 0;
265
+ }
266
+ return parameters;
267
+ }
@@ -0,0 +1,26 @@
1
+ import type { TTSConfigurationParams } from "../../tts";
2
+ import type { EngineSetup, ModelPaths, SynthesisSettings } from "../engineSetup";
3
+ import { type NormalizedFiles } from "../modelFiles";
4
+ export declare const MOSS_BACKBONE_RE: RegExp;
5
+ export declare const MOSS_DIALOGUE_BACKBONE_RE: RegExp;
6
+ export declare const MOSS_DECODER_RE: RegExp;
7
+ export declare const MOSS_ENCODER_RE: RegExp;
8
+ export declare const MOSS_NATIVE_SAMPLE_RATE = 24000;
9
+ export declare const MOSS_FRAMES_PER_SECOND = 12.5;
10
+ export declare const MOSS_MAX_NEW_TOKENS = 2048;
11
+ export declare const MOSS_MAX_CHANNELS = 32;
12
+ export declare const MOSS_TERMINATION_ROWS = 2;
13
+ export declare const MOSS_MAX_DURATION_TOKENS: number;
14
+ export declare const MOSS_INSTANCE_VOICE_KEYS: readonly ["referenceAudio", "dialogueReferences"];
15
+ export declare function assertMossDurationTokens(value: number | null | undefined, where: string): void;
16
+ export declare function resolveMossModelPaths(files: NormalizedFiles, dialogue: boolean): Pick<ModelPaths, "mossBackbonePath" | "mossCodecDecoderPath" | "mossCodecEncoderPath">;
17
+ /** The MOSS voice and length options, read before model paths are resolved. */
18
+ export declare function mossVoiceSettings(options: {
19
+ dialogueReferences?: string[];
20
+ durationTokens?: number;
21
+ }): Pick<SynthesisSettings, "dialogueReferences" | "durationTokens">;
22
+ export declare function assertMossOptions(setup: EngineSetup): void;
23
+ export declare function assertMossOutputRate(setup: EngineSetup): void;
24
+ export declare function buildMossParams(setup: EngineSetup): TTSConfigurationParams;
25
+ export declare function assertMossReloadKeepsVoice(setup: EngineSetup, newConfig: Record<string, unknown>): void;
26
+ export declare function applyMossReload(setup: EngineSetup, newConfig: Record<string, unknown>): void;
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MOSS_INSTANCE_VOICE_KEYS = exports.MOSS_MAX_DURATION_TOKENS = exports.MOSS_TERMINATION_ROWS = exports.MOSS_MAX_CHANNELS = exports.MOSS_MAX_NEW_TOKENS = exports.MOSS_FRAMES_PER_SECOND = exports.MOSS_NATIVE_SAMPLE_RATE = exports.MOSS_ENCODER_RE = exports.MOSS_DECODER_RE = exports.MOSS_DIALOGUE_BACKBONE_RE = exports.MOSS_BACKBONE_RE = void 0;
4
+ exports.assertMossDurationTokens = assertMossDurationTokens;
5
+ exports.resolveMossModelPaths = resolveMossModelPaths;
6
+ exports.mossVoiceSettings = mossVoiceSettings;
7
+ exports.assertMossOptions = assertMossOptions;
8
+ exports.assertMossOutputRate = assertMossOutputRate;
9
+ exports.buildMossParams = buildMossParams;
10
+ exports.assertMossReloadKeepsVoice = assertMossReloadKeepsVoice;
11
+ exports.applyMossReload = applyMossReload;
12
+ const engineIds_1 = require("../engineIds");
13
+ const modelFiles_1 = require("../modelFiles");
14
+ const nativeParams_1 = require("../nativeParams");
15
+ const options_1 = require("../options");
16
+ exports.MOSS_BACKBONE_RE = /^moss-tts-delay(-[a-z0-9_]+)?\.gguf$/i;
17
+ exports.MOSS_DIALOGUE_BACKBONE_RE = /^moss-ttsd(-[a-z0-9_]+)?\.gguf$/i;
18
+ exports.MOSS_DECODER_RE = /^moss-codec-decoder(-[a-z0-9_]+)?\.gguf$/i;
19
+ exports.MOSS_ENCODER_RE = /^moss-codec-encoder(-[a-z0-9_]+)?\.gguf$/i;
20
+ exports.MOSS_NATIVE_SAMPLE_RATE = 24000;
21
+ exports.MOSS_FRAMES_PER_SECOND = 12.5;
22
+ exports.MOSS_MAX_NEW_TOKENS = 2048;
23
+ exports.MOSS_MAX_CHANNELS = 32;
24
+ exports.MOSS_TERMINATION_ROWS = 2;
25
+ exports.MOSS_MAX_DURATION_TOKENS = exports.MOSS_MAX_NEW_TOKENS - (exports.MOSS_MAX_CHANNELS - 1) - exports.MOSS_TERMINATION_ROWS;
26
+ exports.MOSS_INSTANCE_VOICE_KEYS = ["referenceAudio", "dialogueReferences"];
27
+ function isValidDurationTokens(value) {
28
+ return Number.isInteger(value) && value >= 0 && value <= exports.MOSS_MAX_DURATION_TOKENS;
29
+ }
30
+ function assertMossDurationTokens(value, where) {
31
+ if (value == null || isValidDurationTokens(value))
32
+ return;
33
+ throw new Error(`tts-ggml: ${where}: durationTokens must be an integer from 0 to ` +
34
+ `${exports.MOSS_MAX_DURATION_TOKENS} (0 = free length)`);
35
+ }
36
+ function copyDialogueReferences(references) {
37
+ if (references == null)
38
+ return undefined;
39
+ return Array.isArray(references)
40
+ ? [...references]
41
+ : references;
42
+ }
43
+ function isNonEmptyString(value) {
44
+ return typeof value === "string" && value !== "";
45
+ }
46
+ function assertDialogueReferenceList(references) {
47
+ if (Array.isArray(references) &&
48
+ references.length > 0 &&
49
+ references.every(isNonEmptyString)) {
50
+ return;
51
+ }
52
+ throw new Error("tts-ggml: dialogueReferences must be a non-empty array of WAV paths, " +
53
+ "one per speaker");
54
+ }
55
+ /** Dialogue synthesis needs the MOSS-TTSD backbone; plain TTS takes either. */
56
+ function mossBackbonePatterns(dialogue) {
57
+ return dialogue
58
+ ? [exports.MOSS_DIALOGUE_BACKBONE_RE]
59
+ : [exports.MOSS_BACKBONE_RE, exports.MOSS_DIALOGUE_BACKBONE_RE];
60
+ }
61
+ function findMossBackbone(modelDir, dialogue) {
62
+ return (0, modelFiles_1.firstNonEmpty)(...mossBackbonePatterns(dialogue).map((pattern) => (0, modelFiles_1.findQuantRankedGguf)(modelDir, pattern)));
63
+ }
64
+ function resolveMossModelPaths(files, dialogue) {
65
+ return {
66
+ mossBackbonePath: (0, modelFiles_1.firstNonEmpty)(files.mossBackbone, findMossBackbone(files.modelDir, dialogue)),
67
+ mossCodecDecoderPath: (0, modelFiles_1.firstNonEmpty)(files.mossCodecDecoder, (0, modelFiles_1.findQuantRankedGguf)(files.modelDir, exports.MOSS_DECODER_RE)),
68
+ mossCodecEncoderPath: (0, modelFiles_1.firstNonEmpty)(files.mossCodecEncoder, (0, modelFiles_1.findQuantRankedGguf)(files.modelDir, exports.MOSS_ENCODER_RE)),
69
+ };
70
+ }
71
+ /** The MOSS voice and length options, read before model paths are resolved. */
72
+ function mossVoiceSettings(options) {
73
+ return {
74
+ dialogueReferences: copyDialogueReferences(options.dialogueReferences),
75
+ durationTokens: options.durationTokens ?? undefined,
76
+ };
77
+ }
78
+ function assertMossOptions(setup) {
79
+ if (setup.engineType !== engineIds_1.ENGINE_MOSS) {
80
+ assertNoMossOnlyOptions(setup);
81
+ return;
82
+ }
83
+ if (setup.backend.enhancerGgufPath || setup.backend.denoiserGgufPath) {
84
+ throw new Error("tts-ggml: the LavaSR enhancer/denoiser are not supported with " +
85
+ "the moss engine. Drop lavasrEnhancer / lavasrDenoiser.");
86
+ }
87
+ assertMossOutputRate(setup);
88
+ assertMossVoiceConsistent(setup);
89
+ assertMossDurationTokens(setup.settings.durationTokens, "constructor");
90
+ assertMossDialogueReferences(setup);
91
+ }
92
+ function assertNoMossOnlyOptions(setup) {
93
+ const mossOnly = (0, options_1.setOptionNames)({
94
+ durationTokens: setup.settings.durationTokens,
95
+ dialogueReferences: setup.settings.dialogueReferences !== undefined || undefined,
96
+ });
97
+ if (mossOnly.length === 0)
98
+ return;
99
+ throw new Error(`tts-ggml: ${mossOnly.join(", ")} are moss-only options ` +
100
+ `(engine is ${setup.engineType})`);
101
+ }
102
+ function assertMossDialogueReferences(setup) {
103
+ if (setup.settings.dialogueReferences === undefined)
104
+ return;
105
+ assertDialogueReferenceList(setup.settings.dialogueReferences);
106
+ if (setup.settings.referenceAudio) {
107
+ throw new Error("tts-ggml: referenceAudio and dialogueReferences are exclusive; " +
108
+ "pass one recording per speaker in dialogueReferences");
109
+ }
110
+ if (!setup.paths.mossCodecEncoderPath) {
111
+ throw new Error("tts-ggml: dialogue synthesis with the moss engine needs the codec " +
112
+ "encoder GGUF (files.mossCodecEncoder)");
113
+ }
114
+ if (!setup.paths.mossBackbonePath) {
115
+ throw new Error("tts-ggml: dialogue synthesis with the moss engine needs the MOSS-TTSD " +
116
+ "backbone: stage moss-ttsd-*.gguf in modelDir or set files.mossBackbone");
117
+ }
118
+ }
119
+ function assertMossOutputRate(setup) {
120
+ if (setup.outputSampleRate == null ||
121
+ setup.outputSampleRate === exports.MOSS_NATIVE_SAMPLE_RATE) {
122
+ return;
123
+ }
124
+ throw new Error(`tts-ggml: the moss engine emits ${exports.MOSS_NATIVE_SAMPLE_RATE} Hz only; ` +
125
+ `drop outputSampleRate (got ${setup.outputSampleRate}).`);
126
+ }
127
+ function assertMossVoiceConsistent(setup) {
128
+ if (!setup.settings.referenceAudio || setup.paths.mossCodecEncoderPath)
129
+ return;
130
+ throw new Error("tts-ggml: voice cloning with the moss engine needs the codec " +
131
+ "encoder GGUF (files.mossCodecEncoder)");
132
+ }
133
+ function buildMossParams(setup) {
134
+ assertMossOutputRate(setup);
135
+ const parameters = {
136
+ engineType: engineIds_1.ENGINE_MOSS,
137
+ mossBackbonePath: setup.paths.mossBackbonePath || "",
138
+ mossCodecDecoderPath: setup.paths.mossCodecDecoderPath || "",
139
+ language: setup.config.language || "en",
140
+ };
141
+ if (setup.paths.mossCodecEncoderPath) {
142
+ parameters.mossCodecEncoderPath = setup.paths.mossCodecEncoderPath;
143
+ }
144
+ if (setup.settings.referenceAudio != null) {
145
+ parameters.referenceAudio = setup.settings.referenceAudio;
146
+ }
147
+ if (setup.settings.dialogueReferences) {
148
+ parameters.dialogueReferences = [...setup.settings.dialogueReferences];
149
+ }
150
+ if (setup.settings.durationTokens != null) {
151
+ parameters.durationTokens = setup.settings.durationTokens | 0;
152
+ }
153
+ if (setup.settings.streamChunkTokens != null) {
154
+ parameters.streamChunkTokens = setup.settings.streamChunkTokens | 0;
155
+ }
156
+ (0, nativeParams_1.assignBackendParams)(setup, parameters);
157
+ return parameters;
158
+ }
159
+ function assertMossReloadKeepsVoice(setup, newConfig) {
160
+ if (setup.engineType !== engineIds_1.ENGINE_MOSS)
161
+ return;
162
+ const voiceKeys = exports.MOSS_INSTANCE_VOICE_KEYS.filter((key) => newConfig[key] !== undefined);
163
+ if (voiceKeys.length === 0)
164
+ return;
165
+ throw new Error(`tts-ggml: reload: the moss engine encodes ${voiceKeys.join(", ")} ` +
166
+ "once per instance; create a new instance to clone different recordings");
167
+ }
168
+ function applyMossReload(setup, newConfig) {
169
+ if (setup.engineType !== engineIds_1.ENGINE_MOSS)
170
+ return;
171
+ const durationTokens = newConfig
172
+ .durationTokens;
173
+ if (durationTokens === undefined)
174
+ return;
175
+ assertMossDurationTokens(durationTokens, "reload");
176
+ setup.settings.durationTokens = durationTokens ?? undefined;
177
+ }
@@ -0,0 +1,41 @@
1
+ import type { TTSConfigurationParams } from "../../tts";
2
+ import type { EngineSetup, ModelPaths } from "../engineSetup";
3
+ import { type NormalizedFiles } from "../modelFiles";
4
+ export declare const MOSS_SFX_RE: RegExp;
5
+ export declare const MOSS_SFX_NATIVE_SAMPLE_RATE = 48000;
6
+ export declare const MOSS_SFX_MAX_SECONDS = 30;
7
+ export declare const MOSS_SFX_MAX_STEPS = 1000;
8
+ export declare const MOSS_SFX_MAX_GUIDANCE = 50;
9
+ export declare const MOSS_SFX_MAX_SHIFT = 100;
10
+ export declare const MOSS_SFX_CALL_KEYS: readonly ["seconds", "negativePrompt", "steps", "guidance", "shift"];
11
+ /**
12
+ * MOSS-SoundEffect per-call generation controls (moss-sfx only). Each one is
13
+ * optional; unset `steps` / `guidance` / `shift` use the defaults stored in the
14
+ * model file (100 steps, guidance 4, shift 5).
15
+ */
16
+ export interface MossSoundEffectFields {
17
+ /** Length of the sound effect, in (0, 30] seconds, rounded to 0.1 s. Defaults to 10. */
18
+ seconds?: number;
19
+ /** What the sound effect should avoid (classifier-free guidance negative). */
20
+ negativePrompt?: string;
21
+ /** Diffusion steps, 1..1000. More steps cost proportionally more time. */
22
+ steps?: number;
23
+ /** Classifier-free guidance scale, 1..50; 1 skips the negative branch. */
24
+ guidance?: number;
25
+ /** Flow-matching schedule shift, (0, 100]. */
26
+ shift?: number;
27
+ }
28
+ export declare function assertSoundEffectFields(fields: MossSoundEffectFields): void;
29
+ /**
30
+ * Collect the MOSS-SoundEffect generation controls present on `source`.
31
+ * Returns undefined when none are set.
32
+ */
33
+ export declare function pickSoundEffectFields(source: MossSoundEffectFields | null | undefined): MossSoundEffectFields | undefined;
34
+ export declare function resolveMossSoundEffectModelPaths(files: NormalizedFiles): Pick<ModelPaths, "mossSoundEffectPath">;
35
+ export declare function assertMossSoundEffectOptions(setup: EngineSetup): void;
36
+ /**
37
+ * Extract + validate the per-call MOSS-SoundEffect controls from a run
38
+ * input. Returns undefined when none are present.
39
+ */
40
+ export declare function resolveSoundEffectJobFields(setup: EngineSetup, source: MossSoundEffectFields | null | undefined, where: string): MossSoundEffectFields | undefined;
41
+ export declare function buildMossSoundEffectParams(setup: EngineSetup): TTSConfigurationParams;