@qvac/tts-ggml 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +127 -6
  2. package/docs/pocket-tts.md +12 -10
  3. package/examples/moss-sfx-fit.js +14 -7
  4. package/index.d.ts +43 -1130
  5. package/index.js +127 -3042
  6. package/lib/addonEvents.d.ts +26 -0
  7. package/lib/addonEvents.js +51 -0
  8. package/lib/conditioning.d.ts +50 -0
  9. package/lib/conditioning.js +184 -0
  10. package/lib/engineDetection.d.ts +14 -0
  11. package/lib/engineDetection.js +110 -0
  12. package/lib/engineIds.d.ts +15 -0
  13. package/lib/engineIds.js +39 -0
  14. package/lib/engineParams.d.ts +3 -0
  15. package/lib/engineParams.js +34 -0
  16. package/lib/engineSetup.d.ts +153 -0
  17. package/lib/engineSetup.js +2 -0
  18. package/lib/engines/audio8.d.ts +83 -0
  19. package/lib/engines/audio8.js +262 -0
  20. package/lib/engines/chatterbox.d.ts +10 -0
  21. package/lib/engines/chatterbox.js +112 -0
  22. package/lib/engines/cosyvoice.d.ts +87 -0
  23. package/lib/engines/cosyvoice.js +267 -0
  24. package/lib/engines/moss.d.ts +26 -0
  25. package/lib/engines/moss.js +177 -0
  26. package/lib/engines/mossSoundEffect.d.ts +41 -0
  27. package/lib/engines/mossSoundEffect.js +117 -0
  28. package/lib/engines/mossSpeech.d.ts +86 -0
  29. package/lib/engines/mossSpeech.js +228 -0
  30. package/lib/engines/parler.d.ts +71 -0
  31. package/lib/engines/parler.js +287 -0
  32. package/lib/engines/supertonic.d.ts +11 -0
  33. package/lib/engines/supertonic.js +115 -0
  34. package/lib/exclusiveQueue.d.ts +16 -0
  35. package/lib/exclusiveQueue.js +45 -0
  36. package/lib/fit.d.ts +3 -3
  37. package/lib/fit.js +4 -4
  38. package/lib/jobFields.d.ts +20 -0
  39. package/lib/jobFields.js +73 -0
  40. package/lib/modelFiles.d.ts +207 -0
  41. package/lib/modelFiles.js +117 -0
  42. package/lib/nativeParams.d.ts +12 -0
  43. package/lib/nativeParams.js +73 -0
  44. package/lib/optionValidation.d.ts +2 -0
  45. package/lib/optionValidation.js +103 -0
  46. package/lib/options.d.ts +4 -0
  47. package/lib/options.js +17 -0
  48. package/lib/pocketLifecycle.d.ts +67 -0
  49. package/lib/pocketLifecycle.js +170 -0
  50. package/lib/reload.d.ts +12 -0
  51. package/lib/reload.js +76 -0
  52. package/lib/sentenceStream.d.ts +85 -0
  53. package/lib/sentenceStream.js +367 -0
  54. package/lib/setup.d.ts +7 -0
  55. package/lib/setup.js +169 -0
  56. package/lib/streamingSupport.d.ts +8 -0
  57. package/lib/streamingSupport.js +77 -0
  58. package/lib/types.d.ts +465 -0
  59. package/lib/types.js +2 -0
  60. package/lib/voiceControls.d.ts +12 -0
  61. package/lib/voiceControls.js +23 -0
  62. package/package.json +17 -11
  63. package/test/integration/audio8.test.js +6 -12
  64. package/test/integration/fit.test.js +115 -18
  65. package/test/integration/gpu-smoke.test.js +2 -11
  66. package/test/integration/lavasr-enhancer.test.js +2 -15
  67. package/test/integration/moss-fit.test.js +13 -13
  68. package/test/integration/moss-sfx-fit.test.js +14 -14
  69. package/test/integration/moss-sfx.test.js +29 -4
  70. package/test/integration/moss-speech.test.js +19 -5
  71. package/test/integration/moss.test.js +93 -56
  72. package/test/mobile/integration.auto.cjs +10 -0
  73. package/test/mobile/test-groups.json +6 -2
  74. package/test/utils/desktopGpuBackend.js +21 -0
  75. package/test/utils/gpuOnly.js +15 -0
  76. package/test/utils/mossBackend.js +34 -0
  77. package/test/utils/mossGpuValidation.js +70 -0
  78. package/test/utils/perf-helper.js +275 -0
  79. package/test/utils/runTTS.js +1 -1
  80. package/test/utils/testThreads.js +5 -0
@@ -0,0 +1,275 @@
1
+ 'use strict'
2
+ // Performance-recording helper shared by the tts-ggml integration tests
3
+ // (Chatterbox English/MTL, Supertonic, Supertonic MTL, GPU smoke). Holds the
4
+ // singleton perf-reporter, the mobile-safe inline fallback, and
5
+ // `recordTtsStats()` which turns an addon `response.stats` payload into a perf
6
+ // row.
7
+ //
8
+ // Lives in test/utils/ (not test/integration/) so it sits next to the other
9
+ // shared runners (runChatterboxTTS.js, runSupertonicTTS.js, ...) and is never
10
+ // picked up by the mobile test generator or the brittle test runner, which
11
+ // only discover test/integration/*.test.js.
12
+ //
13
+ // Mirrors the structure of the Parakeet/LLM helpers: on desktop we require the
14
+ // shared scripts/test-utils/performance-reporter directly; on mobile that path
15
+ // lives outside the addon package and bare-pack can't bundle it, so we fall
16
+ // back to an inline lightweight reporter that emits [PERF_REPORT_START]/
17
+ // [PERF_CHUNK] markers extract-from-log.js already understands.
18
+
19
+ const fs = require('bare-fs')
20
+ const path = require('bare-path')
21
+ const os = require('bare-os')
22
+ const process = require('bare-process')
23
+
24
+ const platform = os.platform()
25
+ const arch = os.arch()
26
+ const platformLabel = `${platform}-${arch}`
27
+ const isMobile = platform === 'ios' || platform === 'android'
28
+
29
+ // Inject bare-subprocess so performance-reporter.js's _detectGpu() can shell
30
+ // out to nvidia-smi / vulkaninfo / system_profiler on desktop runners.
31
+ // Resolving from this caller file works (it lives next to the addon's
32
+ // node_modules); resolving from inside scripts/test-utils/ does not because
33
+ // require('child_process') throws under Bare. Without this, device.gpu stays
34
+ // null and the perf report shows "N/A" for the GPU. Mirrors the LLM addon's
35
+ // _perf-helper.js. Mobile doesn't need it — the inline fallback below leaves
36
+ // gpu null on Device Farm where the probes wouldn't work anyway.
37
+ let _subprocess = null
38
+ try {
39
+ _subprocess = require('bare-subprocess')
40
+ } catch (_) {}
41
+
42
+ let createPerformanceReporter
43
+ const _scriptBase = path.join('..', '..', '..', '..', 'scripts', 'test-utils')
44
+ try {
45
+ const perfReporterMod = require(path.join(_scriptBase, 'performance-reporter'))
46
+ perfReporterMod.configure({ fs, path, process, os, subprocess: _subprocess })
47
+ createPerformanceReporter = perfReporterMod.createPerformanceReporter
48
+ } catch (_) {
49
+ createPerformanceReporter = function (opts) {
50
+ const _results = []
51
+ const _startedAt = new Date().toISOString()
52
+ const _addon = (opts && opts.addon) || 'tts-ggml'
53
+ const _addonType = (opts && opts.addonType) || 'tts'
54
+ const _device = {
55
+ name: platform,
56
+ platform,
57
+ os_version: '',
58
+ arch: os.arch ? os.arch() : '',
59
+ // GPU label only filled in for desktop runners by
60
+ // performance-reporter.js's _detectGpu(). On Device Farm we leave it
61
+ // null and let the aggregator fall back to the device name.
62
+ gpu: null,
63
+ runner: 'device-farm'
64
+ }
65
+
66
+ return {
67
+ record(testName, metrics, extra) {
68
+ const entry = {
69
+ test: testName,
70
+ execution_provider: (extra && extra.execution_provider) || null,
71
+ model: (extra && extra.model) || null,
72
+ metrics: Object.assign(
73
+ {
74
+ total_time_ms: null,
75
+ tps: null,
76
+ real_time_factor: null,
77
+ sample_count: null,
78
+ audio_duration_ms: null
79
+ },
80
+ metrics
81
+ ),
82
+ input: (extra && extra.input) || null,
83
+ output: (extra && extra.output) || null
84
+ }
85
+ _results.push(entry)
86
+ },
87
+ toJSON() {
88
+ return {
89
+ schema_version: '1.0',
90
+ addon: _addon,
91
+ addon_type: _addonType,
92
+ timestamp: _startedAt,
93
+ device: _device,
94
+ results: _results
95
+ }
96
+ },
97
+ writeReport() {
98
+ const json = JSON.stringify(this.toJSON())
99
+ const dirs = []
100
+ if (global.testDir) dirs.push(global.testDir)
101
+ if (platform === 'android') {
102
+ dirs.push('/sdcard/Android/data/io.tether.test.qvac/files')
103
+ dirs.push('/storage/emulated/0/Android/data/io.tether.test.qvac/files')
104
+ dirs.push('/data/local/tmp')
105
+ }
106
+ dirs.push('/tmp')
107
+ for (let di = 0; di < dirs.length; di++) {
108
+ try {
109
+ try {
110
+ fs.mkdirSync(dirs[di], { recursive: true })
111
+ } catch (_) {}
112
+ const p = path.join(dirs[di], 'perf-report.json')
113
+ fs.writeFileSync(p, json)
114
+ console.log('[PERF_REPORT_PATH]' + p)
115
+ } catch (e) {
116
+ console.log('[perf-reporter] write to ' + dirs[di] + ' failed: ' + e.message)
117
+ }
118
+ }
119
+ },
120
+ writeStepSummary() {},
121
+ writeToConsole() {
122
+ try {
123
+ const json = JSON.stringify(this.toJSON())
124
+ const CHUNK = 800
125
+ if (json.length <= CHUNK) {
126
+ console.log('[PERF_REPORT_START]' + json + '[PERF_REPORT_END]')
127
+ } else {
128
+ const id = Date.now().toString(36)
129
+ const n = Math.ceil(json.length / CHUNK)
130
+ for (let i = 0; i < n; i++) {
131
+ console.log(
132
+ '[PERF_CHUNK:' +
133
+ id +
134
+ ':' +
135
+ i +
136
+ ':' +
137
+ n +
138
+ ']' +
139
+ json.substring(i * CHUNK, (i + 1) * CHUNK)
140
+ )
141
+ }
142
+ }
143
+ } catch (err) {
144
+ console.log('[perf-reporter] mobile console write failed: ' + err.message)
145
+ }
146
+ },
147
+ get length() {
148
+ return _results.length
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ const _perfReporter = createPerformanceReporter({
155
+ addon: 'tts-ggml',
156
+ addonType: 'tts'
157
+ })
158
+
159
+ const _reportPath = path.resolve('.', 'test/results/performance-report.json')
160
+ let _reportScheduled = false
161
+
162
+ function _flushPerfReport() {
163
+ if (_perfReporter.length === 0) return
164
+ try {
165
+ _perfReporter.writeReport(_reportPath)
166
+ } catch (_) {}
167
+ try {
168
+ _perfReporter.writeStepSummary()
169
+ } catch (_) {}
170
+ try {
171
+ _perfReporter.writeToConsole()
172
+ } catch (_) {}
173
+ }
174
+
175
+ function _scheduleReportWrite() {
176
+ if (_reportScheduled) return
177
+ _reportScheduled = true
178
+ process.on('exit', _flushPerfReport)
179
+ }
180
+
181
+ function _num(v) {
182
+ return typeof v === 'number' && Number.isFinite(v) ? v : null
183
+ }
184
+
185
+ /**
186
+ * Record a Chatterbox (ggml) synthesis stats row through the shared perf
187
+ * reporter.
188
+ *
189
+ * @param {string} label - Test label, e.g. 'chatterbox english 1'. A leading
190
+ * [CPU]/[GPU] is optional and only used as a fallback
191
+ * hint — the row is tagged from the resolved backend
192
+ * (stats.backendDevice) when available, and the prefix
193
+ * is normalized to match.
194
+ * @param {Object} stats - response.stats from the addon:
195
+ * { realTimeFactor, audioDurationMs, totalSamples,
196
+ * backendDevice, ... }
197
+ * @param {Object} [extra] - Optional overrides:
198
+ * { wallMs, sampleCount, model, output,
199
+ * executionProvider }.
200
+ */
201
+ function recordTtsStats(label, stats, extra) {
202
+ const s = stats || {}
203
+ const resolvedEp = s.backendDevice === 1 ? 'gpu' : s.backendDevice === 0 ? 'cpu' : null
204
+ const epOverride = extra && extra.executionProvider
205
+ const labelEp = /\[gpu\]/i.test(label) ? 'gpu' : /\[cpu\]/i.test(label) ? 'cpu' : null
206
+ const ep = resolvedEp || epOverride || labelEp
207
+
208
+ const baseLabel = label.replace(/^\s*\[(?:cpu|gpu)\]\s*/i, '')
209
+ const finalLabel = ep ? `[${ep.toUpperCase()}] ${baseLabel}` : baseLabel
210
+
211
+ const rtf = _num(s.realTimeFactor)
212
+ const sampleCount =
213
+ (extra && _num(extra.sampleCount)) != null ? _num(extra.sampleCount) : _num(s.totalSamples)
214
+ const audioMs = _num(s.audioDurationMs) != null ? Math.round(_num(s.audioDurationMs)) : null
215
+ const wallMs = extra && _num(extra.wallMs) != null ? Math.round(_num(extra.wallMs)) : null
216
+ const tps = _num(s.tokensPerSecond)
217
+
218
+ // When the addon doesn't report a positive realTimeFactor (e.g. Supertonic /
219
+ // some GPU paths), derive it from the measured wall time and audio duration
220
+ // — the same fallback the RTF benchmark uses — so the row still carries an
221
+ // RTF instead of n/a. Callers that already pass a derived realTimeFactor
222
+ // (addon.test.js) keep it untouched.
223
+ //
224
+ // The two bases aren't comparable (compute-RTF excludes JS/marshalling
225
+ // overhead that wall-RTF includes), so record rtf_source per row: anyone
226
+ // comparing numbers across rows in a single report can see which is which.
227
+ const usingComputeRtf = rtf != null && rtf > 0
228
+ const effRtf = usingComputeRtf ? rtf : wallMs != null && audioMs ? wallMs / audioMs : null
229
+ const rtfSource = usingComputeRtf ? 'compute' : effRtf != null ? 'wall' : null
230
+
231
+ _perfReporter.record(
232
+ finalLabel,
233
+ {
234
+ total_time_ms: wallMs,
235
+ tps,
236
+ real_time_factor: effRtf,
237
+ rtf_source: rtfSource,
238
+ sample_count: sampleCount,
239
+ audio_duration_ms: audioMs
240
+ },
241
+ {
242
+ execution_provider: ep,
243
+ model: (extra && extra.model) || null,
244
+ output: extra && extra.output ? String(extra.output) : null
245
+ }
246
+ )
247
+ _scheduleReportWrite()
248
+
249
+ if (isMobile) {
250
+ try {
251
+ _perfReporter.writeReport()
252
+ } catch (_) {}
253
+ try {
254
+ _perfReporter.writeToConsole()
255
+ } catch (_) {}
256
+ }
257
+
258
+ const lines = [
259
+ `${finalLabel} Performance Metrics (platform=${platformLabel}):`,
260
+ ` - Wall time: ${wallMs !== null ? wallMs + 'ms' : 'n/a'}`,
261
+ ` - RTF: ${effRtf !== null ? effRtf.toFixed(4) + ' (' + rtfSource + ')' : 'n/a'}`,
262
+ ` - Samples: ${sampleCount !== null ? sampleCount : 'n/a'}`,
263
+ ` - Audio: ${audioMs !== null ? audioMs + 'ms' : 'n/a'}`
264
+ ]
265
+ return lines.join('\n')
266
+ }
267
+
268
+ module.exports = {
269
+ platform,
270
+ arch,
271
+ platformLabel,
272
+ isMobile,
273
+ recordTtsStats,
274
+ flushTtsPerfReport: _flushPerfReport
275
+ }
@@ -58,7 +58,7 @@ async function runTTSWithSplit(model, params, expectation = {}, options = {}) {
58
58
  const splitLanguage =
59
59
  typeof params.splitLanguage === 'string' && params.splitLanguage.length > 0
60
60
  ? params.splitLanguage
61
- : model?._config?.language || 'en'
61
+ : model?._setup?.config?.language || 'en'
62
62
  const splitOpts = {
63
63
  language: splitLanguage,
64
64
  mergeToMaxScalars: false,
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ const TTS_TEST_THREADS = 4
4
+
5
+ module.exports = { TTS_TEST_THREADS }