@qvac/tts-ggml 0.10.1 → 0.11.1
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.
- package/README.md +280 -28
- package/examples/moss-sfx-fit.js +18 -0
- package/examples/moss-sound-effect.js +125 -0
- package/examples/moss-speech.js +136 -0
- package/examples/pcm-chunk-player.js +35 -10
- package/index.d.ts +116 -6
- package/index.js +391 -2
- package/lib/fit.d.ts +22 -2
- package/package.json +35 -17
- package/test/integration/fit.test.js +7 -3
- package/test/integration/moss-fit.test.js +57 -0
- package/test/integration/moss-sfx-fit.test.js +63 -0
- package/test/integration/moss-sfx.test.js +96 -0
- package/test/integration/moss-speech.test.js +110 -0
- package/test/mobile/integration.auto.cjs +15 -0
- package/tts.d.ts +20 -0
- /package/test/{mobile → pocket-worklet}/pocket-worklet-host.cpp +0 -0
- /package/test/{mobile → pocket-worklet}/pocket-worklet.cjs +0 -0
package/README.md
CHANGED
|
@@ -10,7 +10,9 @@ Metal on Apple, CUDA/Vulkan on Linux, Vulkan on Windows, and OpenCL/Adreno
|
|
|
10
10
|
GPU offload on Android), **Audio8**
|
|
11
11
|
(DualAR + neural codec, in-process voice cloning, desktop GPU), and **MOSS**
|
|
12
12
|
(MOSS-TTS Delay, 24 kHz, in-process voice cloning, native chunk streaming,
|
|
13
|
-
desktop),
|
|
13
|
+
desktop), **MOSS-SoundEffect** (text-to-sound-effects, 48 kHz, up to 30 s,
|
|
14
|
+
desktop), and **MOSS-Speech** (speech-to-speech: a spoken question in, a
|
|
15
|
+
spoken answer out, 24 kHz, desktop), plus optional
|
|
14
16
|
LavaSR neural denoise + 48 kHz bandwidth-extension enhancement. Unsure
|
|
15
17
|
which checkpoint to stage? Start with [Choosing a model](#choosing-a-model).
|
|
16
18
|
|
|
@@ -47,8 +49,9 @@ validated on Apple/Metal.
|
|
|
47
49
|
`config.useGPU: true` on GPU-capable hosts — including Android, where
|
|
48
50
|
`tts-cpp` selects the GPU backend per its per-vendor allowlist (see
|
|
49
51
|
[Backends & GPU acceleration](#backends--gpu-acceleration)).
|
|
50
|
-
- **Apple Core ML sidecars** for the Supertonic vocoder
|
|
51
|
-
|
|
52
|
+
- **Apple Core ML sidecars** for the Supertonic vocoder, the Audio8 codec,
|
|
53
|
+
and one stage each of MOSS, MOSS-SoundEffect, and MOSS-Speech on macOS /
|
|
54
|
+
iOS, picked up when staged next to the model (see
|
|
52
55
|
[Core ML sidecars on Apple](#core-ml-sidecars-on-apple)).
|
|
53
56
|
- **Dynamic backend loading on Android** — per-arch CPU + Vulkan +
|
|
54
57
|
OpenCL `.so` files ship under `prebuilds/<bare-target>/qvac__tts-ggml/`
|
|
@@ -74,6 +77,8 @@ obvious for each job.
|
|
|
74
77
|
| Voice cloning at 44.1 kHz with nothing pre-baked | `audio8-lm-q8_0.gguf` + `audio8-codec-decoder-q8_0.gguf` (+ `audio8-codec-encoder-q8_0.gguf` to clone) | Clones from a reference wav **and its transcript**, encoded in-process — no enrolment step, no voice profile. Whole-utterance only (no native chunk streaming). |
|
|
75
78
|
| Multilingual speech with native chunk streaming and cloning on a desktop | `moss-tts-delay-f16.gguf` + `moss-codec-decoder-f16.gguf` (+ `moss-codec-encoder-f16.gguf` to clone) | 8B backbone, desktop only. Clones from a reference wav encoded in-process (no transcript needed); native `streamChunkTokens` chunk streaming. Native 24 kHz. |
|
|
76
79
|
| Multi-speaker dialogue that continues each speaker's reference voice | `moss-ttsd-f16.gguf` + `moss-codec-decoder-f16.gguf` + `moss-codec-encoder-f16.gguf` | 8B backbone, desktop only. One 24 kHz reference per speaker; the text opens with their transcripts. Native 24 kHz. |
|
|
80
|
+
| Sound effects from a text description (not speech) | `moss-sfx-v2-q8_0.gguf` (or `moss-sfx-v2-f16.gguf`) | Diffusion model, desktop GPU recommended. Up to 30 s per clip, `seconds` per call; no streaming. Native 48 kHz. |
|
|
81
|
+
| Answer a spoken question with speech (voice assistant) | `moss-speech-q8_0.gguf` + `moss-speech-codec-f16.gguf` | 9B model, desktop GPU. Audio in, 24 kHz audio (plus the reply text) out; conversation history, reply voice and reply length per call; no streaming. |
|
|
77
82
|
| Indic languages | `parler-indic-q8_0.gguf` | 21 Indic languages; voice / emotion templates. Emotion is officially tested on 10 languages — see [Parler descriptions & emotions](#parler-descriptions--emotions) (or the upstream model card). Native 44.1 kHz. |
|
|
78
83
|
| Chinese dialects (Cantonese, Sichuan, Shanghai, …) | CosyVoice3 dir (`cosyvoice3-llm-*.gguf` + flow / hift / `voice.gguf`) | Instruct-conditioned; 17 dialects via `instruct: { dialect: '…' }`. CPU, with opt-in GPU offload (Metal on Apple, Vulkan on Linux/Windows, OpenCL/Adreno on Android); native 24 kHz. |
|
|
79
84
|
| Zero-shot / cross-lingual cloning (multilingual) | CosyVoice3 dir + `cosyvoice3-s3tok-*.gguf` + `cosyvoice3-campplus-*.gguf` | Clones from a reference wav; its transcript (`promptText`) selects zero-shot, omitting it selects cross-lingual (timbre only, any target language). Composes with `instruct`. |
|
|
@@ -98,6 +103,8 @@ here, so this guide does not go stale when backends change.
|
|
|
98
103
|
| `audio8-lm-q8_0` + codec halves | Text-led (no `language` option) | ~0.7 GB (+ ~120 MB to clone) | 44.1 kHz | Reference wav + transcript, per call too | Sentence streaming |
|
|
99
104
|
| `moss-tts-delay-f16` + codec halves | Multilingual (`language` hint) | ~18.8 GB (+ ~1.8 GB to clone) | 24 kHz | Reference wav, fixed per instance | Sentence + native chunk |
|
|
100
105
|
| `moss-ttsd-f16` + codec halves | Multilingual (`language` hint) | ~20.5 GB | 24 kHz | One reference wav per speaker, fixed per instance | Native chunk |
|
|
106
|
+
| `moss-sfx-v2-q8_0` (or `-f16`) | Sound effects from a text prompt | ~3.5 GB (`q8_0`) / ~6.4 GB (`f16`) | 48 kHz | — | None |
|
|
107
|
+
| `moss-speech-q8_0` (or `-bf16`) + `moss-speech-codec-f16` | Speech-to-speech replies | ~9.7 GB (`q8_0`) / ~18.2 GB (`bf16`), + 1.3 GB codec | 24 kHz | Codec default, or a per-call reference | None |
|
|
101
108
|
|
|
102
109
|
### Legacy Supertonic v1 / v2
|
|
103
110
|
|
|
@@ -172,8 +179,9 @@ dependency, pinned to the exact `@qvac/tts-ggml` version:
|
|
|
172
179
|
|
|
173
180
|
## Model files
|
|
174
181
|
|
|
175
|
-
|
|
176
|
-
CosyVoice3, Audio8, MOSS), each with its own GGUF layout
|
|
182
|
+
Eight engine families are wrapped (Chatterbox, Supertonic, Parler,
|
|
183
|
+
CosyVoice3, Audio8, MOSS, MOSS-SoundEffect, MOSS-Speech), each with its own GGUF layout
|
|
184
|
+
under `models/`:
|
|
177
185
|
|
|
178
186
|
```
|
|
179
187
|
# Chatterbox turbo (English)
|
|
@@ -233,6 +241,20 @@ moss-tts-delay-f16.gguf (~17 GB)
|
|
|
233
241
|
moss-ttsd-f16.gguf (~17 GB; MOSS-TTSD dialogue backbone, optional)
|
|
234
242
|
moss-codec-decoder-f16.gguf (~1.8 GB)
|
|
235
243
|
moss-codec-encoder-f16.gguf (~1.8 GB; cloning and dialogue only)
|
|
244
|
+
moss-codec-decoder.mlmodelc/ (optional, macOS / iOS: Apple Core ML sidecar for
|
|
245
|
+
the codec decoder while streaming; see Core ML
|
|
246
|
+
sidecars on Apple)
|
|
247
|
+
|
|
248
|
+
# MOSS-SoundEffect (OpenMOSS MOSS-SoundEffect-v2; 48 kHz text-to-sound-effects)
|
|
249
|
+
moss-sfx-v2-q8_0.gguf (~3.5 GB; or moss-sfx-v2-f16.gguf, ~6.4 GB)
|
|
250
|
+
moss-sfx-v2-dit.mlmodelc/ (optional, macOS / iOS: Apple Core ML sidecars for
|
|
251
|
+
moss-sfx-v2-vae.mlmodelc/ the diffusion transformer and the VAE decoder)
|
|
252
|
+
|
|
253
|
+
# MOSS-Speech (fnlp/MOSS-Speech; 24 kHz speech-to-speech)
|
|
254
|
+
moss-speech-q8_0.gguf (~9.7 GB; or moss-speech-bf16.gguf, ~18.2 GB)
|
|
255
|
+
moss-speech-codec-f16.gguf (~1.3 GB; carries the default reply voice)
|
|
256
|
+
moss-speech-codec-tokenizer.mlmodelc/ (optional, macOS / iOS: Apple Core ML
|
|
257
|
+
sidecar for the speech tokenizer)
|
|
236
258
|
```
|
|
237
259
|
|
|
238
260
|
Download the registry-published Chatterbox, Supertonic, and Parler models into
|
|
@@ -244,7 +266,7 @@ npm run download-models:registry -- --group chatterbox,supertonic3
|
|
|
244
266
|
npm run download-models:registry -- --output /path/to/models
|
|
245
267
|
```
|
|
246
268
|
|
|
247
|
-
CosyVoice3, Audio8 and MOSS are not currently included in that registry command.
|
|
269
|
+
CosyVoice3, Audio8, MOSS, MOSS-SoundEffect and MOSS-Speech are not currently included in that registry command.
|
|
248
270
|
Stage their layouts shown above from local converted artifacts. CosyVoice3
|
|
249
271
|
resolves each component by filename prefix and does not rank quantizations, so
|
|
250
272
|
stage one file per component or name it explicitly with
|
|
@@ -270,7 +292,9 @@ qvac-fabric-speech.cpp (`engines/tts/scripts/convert-audio8-lm-to-gguf.py`
|
|
|
270
292
|
and `convert-audio8-codec-to-gguf.py`) until they are published to the model
|
|
271
293
|
registry alongside the other engines. The MOSS GGUFs come from
|
|
272
294
|
`convert-moss-delay-to-gguf.py` and `convert-moss-codec-to-gguf.py` in the same
|
|
273
|
-
directory.
|
|
295
|
+
directory, the MOSS-SoundEffect GGUF from `convert-moss-sfx-to-gguf.py`, and the
|
|
296
|
+
MOSS-Speech GGUFs from `convert-moss-speech-to-gguf.py` and
|
|
297
|
+
`convert-moss-speech-codec-to-gguf.py`.
|
|
274
298
|
|
|
275
299
|
Point the addon at a custom location via `files.modelDir` (engine
|
|
276
300
|
auto-detected from the gguf filenames present), or pass explicit
|
|
@@ -281,7 +305,9 @@ auto-detected from the gguf filenames present), or pass explicit
|
|
|
281
305
|
`files.audio8Lm` + `files.audio8CodecDecoder` (+ `files.audio8CodecEncoder`
|
|
282
306
|
to clone) (Audio8) /
|
|
283
307
|
`files.mossBackbone` + `files.mossCodecDecoder` (+ `files.mossCodecEncoder`
|
|
284
|
-
to clone) (MOSS)
|
|
308
|
+
to clone) (MOSS) /
|
|
309
|
+
`files.mossSoundEffect` (MOSS-SoundEffect) /
|
|
310
|
+
`files.mossSpeechModel` + `files.mossSpeechCodec` (MOSS-Speech).
|
|
285
311
|
|
|
286
312
|
## Quick start
|
|
287
313
|
|
|
@@ -677,7 +703,8 @@ keeps the automatic preference above.
|
|
|
677
703
|
### Core ML sidecars on Apple
|
|
678
704
|
|
|
679
705
|
The macOS / iOS prebuilds carry the Apple Core ML (Neural Engine) sidecars
|
|
680
|
-
for the Supertonic vocoder
|
|
706
|
+
for the Supertonic vocoder, the Audio8 codec, and the MOSS models' fixed-shape
|
|
707
|
+
stages. They are presence-driven:
|
|
681
708
|
each stage runs on a compiled `.mlmodelc` found next to its model file
|
|
682
709
|
(`supertonic3-q8_0.gguf` -> `supertonic3-vocoder.mlmodelc`) and falls back to
|
|
683
710
|
the ggml graph when it is absent, so a model directory without sidecars
|
|
@@ -688,14 +715,20 @@ published model set yet; supply your own to opt in.
|
|
|
688
715
|
| Model | Sidecar next to the GGUF | Stage on Core ML | Runs on ggml instead | Force ggml |
|
|
689
716
|
| --- | --- | --- | --- | --- |
|
|
690
717
|
| Supertonic 1 / 2 / 3 | `<model>-vocoder.mlmodelc` | vocoder, in 64-latent-frame windows | GGUFs whose vocoder weights are stored below 8 bits (`q4_0`) | `SUPERTONIC_COREML_DISABLE=1` |
|
|
691
|
-
| Audio8 | `audio8-codec-decoder.mlmodelc`, beside the codec decoder GGUF | codec synthesis stack (upsampling + DAC decoder), in 64-post-frame windows; the language model stays on the ggml backend | a call that fails on the sidecar, which also retires it for every later call on that instance | `AUDIO8_COREML_DISABLE=1` |
|
|
692
|
-
|
|
|
718
|
+
| Audio8 | `audio8-codec-decoder.mlmodelc`, beside the codec decoder GGUF | codec synthesis stack (upsampling + DAC decoder), in 64-post-frame windows synthesised while the language model is still generating, so only the last one is left after it; the language model stays on the ggml backend | a call that fails on the sidecar, which also retires it for every later call on that instance | `AUDIO8_COREML_DISABLE=1` |
|
|
719
|
+
| MOSS, MOSS-TTSD | `moss-codec-decoder.mlmodelc`, beside the codec decoder GGUF | codec decoder, 25 frames (2 s) per call, keeping its attention cache between calls (macOS 15 / iOS 18) | non-streaming synthesis and streams with `streamChunkTokens` above 25, which ggml decodes faster in one pass | `MOSS_COREML_DISABLE=1` |
|
|
720
|
+
| MOSS-SoundEffect | `moss-sfx-v2-dit.mlmodelc` and `moss-sfx-v2-vae.mlmodelc` | diffusion transformer, one velocity per call; VAE decoder in 320-frame windows | a call that fails on the sidecar, which retires it | `MOSS_COREML_DISABLE=1` |
|
|
721
|
+
| MOSS-Speech | `moss-speech-codec-tokenizer.mlmodelc`, beside the codec GGUF | speech tokenizer (Whisper-VQ encoder), one full 30 s segment per call | segments shorter than 30 s, which ggml encodes faster; so most spoken questions | `MOSS_COREML_DISABLE=1` |
|
|
722
|
+
| Chatterbox, Parler, CosyVoice3, LavaSR | none | — | always | — |
|
|
693
723
|
|
|
694
724
|
Set the force-ggml variables in the process environment before `load()`.
|
|
695
725
|
Audio8 reports its codec path in `response.stats`: `codecSidecarLoaded` is 1
|
|
696
726
|
while the sidecar is attached and `codecOnCoreml` is 1 when that synthesis
|
|
697
727
|
ran its codec on it (see [Response shape](#response-shape)). The Supertonic
|
|
698
|
-
vocoder path is not reported in the stats.
|
|
728
|
+
vocoder path is not reported in the stats, and neither are the MOSS paths.
|
|
729
|
+
The MOSS sidecars run on the GPU unless `MOSS_COREML_COMPUTE_UNITS` (`all`,
|
|
730
|
+
`cpu_and_ane`, `cpu_only`) says otherwise: none of their stages ran faster on
|
|
731
|
+
the Neural Engine, and the speech tokenizer loses code accuracy there.
|
|
699
732
|
|
|
700
733
|
Worth it where the GPU is consumer-class: on an Apple M4 the Supertonic
|
|
701
734
|
vocoder runs 1.6-2.9x faster on the Neural Engine than on Metal (1.06-1.13x
|
|
@@ -704,14 +737,25 @@ so do not stage a sidecar there. `q4_0` models ignore the vocoder sidecar:
|
|
|
704
737
|
it carries full-precision weights and would substitute a different vocoder
|
|
705
738
|
rather than accelerate the quantized one.
|
|
706
739
|
|
|
740
|
+
The MOSS sidecars, per stage against Metal with f16 models:
|
|
741
|
+
|
|
742
|
+
| Stage | Apple M4 | Apple M3 Ultra |
|
|
743
|
+
| --- | --- | --- |
|
|
744
|
+
| MOSS-SoundEffect diffusion transformer | 1.23x | 1.06x |
|
|
745
|
+
| MOSS-SoundEffect VAE decoder | 1.70x | 1.81x |
|
|
746
|
+
| MOSS-Speech tokenizer, full 30 s segment | 1.24x | 1.08x |
|
|
747
|
+
| MOSS codec, streamed in 25-frame chunks | 1.03x | 1.13x |
|
|
748
|
+
|
|
707
749
|
Export the sidecars from the model GGUFs with
|
|
708
|
-
`engines/tts/scripts/export-supertonic-coreml.py
|
|
709
|
-
`engines/tts/scripts/export-audio8-codec-coreml.py
|
|
750
|
+
`engines/tts/scripts/export-supertonic-coreml.py`,
|
|
751
|
+
`engines/tts/scripts/export-audio8-codec-coreml.py`, and
|
|
752
|
+
`engines/tts/scripts/export-moss-{codec,sfx,speech-tokenizer}-coreml.py` from the
|
|
710
753
|
[`qvac-fabric-speech.cpp`](https://github.com/tetherto/qvac-fabric-speech.cpp)
|
|
711
754
|
tree at the ref `speech-cpp` pins. Its
|
|
712
|
-
[Supertonic](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/supertonic.md#core-ml-vocoder-sidecar)
|
|
755
|
+
[Supertonic](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/supertonic.md#core-ml-vocoder-sidecar),
|
|
756
|
+
[Audio8](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/audio8.md#core-ml-codec-sidecar),
|
|
713
757
|
and
|
|
714
|
-
[
|
|
758
|
+
[MOSS](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/moss.md#core-ml-codec-decoder)
|
|
715
759
|
guides cover export, placement, and measurements.
|
|
716
760
|
|
|
717
761
|
When the addon is built with `ENABLE_CUDA` — on in the published linux-x64
|
|
@@ -797,6 +841,8 @@ each engine's subset straight from the native library, without loading a model
|
|
|
797
841
|
| Chatterbox | not supported | not supported | — | `speed` |
|
|
798
842
|
| Audio8 | not supported | not supported | — | — |
|
|
799
843
|
| MOSS | not supported | not supported | — | — |
|
|
844
|
+
| MOSS-SoundEffect | not supported | not supported | — | — |
|
|
845
|
+
| MOSS-Speech | not supported | not supported | — | — |
|
|
800
846
|
|
|
801
847
|
The 12 canonical emotions (case-insensitive): `command`, `anger`, `narration`,
|
|
802
848
|
`conversation`, `disgust`, `fear`, `happy`, `neutral`, `proper noun`, `news`,
|
|
@@ -1014,6 +1060,104 @@ falls back to CPU and sets `response.stats.gpuUnsupported`. `backendsDir`
|
|
|
1014
1060
|
reaches MOSS like every other engine, so builds that load ggml backends at
|
|
1015
1061
|
runtime find them.
|
|
1016
1062
|
|
|
1063
|
+
## MOSS-SoundEffect
|
|
1064
|
+
|
|
1065
|
+
MOSS-SoundEffect-v2 generates a sound effect from a text description: a Qwen3
|
|
1066
|
+
text encoder reads the prompt, a DiT denoises a latent over a number of
|
|
1067
|
+
flow-matching steps, and a DAC decoder turns it into 48 kHz mono audio of up
|
|
1068
|
+
to 30 seconds. It is not a speech model, and it ships as one GGUF. Select it
|
|
1069
|
+
with `engine: TTSGgml.ENGINE_MOSS_SFX` or by staging `moss-sfx-*.gguf` in the
|
|
1070
|
+
`modelDir`.
|
|
1071
|
+
|
|
1072
|
+
```js
|
|
1073
|
+
const sfx = new TTSGgml({
|
|
1074
|
+
engine: TTSGgml.ENGINE_MOSS_SFX,
|
|
1075
|
+
files: { modelDir: './models' },
|
|
1076
|
+
config: { useGPU: true },
|
|
1077
|
+
opts: { stats: true }
|
|
1078
|
+
})
|
|
1079
|
+
await sfx.load()
|
|
1080
|
+
const response = await sfx.run({
|
|
1081
|
+
input: 'Heavy rain falling on a tin roof with distant thunder.',
|
|
1082
|
+
seconds: 10, // (0, 30], rounded to 0.1 s; defaults to 10
|
|
1083
|
+
negativePrompt: 'music', // optional
|
|
1084
|
+
steps: 100, // optional; model default 100
|
|
1085
|
+
guidance: 4, // optional; model default 4, 1 skips the negative branch
|
|
1086
|
+
shift: 5 // optional; model default 5
|
|
1087
|
+
})
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
The generation controls are per call and moss-sfx-only (`steps` in the
|
|
1091
|
+
constructor is rejected); the output lasts `seconds` rounded to 0.1 s. `seed` (constructor) makes a clip reproducible. Every clip
|
|
1092
|
+
costs about the same whatever its length, because the model always denoises a
|
|
1093
|
+
30-second latent and crops it: about two minutes for 100 steps on an Apple M1
|
|
1094
|
+
Ultra GPU, and much longer on CPU, so fewer `steps` trade quality for time.
|
|
1095
|
+
There is no streaming (`runStream()`, `runStreaming()` and
|
|
1096
|
+
`run({ streamOutput: true })` are rejected), `config.outputSampleRate` must be
|
|
1097
|
+
unset or `48000`, and the LavaSR stages and voice options are not supported.
|
|
1098
|
+
|
|
1099
|
+
## MOSS-Speech
|
|
1100
|
+
|
|
1101
|
+
MOSS-Speech answers a spoken question with speech, with no text step in
|
|
1102
|
+
between: a 9B language model with separate text and audio branches reads the
|
|
1103
|
+
question's speech tokens and writes the reply's, and the codec speaks them in a
|
|
1104
|
+
24 kHz voice. It ships as two GGUFs, the language model and the codec (which
|
|
1105
|
+
also carries the default reply voice). Select it with
|
|
1106
|
+
`engine: TTSGgml.ENGINE_MOSS_SPEECH` or by staging `moss-speech-*.gguf` and
|
|
1107
|
+
`moss-speech-codec-*.gguf` in the `modelDir`; `q8_0` is preferred over `bf16`
|
|
1108
|
+
when both are there.
|
|
1109
|
+
|
|
1110
|
+
```js
|
|
1111
|
+
const assistant = new TTSGgml({
|
|
1112
|
+
engine: TTSGgml.ENGINE_MOSS_SPEECH,
|
|
1113
|
+
files: { modelDir: './models' },
|
|
1114
|
+
config: { useGPU: true },
|
|
1115
|
+
opts: { stats: true }
|
|
1116
|
+
})
|
|
1117
|
+
await assistant.load()
|
|
1118
|
+
const response = await assistant.run({
|
|
1119
|
+
input: '', // or a typed question instead of `audio`
|
|
1120
|
+
audio: questionPcm, // Int16Array or Float32Array, mono
|
|
1121
|
+
sampleRate: 16000, // of `audio`, 8000..192000
|
|
1122
|
+
messages: [ // optional earlier turns, oldest first
|
|
1123
|
+
{ role: 'user', text: 'Hi, I am planning a trip.' },
|
|
1124
|
+
{ role: 'assistant', text: 'Great, where to?' }
|
|
1125
|
+
],
|
|
1126
|
+
systemPrompt: 'Answer briefly.', // optional; replaces the default system turn
|
|
1127
|
+
replyVoice: voicePcm, // optional reply voice (up to 60 s of mono PCM)
|
|
1128
|
+
replyVoiceSampleRate: 24000,
|
|
1129
|
+
maxReplySeconds: 20, // optional cut of the spoken reply
|
|
1130
|
+
maxNewTokens: 1000, // optional, 1..4096
|
|
1131
|
+
textReply: false, // true: answer in text only, no audio
|
|
1132
|
+
greedy: false, temperature: 0.7, topP: 0.95, topK: 20 // optional sampling
|
|
1133
|
+
})
|
|
1134
|
+
await response.onUpdate((data) => {
|
|
1135
|
+
// data.outputArray: 24 kHz Int16 PCM of the spoken reply
|
|
1136
|
+
// data.text: the reply's text channel (the whole answer with textReply)
|
|
1137
|
+
}).await()
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
When `maxNewTokens` is omitted, MOSS-Speech uses the remaining model context
|
|
1141
|
+
after the prompt instead of an implicit 1000-token budget. The JavaScript API
|
|
1142
|
+
currently accepts explicit budgets from 1 to 4096; the native engine also
|
|
1143
|
+
checks that the requested budget fits the remaining context. `maxReplySeconds`
|
|
1144
|
+
is optional and defaults to no time cut.
|
|
1145
|
+
|
|
1146
|
+
The user turn is either `audio` (with `sampleRate`) or the run `input` text,
|
|
1147
|
+
not both; each entry of `messages` carries exactly one of `text` or `audio`
|
|
1148
|
+
(with its `sampleRate`). Every call is one exchange: pass the earlier turns in
|
|
1149
|
+
`messages` to keep a conversation going. `seed` (constructor) makes sampled
|
|
1150
|
+
replies reproducible. The reply comes back in one piece: there is no streaming
|
|
1151
|
+
(`runStream()`, `runStreaming()`, `run({ streamOutput: true })` and
|
|
1152
|
+
`streamChunkTokens` are rejected), `config.outputSampleRate` must be unset or
|
|
1153
|
+
`24000`, and the LavaSR stages and `referenceAudio` are not supported. On an
|
|
1154
|
+
Apple M1 Ultra GPU the model generates about 47 reply tokens per second in
|
|
1155
|
+
`q8_0` (32 in `bf16`) against the 12.5 tokens per second of speech it produces;
|
|
1156
|
+
preparing the default voice adds a few seconds to the first reply of an
|
|
1157
|
+
instance. Runtime stats add `promptTokens`, `generatedTokens`, `replyTokens`,
|
|
1158
|
+
`truncated` and the per-stage `encodeMs` / `prefillMs` / `generateMs` /
|
|
1159
|
+
`decodeMs`.
|
|
1160
|
+
|
|
1017
1161
|
## API overview
|
|
1018
1162
|
|
|
1019
1163
|
### Constructor — `new TTSGgml(options)`
|
|
@@ -1036,15 +1180,18 @@ runtime find them.
|
|
|
1036
1180
|
| `files.mossBackbone` | string | — | MOSS Delay backbone GGUF: MOSS-TTS or the MOSS-TTSD dialogue checkpoint (overrides `modelDir`) |
|
|
1037
1181
|
| `files.mossCodecDecoder` | string | — | MOSS codec synthesis half — codes to wav (overrides `modelDir`) |
|
|
1038
1182
|
| `files.mossCodecEncoder` | string | — | MOSS codec analysis half — wav to codes; only needed to clone a voice or for `dialogueReferences` |
|
|
1183
|
+
| `files.mossSoundEffect` | string | — | MOSS-SoundEffect GGUF (`moss-sfx-*.gguf`); routes to the `moss-sfx` engine |
|
|
1184
|
+
| `files.mossSpeechModel` | string | — | MOSS-Speech language model GGUF (`moss-speech-*.gguf`); routes to the `moss-speech` engine |
|
|
1185
|
+
| `files.mossSpeechCodec` | string | — | MOSS-Speech codec GGUF (`moss-speech-codec-*.gguf`), required with `files.mossSpeechModel` |
|
|
1039
1186
|
| `files.lavasrEnhancer` | string | — | LavaSR enhancer GGUF — supplying it turns on 48 kHz enhancement |
|
|
1040
1187
|
| `files.lavasrDenoiser` | string | — | LavaSR denoiser GGUF — supplying it turns on denoising (batch only) |
|
|
1041
|
-
| `engine` | string | auto | Force `'chatterbox'`, `'supertonic'`, `'cosyvoice3'`, `'parler'`, `'audio8'` or `'moss'` (`TTSGgml.ENGINE_CHATTERBOX` / `ENGINE_SUPERTONIC` / `ENGINE_COSYVOICE3` / `ENGINE_PARLER` / `ENGINE_AUDIO8` / `ENGINE_MOSS`); auto-detected from the GGUFs present otherwise |
|
|
1188
|
+
| `engine` | string | auto | Force `'chatterbox'`, `'supertonic'`, `'cosyvoice3'`, `'parler'`, `'audio8'`, `'moss'`, `'moss-sfx'` or `'moss-speech'` (`TTSGgml.ENGINE_CHATTERBOX` / `ENGINE_SUPERTONIC` / `ENGINE_COSYVOICE3` / `ENGINE_PARLER` / `ENGINE_AUDIO8` / `ENGINE_MOSS` / `ENGINE_MOSS_SFX` / `ENGINE_MOSS_SPEECH`); auto-detected from the GGUFs present otherwise |
|
|
1042
1189
|
| `referenceAudio` | string | — | Wav to clone (Chatterbox: mono, ≥ 5 s; CosyVoice3: 0.5-30 s, multichannel downmixed to mono, needs the s3tok + campplus GGUFs; Audio8: also needs `referenceText`; MOSS: needs `files.mossCodecEncoder`). Audio8 accepts it per call too |
|
|
1043
1190
|
| `referenceText` | string | — | Audio8-only: what `referenceAudio` says, verbatim. Required whenever a reference is set; accepted per call |
|
|
1044
1191
|
| `dialogueReferences` | string[] | — | MOSS-only: one 24 kHz recording per speaker (`[S1]`, `[S2]`, ...) for dialogue synthesis; the text must open with each recording's transcript under its tag; needs `files.mossCodecEncoder`, excludes `referenceAudio`, fixed per instance |
|
|
1045
1192
|
| `durationTokens` | number | `0` | MOSS-only: target length in codec frames (12.5 per second); `0` keeps the length free; reloadable |
|
|
1046
1193
|
| `voiceDir` | string | — | Pre-baked voice profile |
|
|
1047
|
-
| `seed` | number | 42 | RNG seed (CFM noise + sampling); MOSS defaults to its engine's 1234 |
|
|
1194
|
+
| `seed` | number | 42 | RNG seed (CFM noise + sampling); MOSS defaults to its engine's 1234, MOSS-SoundEffect and MOSS-Speech to 0, so an unseeded prompt gives the same clip or reply every call |
|
|
1048
1195
|
| `nGpuLayers` | number | 0 | Layers offloaded to GPU (mirrors `useGPU`; pass `99` to offload all) |
|
|
1049
1196
|
| `nCtx` | number | 4096 | Chatterbox T3 context limit for the prompt plus generated speech tokens (25 tokens ≈ 1 s of audio). The KV cache is allocated up front at this length, so the 4096-token default directly bounds memory. Pass `0` to use the GGUF metadata value |
|
|
1050
1197
|
| `kvCacheType` | string | `f16` | T3 KV-cache dtype: `f32` \| `f16` \| `q8_0`. `f16` (~50% of f32) is the safe cross-backend default. `q8_0` stores the cache at ~27% of f32 and decodes 20-30% faster on Metal, but only works on backends with a q8_0 CONT op (CPU, CUDA) — it hard-aborts the multilingual model on Metal, so it is opt-in. Turbo greedy decoding is byte-identical across all three (upstream-validated). Pass `f32` for bit-exact pre-quantisation behaviour |
|
|
@@ -1065,7 +1212,7 @@ runtime find them.
|
|
|
1065
1212
|
| `voice` / `voiceName` | string | — | Supertonic voice id (e.g. `'F1'`, `'M1'`); Parler template speaker name (e.g. `'Laura'`, `'Rohit'`) |
|
|
1066
1213
|
| `voiceJsonPath` | string | — | Supertonic-only: external voice JSON (`{ style_ttl, style_dp }`, e.g. a cloned voice) that overrides `voice`; the engine checks the tensor sizes against the model |
|
|
1067
1214
|
| `prewarmText` | string | — | Supertonic-only: text synthesized once at load so GPU pipelines compile, and a Core ML vocoder sidecar specializes, before the first `run()` (skipped on a plain CPU run); use a representative length. Wins over the `vulkanCacheDir` default pre-warm |
|
|
1068
|
-
| `vulkanDevice` | number | 0 | Supertonic / CosyVoice3 Vulkan adapter, also used by their LavaSR enhancer: `0` = first, `N` = the Nth, `-1` = auto-pick by free VRAM preferring a discrete GPU |
|
|
1215
|
+
| `vulkanDevice` | number | 0 | Supertonic / CosyVoice3 Vulkan adapter, also used by their LavaSR enhancer: `0` = first, `N` = the Nth, `-1` = auto-pick by free VRAM preferring a discrete GPU. Audio8 always auto-picks |
|
|
1069
1216
|
| `flowCutPrompt` | boolean | `false` | CosyVoice3-only: attention-only prompt frames in the flow — faster, with output that deviates slightly from the reference |
|
|
1070
1217
|
| `steps` / `numInferenceSteps` | number | GGUF default | Supertonic vector-estimator CFM steps (`0` = GGUF default) |
|
|
1071
1218
|
| `noiseNpyPath` | string | — | Supertonic: optional fixed CFM noise `.npy` for reproducibility |
|
|
@@ -1085,8 +1232,8 @@ runtime find them.
|
|
|
1085
1232
|
| `openclCacheDir` | string | unset | Android-only: directory where the OpenCL backend persists its compiled program-binary cache. Setting it across runs avoids re-JITing the kernels on every fresh process |
|
|
1086
1233
|
| `vulkanCacheDir` | string | unset | Supertonic + `useGPU: true` only: writable directory where the Vulkan backend persists its compiled pipeline cache (`GGML_VK_PIPELINE_CACHE_DIR`). Moves the one-time first-dispatch pipeline-compile cost (seconds on Mali) off the first `run()` — paid once per install instead of once per process — and enables a load-time pre-warm. Fully opt-in: unset -> no cross-process cache, no pre-warm, behaviour unchanged |
|
|
1087
1234
|
| `config.language` | string | `"en"` | Chatterbox MTL accepts `es/fr/de/pt/it/zh/ja/ko/...`; turbo & Supertonic are English; MOSS takes it as a prompt hint (`en`, `zh`, ...) |
|
|
1088
|
-
| `config.useGPU` | boolean | `false` | Set to `true` to route through Metal / CUDA / Vulkan / OpenCL if available. Honored for Chatterbox/Supertonic on GPU-capable hosts (including Android, per `tts-cpp`'s per-vendor allowlist); Parler is validated on Apple/Metal, linux CUDA, and Android/ARM Mali Vulkan; CosyVoice3 and Audio8 offload on Apple/Metal, desktop linux CUDA/Vulkan, Windows Vulkan, and Android OpenCL/Adreno; MOSS GPU offload is validated on Apple/Metal. Unsupported backends fall back to CPU. See [Backends & GPU acceleration](#backends--gpu-acceleration) |
|
|
1089
|
-
| `config.outputSampleRate` | number | — (engine-native) | Resample the output to this rate (8000–192000 Hz). Omit to keep the engine-native rate (Chatterbox 24 kHz, Supertonic / Parler / Audio8 44.1 kHz, CosyVoice3 24 kHz, MOSS 24 kHz, enhancer 48 kHz). Parler native chunk streaming accepts a non-native rate only with the enhancer active; MOSS
|
|
1235
|
+
| `config.useGPU` | boolean | `false` | Set to `true` to route through Metal / CUDA / Vulkan / OpenCL if available. Honored for Chatterbox/Supertonic on GPU-capable hosts (including Android, per `tts-cpp`'s per-vendor allowlist); Parler is validated on Apple/Metal, linux CUDA, and Android/ARM Mali Vulkan; CosyVoice3 and Audio8 offload on Apple/Metal, desktop linux CUDA/Vulkan, Windows Vulkan, and Android OpenCL/Adreno; MOSS, MOSS-SoundEffect and MOSS-Speech GPU offload is validated on Apple/Metal. Unsupported backends fall back to CPU. See [Backends & GPU acceleration](#backends--gpu-acceleration) |
|
|
1236
|
+
| `config.outputSampleRate` | number | — (engine-native) | Resample the output to this rate (8000–192000 Hz). Omit to keep the engine-native rate (Chatterbox 24 kHz, Supertonic / Parler / Audio8 44.1 kHz, CosyVoice3 24 kHz, MOSS 24 kHz, MOSS-SoundEffect 48 kHz, MOSS-Speech 24 kHz, enhancer 48 kHz). Parler native chunk streaming accepts a non-native rate only with the enhancer active; MOSS, MOSS-SoundEffect and MOSS-Speech accept only their native rate |
|
|
1090
1237
|
| `opts.stats` | boolean | `false` | Populate `response.stats` with RTF, `backendDevice` (0=CPU, 1=GPU), `backendId` (0=CPU, 1=Metal, 2=CUDA, 3=Vulkan, 4=OpenCL, 99=other), `enhancerBackendDevice` / `enhancerBackendId` and `denoiserBackendDevice` / `denoiserBackendId` (`-1` when that LavaSR stage is off), and the engine's own stage stats: Chatterbox `t3Ms` / `s3genMs` / `t3Tokens`; CosyVoice3 per-stage ms (`lmPrefillMs` … `hiftDecodeMs`, `stageTotalMs`) and work counters (`speechTokens`, `decodeSteps`, …); Audio8 per-stage ms (`prefillMs`, `fastDecodeMs`, `codecSynthMs`, …, `stageTotalMs`) |
|
|
1091
1238
|
| `exclusiveRun` | boolean | `false` | **Top-level** option (not under `opts`): serialize overlapping streaming runs |
|
|
1092
1239
|
|
|
@@ -1103,6 +1250,14 @@ runtime find them.
|
|
|
1103
1250
|
- `TTSGgml.getVoiceControls()` — static: tts-cpp's emotion / pace vocabulary
|
|
1104
1251
|
and each engine's supported subset (see [Emotion & pace](#emotion--pace-cross-engine)).
|
|
1105
1252
|
- `model.run({ input, type: 'text' })` → `QvacResponse`.
|
|
1253
|
+
- `model.run({ input, seconds, negativePrompt, steps, guidance, shift })` →
|
|
1254
|
+
moss-sfx: one sound effect of `seconds` length; the controls are optional
|
|
1255
|
+
and moss-sfx-only (see [MOSS-SoundEffect](#moss-soundeffect)).
|
|
1256
|
+
- `model.run({ input, audio, sampleRate, messages, systemPrompt, replyVoice,
|
|
1257
|
+
replyVoiceSampleRate, maxReplySeconds, maxNewTokens, textReply, greedy,
|
|
1258
|
+
temperature, topP, topK })` → moss-speech: one spoken (or text) reply with
|
|
1259
|
+
its text in `data.text`; the fields are moss-speech-only (see
|
|
1260
|
+
[MOSS-Speech](#moss-speech)).
|
|
1106
1261
|
- `model.run({ input, streamOutput: true })` → sentence-chunked
|
|
1107
1262
|
synthesis driven by the JS-side sentence splitter (see
|
|
1108
1263
|
`lib/textChunker.js`). Equivalent to `runStream(input)`.
|
|
@@ -1128,7 +1283,8 @@ All `run*` methods return a `QvacResponse` (from `@qvac/infer-base`):
|
|
|
1128
1283
|
```js
|
|
1129
1284
|
response.onUpdate(data => {
|
|
1130
1285
|
data.outputArray // Int16Array — mono PCM
|
|
1131
|
-
data.sampleRate // actual rate: Chatterbox/CosyVoice3/MOSS 24000, Supertonic/Parler/Audio8 44100, enhancer 48000
|
|
1286
|
+
data.sampleRate // actual rate: Chatterbox/CosyVoice3/MOSS/MOSS-Speech 24000, Supertonic/Parler/Audio8 44100, MOSS-SoundEffect and enhancer 48000
|
|
1287
|
+
data.text // MOSS-Speech only: the reply's text
|
|
1132
1288
|
data.chunkIndex // present on sentence-streaming events only
|
|
1133
1289
|
data.sentenceChunk // present on sentence-streaming events only
|
|
1134
1290
|
})
|
|
@@ -1228,6 +1384,8 @@ fit.report
|
|
|
1228
1384
|
| `chatterbox` | `t3ModelPath`, `s3genModelPath` | `textTokens`, `predictTokens` |
|
|
1229
1385
|
| `audio8` | `audio8LmPath`, `audio8CodecDecoderPath`, `audio8CodecEncoderPath` | `promptTokens`, `maxFrames`, `referenceSeconds` |
|
|
1230
1386
|
| `cosyvoice3` | `cosyvoiceLlmModelPath`, `cosyvoiceFlowModelPath`, `cosyvoiceHiftModelPath`, `cosyvoiceVoiceModelPath` | `textTokens`, `speechTokens` |
|
|
1387
|
+
| `moss` | `mossBackbonePath`, `mossCodecDecoderPath`, optional `mossCodecEncoderPath` | Required `promptRows`, `referenceSamples`, `streaming`; load controls `durationTokens`, `streamChunkTokens`, `threads` |
|
|
1388
|
+
| `moss-sfx` | Required `mossSoundEffectPath` | Required `prompt`, `seconds`; optional `negativePrompt`, `steps`, `guidance`, `shift`, `threads` |
|
|
1231
1389
|
|
|
1232
1390
|
Everything else comes from the load config: `nGpuLayers` and `useGPU` carry the offload intent, `nCtx` and `kvCacheType` size the chatterbox cache, `steps` takes the GGUF's own default at 0, and `vulkanDevice` and `backendsDir` place the backend. Supplying `audio8CodecEncoderPath` projects voice cloning, which the decoder alone cannot do. `marginBytes` sets the free memory that must remain for the projection to count as fitting.
|
|
1233
1391
|
|
|
@@ -1235,10 +1393,60 @@ Everything else comes from the load config: `nGpuLayers` and `useGPU` carry the
|
|
|
1235
1393
|
|
|
1236
1394
|
`deviceSharesHostMemory` reports that the device pool is system RAM, so host bytes compete with device bytes.
|
|
1237
1395
|
|
|
1238
|
-
A model the engine cannot read is `status: "error"`, as is a voice with no fitter, which reports `reason: "unsupported-engine"`. MOSS
|
|
1396
|
+
A model the engine cannot read is `status: "error"`, as is a voice with no fitter, which reports `reason: "unsupported-engine"`. MOSS-TTS, MOSS-TTSD and MOSS-SoundEffect are supported. MOSS-Speech has no SDK fit projection. A broken request, or a host with no native binding, throws.
|
|
1397
|
+
|
|
1398
|
+
MOSS-TTS and MOSS-TTSD share the `moss` fitter. `promptRows` is the complete
|
|
1399
|
+
native prompt length, including special tokens, encoded speaker references and
|
|
1400
|
+
TTSD continuation rows. `referenceSamples` is the total mono reference sample
|
|
1401
|
+
count (0 without a reference); a positive count requires the encoder path.
|
|
1402
|
+
`streaming` explicitly selects native chunk streaming or batch synthesis.
|
|
1403
|
+
The fitter does not open reference recordings. Use the TTSD backbone to assess
|
|
1404
|
+
dialogue, supplying its complete prompt and total reference workload.
|
|
1405
|
+
|
|
1406
|
+
```js
|
|
1407
|
+
const fit = TTSGgml.assessFit({
|
|
1408
|
+
engineType: 'moss',
|
|
1409
|
+
mossBackbonePath: './moss-tts-delay-f16.gguf',
|
|
1410
|
+
mossCodecDecoderPath: './moss-codec-decoder-f16.gguf',
|
|
1411
|
+
promptRows: 128,
|
|
1412
|
+
referenceSamples: 0,
|
|
1413
|
+
streaming: false,
|
|
1414
|
+
useGPU: true
|
|
1415
|
+
})
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
The result includes LM weights and KV state, codec weights and compute, streaming
|
|
1419
|
+
caches when applicable, and host staging/audio memory. It sums component peaks
|
|
1420
|
+
as a conservative upper bound, including the reference encoder when requested;
|
|
1421
|
+
this may reject a workload whose actual peak is smaller. The fit follows the
|
|
1422
|
+
load's generation settings and does not change synthesis behavior.
|
|
1239
1423
|
|
|
1240
1424
|
The supertonic fitter covers the fused graph path: a validated GPU, or a CPU without the Accelerate pointwise kernels. Elsewhere it answers `compute-path-not-supported` and projects nothing.
|
|
1241
1425
|
|
|
1426
|
+
### MOSS-SoundEffect fit
|
|
1427
|
+
|
|
1428
|
+
```js
|
|
1429
|
+
const fit = TTSGgml.assessFit({
|
|
1430
|
+
engineType: 'moss-sfx',
|
|
1431
|
+
mossSoundEffectPath: './moss-sfx-v2-q8_0.gguf',
|
|
1432
|
+
prompt: 'Rain falling on a tin roof.',
|
|
1433
|
+
seconds: 8,
|
|
1434
|
+
useGPU: false
|
|
1435
|
+
})
|
|
1436
|
+
console.log(fit.report)
|
|
1437
|
+
```
|
|
1438
|
+
|
|
1439
|
+
The fitter uses generation's tokenizer, request validation and graph builders,
|
|
1440
|
+
without loading weights or generating audio. It supports full and metadata-only
|
|
1441
|
+
GGUF files. `negativePrompt`, `steps`, `guidance` and `shift` keep their generation
|
|
1442
|
+
semantics; `marginBytes` keeps the shared 256 MiB default. DiT processes the model's
|
|
1443
|
+
full latent duration even for a short output. Requested seconds size decoder
|
|
1444
|
+
windows and audio buffers. The shared compute arena is counted at its peak:
|
|
1445
|
+
`lmComputeBytes` covers text/DiT, `codecComputeBytes` any additional VAE demand.
|
|
1446
|
+
Host memory includes conditioning, temporary arrays and CPU fallback buffers.
|
|
1447
|
+
|
|
1448
|
+
Run `bare examples/moss-sfx-fit.js ./moss-sfx-v2-q8_0.gguf "Rain on a roof." 8`.
|
|
1449
|
+
|
|
1242
1450
|
## Examples
|
|
1243
1451
|
|
|
1244
1452
|
Runnable demos under `examples/`:
|
|
@@ -1264,16 +1472,25 @@ Runnable demos under `examples/`:
|
|
|
1264
1472
|
| `cosyvoice-enhanced.js` | CosyVoice3 + LavaSR 48 kHz enhancement (add `--denoise` for the denoiser). `bare examples/cosyvoice-enhanced.js "Hello"` |
|
|
1265
1473
|
| `audio8-tts.js` | Audio8 batch synth, optionally cloning a reference. Set `QVAC_TTS_AUDIO8_GPU=1` for the desktop GPU backend (CUDA or Vulkan on linux, Vulkan on Windows). `bare examples/audio8-tts.js "Hello" voice.wav "What it says."` |
|
|
1266
1474
|
| `moss-tts.js` | MOSS batch synth, optionally cloning a reference. Set `QVAC_TTS_MOSS_STREAM_FRAMES=25` for native chunk streaming, `QVAC_TTS_MOSS_DURATION=38` for a target length and `QVAC_TTS_MOSS_GPU=1` for the GPU backend. `bare examples/moss-tts.js "Hello" voice.wav` |
|
|
1475
|
+
| `moss-sound-effect.js` | MOSS-SoundEffect text-to-sound-effects at 48 kHz. Set `QVAC_TTS_MOSS_SFX_GPU=1` for the GPU backend and `QVAC_TTS_MOSS_SFX_STEPS` to trade quality for time. `bare examples/moss-sound-effect.js "Rain on a tin roof." 5` |
|
|
1476
|
+
| `moss-speech.js` | MOSS-Speech speech-to-speech: answers a spoken question WAV with a 24 kHz spoken reply (optionally in the voice of a second WAV). Set `QVAC_TTS_MOSS_SPEECH_GPU=1` for the GPU backend and `QVAC_TTS_MOSS_SPEECH_TEXT=1` for a text-only answer. `bare examples/moss-speech.js test/reference-audio/jfk.wav` |
|
|
1267
1477
|
| `moss-dialogue-tts.js` | MOSS-TTSD multi-speaker dialogue from one 24 kHz reference per speaker; the text opens with each reference's transcript. Set `QVAC_TTS_MOSS_GPU=1` for the GPU backend. `bare examples/moss-dialogue-tts.js "[S1] What alice.wav says. [S2] What bob.wav says. [S1] Hi. [S2] Hello." alice.wav bob.wav` |
|
|
1268
1478
|
|
|
1269
|
-
The
|
|
1479
|
+
The streaming examples feed PCM into a single long-running
|
|
1270
1480
|
`sox play` / `ffplay` process so chunks play back-to-back without any
|
|
1271
|
-
per-chunk spawn gaps
|
|
1272
|
-
`brew install ffmpeg` on macOS)
|
|
1273
|
-
|
|
1481
|
+
per-chunk spawn gaps. Playback needs one of those players (`brew install sox`
|
|
1482
|
+
or `brew install ffmpeg` on macOS) and `bare-subprocess`, a development
|
|
1483
|
+
dependency: run `npm install bare-subprocess` when you run the examples from
|
|
1484
|
+
an installed package. Without them the demos still run and write the
|
|
1485
|
+
concatenated wav.
|
|
1274
1486
|
|
|
1275
1487
|
## Testing
|
|
1276
1488
|
|
|
1489
|
+
The consolidated C++ CI lane uses persistent vcpkg binaries, a package-specific
|
|
1490
|
+
compiler cache, and two build workers. The deterministic C++ tier remains a
|
|
1491
|
+
required gate. See [C++ CI configuration](../../docs/ci/nx-ci-consolidation.md#optionsci-cheat-sheet)
|
|
1492
|
+
for cache warming and resource settings.
|
|
1493
|
+
|
|
1277
1494
|
```bash
|
|
1278
1495
|
npm run test:unit # mocked binding; fast
|
|
1279
1496
|
npm run test:integration # spins up the real engine; needs models
|
|
@@ -1311,12 +1528,39 @@ Parler tests need Metal, so off Apple they are skipped too.
|
|
|
1311
1528
|
The MOSS integration test (`moss.test.js`) does not download its GGUFs:
|
|
1312
1529
|
set `QVAC_TEST_MOSS_MODEL_DIR` to a directory holding them, otherwise it is
|
|
1313
1530
|
skipped. The C++ suite runs its MOSS synthesis cases when
|
|
1314
|
-
`QVAC_TEST_MOSS_BACKBONE_GGUF` and `QVAC_TEST_MOSS_DECODER_GGUF` are set.
|
|
1531
|
+
`QVAC_TEST_MOSS_BACKBONE_GGUF` and `QVAC_TEST_MOSS_DECODER_GGUF` are set. The
|
|
1532
|
+
MOSS-SoundEffect integration test (`moss-sfx.test.js`) likewise needs
|
|
1533
|
+
`QVAC_TEST_MOSS_SFX_MODEL_DIR`, and the C++ suite generates a short clip when
|
|
1534
|
+
`QVAC_TEST_MOSS_SFX_GGUF` is set. The MOSS-Speech integration test
|
|
1535
|
+
(`moss-speech.test.js`) needs `QVAC_TEST_MOSS_SPEECH_MODEL_DIR` (the model and
|
|
1536
|
+
its codec), and the C++ suite answers a short spoken turn when
|
|
1537
|
+
`QVAC_TEST_MOSS_SPEECH_GGUF` and `QVAC_TEST_MOSS_SPEECH_CODEC_GGUF` are set.
|
|
1315
1538
|
|
|
1316
1539
|
To stress-test long inputs, set `INPUT_SENTENCES=medium` (or `long`)
|
|
1317
1540
|
and re-run the integration suite — `addon.test.js` reads the env var to
|
|
1318
1541
|
pick its sentence corpus from `test/data/sentences-{medium,long}.js`.
|
|
1319
1542
|
|
|
1543
|
+
### Fuzzing
|
|
1544
|
+
|
|
1545
|
+
The JS-adapter config string parsers have a [Google FuzzTest][fuzztest]
|
|
1546
|
+
target, `tts-config-parse-fuzz`. Fuzzing is Linux-only: it needs clang with
|
|
1547
|
+
libFuzzer and AddressSanitizer, plus the [build-from-source](#build-from-source)
|
|
1548
|
+
prerequisites. The target does not link tts-cpp, so it runs with full ASan and
|
|
1549
|
+
LeakSanitizer.
|
|
1550
|
+
|
|
1551
|
+
```bash
|
|
1552
|
+
npm run fuzz # bounded run of every FUZZ_TEST, as Linux CI does
|
|
1553
|
+
npm run fuzz:continuous # coverage-guided, one FUZZ_TEST at a time
|
|
1554
|
+
npm run fuzz:continuous -- TtsConfigParseFuzz.ParseFloatNeverCrashes --fuzz_for=30m
|
|
1555
|
+
```
|
|
1556
|
+
|
|
1557
|
+
Flags other than `--continuous` and `--build-dir` go to the fuzz binary. See
|
|
1558
|
+
[`docs/architecture/ADDON-FUZZING.md`](../../docs/architecture/ADDON-FUZZING.md)
|
|
1559
|
+
for the fuzzing design, the vcpkg-supplied FuzzTest stack, and how to add a
|
|
1560
|
+
target.
|
|
1561
|
+
|
|
1562
|
+
[fuzztest]: https://github.com/google/fuzztest
|
|
1563
|
+
|
|
1320
1564
|
## Build from source
|
|
1321
1565
|
|
|
1322
1566
|
Prerequisites: `clang` with C++20 support, CMake ≥ 3.25,
|
|
@@ -1335,6 +1579,14 @@ The vcpkg port is hosted in
|
|
|
1335
1579
|
[`vcpkg-configuration.json`](./vcpkg-configuration.json) for the
|
|
1336
1580
|
baseline commit.
|
|
1337
1581
|
|
|
1582
|
+
`ggml-speech` and `speech-cpp` are pinned with vcpkg `overrides` to the
|
|
1583
|
+
same versions in every speech addon (`@qvac/asr-ggml`,
|
|
1584
|
+
`@qvac/audiogen-ggml`, `@qvac/bci-whispercpp`, `@qvac/tts-ggml`), and
|
|
1585
|
+
`package.json` records the `ggml-speech` build as `qvac["ggml-speech"]`.
|
|
1586
|
+
The speech addons ship their ggml backends under the same file names, so
|
|
1587
|
+
an app that bundles several of them keeps one copy of each: use releases
|
|
1588
|
+
that record the same `ggml-speech`.
|
|
1589
|
+
|
|
1338
1590
|
GPU backends are controlled by the `speech-cpp` port's vcpkg features:
|
|
1339
1591
|
`metal` (default on osx/ios), `vulkan` (default on
|
|
1340
1592
|
linux/windows/android), `opencl` (default on android), and `cuda`
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const process = require('bare-process')
|
|
4
|
+
const TTSGgml = require('@qvac/tts-ggml')
|
|
5
|
+
|
|
6
|
+
const [, , model, prompt, seconds] = process.argv
|
|
7
|
+
if (!model || !prompt || seconds === undefined) {
|
|
8
|
+
throw new Error('Usage: bare examples/moss-sfx-fit.js <model.gguf> <prompt> <seconds>')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const fit = TTSGgml.assessFit({
|
|
12
|
+
engineType: 'moss-sfx',
|
|
13
|
+
mossSoundEffectPath: model,
|
|
14
|
+
prompt,
|
|
15
|
+
seconds: Number(seconds),
|
|
16
|
+
useGPU: false
|
|
17
|
+
})
|
|
18
|
+
console.log(fit.report || `${fit.status}: ${fit.reason}`)
|