@qvac/tts-ggml 0.14.1 → 0.15.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.
- package/README.md +227 -39
- package/docs/pocket-tts.md +32 -4
- package/examples/fabric-controls.js +71 -0
- package/examples/lavasr-coreml.js +76 -0
- package/examples/moss-sfx-fit.js +1 -1
- package/index.d.ts +6 -0
- package/index.js +24 -2
- package/lib/engineParams.js +14 -0
- package/lib/engineSetup.d.ts +8 -1
- package/lib/engines/audio8.js +1 -1
- package/lib/engines/moss.d.ts +1 -1
- package/lib/engines/moss.js +8 -7
- package/lib/engines/parler.d.ts +13 -0
- package/lib/engines/parler.js +54 -1
- package/lib/fabricOptions.d.ts +29 -0
- package/lib/fabricOptions.js +133 -0
- package/lib/fit.d.ts +46 -36
- package/lib/fit.js +5 -3
- package/lib/jobFields.js +1 -0
- package/lib/nativeParams.js +12 -0
- package/lib/pocketConfig.js +27 -4
- package/lib/pocketLifecycle.js +9 -1
- package/lib/reload.js +19 -0
- package/lib/sentenceStream.d.ts +15 -3
- package/lib/sentenceStream.js +49 -5
- package/lib/setup.js +38 -2
- package/lib/types.d.ts +34 -13
- package/package.json +12 -11
- package/test/integration/fabric-controls.test.js +124 -0
- package/test/integration/fit.test.js +67 -77
- package/test/integration/moss-fit.test.js +7 -7
- package/test/integration/moss-sfx-fit.test.js +7 -20
- package/test/integration/native-options.test.js +41 -0
- package/tts.d.ts +1 -1
package/README.md
CHANGED
|
@@ -29,8 +29,27 @@ The LM, flow frontend/decoder, F0 predictor and HiFT vocoder use resident
|
|
|
29
29
|
sidecars when present and fall back to ggml when unavailable. HiFT exports
|
|
30
30
|
use FP32 for waveform parity. Core ML chooses operation placement, so a
|
|
31
31
|
sidecar does not guarantee ANE execution or a speed improvement.
|
|
32
|
-
When a sidecar is present, `assessFit`
|
|
33
|
-
|
|
32
|
+
When a sidecar is present, `assessFit` answers `unknown`, since the sidecar's
|
|
33
|
+
memory usage is not projected.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Chatterbox Core ML sidecars
|
|
37
|
+
|
|
38
|
+
Apple builds support optional Chatterbox Turbo and Multilingual Core ML
|
|
39
|
+
sidecars for the S3Gen encoder, CFM estimator and HiFT vocoder through the
|
|
40
|
+
native `speech-cpp` 2026-10-30 dependency. Export and placement details are in
|
|
41
|
+
the [Chatterbox Core ML guide](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/9ed06a668923b1ea3f68ad28614228aa1156223e/engines/tts/docs/chatterbox.md#core-ml-sidecars).
|
|
42
|
+
|
|
43
|
+
The three stages run on the Neural Engine when their sidecars sit next to the
|
|
44
|
+
S3Gen GGUF and fall back to ggml when unavailable; T3 keeps the GPU. Set
|
|
45
|
+
`maxSentenceChars` (the reference CLI uses 180) so S3Gen vocodes each sentence
|
|
46
|
+
on the Neural Engine while T3 decodes the next one: on an Apple M2 a
|
|
47
|
+
three-sentence prompt then synthesizes in 31% (Turbo) and 33% (Multilingual)
|
|
48
|
+
less time than on Metal. Without a split there is nothing to overlap, so a call
|
|
49
|
+
gains only the S3Gen time. The first use of each sidecar function by an
|
|
50
|
+
executable compiles it for the Neural Engine (3 to 26 s per function on an M2;
|
|
51
|
+
the OS caches the result). When a sidecar is present, `assessFit` answers
|
|
52
|
+
`unknown`, since the sidecar's memory usage is not projected.
|
|
34
53
|
|
|
35
54
|
|
|
36
55
|
Runs in-process with a persistent native engine — the GGUFs, the S3Gen
|
|
@@ -67,6 +86,7 @@ and registry prerequisites described under [MOSS](#moss).
|
|
|
67
86
|
`tts-cpp` selects the GPU backend per its per-vendor allowlist (see
|
|
68
87
|
[Backends & GPU acceleration](#backends--gpu-acceleration)).
|
|
69
88
|
- **Apple Core ML sidecars** for the Supertonic vocoder, the Audio8 codec,
|
|
89
|
+
the Chatterbox S3Gen encoder, CFM and HiFT,
|
|
70
90
|
and one stage each of MOSS, MOSS-SoundEffect, and MOSS-Speech on macOS /
|
|
71
91
|
iOS, picked up when staged next to the model (see
|
|
72
92
|
[Core ML sidecars on Apple](#core-ml-sidecars-on-apple)).
|
|
@@ -204,10 +224,13 @@ under `models/`:
|
|
|
204
224
|
# Chatterbox turbo (English)
|
|
205
225
|
chatterbox-t3-turbo.gguf (~742 MB) — T3 GPT-2 Medium + BPE + VoiceEncoder
|
|
206
226
|
chatterbox-s3gen.gguf (~1.0 GB) — S3Gen encoder/CFM + HiFT + CAMPPlus + S3TokenizerV2
|
|
227
|
+
chatterbox-s3gen-{encoder,cfm,hift}.mlmodelc/ (optional, macOS / iOS: Apple Core ML
|
|
228
|
+
sidecars for the S3Gen stages; see Core ML sidecars on Apple)
|
|
207
229
|
|
|
208
230
|
# Chatterbox multilingual (en/es/fr/de/pt/it/zh/ja/ko/...)
|
|
209
231
|
chatterbox-t3-mtl.gguf (~1.0 GB)
|
|
210
232
|
chatterbox-s3gen-mtl.gguf (~1.0 GB)
|
|
233
|
+
chatterbox-s3gen-mtl-{encoder,cfm,hift}.mlmodelc/ (optional, macOS / iOS)
|
|
211
234
|
|
|
212
235
|
# Supertonic 3 (Supertone/supertonic-3; 31 languages) — preferred Supertonic
|
|
213
236
|
# checkpoint; published per quant tier (auto-detected from modelDir)
|
|
@@ -577,7 +600,7 @@ accepting a new one, so changing voices means constructing a new instance.
|
|
|
577
600
|
|
|
578
601
|
Opt-in neural post-processing that bandwidth-extends the synthesized audio to
|
|
579
602
|
**48 kHz** with a synthesised high band, using the LavaSR Vocos enhancer
|
|
580
|
-
(ConvNeXt backbone + ISTFT spec head) converted to a single GGUF.
|
|
603
|
+
(ConvNeXt backbone + ISTFT spec head) converted to a single GGUF. Without an explicit Core ML selection, it follows the
|
|
581
604
|
engine's GPU intent: with `config.useGPU: true` (or `nGpuLayers`) the enhancer
|
|
582
605
|
runs on the GPU (Vulkan on Linux/Windows, Metal on macOS/iOS) and falls back to
|
|
583
606
|
CPU otherwise. It is fully backward compatible — provide no enhancer GGUF and
|
|
@@ -698,8 +721,59 @@ Notes:
|
|
|
698
721
|
(scalar CPU port, validated bit-close to the ONNX reference); it requires a
|
|
699
722
|
`tts-cpp` build that includes that port — see the pinned version in `vcpkg.json`.
|
|
700
723
|
|
|
724
|
+
|
|
725
|
+
### LavaSR Core ML
|
|
726
|
+
|
|
727
|
+
On Apple platforms, either stage can use a compiled Core ML model explicitly.
|
|
728
|
+
Provide `coremlModelPath` and `coremlComputeUnits` together inside `enhancer`
|
|
729
|
+
or `denoiser`, alongside the corresponding GGUF path (or its `files` alias).
|
|
730
|
+
|
|
731
|
+
```js
|
|
732
|
+
const model = new TTSGgml({
|
|
733
|
+
engine: TTSGgml.ENGINE_SUPERTONIC,
|
|
734
|
+
files: { supertonicModel: 'models/supertonic.gguf' },
|
|
735
|
+
enhancer: {
|
|
736
|
+
type: 'lavasr',
|
|
737
|
+
enhancerPath: 'models/lavasr-enhancer.gguf',
|
|
738
|
+
coremlModelPath: 'models/enhancer.mlmodelc',
|
|
739
|
+
coremlComputeUnits: 'CPU_AND_NE'
|
|
740
|
+
},
|
|
741
|
+
denoiser: {
|
|
742
|
+
type: 'lavasr',
|
|
743
|
+
denoiserPath: 'models/lavasr-denoiser.gguf',
|
|
744
|
+
coremlModelPath: 'models/denoiser.mlmodelc',
|
|
745
|
+
coremlComputeUnits: 'CPU_AND_NE'
|
|
746
|
+
}
|
|
747
|
+
})
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
The accepted policies are `CPU_ONLY`, `CPU_AND_GPU`, `CPU_AND_NE`, and `ALL`.
|
|
751
|
+
Each stage has its own policy; it does not change the synthesis engine's
|
|
752
|
+
`config.useGPU`. Models stay loaded with the instance. Omitting both options
|
|
753
|
+
keeps the existing ggml/scalar selection. An invalid, missing, incompatible,
|
|
754
|
+
or unsupported explicitly selected Core ML model fails loading; it does not
|
|
755
|
+
fall back to ggml. Existing streaming restrictions still apply.
|
|
756
|
+
|
|
757
|
+
Core ML policies select eligible devices; they do not guarantee that every
|
|
758
|
+
operation runs on ANE or GPU. Stage stats report device `2` for Core ML-managed
|
|
759
|
+
placement and backend id `99` (other), rather than claiming CPU or GPU.
|
|
760
|
+
|
|
761
|
+
Export matching models with the native Fabric
|
|
762
|
+
[`export-lavasr-coreml.py`](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/8d9c6247ac18ff912bb3e1269da5f3fd679e77aa/engines/tts/scripts/export-lavasr-coreml.py)
|
|
763
|
+
script; precision is explicit (`float32` or `float16`). See the
|
|
764
|
+
[native LavaSR guide](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/8d9c6247ac18ff912bb3e1269da5f3fd679e77aa/engines/tts/docs/lavasr.md#core-ml)
|
|
765
|
+
for export and compilation commands. A runnable addon example is
|
|
766
|
+
[`examples/lavasr-coreml.js`](examples/lavasr-coreml.js).
|
|
767
|
+
|
|
701
768
|
## Backends & GPU acceleration
|
|
702
769
|
|
|
770
|
+
`config.backend` selects the native backend explicitly. Accepted selectors
|
|
771
|
+
include `auto`, `cpu`, `opencl`, `webgpu`, and exact registered
|
|
772
|
+
device names such as `MTL0`, `CUDA0`, `Vulkan0`. Exact device names
|
|
773
|
+
depend on the host. Native selection reports an unavailable explicit backend
|
|
774
|
+
as an error; omitting this option preserves the existing `useGPU` policy.
|
|
775
|
+
The same selector is accepted as top-level `backend` by `assessFit()`.
|
|
776
|
+
|
|
703
777
|
The addon delegates backend selection to `tts-cpp`'s registry-only
|
|
704
778
|
init path. At `load()` time the engine walks the ggml-backend registry
|
|
705
779
|
once and picks the first available accelerator that matches the
|
|
@@ -719,11 +793,67 @@ On hosts where more than one backend is usable, `TTS_CPP_GPU_BACKEND`
|
|
|
719
793
|
and fails loudly when that backend cannot be resolved; unset (or empty)
|
|
720
794
|
keeps the automatic preference above.
|
|
721
795
|
|
|
796
|
+
### Optional WebGPU builds
|
|
797
|
+
|
|
798
|
+
Build with `bare-make generate -D TTS_WEBGPU=ON`, followed by
|
|
799
|
+
`bare-make build`, `bare-make install`, and `npm run link:platform` to include
|
|
800
|
+
and load the local Dawn backend.
|
|
801
|
+
WebGPU is exposed only for the native library's established model families:
|
|
802
|
+
Supertonic **3**, Parler, CosyVoice3 and Audio8. Explicit WebGPU requests
|
|
803
|
+
for other families, including Supertonic 1/2, fail before loading weights.
|
|
804
|
+
The manifest pins the compatible Dawn version; default builds stay unchanged.
|
|
805
|
+
|
|
806
|
+
Hexagon is not included in JavaScript package builds. Stats add `backendId: 5`
|
|
807
|
+
for WebGPU. LavaSR's existing `backendDevice: 2` continues to mean Core ML-managed
|
|
808
|
+
placement.
|
|
809
|
+
|
|
810
|
+
## Native engine controls
|
|
811
|
+
|
|
812
|
+
These options are available at construction and through `reload()`. Leaving
|
|
813
|
+
them unset retains the native engine defaults.
|
|
814
|
+
|
|
815
|
+
| Engine | Options | Meaning |
|
|
816
|
+
| --- | --- | --- |
|
|
817
|
+
| MOSS / TTSD | `nCtx`, `maxNewTokens` | Context and generation budgets; positive integers. Duration validation uses the configured token budget, reserving the native 33 delay/EOS rows. |
|
|
818
|
+
| MOSS / TTSD | `textTemperature`, `textTopP`, `textTopK` | Text-channel sampling. |
|
|
819
|
+
| MOSS / TTSD | `audioTemperature`, `audioTopP`, `audioTopK`, `audioRepetitionPenalty` | Independent audio-channel sampling. |
|
|
820
|
+
| Parler | `coremlDecoder` | Explicit decoder-LM Core ML opt-in. The separate DAC sidecar remains presence-driven. |
|
|
821
|
+
| Parler | `coremlComputeUnits`, `coremlStrict` | Compute policy (`all`, `cpu_and_ane`, `cpu_and_gpu`, `cpu_only`) and optional strict/fallback selection. Omission preserves native/environment policy. |
|
|
822
|
+
| Supertonic | `precision` | `auto`, `f32`, `f16`, `q8_0`. |
|
|
823
|
+
| Supertonic | `f16Attn`, `f16Weights`, `kvAttnType` | Native attention/weight precision overrides; `-1` leaves automatic selection. |
|
|
824
|
+
| Supertonic | `f16WeightsDenyList` | Tensor-name patterns retained at their original precision. |
|
|
825
|
+
| Supertonic | `vulkanEnvOverrides` | String-valued Vulkan environment overrides passed to the engine. Explicit entries take precedence over the `vulkanCacheDir` convenience setting. |
|
|
826
|
+
| Supertonic | `voiceStyleTtl`, `voiceStyleDp` | Paired flattened `number[]` or `Float32Array` voice tensors; the engine validates model-specific sizes. They take precedence over JSON and baked voices. |
|
|
827
|
+
| CosyVoice3 | `greedy`, `forceSpeechTokens` | Greedy LM decoding or an explicit integer speech-token trajectory for replay. An empty trajectory restores generation. |
|
|
828
|
+
|
|
829
|
+
After loading Supertonic, `await model.warmUp(text)` invokes the engine's
|
|
830
|
+
warm-up directly, completes without returning PCM, and leaves the loaded
|
|
831
|
+
instance available for normal synthesis. It shares the instance's exclusive
|
|
832
|
+
execution queue. Other engines reject the method.
|
|
833
|
+
|
|
834
|
+
CosyVoice3 responses expose owned `speechTokenIds` arrays. Batch output
|
|
835
|
+
includes them with PCM; native streaming delivers them on a final
|
|
836
|
+
`isLast: true` empty-PCM chunk. Retain those IDs and pass them to
|
|
837
|
+
`reload({ forceSpeechTokens: ids })` to replay the trajectory. The existing
|
|
838
|
+
numeric `response.stats.speechTokens` remains a count.
|
|
839
|
+
|
|
840
|
+
[`examples/fabric-controls.js`](examples/fabric-controls.js) accepts a JSON
|
|
841
|
+
constructor configuration through `QVAC_TTS_OPTIONS`, optional preflight
|
|
842
|
+
through `QVAC_TTS_FIT`, and `QVAC_TTS_WARM_UP=1` for Supertonic. For example:
|
|
843
|
+
|
|
844
|
+
```bash
|
|
845
|
+
QVAC_TTS_OPTIONS='{"engine":"pocket","files":{"modelDir":"/models/pocket"},"config":{"useGPU":true}}' \
|
|
846
|
+
npm run example:fabric-controls -- "Speech runs on the selected backend." pocket.wav
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
The example saves a WAV and prints stats. For CosyVoice3 it also saves
|
|
850
|
+
`<output>.tokens.json` containing the returned speech token IDs.
|
|
851
|
+
|
|
722
852
|
### Core ML sidecars on Apple
|
|
723
853
|
|
|
724
854
|
The macOS / iOS prebuilds carry the Apple Core ML (Neural Engine) sidecars
|
|
725
|
-
for the Supertonic vocoder, the Audio8 codec,
|
|
726
|
-
stages. They are presence-driven:
|
|
855
|
+
for the Supertonic vocoder, the Audio8 codec, the Chatterbox S3Gen stages, and
|
|
856
|
+
the MOSS models' fixed-shape stages. They are presence-driven:
|
|
727
857
|
each stage runs on a compiled `.mlmodelc` found next to its model file
|
|
728
858
|
(`supertonic3-q8_0.gguf` -> `supertonic3-vocoder.mlmodelc`) and falls back to
|
|
729
859
|
the ggml graph when it is absent, so a model directory without sidecars
|
|
@@ -738,13 +868,20 @@ published model set yet; supply your own to opt in.
|
|
|
738
868
|
| 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` |
|
|
739
869
|
| 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` |
|
|
740
870
|
| 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` |
|
|
741
|
-
|
|
|
871
|
+
| Parler | `<model>-dac.mlmodelc` and optional `<model>-decoder.mlmodelc` (drop the GGUF quant suffix) | DAC; decoder steps when `coremlDecoder: true` (macOS 15 / iOS 18); T5 and prefill stay on ggml | a failed sidecar, unless strict mode rejects it; see [Parler Core ML sidecars](#parler-core-ml-sidecars) | `PARLER_COREML_DISABLE=1` |
|
|
872
|
+
| LavaSR | explicit `enhancer.coremlModelPath` / `denoiser.coremlModelPath` | denoiser and enhancer neural cores | only when Core ML options are absent; explicit load errors fail | omit both Core ML options |
|
|
873
|
+
| Chatterbox Turbo / Multilingual | `<s3gen>-encoder.mlmodelc`, `<s3gen>-cfm.mlmodelc` and `<s3gen>-hift.mlmodelc`, beside the S3Gen GGUF (`chatterbox-s3gen-q8_0.gguf` -> `chatterbox-s3gen-{encoder,cfm,hift}.mlmodelc`) | S3Gen encoder and CFM estimator, one function per width up to 1536 mel frames (voice prompt included); HiFT in 128-frame windows. With `maxSentenceChars` set, each sentence is vocoded while T3 decodes the next; T3 stays on the ggml backend | inputs past 1536 mel frames (encoder and CFM), and a call that fails on a sidecar, which also retires it | `CHATTERBOX_COREML_DISABLE=1` |
|
|
874
|
+
| CosyVoice3 | none | — | always | — |
|
|
742
875
|
|
|
743
876
|
Set the force-ggml variables in the process environment before `load()`.
|
|
744
877
|
Audio8 reports its codec path in `response.stats`: `codecSidecarLoaded` is 1
|
|
745
878
|
while the sidecar is attached and `codecOnCoreml` is 1 when that synthesis
|
|
746
879
|
ran its codec on it (see [Response shape](#response-shape)). The Supertonic
|
|
747
|
-
vocoder path is not reported in the stats, and neither are the
|
|
880
|
+
vocoder path is not reported in the stats, and neither are the Chatterbox and
|
|
881
|
+
MOSS paths. The Chatterbox stages run on the CPU and Neural Engine, keeping the
|
|
882
|
+
GPU for T3, unless `CHATTERBOX_COREML_COMPUTE_UNITS` (`all`, `cpu_and_gpu`,
|
|
883
|
+
`cpu_only`) says otherwise; `CHATTERBOX_COREML_STRICT=1` turns a fallback into
|
|
884
|
+
an error.
|
|
748
885
|
The MOSS sidecars run on the GPU unless `MOSS_COREML_COMPUTE_UNITS` (`all`,
|
|
749
886
|
`cpu_and_ane`, `cpu_only`) says otherwise: none of their stages ran faster on
|
|
750
887
|
the Neural Engine, and the speech tokenizer loses code accuracy there.
|
|
@@ -767,12 +904,14 @@ The MOSS sidecars, per stage against Metal with f16 models:
|
|
|
767
904
|
|
|
768
905
|
Export the sidecars from the model GGUFs with
|
|
769
906
|
`engines/tts/scripts/export-supertonic-coreml.py`,
|
|
770
|
-
`engines/tts/scripts/export-audio8-codec-coreml.py`,
|
|
907
|
+
`engines/tts/scripts/export-audio8-codec-coreml.py`,
|
|
908
|
+
`engines/tts/scripts/export-chatterbox-coreml.py`, and
|
|
771
909
|
`engines/tts/scripts/export-moss-{codec,sfx,speech-tokenizer}-coreml.py` from the
|
|
772
910
|
[`qvac-fabric-speech.cpp`](https://github.com/tetherto/qvac-fabric-speech.cpp)
|
|
773
911
|
tree at the ref `speech-cpp` pins. Its
|
|
774
912
|
[Supertonic](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/supertonic.md#core-ml-vocoder-sidecar),
|
|
775
913
|
[Audio8](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/audio8.md#core-ml-codec-sidecar),
|
|
914
|
+
[Chatterbox](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/chatterbox.md#core-ml-sidecars),
|
|
776
915
|
and
|
|
777
916
|
[MOSS](https://github.com/tetherto/qvac-fabric-speech.cpp/blob/master/engines/tts/docs/moss.md#core-ml-codec-decoder)
|
|
778
917
|
guides cover export, placement, and measurements.
|
|
@@ -929,6 +1068,55 @@ Android GPU backend (`useGPU: true` / `nGpuLayers`), including Vulkan on ARM
|
|
|
929
1068
|
Mali. Unsupported or unavailable backends fall back to CPU. It emits native
|
|
930
1069
|
44.1 kHz.
|
|
931
1070
|
|
|
1071
|
+
### Parler Core ML sidecars
|
|
1072
|
+
|
|
1073
|
+
On Apple platforms, Parler can use optional Core ML decoder and DAC sidecars.
|
|
1074
|
+
Set these **top-level constructor options**, or pass them to `reload()`:
|
|
1075
|
+
|
|
1076
|
+
```js
|
|
1077
|
+
const model = new TTSGgml({
|
|
1078
|
+
files: { parlerModel: './models/parler-mini-v1-q8_0.gguf' },
|
|
1079
|
+
coremlDecoder: true,
|
|
1080
|
+
coremlComputeUnits: 'cpu_and_ane',
|
|
1081
|
+
coremlStrict: false,
|
|
1082
|
+
opts: { stats: true }
|
|
1083
|
+
})
|
|
1084
|
+
await model.load()
|
|
1085
|
+
await model.reload({ coremlDecoder: false })
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
`coremlDecoder` is a boolean; omitting it keeps the engine's default.
|
|
1089
|
+
`coremlComputeUnits` accepts `all`, `cpu_and_ane`, `cpu_and_gpu`, or `cpu_only`;
|
|
1090
|
+
omitting it inherits the engine's environment/default policy. `coremlStrict`
|
|
1091
|
+
is a boolean: omit it to inherit strict mode, or pass `false` to explicitly
|
|
1092
|
+
permit fallback. These options are Parler-only load settings and are rejected
|
|
1093
|
+
on per-call APIs. Reload options that fail validation are rejected before
|
|
1094
|
+
replacing the current addon.
|
|
1095
|
+
`assessFit()` accepts `coremlDecoder` to match the load's decoder policy.
|
|
1096
|
+
|
|
1097
|
+
Strict mode requires a usable DAC sidecar even when `coremlDecoder` is false;
|
|
1098
|
+
enabling the decoder also requires its sidecar. A build without Core ML fails
|
|
1099
|
+
in strict mode. Combining strict mode with `PARLER_COREML_DISABLE` remains a
|
|
1100
|
+
fatal configuration under the engine's existing policy. On reload, omitted or
|
|
1101
|
+
`undefined` settings retain their current values; an empty `coremlComputeUnits`
|
|
1102
|
+
string is invalid. To return to inherited compute-unit or strict policy,
|
|
1103
|
+
construct a new instance with the corresponding options omitted.
|
|
1104
|
+
|
|
1105
|
+
If the replacement model fails to load, `reload()` rejects and leaves the
|
|
1106
|
+
instance unloaded with the requested settings retained. Recover by reloading
|
|
1107
|
+
with a usable configuration or constructing a new instance.
|
|
1108
|
+
|
|
1109
|
+
With stats enabled, `decoderSidecarLoaded` and `dacSidecarLoaded` report `1`
|
|
1110
|
+
while the corresponding sidecar remains attached. `decoderOnCoreml` and
|
|
1111
|
+
`dacOnCoreml` report `1` only when that synthesis used the corresponding Core ML
|
|
1112
|
+
stage; ggml fallback and mixed Core ML/ggml execution report `0`. Sentence
|
|
1113
|
+
streams report Core ML execution only if every reported sentence used it;
|
|
1114
|
+
sidecar availability retains the last report. These numeric flags describe
|
|
1115
|
+
execution paths; they do not
|
|
1116
|
+
identify whether Core ML scheduled work on the CPU, GPU, or Neural Engine.
|
|
1117
|
+
`backendDevice` / `backendId` continue to identify the ggml backend, so a mixed
|
|
1118
|
+
pipeline does not report all stages as Core ML.
|
|
1119
|
+
|
|
932
1120
|
## CosyVoice3 instruct
|
|
933
1121
|
|
|
934
1122
|
Beyond `emotion` / `pace` above, CosyVoice3 accepts a natural-language
|
|
@@ -1348,7 +1536,7 @@ instance. Runtime stats add `promptTokens`, `generatedTokens`, `replyTokens`,
|
|
|
1348
1536
|
| `cfmSteps` | number | 2 | Chatterbox native-streaming CFM steps: 1 = faster (halved CFM cost) |
|
|
1349
1537
|
| `batchCfmSteps` | number | engine default | Chatterbox-only batch CFM steps (`0` = 2-step meanflow on Turbo, the model's full schedule on multilingual). The multilingual engine does not floor it, so values well below its schedule under-integrate |
|
|
1350
1538
|
| `nPredict` | number | 1000 | Chatterbox-only cap on speech tokens per synthesis (25 ≈ 1 s, so ~40 s by default). Longer single calls also need `nCtx` to fit, or use `maxSentenceChars` |
|
|
1351
|
-
| `maxSentenceChars` | number | 0 | Chatterbox-only sentence auto-split: T3 + S3Gen run per segment of at most this many bytes (the reference CLI uses 180), so `nPredict` applies per segment |
|
|
1539
|
+
| `maxSentenceChars` | number | 0 | Chatterbox-only sentence auto-split: T3 + S3Gen run per segment of at most this many bytes (the reference CLI uses 180), so `nPredict` applies per segment; with [Chatterbox Core ML sidecars](#chatterbox-core-ml-sidecars), S3Gen vocodes a segment while T3 decodes the next |
|
|
1352
1540
|
| `crossfadeMs` | number | 30 | Chatterbox-only raised-cosine crossfade between auto-split batch segments; native streaming stays gapless |
|
|
1353
1541
|
| `repeatPenalty` | number | 1.2 | Chatterbox-only T3 repetition penalty (`1` = off) |
|
|
1354
1542
|
| `exaggeration` / `cfgWeight` / `minP` | number | 0.5 / 0.5 / 0 | Chatterbox multilingual only (Turbo ignores them): prosody intensity (> 0 more expressive, < 0 flatter), T3 classifier-free guidance scale (`0` = off; `cfgRate` is the separate S3Gen rate), and the min-p sampling threshold |
|
|
@@ -1365,7 +1553,7 @@ instance. Runtime stats add `promptTokens`, `generatedTokens`, `replyTokens`,
|
|
|
1365
1553
|
| `pitch`, `expressivity`, `noise`, `reverb`, `quality` | string | — | Parler-only template fields (see [Parler descriptions](#parler-descriptions)); invalid values error listing the valid set |
|
|
1366
1554
|
| `temperature` / `topK` / `topP` | number | engine defaults | Parler, Audio8 and Chatterbox sampling knobs (omit for the engine's own defaults — Parler temp 1.0 / top-k 50, Audio8 temp 0.7 / top-k 50 / top-p 0.9, Chatterbox temp 0.8 / top-k 1000 / top-p 0.95 where temp `0` is greedy and top-k `0` disables the cutoff; `topP` in `(0, 1]`) |
|
|
1367
1555
|
| `maxFrames` | number | engine max | Parler + Audio8 generation cap in frames (Parler ~86/s of audio, Audio8 ~21.5/s); `0` = model default, Parler rejects 1–9 |
|
|
1368
|
-
| `greedy` | boolean | `false` | Audio8
|
|
1556
|
+
| `greedy` | boolean | `false` | Audio8 / CosyVoice3: take the argmax instead of sampling; Audio8 ignores `temperature` / `topK` / `topP` |
|
|
1369
1557
|
| `minNewTokens` | number | GGUF default | Parler-only minimum tokens before EOS (`-1` = model default) |
|
|
1370
1558
|
| `normalizeNumbers` | boolean | `true` | Parler-only: expand digits before tokenization (English words; script-native digits on indic) — parler voices raw digits badly |
|
|
1371
1559
|
| `instruct` | object \| string | — | CosyVoice3-only: instruction controls (`dialect` / `volume` / `style`, resolved by precedence in that order) or a raw instruction string; an unknown key or invalid value throws, and it counts toward the one-instruction rule (see [CosyVoice3 instruct](#cosyvoice3-instruct)). Also accepted per call, where it replaces the configured conditioning like a per-call `emotion` / `pace` |
|
|
@@ -1376,9 +1564,10 @@ instance. Runtime stats add `promptTokens`, `generatedTokens`, `replyTokens`,
|
|
|
1376
1564
|
| `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 |
|
|
1377
1565
|
| `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 |
|
|
1378
1566
|
| `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`, ...) |
|
|
1567
|
+
| `config.backend` | string | unset | Native backend selector or exact registered device name; see [Backends](#backends--gpu-acceleration) |
|
|
1379
1568
|
| `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) |
|
|
1380
1569
|
| `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 |
|
|
1381
|
-
| `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`) |
|
|
1570
|
+
| `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, 5=WebGPU, 99=other), `enhancerBackendDevice` / `enhancerBackendId` and `denoiserBackendDevice` / `denoiserBackendId` (`-1` when that LavaSR stage is off; device `2` and id `99` for Core ML-managed placement), 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`) |
|
|
1382
1571
|
| `exclusiveRun` | boolean | `false` | **Top-level** option (not under `opts`): serialize overlapping streaming runs |
|
|
1383
1572
|
|
|
1384
1573
|
### Methods
|
|
@@ -1391,6 +1580,7 @@ instance. Runtime stats add `promptTokens`, `generatedTokens`, `replyTokens`,
|
|
|
1391
1580
|
config (`language`, `useGPU`, `outputSampleRate`, …).
|
|
1392
1581
|
- `await model.destroy()` — `unload()` + mark this instance dead.
|
|
1393
1582
|
- `await model.cancel()` — best-effort cancel of any in-flight run.
|
|
1583
|
+
- `await model.warmUp(text)` — Supertonic-only native warm-up without a PCM response.
|
|
1394
1584
|
- `TTSGgml.getVoiceControls()` — static: tts-cpp's emotion / pace vocabulary
|
|
1395
1585
|
and each engine's supported subset (see [Emotion & pace](#emotion--pace-cross-engine)).
|
|
1396
1586
|
- `model.run({ input, type: 'text' })` → `QvacResponse`.
|
|
@@ -1444,14 +1634,16 @@ response.stats.generatedFrames // Audio8 and MOSS only: codec frames (Audio8 e
|
|
|
1444
1634
|
response.stats.codecSidecarLoaded // Audio8 only, macOS / iOS: 1 while the Apple Core ML codec sidecar is attached
|
|
1445
1635
|
response.stats.codecOnCoreml // Audio8 only: 1 when this synthesis ran the codec on that sidecar, 0 when on the ggml backend
|
|
1446
1636
|
response.stats.backendDevice // 0=CPU, 1=GPU
|
|
1447
|
-
response.stats.backendId // 0=CPU, 1=Metal, 2=CUDA, 3=Vulkan, 4=OpenCL, 99=other
|
|
1637
|
+
response.stats.backendId // 0=CPU, 1=Metal, 2=CUDA, 3=Vulkan, 4=OpenCL, 5=WebGPU, 99=other
|
|
1448
1638
|
// present when a LavaSR enhancer is active:
|
|
1449
1639
|
response.stats.enhancerBackendDevice // -1 none, 0 CPU, 1 GPU
|
|
1450
1640
|
response.stats.enhancerBackendId
|
|
1451
1641
|
```
|
|
1452
1642
|
|
|
1453
|
-
For `runStream()` and `runStreaming()`, `
|
|
1454
|
-
`
|
|
1643
|
+
For `runStream()` and `runStreaming()`, the backend fields (`backendDevice`,
|
|
1644
|
+
`backendId`, `gpuUnsupported`, and the LavaSR `enhancerBackend*` and
|
|
1645
|
+
`denoiserBackend*` pairs) and the Audio8 codec fields (`codecSidecarLoaded`,
|
|
1646
|
+
`codecOnCoreml`) retain the last chunk that reported each field, including zero
|
|
1455
1647
|
when a sidecar is retired or synthesis falls back to ggml. They are not summed;
|
|
1456
1648
|
if no chunk reports a field, it remains absent.
|
|
1457
1649
|
|
|
@@ -1507,20 +1699,17 @@ const fit = await TTSGgml.assessFit({
|
|
|
1507
1699
|
audioSeconds: 30
|
|
1508
1700
|
})
|
|
1509
1701
|
|
|
1510
|
-
fit.status // 'fits' | 'does-not-fit' | '
|
|
1511
|
-
fit.
|
|
1512
|
-
fit.
|
|
1513
|
-
fit.deviceName
|
|
1514
|
-
fit.deviceBytes
|
|
1515
|
-
fit.weightsBytes
|
|
1516
|
-
fit.stateBytes
|
|
1517
|
-
fit.lmComputeBytes // 0 for a pipeline with no language-model stage, such as supertonic
|
|
1518
|
-
fit.codecComputeBytes // 0 for a pipeline with no separate codec stage
|
|
1519
|
-
fit.hostBytes
|
|
1520
|
-
fit.lavasrFileBytes // the LavaSR stages' size on disk, 0 when none was named
|
|
1521
|
-
fit.report
|
|
1702
|
+
fit.status // 'fits' | 'does-not-fit' | 'unknown'
|
|
1703
|
+
fit.devices // [{ id, name, unified, usedBytes, freeBytes, totalBytes }]
|
|
1704
|
+
fit.stats // {}
|
|
1522
1705
|
```
|
|
1523
1706
|
|
|
1707
|
+
`devices` holds the device the load resolves to, then the host CPU, which holds the host-side buffers. A load on the CPU is one row. `id` is the PCI bus ID when the backend reports one, so the same GPU matches across addons built on different backends, and the device name otherwise. `unified` is true for memory the host owns: the CPU, an integrated GPU, a GPU that shares the host's memory, Android's OpenCL GPU, and a device that reports no memory of its own, which takes the host's figures. `usedBytes` is what the load takes on that device.
|
|
1708
|
+
|
|
1709
|
+
On an iOS device every unified row shares the app's own memory limit, `os_proc_available_memory()`. Each such row's free memory is capped at that limit, and a load whose unified rows together take more than the limit less the margin does not fit. A limit of 0 means the app has none left.
|
|
1710
|
+
|
|
1711
|
+
`unknown` is a load the fitter cannot size, and it carries no rows: supertonic outside the fused graph path it models, which is a validated GPU, or a CPU without the Accelerate pointwise kernels; a CosyVoice3 or Parler load with Core ML sidecars; or a load whose memory the engine fails to measure.
|
|
1712
|
+
|
|
1524
1713
|
`engineType` picks the fitter, the same key a load uses, and is required: a fit request carries none of the file keys a load is inferred from. Each engine takes the load's own file keys, plus a workload the fit adds:
|
|
1525
1714
|
|
|
1526
1715
|
| `engineType` | Files | Workload |
|
|
@@ -1530,16 +1719,15 @@ fit.report
|
|
|
1530
1719
|
| `chatterbox` | `t3ModelPath`, `s3genModelPath` | `textTokens`, `predictTokens` |
|
|
1531
1720
|
| `audio8` | `audio8LmPath`, `audio8CodecDecoderPath`, `audio8CodecEncoderPath` | `promptTokens`, `maxFrames`, `referenceSeconds` |
|
|
1532
1721
|
| `cosyvoice3` | `cosyvoiceLlmModelPath`, `cosyvoiceFlowModelPath`, `cosyvoiceHiftModelPath`, `cosyvoiceVoiceModelPath` | `textTokens`, `speechTokens` |
|
|
1722
|
+
| `pocket` | `pocketFlowModelPath`, `pocketMimiModelPath`, `pocketFrontendPath`, optional `pocketVoicePath` / `referenceAudio` | Required `text`; optional `memoryBudgetBytes`, `marginBytes`, and native load controls |
|
|
1533
1723
|
| `moss` | `mossBackbonePath`, `mossCodecDecoderPath`, optional `mossCodecEncoderPath` | Required `promptRows`, `referenceSamples`, `streaming`; load controls `durationTokens`, `streamChunkTokens`, `threads` |
|
|
1534
1724
|
| `moss-sfx` | Required `mossSoundEffectPath` | Required `prompt`, `seconds`; optional `negativePrompt`, `steps`, `guidance`, `shift`, `threads` |
|
|
1535
1725
|
|
|
1536
1726
|
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.
|
|
1537
1727
|
|
|
1538
|
-
|
|
1728
|
+
The LavaSR enhancer and denoiser have no fitter, and the request takes no LavaSR paths. The estimate for a load with LavaSR stages leaves those stages out.
|
|
1539
1729
|
|
|
1540
|
-
`
|
|
1541
|
-
|
|
1542
|
-
A model the engine cannot read resolves to `status: "error"`, as does 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, rejects.
|
|
1730
|
+
A model the engine cannot read, a workload it cannot project, a voice with no fitter, or a broken request rejects with an `InvalidArgument` error, and a host with no native binding rejects as well. MOSS-TTS, MOSS-TTSD and MOSS-SoundEffect are supported. MOSS-Speech has no fitter.
|
|
1543
1731
|
|
|
1544
1732
|
MOSS-TTS and MOSS-TTSD share the `moss` fitter. `promptRows` is the complete
|
|
1545
1733
|
native prompt length, including special tokens, encoded speaker references and
|
|
@@ -1567,8 +1755,6 @@ as a conservative upper bound, including the reference encoder when requested;
|
|
|
1567
1755
|
this may reject a workload whose actual peak is smaller. The fit follows the
|
|
1568
1756
|
load's generation settings and does not change synthesis behavior.
|
|
1569
1757
|
|
|
1570
|
-
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.
|
|
1571
|
-
|
|
1572
1758
|
### MOSS-SoundEffect fit
|
|
1573
1759
|
|
|
1574
1760
|
```js
|
|
@@ -1579,7 +1765,7 @@ const fit = await TTSGgml.assessFit({
|
|
|
1579
1765
|
seconds: 8,
|
|
1580
1766
|
useGPU: false
|
|
1581
1767
|
})
|
|
1582
|
-
console.log(fit.
|
|
1768
|
+
console.log(fit.devices)
|
|
1583
1769
|
```
|
|
1584
1770
|
|
|
1585
1771
|
The fitter uses generation's tokenizer, request validation and graph builders,
|
|
@@ -1587,8 +1773,8 @@ without loading weights or generating audio. It supports full and metadata-only
|
|
|
1587
1773
|
GGUF files. `negativePrompt`, `steps`, `guidance` and `shift` keep their generation
|
|
1588
1774
|
semantics; `marginBytes` keeps the shared 256 MiB default. DiT processes the model's
|
|
1589
1775
|
full latent duration even for a short output. Requested seconds size decoder
|
|
1590
|
-
windows and audio buffers. The shared compute arena
|
|
1591
|
-
|
|
1776
|
+
windows and audio buffers. The shared compute arena for text/DiT and any
|
|
1777
|
+
additional VAE demand is counted at its peak.
|
|
1592
1778
|
Host memory includes conditioning, temporary arrays and CPU fallback buffers.
|
|
1593
1779
|
|
|
1594
1780
|
Run `bare examples/moss-sfx-fit.js ./moss-sfx-v2-q8_0.gguf "Rain on a roof." 8`.
|
|
@@ -1611,6 +1797,7 @@ Runnable demos under `examples/`:
|
|
|
1611
1797
|
| `supertonic-mtl-sweep-tts.js` | Multilingual Supertonic sweep across languages |
|
|
1612
1798
|
| `supertonic-sentence-stream-tts.js` | Supertonic sentence-level streaming |
|
|
1613
1799
|
| `supertonic-chunk-stream-tts.js` | Supertonic native chunk streaming via `streamChunkTokens`, with streaming playback |
|
|
1800
|
+
| `lavasr-coreml.js` | Supertonic + explicit Core ML denoiser/enhancer. Run without arguments for usage. |
|
|
1614
1801
|
| `supertonic-enhanced.js` | Supertonic + LavaSR 48 kHz enhancement. `bare examples/supertonic-enhanced.js "Hello"` |
|
|
1615
1802
|
| `parler-tts.js` | Parler batch synth with voice/emotion templates. `bare examples/parler-tts.js "Hello" Laura happy` |
|
|
1616
1803
|
| `parler-enhanced.js` | Parler + LavaSR 48 kHz enhancement. `bare examples/parler-enhanced.js "Hello" Laura happy` |
|
|
@@ -1788,9 +1975,10 @@ Apache-2.0. See [LICENSE](./LICENSE).
|
|
|
1788
1975
|
|
|
1789
1976
|
## Pocket TTS
|
|
1790
1977
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1978
|
+
Pocket supports CPU, Metal, CUDA, Vulkan and OpenCL through the addon.
|
|
1979
|
+
CPU remains the default. Set `config.useGPU: true`, `nGpuLayers`, or
|
|
1980
|
+
`config.backend` to select an accelerator; runtime stats identify the
|
|
1981
|
+
backend actually selected. Metadata-only `assessFit()` uses the same
|
|
1982
|
+
native configuration and accepts an optional application memory budget.
|
|
1795
1983
|
|
|
1796
1984
|
See [Pocket TTS](docs/pocket-tts.md) for model conversion, addon/SDK usage, supported controls and validation commands.
|
package/docs/pocket-tts.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Pocket TTS
|
|
2
2
|
|
|
3
3
|
Pocket is an English TTS engine implemented in the Fabric speech library.
|
|
4
|
-
The native library
|
|
5
|
-
|
|
4
|
+
The native library and `@qvac/tts-ggml` addon support CPU, Metal, CUDA,
|
|
5
|
+
Vulkan and OpenCL. CPU remains the default; set `config.useGPU: true`
|
|
6
|
+
or a nonzero `nGpuLayers` to request GPU inference. `config.backend`
|
|
7
|
+
can select an exact registered device such as `MTL0`.
|
|
6
8
|
It streams PCM16 audio through the addon. Python
|
|
7
9
|
is used only to convert weights and benchmark the upstream reference; it is
|
|
8
10
|
not needed for native synthesis. Inference plugin and public SDK integration
|
|
@@ -71,13 +73,39 @@ chunk with `isLast: true`; other engines may omit this metadata.
|
|
|
71
73
|
|
|
72
74
|
## Supported controls
|
|
73
75
|
|
|
74
|
-
English only;
|
|
76
|
+
English only; `threads` defaults to one per worker (two workers).
|
|
75
77
|
`seed` accepts unsigned 32-bit integers. `temperature`, `steps`, `nCtx`,
|
|
76
78
|
`maxTokens`, `noiseClamp`, `eosThreshold`, `framesAfterEos` and
|
|
77
79
|
`outputSampleRate` are validated before native loading. Output resampling
|
|
78
|
-
supports 8–192 kHz. Other engines' sampling, emotional, speed
|
|
80
|
+
supports 8–192 kHz. Other engines' sampling, emotional, speed and
|
|
79
81
|
LavaSR settings are rejected instead of silently ignored.
|
|
80
82
|
|
|
83
|
+
`config.useGPU` and `nGpuLayers` must agree when supplied together.
|
|
84
|
+
Reloading preserves the selected backend unless it is explicitly changed.
|
|
85
|
+
|
|
86
|
+
### Memory preflight
|
|
87
|
+
|
|
88
|
+
`assessFit()` reads GGUF metadata and frontend/voice metadata without loading
|
|
89
|
+
weights. Pass the same backend and workload that synthesis will use:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
const fit = await TTSGgml.assessFit({
|
|
93
|
+
engineType: 'pocket',
|
|
94
|
+
pocketFlowModelPath: '/models/pocket/flow-lm.gguf',
|
|
95
|
+
pocketMimiModelPath: '/models/pocket/mimi.gguf',
|
|
96
|
+
pocketFrontendPath: '/models/pocket/frontend.json',
|
|
97
|
+
pocketVoicePath: '/models/pocket/voice.gguf',
|
|
98
|
+
text: 'Hello from Pocket.',
|
|
99
|
+
useGPU: true
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`marginBytes` leaves the requested memory headroom. Optional
|
|
104
|
+
`memoryBudgetBytes` applies an application ceiling to each memory pool;
|
|
105
|
+
zero or omission retains the native telemetry-based budget. Results use
|
|
106
|
+
the common `fits`, `does-not-fit` and `error` statuses. The projection
|
|
107
|
+
does not load a model or change a synthesis setting.
|
|
108
|
+
|
|
81
109
|
Fabric defaults to one sampling step, matching the native CLI and upstream
|
|
82
110
|
reference. An explicit `steps: 4` (or
|
|
83
111
|
addon `numInferenceSteps: 4`) offers a quality option at additional compute
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('bare-fs')
|
|
4
|
+
const proc = require('bare-process')
|
|
5
|
+
const TTSGgml = require('../')
|
|
6
|
+
const { createWav } = require('./wav-helper')
|
|
7
|
+
|
|
8
|
+
const DEFAULT_TEXT = 'Speech controls are available directly from the addon.'
|
|
9
|
+
const DEFAULT_OUTPUT = 'tts-controls.wav'
|
|
10
|
+
|
|
11
|
+
function readOptions() {
|
|
12
|
+
const value = proc.env.QVAC_TTS_OPTIONS
|
|
13
|
+
if (!value) throw new Error('Set QVAC_TTS_OPTIONS to a JSON constructor configuration')
|
|
14
|
+
return JSON.parse(value)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function concatenatePcm(chunks) {
|
|
18
|
+
const size = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
19
|
+
const result = new Int16Array(size)
|
|
20
|
+
let offset = 0
|
|
21
|
+
for (const chunk of chunks) {
|
|
22
|
+
result.set(chunk, offset)
|
|
23
|
+
offset += chunk.length
|
|
24
|
+
}
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function collectAudio(response) {
|
|
29
|
+
const chunks = []
|
|
30
|
+
let sampleRate
|
|
31
|
+
let speechTokenIds
|
|
32
|
+
for await (const chunk of response.iterate()) {
|
|
33
|
+
chunks.push(chunk.outputArray)
|
|
34
|
+
sampleRate = chunk.sampleRate
|
|
35
|
+
if (chunk.speechTokenIds) speechTokenIds = chunk.speechTokenIds
|
|
36
|
+
}
|
|
37
|
+
return { pcm: concatenatePcm(chunks), sampleRate, speechTokenIds }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
const argv = global.Bare ? global.Bare.argv : proc.argv
|
|
42
|
+
const text = argv[2] || DEFAULT_TEXT
|
|
43
|
+
const output = argv[3] || DEFAULT_OUTPUT
|
|
44
|
+
const options = readOptions()
|
|
45
|
+
if (proc.env.QVAC_TTS_FIT) {
|
|
46
|
+
console.log('Memory preflight:', await TTSGgml.assessFit(JSON.parse(proc.env.QVAC_TTS_FIT)))
|
|
47
|
+
}
|
|
48
|
+
const model = new TTSGgml({ ...options, opts: { ...options.opts, stats: true } })
|
|
49
|
+
try {
|
|
50
|
+
await model.load()
|
|
51
|
+
if (proc.env.QVAC_TTS_WARM_UP === '1') await model.warmUp(text)
|
|
52
|
+
const response = await model.run({ input: text })
|
|
53
|
+
const result = await collectAudio(response)
|
|
54
|
+
if (!result.pcm.length) throw new Error('No audio was returned')
|
|
55
|
+
createWav(result.pcm, result.sampleRate, output)
|
|
56
|
+
console.log('Saved:', output)
|
|
57
|
+
console.log('Stats:', response.stats)
|
|
58
|
+
if (result.speechTokenIds) {
|
|
59
|
+
fs.writeFileSync(output + '.tokens.json', JSON.stringify(result.speechTokenIds))
|
|
60
|
+
console.log('Speech token IDs:', result.speechTokenIds.length)
|
|
61
|
+
}
|
|
62
|
+
} finally {
|
|
63
|
+
await model.destroy()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
main().catch((error) => {
|
|
68
|
+
console.error(error)
|
|
69
|
+
if (global.Bare) global.Bare.exit(1)
|
|
70
|
+
else proc.exitCode = 1
|
|
71
|
+
})
|