@qvac/tts-ggml 0.10.0 → 0.11.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 +251 -23
- 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 +33 -16
- package/test/integration/addon.test.js +8 -7
- package/test/integration/chatterbox-mtl.test.js +14 -9
- package/test/integration/chatterbox-speed.test.js +4 -2
- package/test/integration/cosyvoice3-clone.test.js +3 -3
- package/test/integration/cosyvoice3-lavasr.test.js +15 -11
- package/test/integration/cosyvoice3.test.js +14 -8
- package/test/integration/fit.test.js +7 -3
- package/test/integration/lavasr-enhancer.test.js +3 -2
- 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/integration/moss.test.js +21 -16
- package/test/integration/multiple-runs.test.js +69 -58
- package/test/integration/output-sample-rate.test.js +4 -2
- package/test/integration/parler-wer.test.js +7 -8
- package/test/integration/parler.test.js +27 -21
- 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
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MOSS-Speech speech-to-speech for @qvac/tts-ggml.
|
|
5
|
+
*
|
|
6
|
+
* MOSS-Speech answers a spoken question with speech, with no text step in
|
|
7
|
+
* between: a 9B language model with separate text and audio branches reads
|
|
8
|
+
* the question's speech tokens and writes the reply's, and the codec speaks
|
|
9
|
+
* them in a 24 kHz voice (the codec's default voice, or a reference WAV).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* bare examples/moss-speech.js question.wav [reply-voice.wav]
|
|
13
|
+
*
|
|
14
|
+
* Examples:
|
|
15
|
+
* bare examples/moss-speech.js test/reference-audio/jfk.wav
|
|
16
|
+
* QVAC_TTS_MOSS_SPEECH_GPU=1 bare examples/moss-speech.js question.wav my-voice.wav
|
|
17
|
+
* QVAC_TTS_MOSS_SPEECH_TEXT=1 bare examples/moss-speech.js question.wav
|
|
18
|
+
*
|
|
19
|
+
* Expects the MOSS-Speech language model (moss-speech-q8_0.gguf or
|
|
20
|
+
* moss-speech-bf16.gguf) and its codec (moss-speech-codec-f16.gguf) under:
|
|
21
|
+
* models/
|
|
22
|
+
* Produce them with engines/tts/scripts/convert-moss-speech-to-gguf.py and
|
|
23
|
+
* convert-moss-speech-codec-to-gguf.py in qvac-fabric-speech.cpp until they
|
|
24
|
+
* are published to the model registry. Desktop only; use a GPU.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const path = require('bare-path')
|
|
28
|
+
const proc = require('bare-process')
|
|
29
|
+
const TTSGgml = require('../')
|
|
30
|
+
const { createWav, readWavAsFloat32 } = require('./wav-helper')
|
|
31
|
+
const { setLogger, releaseLogger } = require('../addonLogging')
|
|
32
|
+
|
|
33
|
+
const SPEECH_SAMPLE_RATE = 24000
|
|
34
|
+
|
|
35
|
+
const argv = global.Bare ? global.Bare.argv : process.argv
|
|
36
|
+
const questionArg = argv[2]
|
|
37
|
+
const voiceArg = argv[3]
|
|
38
|
+
const textReply = proc.env.QVAC_TTS_MOSS_SPEECH_TEXT === '1'
|
|
39
|
+
|
|
40
|
+
function fail(message) {
|
|
41
|
+
console.error(message)
|
|
42
|
+
if (global.Bare) global.Bare.exit(1)
|
|
43
|
+
else process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!questionArg) {
|
|
47
|
+
fail('Usage: moss-speech.js <question.wav> [reply-voice.wav]')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pkgRoot = path.join(__dirname, '..')
|
|
51
|
+
const modelDir = path.join(pkgRoot, 'models')
|
|
52
|
+
|
|
53
|
+
function buildModel() {
|
|
54
|
+
return new TTSGgml({
|
|
55
|
+
engine: TTSGgml.ENGINE_MOSS_SPEECH,
|
|
56
|
+
files: { modelDir },
|
|
57
|
+
config: { useGPU: proc.env.QVAC_TTS_MOSS_SPEECH_GPU === '1' },
|
|
58
|
+
logger: console,
|
|
59
|
+
opts: { stats: true }
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function replyVoiceFields() {
|
|
64
|
+
if (!voiceArg) return {}
|
|
65
|
+
const voice = readWavAsFloat32(path.resolve(voiceArg))
|
|
66
|
+
return { replyVoice: voice.samples, replyVoiceSampleRate: voice.sampleRate }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function reportStats(stats) {
|
|
70
|
+
if (!stats) return
|
|
71
|
+
console.log(
|
|
72
|
+
`Reply stats: totalTime=${stats.totalTime.toFixed(2)}s, ` +
|
|
73
|
+
`audioDuration=${stats.audioDurationMs}ms, promptTokens=${stats.promptTokens}, ` +
|
|
74
|
+
`replyTokens=${stats.replyTokens}, truncated=${stats.truncated}, ` +
|
|
75
|
+
`backendDevice=${stats.backendDevice}, backendId=${stats.backendId}`
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function collectReply(response) {
|
|
80
|
+
const reply = { pcm: [], text: '' }
|
|
81
|
+
await response
|
|
82
|
+
.onUpdate((data) => {
|
|
83
|
+
if (data && data.outputArray) reply.pcm = reply.pcm.concat(Array.from(data.outputArray))
|
|
84
|
+
if (data && typeof data.text === 'string') reply.text += data.text
|
|
85
|
+
})
|
|
86
|
+
.await()
|
|
87
|
+
return reply
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function main() {
|
|
91
|
+
setLogger((priority, message) => {
|
|
92
|
+
if (priority > 1) return
|
|
93
|
+
const names = { 0: 'ERROR', 1: 'WARNING', 2: 'INFO', 3: 'DEBUG', 4: 'OFF' }
|
|
94
|
+
const name = names[priority] || 'UNKNOWN'
|
|
95
|
+
console.log(`[${new Date().toISOString()}] [C++ log] [${name}]: ${message}`)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const outputFile = path.join(__dirname, 'moss-speech-reply.wav')
|
|
99
|
+
const question = readWavAsFloat32(path.resolve(questionArg))
|
|
100
|
+
const model = buildModel()
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
console.log('Loading MOSS-Speech model...')
|
|
104
|
+
await model.load()
|
|
105
|
+
console.log('Model loaded.')
|
|
106
|
+
|
|
107
|
+
console.log(`Answering ${questionArg} (${(question.samples.length / question.sampleRate).toFixed(1)} s)`)
|
|
108
|
+
const response = await model.run({
|
|
109
|
+
type: 'text',
|
|
110
|
+
input: '',
|
|
111
|
+
audio: question.samples,
|
|
112
|
+
sampleRate: question.sampleRate,
|
|
113
|
+
textReply,
|
|
114
|
+
...replyVoiceFields()
|
|
115
|
+
})
|
|
116
|
+
const reply = await collectReply(response)
|
|
117
|
+
|
|
118
|
+
console.log('Reply finished!')
|
|
119
|
+
if (reply.text) console.log(`Reply text: ${reply.text}`)
|
|
120
|
+
reportStats(response.stats)
|
|
121
|
+
|
|
122
|
+
if (reply.pcm.length > 0) {
|
|
123
|
+
createWav(reply.pcm, SPEECH_SAMPLE_RATE, outputFile)
|
|
124
|
+
console.log(`Finished writing to ${outputFile}`)
|
|
125
|
+
}
|
|
126
|
+
} finally {
|
|
127
|
+
await model.unload()
|
|
128
|
+
releaseLogger()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
main().catch((err) => {
|
|
133
|
+
console.error(err)
|
|
134
|
+
if (global.Bare) global.Bare.exit(1)
|
|
135
|
+
else process.exit(1)
|
|
136
|
+
})
|
|
@@ -28,18 +28,41 @@
|
|
|
28
28
|
const fs = require('bare-fs')
|
|
29
29
|
const os = require('bare-os')
|
|
30
30
|
const path = require('bare-path')
|
|
31
|
-
const { spawn, spawnSync } = require('bare-subprocess')
|
|
32
31
|
const { createWav } = require('./wav-helper')
|
|
33
32
|
|
|
33
|
+
const MISSING_SUBPROCESS_WARNING =
|
|
34
|
+
'[pcm-chunk-player] bare-subprocess is not installed, so audio will not play. ' +
|
|
35
|
+
'Install it for live playback: `npm install bare-subprocess`.'
|
|
36
|
+
|
|
37
|
+
const subprocess = loadSubprocess()
|
|
38
|
+
|
|
34
39
|
let _seq = 0
|
|
35
40
|
let _hasFfplay
|
|
36
41
|
let _hasPlay
|
|
37
42
|
let _hasAplay
|
|
43
|
+
let _subprocessWarned = false
|
|
44
|
+
|
|
45
|
+
function loadSubprocess () {
|
|
46
|
+
try {
|
|
47
|
+
return require('bare-subprocess')
|
|
48
|
+
} catch {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function hasSubprocess () {
|
|
54
|
+
if (subprocess !== null) return true
|
|
55
|
+
if (!_subprocessWarned) {
|
|
56
|
+
console.warn(MISSING_SUBPROCESS_WARNING)
|
|
57
|
+
_subprocessWarned = true
|
|
58
|
+
}
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
38
61
|
|
|
39
62
|
function syncOk (cmd, args) {
|
|
40
63
|
try {
|
|
41
|
-
const r = spawnSync(cmd, args, { stdio: ['ignore', 'ignore', 'ignore'] })
|
|
42
|
-
return
|
|
64
|
+
const r = subprocess.spawnSync(cmd, args, { stdio: ['ignore', 'ignore', 'ignore'] })
|
|
65
|
+
return !r.error && r.status === 0
|
|
43
66
|
} catch {
|
|
44
67
|
return false
|
|
45
68
|
}
|
|
@@ -64,6 +87,7 @@ function detectAplay () {
|
|
|
64
87
|
}
|
|
65
88
|
|
|
66
89
|
function canPlayPcmChunks () {
|
|
90
|
+
if (!hasSubprocess()) return false
|
|
67
91
|
if (detectFfplay()) return true
|
|
68
92
|
if (detectPlay()) return true
|
|
69
93
|
if (detectAplay()) return true
|
|
@@ -87,7 +111,7 @@ function unlinkQuiet (p) {
|
|
|
87
111
|
function spawnAsync (cmd, args, opts) {
|
|
88
112
|
return new Promise((resolve, reject) => {
|
|
89
113
|
try {
|
|
90
|
-
const child = spawn(cmd, args, opts)
|
|
114
|
+
const child = subprocess.spawn(cmd, args, opts)
|
|
91
115
|
child.on('exit', (code) => resolve(code))
|
|
92
116
|
child.on('error', reject)
|
|
93
117
|
} catch (err) {
|
|
@@ -106,6 +130,7 @@ function spawnAsync (cmd, args, opts) {
|
|
|
106
130
|
* `sampleRate` defaults to 24000 (Chatterbox native rate).
|
|
107
131
|
*/
|
|
108
132
|
function createStreamingPlayer ({ sampleRate = 24000, channels = 1 } = {}) {
|
|
133
|
+
if (!hasSubprocess()) return null
|
|
109
134
|
// sox `play` is preferred on darwin: on some macOS builds ffplay's
|
|
110
135
|
// SDL output is silent for raw-piped audio; sox uses CoreAudio
|
|
111
136
|
// directly and works reliably. (qvac-tts.cpp's README documents
|
|
@@ -164,7 +189,7 @@ function tryAplay (sampleRate, channels) {
|
|
|
164
189
|
}
|
|
165
190
|
|
|
166
191
|
function spawnStreamingPlayer (cmd, args) {
|
|
167
|
-
const child = spawn(cmd, args, { stdio: ['pipe', 'ignore', 'pipe'] })
|
|
192
|
+
const child = subprocess.spawn(cmd, args, { stdio: ['pipe', 'ignore', 'pipe'] })
|
|
168
193
|
let exited = false
|
|
169
194
|
let exitResolve
|
|
170
195
|
const exitPromise = new Promise((resolve) => { exitResolve = resolve })
|
|
@@ -255,7 +280,7 @@ function createAfplayFallback (sampleRate) {
|
|
|
255
280
|
|
|
256
281
|
function playInt16ChunkSync (samples, sampleRate) {
|
|
257
282
|
const arr = samples instanceof Int16Array ? samples : Int16Array.from(samples)
|
|
258
|
-
if (arr.length === 0) return
|
|
283
|
+
if (arr.length === 0 || !hasSubprocess()) return
|
|
259
284
|
|
|
260
285
|
const id = `${Date.now()}-${++_seq}`
|
|
261
286
|
const tmpDir = os.tmpdir()
|
|
@@ -264,14 +289,14 @@ function playInt16ChunkSync (samples, sampleRate) {
|
|
|
264
289
|
if (plat === 'darwin') {
|
|
265
290
|
const tmpWav = path.join(tmpDir, `qvac-tts-stream-${id}.wav`)
|
|
266
291
|
createWav(Array.from(arr), sampleRate, tmpWav)
|
|
267
|
-
spawnSync('afplay', [tmpWav], { stdio: 'ignore' })
|
|
292
|
+
subprocess.spawnSync('afplay', [tmpWav], { stdio: 'ignore' })
|
|
268
293
|
unlinkQuiet(tmpWav)
|
|
269
294
|
return
|
|
270
295
|
}
|
|
271
296
|
if (detectFfplay()) {
|
|
272
297
|
const tmpWav = path.join(tmpDir, `qvac-tts-stream-${id}.wav`)
|
|
273
298
|
createWav(Array.from(arr), sampleRate, tmpWav)
|
|
274
|
-
spawnSync(
|
|
299
|
+
subprocess.spawnSync(
|
|
275
300
|
'ffplay',
|
|
276
301
|
['-nodisp', '-autoexit', '-loglevel', 'error', '-i', tmpWav],
|
|
277
302
|
{ stdio: 'ignore' }
|
|
@@ -282,7 +307,7 @@ function playInt16ChunkSync (samples, sampleRate) {
|
|
|
282
307
|
if (detectAplay()) {
|
|
283
308
|
const rawPath = path.join(tmpDir, `qvac-tts-stream-${id}.raw`)
|
|
284
309
|
fs.writeFileSync(rawPath, toInt16Buffer(arr))
|
|
285
|
-
spawnSync(
|
|
310
|
+
subprocess.spawnSync(
|
|
286
311
|
'aplay',
|
|
287
312
|
['-q', '-t', 'raw', '-f', 'S16_LE', '-r', String(sampleRate), '-c', '1', rawPath],
|
|
288
313
|
{ stdio: 'ignore' }
|
|
@@ -293,7 +318,7 @@ function playInt16ChunkSync (samples, sampleRate) {
|
|
|
293
318
|
|
|
294
319
|
async function playInt16Chunk (samples, sampleRate) {
|
|
295
320
|
const arr = samples instanceof Int16Array ? samples : Int16Array.from(samples)
|
|
296
|
-
if (arr.length === 0) return
|
|
321
|
+
if (arr.length === 0 || !hasSubprocess()) return
|
|
297
322
|
|
|
298
323
|
const id = `${Date.now()}-${++_seq}`
|
|
299
324
|
const tmpDir = os.tmpdir()
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type QvacResponse } from "@qvac/infer-base";
|
|
2
2
|
import * as errorModule from "./lib/error";
|
|
3
3
|
import { resolveBackendsDir as resolveBackendsDirImpl } from "./lib/backends";
|
|
4
|
-
import { assessFit as assessFitImpl, type Audio8FitRequest, type MossFitRequest, type ChatterboxFitRequest, type CosyvoiceFitRequest, type ParlerFitRequest, type SupertonicFitRequest, type TtsFitEngine, type TtsFitRequest, type TtsFitResult, type TtsFitStatus } from "./lib/fit";
|
|
4
|
+
import { assessFit as assessFitImpl, type Audio8FitRequest, type MossFitRequest, type MossSoundEffectFitRequest, type ChatterboxFitRequest, type CosyvoiceFitRequest, type ParlerFitRequest, type SupertonicFitRequest, type TtsFitEngine, type TtsFitRequest, type TtsFitResult, type TtsFitStatus } from "./lib/fit";
|
|
5
5
|
import { type SentenceDelimiterPreset } from "./lib/textStreamAccumulator";
|
|
6
6
|
declare const ENGINE_CHATTERBOX = "chatterbox";
|
|
7
7
|
declare const ENGINE_SUPERTONIC = "supertonic";
|
|
@@ -9,6 +9,8 @@ declare const ENGINE_COSYVOICE3 = "cosyvoice3";
|
|
|
9
9
|
declare const ENGINE_PARLER = "parler";
|
|
10
10
|
declare const ENGINE_AUDIO8 = "audio8";
|
|
11
11
|
declare const ENGINE_MOSS = "moss";
|
|
12
|
+
declare const ENGINE_MOSS_SFX = "moss-sfx";
|
|
13
|
+
declare const ENGINE_MOSS_SPEECH = "moss-speech";
|
|
12
14
|
declare const ENGINE_POCKET = "pocket";
|
|
13
15
|
declare const COSYVOICE_DIALECTS: {
|
|
14
16
|
readonly cantonese: "广东话";
|
|
@@ -57,7 +59,8 @@ declare const EMOTIONS: readonly ["command", "anger", "narration", "conversation
|
|
|
57
59
|
declare const PACES: readonly ["slow", "moderate", "fast"];
|
|
58
60
|
type Emotion = (typeof EMOTIONS)[number];
|
|
59
61
|
type Pace = (typeof PACES)[number];
|
|
60
|
-
|
|
62
|
+
declare const MOSS_SPEECH_ROLES: readonly ["system", "user", "assistant"];
|
|
63
|
+
type EngineType = typeof ENGINE_CHATTERBOX | typeof ENGINE_SUPERTONIC | typeof ENGINE_COSYVOICE3 | typeof ENGINE_PARLER | typeof ENGINE_AUDIO8 | typeof ENGINE_MOSS | typeof ENGINE_MOSS_SFX | typeof ENGINE_MOSS_SPEECH | typeof ENGINE_POCKET;
|
|
61
64
|
/**
|
|
62
65
|
* Model file paths for the GGML TTS backend. Engine is auto-detected
|
|
63
66
|
* from these fields (Chatterbox vs Supertonic) unless overridden via
|
|
@@ -120,6 +123,25 @@ interface TTSGgmlFiles {
|
|
|
120
123
|
*/
|
|
121
124
|
mossCodecEncoder?: string;
|
|
122
125
|
mossCodecEncoderPath?: string;
|
|
126
|
+
/**
|
|
127
|
+
* MOSS-SoundEffect GGUF (`moss-sfx-*.gguf`): text encoder, DiT and VAE in
|
|
128
|
+
* one file. Routes to the `moss-sfx` engine. Overrides `modelDir`.
|
|
129
|
+
*/
|
|
130
|
+
mossSoundEffect?: string;
|
|
131
|
+
mossSoundEffectPath?: string;
|
|
132
|
+
/**
|
|
133
|
+
* MOSS-Speech language model GGUF (`moss-speech-*.gguf`, e.g.
|
|
134
|
+
* `moss-speech-q8_0.gguf`). Routes to the `moss-speech` engine together
|
|
135
|
+
* with `mossSpeechCodec`. Overrides `modelDir`.
|
|
136
|
+
*/
|
|
137
|
+
mossSpeechModel?: string;
|
|
138
|
+
mossSpeechModelPath?: string;
|
|
139
|
+
/**
|
|
140
|
+
* MOSS-Speech codec GGUF (`moss-speech-codec-*.gguf`): speech tokenizer,
|
|
141
|
+
* reply decoder and the default reply voice. Overrides `modelDir`.
|
|
142
|
+
*/
|
|
143
|
+
mossSpeechCodec?: string;
|
|
144
|
+
mossSpeechCodecPath?: string;
|
|
123
145
|
/**
|
|
124
146
|
* CosyVoice3 model directory holding the sub-model GGUFs
|
|
125
147
|
* (`cosyvoice3-{llm,flow,hift}-*.gguf`) plus `voice.gguf`, `vocab.json` and
|
|
@@ -310,6 +332,66 @@ interface Audio8VoiceFields {
|
|
|
310
332
|
/** Audio8: what `referenceAudio` says. Required when cloning. */
|
|
311
333
|
referenceText?: string;
|
|
312
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* MOSS-SoundEffect per-call generation controls (moss-sfx only). Each one is
|
|
337
|
+
* optional; unset `steps` / `guidance` / `shift` use the defaults stored in the
|
|
338
|
+
* model file (100 steps, guidance 4, shift 5).
|
|
339
|
+
*/
|
|
340
|
+
interface MossSoundEffectFields {
|
|
341
|
+
/** Length of the sound effect, in (0, 30] seconds, rounded to 0.1 s. Defaults to 10. */
|
|
342
|
+
seconds?: number;
|
|
343
|
+
/** What the sound effect should avoid (classifier-free guidance negative). */
|
|
344
|
+
negativePrompt?: string;
|
|
345
|
+
/** Diffusion steps, 1..1000. More steps cost proportionally more time. */
|
|
346
|
+
steps?: number;
|
|
347
|
+
/** Classifier-free guidance scale, 1..50; 1 skips the negative branch. */
|
|
348
|
+
guidance?: number;
|
|
349
|
+
/** Flow-matching schedule shift, (0, 100]. */
|
|
350
|
+
shift?: number;
|
|
351
|
+
}
|
|
352
|
+
/** Mono PCM accepted by the MOSS-Speech engine: 16-bit integers or floats in [-1, 1]. */
|
|
353
|
+
type MossSpeechPcm = Int16Array | Float32Array;
|
|
354
|
+
/** One conversation turn for the MOSS-Speech engine: text or audio, never both. */
|
|
355
|
+
interface MossSpeechMessage {
|
|
356
|
+
role: (typeof MOSS_SPEECH_ROLES)[number];
|
|
357
|
+
text?: string;
|
|
358
|
+
audio?: MossSpeechPcm;
|
|
359
|
+
/** Sample rate of `audio`, 8000..192000 Hz. */
|
|
360
|
+
sampleRate?: number;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* MOSS-Speech per-call fields (moss-speech only). The user turn is `audio`
|
|
364
|
+
* (with `sampleRate`) or the run `input` text; `messages` carries the earlier
|
|
365
|
+
* conversation.
|
|
366
|
+
*/
|
|
367
|
+
interface MossSpeechFields {
|
|
368
|
+
/** The user's spoken turn, mono PCM. */
|
|
369
|
+
audio?: MossSpeechPcm;
|
|
370
|
+
/** Sample rate of `audio`, 8000..192000 Hz. */
|
|
371
|
+
sampleRate?: number;
|
|
372
|
+
/** Earlier turns, oldest first; the new user turn is appended after them. */
|
|
373
|
+
messages?: MossSpeechMessage[];
|
|
374
|
+
/** A system turn placed first; replaces the model's default system prompt. */
|
|
375
|
+
systemPrompt?: string;
|
|
376
|
+
/** Reply voice prompt (up to 60 s of mono PCM); the codec's default voice otherwise. */
|
|
377
|
+
replyVoice?: MossSpeechPcm;
|
|
378
|
+
/** Sample rate of `replyVoice`, 8000..192000 Hz. */
|
|
379
|
+
replyVoiceSampleRate?: number;
|
|
380
|
+
/** Reply with text only (no audio); the text comes back as `text`. */
|
|
381
|
+
textReply?: boolean;
|
|
382
|
+
/** Cut the spoken reply after this many seconds (0 = no cut). */
|
|
383
|
+
maxReplySeconds?: number;
|
|
384
|
+
/** Explicit bound on generated rows, 1..4096; omitted uses remaining model context. */
|
|
385
|
+
maxNewTokens?: number;
|
|
386
|
+
/** Greedy decoding instead of sampling. */
|
|
387
|
+
greedy?: boolean;
|
|
388
|
+
/** Sampling temperature, (0, 10] (default 0.7). */
|
|
389
|
+
temperature?: number;
|
|
390
|
+
/** Nucleus sampling, (0, 1] (default 0.95). */
|
|
391
|
+
topP?: number;
|
|
392
|
+
/** Top-k sampling, >= 0 (default 20). */
|
|
393
|
+
topK?: number;
|
|
394
|
+
}
|
|
313
395
|
/**
|
|
314
396
|
* CosyVoice3 per-call instruction. Same values as the constructor's
|
|
315
397
|
* `instruct`; a per-call conditioning control replaces the configured one for
|
|
@@ -333,9 +415,10 @@ interface TTSGgmlOptions extends ParlerDescriptionFields, Audio8VoiceFields, TTS
|
|
|
333
415
|
/** Chatterbox: directory of baked voice-conditioning tensors. */
|
|
334
416
|
voiceDir?: string;
|
|
335
417
|
/**
|
|
336
|
-
* RNG seed for Chatterbox CFM/SineGen, Supertonic latent generation,
|
|
337
|
-
*
|
|
338
|
-
*
|
|
418
|
+
* RNG seed for Chatterbox CFM/SineGen, Supertonic latent generation,
|
|
419
|
+
* the MOSS-SoundEffect diffusion noise (0 when unset, so an unseeded prompt
|
|
420
|
+
* repeats its clip), or Pocket's portable sampling RNG. Pocket accepts
|
|
421
|
+
* integers from 0 to 4294967295 (inclusive).
|
|
339
422
|
*/
|
|
340
423
|
seed?: number;
|
|
341
424
|
/**
|
|
@@ -590,6 +673,8 @@ interface TTSOutputChunk {
|
|
|
590
673
|
isLast?: boolean;
|
|
591
674
|
/** Signed 16-bit mono PCM audio payload. */
|
|
592
675
|
outputArray: Int16Array;
|
|
676
|
+
/** MOSS-Speech: the reply's text channel (the whole answer with `textReply`). */
|
|
677
|
+
text?: string;
|
|
593
678
|
/**
|
|
594
679
|
* Output sample rate. The native engine rate (24000 for Chatterbox,
|
|
595
680
|
* CosyVoice3 and MOSS; 44100 for Supertonic, Parler, and Audio8), or 48000
|
|
@@ -718,7 +803,7 @@ interface RunStreamingOptions extends ParlerDescriptionFields, Audio8VoiceFields
|
|
|
718
803
|
}
|
|
719
804
|
/** Input accepted by `runStreaming`. */
|
|
720
805
|
type TextStreamInput = string | string[] | Iterable<string> | AsyncIterable<string>;
|
|
721
|
-
interface TTSRunInput extends ParlerDescriptionFields, Audio8VoiceFields, TTSConditioningFields, CosyvoiceJobFields {
|
|
806
|
+
interface TTSRunInput extends ParlerDescriptionFields, Audio8VoiceFields, TTSConditioningFields, CosyvoiceJobFields, MossSoundEffectFields, MossSpeechFields {
|
|
722
807
|
type?: string;
|
|
723
808
|
input: string;
|
|
724
809
|
streamOutput?: boolean;
|
|
@@ -750,6 +835,8 @@ declare class TTSGgml {
|
|
|
750
835
|
static readonly ENGINE_PARLER = "parler";
|
|
751
836
|
static readonly ENGINE_AUDIO8 = "audio8";
|
|
752
837
|
static readonly ENGINE_MOSS = "moss";
|
|
838
|
+
static readonly ENGINE_MOSS_SFX = "moss-sfx";
|
|
839
|
+
static readonly ENGINE_MOSS_SPEECH = "moss-speech";
|
|
753
840
|
static readonly ENGINE_POCKET = "pocket";
|
|
754
841
|
opts: object;
|
|
755
842
|
exclusiveRun: boolean;
|
|
@@ -829,6 +916,9 @@ declare class TTSGgml {
|
|
|
829
916
|
private _mossBackbonePath?;
|
|
830
917
|
private _mossCodecDecoderPath?;
|
|
831
918
|
private _mossCodecEncoderPath?;
|
|
919
|
+
private _mossSoundEffectPath?;
|
|
920
|
+
private _mossSpeechModelPath?;
|
|
921
|
+
private _mossSpeechCodecPath?;
|
|
832
922
|
private _dialogueReferences?;
|
|
833
923
|
private _durationTokens?;
|
|
834
924
|
private _referenceText?;
|
|
@@ -865,6 +955,10 @@ declare class TTSGgml {
|
|
|
865
955
|
*/
|
|
866
956
|
private _assertEngineScopedOptions;
|
|
867
957
|
private _assertAudio8OptionConsistency;
|
|
958
|
+
private _assertMossSoundEffectOptionConsistency;
|
|
959
|
+
private _assertMossSpeechOptionConsistency;
|
|
960
|
+
private _assertMossSpeechOutputRate;
|
|
961
|
+
private _assertMossSoundEffectOutputRate;
|
|
868
962
|
private _assertMossOptionConsistency;
|
|
869
963
|
private _assertNoMossOnlyOptions;
|
|
870
964
|
private _assertMossDialogueReferences;
|
|
@@ -917,6 +1011,18 @@ declare class TTSGgml {
|
|
|
917
1011
|
* streaming options. Returns undefined when none are present.
|
|
918
1012
|
*/
|
|
919
1013
|
private _resolveAudio8JobFields;
|
|
1014
|
+
private _assertNoSpeechFields;
|
|
1015
|
+
private _assertNoOtherEngineFields;
|
|
1016
|
+
/**
|
|
1017
|
+
* Validate the MOSS-Speech call and fold the new user turn (spoken `audio`
|
|
1018
|
+
* or text `input`) and `systemPrompt` into the native `messages` list.
|
|
1019
|
+
*/
|
|
1020
|
+
private _resolveSpeechJobFields;
|
|
1021
|
+
/**
|
|
1022
|
+
* Extract + validate the per-call MOSS-SoundEffect controls from a run
|
|
1023
|
+
* input. Returns undefined when none are present.
|
|
1024
|
+
*/
|
|
1025
|
+
private _resolveSoundEffectJobFields;
|
|
920
1026
|
/**
|
|
921
1027
|
* The per-call fields of whichever engine is loaded, if any are set. Parler
|
|
922
1028
|
* takes the full description/template surface, Audio8 its voice override,
|
|
@@ -969,6 +1075,8 @@ declare class TTSGgml {
|
|
|
969
1075
|
private _buildParlerParams;
|
|
970
1076
|
private _buildAudio8Params;
|
|
971
1077
|
private _buildMossParams;
|
|
1078
|
+
private _buildMossSoundEffectParams;
|
|
1079
|
+
private _buildMossSpeechParams;
|
|
972
1080
|
/** The knobs every engine in SAMPLING_ENGINES reads. */
|
|
973
1081
|
private _assignSamplingParams;
|
|
974
1082
|
/**
|
|
@@ -1072,6 +1180,7 @@ type NamespaceParlerFit = ParlerFitRequest;
|
|
|
1072
1180
|
type NamespaceChatterboxFit = ChatterboxFitRequest;
|
|
1073
1181
|
type NamespaceAudio8Fit = Audio8FitRequest;
|
|
1074
1182
|
type NamespaceMossFit = MossFitRequest;
|
|
1183
|
+
type NamespaceMossSoundEffectFit = MossSoundEffectFitRequest;
|
|
1075
1184
|
type NamespaceCosyvoiceFit = CosyvoiceFitRequest;
|
|
1076
1185
|
declare namespace TTSGgml {
|
|
1077
1186
|
export import QvacErrorAddonTTSGgml = errorModule.QvacErrorAddonTTSGgml;
|
|
@@ -1100,6 +1209,7 @@ declare namespace TTSGgml {
|
|
|
1100
1209
|
type ChatterboxFitRequest = NamespaceChatterboxFit;
|
|
1101
1210
|
type Audio8FitRequest = NamespaceAudio8Fit;
|
|
1102
1211
|
type MossFitRequest = NamespaceMossFit;
|
|
1212
|
+
type MossSoundEffectFitRequest = NamespaceMossSoundEffectFit;
|
|
1103
1213
|
type CosyvoiceFitRequest = NamespaceCosyvoiceFit;
|
|
1104
1214
|
const resolveBackendsDir: typeof resolveBackendsDirImpl;
|
|
1105
1215
|
const assessFit: typeof assessFitImpl;
|