react-native-sherpa-onnx 0.3.0 → 0.3.2
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 +21 -7
- package/SherpaOnnx.podspec +1 -1
- package/android/build.gradle +35 -26
- package/android/prebuilt-download.gradle +27 -14
- package/android/src/main/cpp/CMakeLists.txt +51 -17
- package/android/src/main/cpp/jni/archive/sherpa-onnx-archive-helper.cpp +14 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +16 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +3 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +19 -2
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +2 -1
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +1 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +114 -8
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxOnlineSttHelper.kt +535 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +10 -10
- package/ios/SherpaOnnx+OnlineSTT.mm +365 -0
- package/ios/SherpaOnnx+TTS.mm +35 -9
- package/ios/SherpaOnnx.mm +6 -0
- package/ios/model_detect/sherpa-onnx-model-detect-helper.h +3 -0
- package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +16 -0
- package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +19 -2
- package/ios/model_detect/sherpa-onnx-model-detect.h +2 -1
- package/ios/online_stt/sherpa-onnx-online-stt-wrapper.h +85 -0
- package/ios/online_stt/sherpa-onnx-online-stt-wrapper.mm +270 -0
- package/lib/module/NativeSherpaOnnx.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/stt/index.js +4 -0
- package/lib/module/stt/index.js.map +1 -1
- package/lib/module/stt/streaming.js +257 -0
- package/lib/module/stt/streaming.js.map +1 -0
- package/lib/module/stt/streamingTypes.js +38 -0
- package/lib/module/stt/streamingTypes.js.map +1 -0
- package/lib/module/tts/index.js +4 -43
- package/lib/module/tts/index.js.map +1 -1
- package/lib/module/tts/streaming.js +220 -0
- package/lib/module/tts/streaming.js.map +1 -0
- package/lib/module/tts/streamingTypes.js +4 -0
- package/lib/module/tts/streamingTypes.js.map +1 -0
- package/lib/module/tts/types.js +8 -1
- package/lib/module/tts/types.js.map +1 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts +66 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
- package/lib/typescript/src/stt/index.d.ts +3 -0
- package/lib/typescript/src/stt/index.d.ts.map +1 -1
- package/lib/typescript/src/stt/streaming.d.ts +42 -0
- package/lib/typescript/src/stt/streaming.d.ts.map +1 -0
- package/lib/typescript/src/stt/streamingTypes.d.ts +122 -0
- package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -0
- package/lib/typescript/src/tts/index.d.ts +3 -1
- package/lib/typescript/src/tts/index.d.ts.map +1 -1
- package/lib/typescript/src/tts/streaming.d.ts +24 -0
- package/lib/typescript/src/tts/streaming.d.ts.map +1 -0
- package/lib/typescript/src/tts/streamingTypes.d.ts +27 -0
- package/lib/typescript/src/tts/streamingTypes.d.ts.map +1 -0
- package/lib/typescript/src/tts/types.d.ts +19 -6
- package/lib/typescript/src/tts/types.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/NativeSherpaOnnx.ts +95 -0
- package/src/index.tsx +2 -2
- package/src/stt/index.ts +17 -0
- package/src/stt/streaming.ts +361 -0
- package/src/stt/streamingTypes.ts +151 -0
- package/src/tts/index.ts +6 -66
- package/src/tts/streaming.ts +336 -0
- package/src/tts/streamingTypes.ts +54 -0
- package/src/tts/types.ts +20 -10
- package/android/codegen.gradle +0 -57
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { DeviceEventEmitter } from 'react-native';
|
|
2
|
+
import SherpaOnnx from '../NativeSherpaOnnx';
|
|
3
|
+
import type {
|
|
4
|
+
TTSInitializeOptions,
|
|
5
|
+
TTSModelType,
|
|
6
|
+
TtsModelOptions,
|
|
7
|
+
TtsGenerationOptions,
|
|
8
|
+
TtsStreamChunk,
|
|
9
|
+
TtsStreamEnd,
|
|
10
|
+
TtsStreamError,
|
|
11
|
+
TtsStreamHandlers,
|
|
12
|
+
TtsStreamController,
|
|
13
|
+
TTSModelInfo,
|
|
14
|
+
} from './types';
|
|
15
|
+
import type { StreamingTtsEngine } from './streamingTypes';
|
|
16
|
+
import type { ModelPathConfig } from '../types';
|
|
17
|
+
import { resolveModelPath } from '../utils';
|
|
18
|
+
|
|
19
|
+
let streamingTtsInstanceCounter = 0;
|
|
20
|
+
let ttsRequestIdCounter = 0;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Flatten model-specific options for the given model type to native init params.
|
|
24
|
+
*/
|
|
25
|
+
function flattenTtsModelOptionsForNative(
|
|
26
|
+
modelType: TTSModelType | undefined,
|
|
27
|
+
modelOptions: TtsModelOptions | undefined
|
|
28
|
+
): {
|
|
29
|
+
noiseScale: number | undefined;
|
|
30
|
+
noiseScaleW: number | undefined;
|
|
31
|
+
lengthScale: number | undefined;
|
|
32
|
+
} {
|
|
33
|
+
if (
|
|
34
|
+
!modelOptions ||
|
|
35
|
+
!modelType ||
|
|
36
|
+
modelType === 'auto' ||
|
|
37
|
+
modelType === 'zipvoice'
|
|
38
|
+
)
|
|
39
|
+
return {
|
|
40
|
+
noiseScale: undefined,
|
|
41
|
+
noiseScaleW: undefined,
|
|
42
|
+
lengthScale: undefined,
|
|
43
|
+
};
|
|
44
|
+
const block =
|
|
45
|
+
modelType === 'vits'
|
|
46
|
+
? modelOptions.vits
|
|
47
|
+
: modelType === 'matcha'
|
|
48
|
+
? modelOptions.matcha
|
|
49
|
+
: modelType === 'kokoro'
|
|
50
|
+
? modelOptions.kokoro
|
|
51
|
+
: modelType === 'kitten'
|
|
52
|
+
? modelOptions.kitten
|
|
53
|
+
: modelType === 'pocket'
|
|
54
|
+
? modelOptions.pocket
|
|
55
|
+
: undefined;
|
|
56
|
+
if (!block)
|
|
57
|
+
return {
|
|
58
|
+
noiseScale: undefined,
|
|
59
|
+
noiseScaleW: undefined,
|
|
60
|
+
lengthScale: undefined,
|
|
61
|
+
};
|
|
62
|
+
const n = block as {
|
|
63
|
+
noiseScale?: number;
|
|
64
|
+
noiseScaleW?: number;
|
|
65
|
+
lengthScale?: number;
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
noiseScale:
|
|
69
|
+
n.noiseScale !== undefined && typeof n.noiseScale === 'number'
|
|
70
|
+
? n.noiseScale
|
|
71
|
+
: undefined,
|
|
72
|
+
noiseScaleW:
|
|
73
|
+
n.noiseScaleW !== undefined && typeof n.noiseScaleW === 'number'
|
|
74
|
+
? n.noiseScaleW
|
|
75
|
+
: undefined,
|
|
76
|
+
lengthScale:
|
|
77
|
+
n.lengthScale !== undefined && typeof n.lengthScale === 'number'
|
|
78
|
+
? n.lengthScale
|
|
79
|
+
: undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function toNativeTtsOptions(
|
|
84
|
+
options?: TtsGenerationOptions
|
|
85
|
+
): Record<string, unknown> {
|
|
86
|
+
if (options == null) return {};
|
|
87
|
+
const out: Record<string, unknown> = {};
|
|
88
|
+
if (options.sid !== undefined) out.sid = options.sid;
|
|
89
|
+
if (options.speed !== undefined) out.speed = options.speed;
|
|
90
|
+
if (options.silenceScale !== undefined)
|
|
91
|
+
out.silenceScale = options.silenceScale;
|
|
92
|
+
if (options.referenceAudio != null) {
|
|
93
|
+
out.referenceAudio = options.referenceAudio.samples;
|
|
94
|
+
out.referenceSampleRate = options.referenceAudio.sampleRate;
|
|
95
|
+
}
|
|
96
|
+
if (options.referenceText !== undefined)
|
|
97
|
+
out.referenceText = options.referenceText;
|
|
98
|
+
if (options.numSteps !== undefined) out.numSteps = options.numSteps;
|
|
99
|
+
if (options.extra != null && Object.keys(options.extra).length > 0)
|
|
100
|
+
out.extra = options.extra;
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a streaming TTS engine instance. Use for incremental generation with
|
|
106
|
+
* chunk callbacks and PCM playback. Call destroy() when done.
|
|
107
|
+
*
|
|
108
|
+
* @param options - TTS initialization options or model path configuration
|
|
109
|
+
* @returns Promise resolving to a StreamingTtsEngine instance
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const tts = await createStreamingTTS({
|
|
113
|
+
* modelPath: { type: 'asset', path: 'models/vits-piper-en' },
|
|
114
|
+
* modelType: 'vits',
|
|
115
|
+
* });
|
|
116
|
+
* const controller = await tts.generateSpeechStream('Hello', undefined, {
|
|
117
|
+
* onChunk: (chunk) => playPcm(chunk.samples, chunk.sampleRate),
|
|
118
|
+
* onEnd: () => {},
|
|
119
|
+
* });
|
|
120
|
+
* await tts.destroy();
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export async function createStreamingTTS(
|
|
124
|
+
options: TTSInitializeOptions | ModelPathConfig
|
|
125
|
+
): Promise<StreamingTtsEngine> {
|
|
126
|
+
const instanceId = `streaming_tts_${++streamingTtsInstanceCounter}`;
|
|
127
|
+
|
|
128
|
+
let modelPath: ModelPathConfig;
|
|
129
|
+
let modelType: TTSModelType | undefined;
|
|
130
|
+
let provider: string | undefined;
|
|
131
|
+
let numThreads: number | undefined;
|
|
132
|
+
let debug: boolean | undefined;
|
|
133
|
+
let modelOptions: TtsModelOptions | undefined;
|
|
134
|
+
let ruleFsts: string | undefined;
|
|
135
|
+
let ruleFars: string | undefined;
|
|
136
|
+
let maxNumSentences: number | undefined;
|
|
137
|
+
let silenceScale: number | undefined;
|
|
138
|
+
|
|
139
|
+
if ('modelPath' in options) {
|
|
140
|
+
modelPath = options.modelPath;
|
|
141
|
+
modelType = options.modelType;
|
|
142
|
+
provider = options.provider;
|
|
143
|
+
numThreads = options.numThreads;
|
|
144
|
+
debug = options.debug;
|
|
145
|
+
modelOptions = options.modelOptions;
|
|
146
|
+
ruleFsts = options.ruleFsts;
|
|
147
|
+
ruleFars = options.ruleFars;
|
|
148
|
+
maxNumSentences = options.maxNumSentences;
|
|
149
|
+
silenceScale = options.silenceScale;
|
|
150
|
+
} else {
|
|
151
|
+
modelPath = options;
|
|
152
|
+
modelType = undefined;
|
|
153
|
+
provider = undefined;
|
|
154
|
+
numThreads = undefined;
|
|
155
|
+
debug = undefined;
|
|
156
|
+
modelOptions = undefined;
|
|
157
|
+
ruleFsts = undefined;
|
|
158
|
+
ruleFars = undefined;
|
|
159
|
+
maxNumSentences = undefined;
|
|
160
|
+
silenceScale = undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const flat = flattenTtsModelOptionsForNative(modelType, modelOptions);
|
|
164
|
+
const resolvedPath = await resolveModelPath(modelPath);
|
|
165
|
+
|
|
166
|
+
const result = await SherpaOnnx.initializeTts(
|
|
167
|
+
instanceId,
|
|
168
|
+
resolvedPath,
|
|
169
|
+
modelType ?? 'auto',
|
|
170
|
+
numThreads ?? 2,
|
|
171
|
+
debug ?? false,
|
|
172
|
+
flat.noiseScale,
|
|
173
|
+
flat.noiseScaleW,
|
|
174
|
+
flat.lengthScale,
|
|
175
|
+
ruleFsts,
|
|
176
|
+
ruleFars,
|
|
177
|
+
maxNumSentences,
|
|
178
|
+
silenceScale,
|
|
179
|
+
provider
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (!result.success) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`TTS initialization failed: ${JSON.stringify(
|
|
185
|
+
result.detectedModels ?? []
|
|
186
|
+
)}`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let destroyed = false;
|
|
191
|
+
|
|
192
|
+
const guard = () => {
|
|
193
|
+
if (destroyed) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Streaming TTS instance ${instanceId} has been destroyed; cannot call methods on it.`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const engine: StreamingTtsEngine = {
|
|
201
|
+
get instanceId() {
|
|
202
|
+
return instanceId;
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
async generateSpeechStream(
|
|
206
|
+
text: string,
|
|
207
|
+
opts: TtsGenerationOptions | undefined,
|
|
208
|
+
handlers: TtsStreamHandlers
|
|
209
|
+
): Promise<TtsStreamController> {
|
|
210
|
+
guard();
|
|
211
|
+
const requestId = `tts_req_${++ttsRequestIdCounter}`;
|
|
212
|
+
const subscriptions: Array<{ remove: () => void }> = [];
|
|
213
|
+
let unsubscribed = false;
|
|
214
|
+
|
|
215
|
+
const unsubscribe = () => {
|
|
216
|
+
if (unsubscribed) return;
|
|
217
|
+
unsubscribed = true;
|
|
218
|
+
subscriptions.forEach((sub) => sub.remove());
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const matchesRequest = (e: { instanceId?: string; requestId?: string }) =>
|
|
222
|
+
(e.instanceId == null || e.instanceId === instanceId) &&
|
|
223
|
+
(e.requestId == null || e.requestId === requestId);
|
|
224
|
+
|
|
225
|
+
subscriptions.push(
|
|
226
|
+
DeviceEventEmitter.addListener('ttsStreamChunk', (event: unknown) => {
|
|
227
|
+
const e = event as TtsStreamChunk;
|
|
228
|
+
if (!matchesRequest(e)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
handlers.onChunk?.(e);
|
|
232
|
+
}),
|
|
233
|
+
DeviceEventEmitter.addListener('ttsStreamEnd', (event: unknown) => {
|
|
234
|
+
const e = event as TtsStreamEnd;
|
|
235
|
+
if (!matchesRequest(e)) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
handlers.onEnd?.(e);
|
|
240
|
+
} finally {
|
|
241
|
+
unsubscribe();
|
|
242
|
+
}
|
|
243
|
+
}),
|
|
244
|
+
DeviceEventEmitter.addListener('ttsStreamError', (event: unknown) => {
|
|
245
|
+
const e = event as TtsStreamError;
|
|
246
|
+
if (!matchesRequest(e)) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
handlers.onError?.(e);
|
|
251
|
+
} finally {
|
|
252
|
+
unsubscribe();
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// Yield so the bridge can register listeners before native emits (avoids "no listeners" / "already in progress")
|
|
258
|
+
await new Promise<void>((resolve) => {
|
|
259
|
+
if (typeof setImmediate === 'function') {
|
|
260
|
+
setImmediate(resolve);
|
|
261
|
+
} else {
|
|
262
|
+
setTimeout(resolve, 0);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
await SherpaOnnx.generateTtsStream(
|
|
268
|
+
instanceId,
|
|
269
|
+
requestId,
|
|
270
|
+
text,
|
|
271
|
+
toNativeTtsOptions(opts)
|
|
272
|
+
);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
unsubscribe();
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const controller: TtsStreamController = {
|
|
279
|
+
async cancel(): Promise<void> {
|
|
280
|
+
guard();
|
|
281
|
+
await SherpaOnnx.cancelTtsStream(instanceId);
|
|
282
|
+
unsubscribe();
|
|
283
|
+
},
|
|
284
|
+
unsubscribe,
|
|
285
|
+
};
|
|
286
|
+
return controller;
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
async cancelSpeechStream(): Promise<void> {
|
|
290
|
+
guard();
|
|
291
|
+
return SherpaOnnx.cancelTtsStream(instanceId);
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
async startPcmPlayer(sampleRate: number, channels: number): Promise<void> {
|
|
295
|
+
guard();
|
|
296
|
+
return SherpaOnnx.startTtsPcmPlayer(instanceId, sampleRate, channels);
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
async writePcmChunk(samples: number[]): Promise<void> {
|
|
300
|
+
guard();
|
|
301
|
+
return SherpaOnnx.writeTtsPcmChunk(instanceId, samples);
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
async stopPcmPlayer(): Promise<void> {
|
|
305
|
+
guard();
|
|
306
|
+
return SherpaOnnx.stopTtsPcmPlayer(instanceId);
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
async getModelInfo(): Promise<TTSModelInfo> {
|
|
310
|
+
guard();
|
|
311
|
+
const [sampleRate, numSpeakers] = await Promise.all([
|
|
312
|
+
SherpaOnnx.getTtsSampleRate(instanceId),
|
|
313
|
+
SherpaOnnx.getTtsNumSpeakers(instanceId),
|
|
314
|
+
]);
|
|
315
|
+
return { sampleRate, numSpeakers };
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
async getSampleRate(): Promise<number> {
|
|
319
|
+
guard();
|
|
320
|
+
return SherpaOnnx.getTtsSampleRate(instanceId);
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
async getNumSpeakers(): Promise<number> {
|
|
324
|
+
guard();
|
|
325
|
+
return SherpaOnnx.getTtsNumSpeakers(instanceId);
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
async destroy(): Promise<void> {
|
|
329
|
+
if (destroyed) return;
|
|
330
|
+
destroyed = true;
|
|
331
|
+
await SherpaOnnx.unloadTts(instanceId);
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
return engine;
|
|
336
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TtsStreamHandlers,
|
|
3
|
+
TtsStreamController,
|
|
4
|
+
TtsGenerationOptions,
|
|
5
|
+
TTSModelInfo,
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
// Re-export streaming event types for consumers who import from streamingTypes
|
|
9
|
+
export type {
|
|
10
|
+
TtsStreamChunk,
|
|
11
|
+
TtsStreamEnd,
|
|
12
|
+
TtsStreamError,
|
|
13
|
+
TtsStreamHandlers,
|
|
14
|
+
TtsStreamController,
|
|
15
|
+
TtsGenerationOptions,
|
|
16
|
+
TTSModelInfo,
|
|
17
|
+
} from './types';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Streaming-only TTS engine returned by createStreamingTTS().
|
|
21
|
+
* Use for incremental generation with chunk callbacks and PCM playback.
|
|
22
|
+
* Call destroy() when done to free native resources.
|
|
23
|
+
*/
|
|
24
|
+
export interface StreamingTtsEngine {
|
|
25
|
+
readonly instanceId: string;
|
|
26
|
+
|
|
27
|
+
/** Generate speech in streaming mode; audio delivered via handlers. */
|
|
28
|
+
generateSpeechStream(
|
|
29
|
+
text: string,
|
|
30
|
+
options: TtsGenerationOptions | undefined,
|
|
31
|
+
handlers: TtsStreamHandlers
|
|
32
|
+
): Promise<TtsStreamController>;
|
|
33
|
+
|
|
34
|
+
/** Cancel the current streaming generation. */
|
|
35
|
+
cancelSpeechStream(): Promise<void>;
|
|
36
|
+
|
|
37
|
+
/** Start built-in PCM playback (e.g. for play-while-generating). */
|
|
38
|
+
startPcmPlayer(sampleRate: number, channels: number): Promise<void>;
|
|
39
|
+
|
|
40
|
+
/** Write float PCM samples to the player. Use from onChunk. */
|
|
41
|
+
writePcmChunk(samples: number[]): Promise<void>;
|
|
42
|
+
|
|
43
|
+
/** Stop and release the PCM player. */
|
|
44
|
+
stopPcmPlayer(): Promise<void>;
|
|
45
|
+
|
|
46
|
+
/** Model sample rate and number of speakers. */
|
|
47
|
+
getModelInfo(): Promise<TTSModelInfo>;
|
|
48
|
+
|
|
49
|
+
getSampleRate(): Promise<number>;
|
|
50
|
+
getNumSpeakers(): Promise<number>;
|
|
51
|
+
|
|
52
|
+
/** Release native TTS resources. Do not use the engine after this. */
|
|
53
|
+
destroy(): Promise<void>;
|
|
54
|
+
}
|
package/src/tts/types.ts
CHANGED
|
@@ -289,6 +289,8 @@ export interface GeneratedAudioWithTimestamps extends GeneratedAudio {
|
|
|
289
289
|
export interface TtsStreamChunk {
|
|
290
290
|
/** Instance ID (set by native for multi-instance routing). */
|
|
291
291
|
instanceId?: string;
|
|
292
|
+
/** Request ID for this generation (distinguishes concurrent streams on same instance). */
|
|
293
|
+
requestId?: string;
|
|
292
294
|
samples: number[];
|
|
293
295
|
sampleRate: number;
|
|
294
296
|
progress: number;
|
|
@@ -301,6 +303,8 @@ export interface TtsStreamChunk {
|
|
|
301
303
|
export interface TtsStreamEnd {
|
|
302
304
|
/** Instance ID (set by native for multi-instance routing). */
|
|
303
305
|
instanceId?: string;
|
|
306
|
+
/** Request ID for this generation. */
|
|
307
|
+
requestId?: string;
|
|
304
308
|
cancelled: boolean;
|
|
305
309
|
}
|
|
306
310
|
|
|
@@ -310,9 +314,22 @@ export interface TtsStreamEnd {
|
|
|
310
314
|
export interface TtsStreamError {
|
|
311
315
|
/** Instance ID (set by native for multi-instance routing). */
|
|
312
316
|
instanceId?: string;
|
|
317
|
+
/** Request ID for this generation. */
|
|
318
|
+
requestId?: string;
|
|
313
319
|
message: string;
|
|
314
320
|
}
|
|
315
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Controller returned by generateSpeechStream().
|
|
324
|
+
* Use cancel() to stop generation, unsubscribe() to remove event listeners.
|
|
325
|
+
*/
|
|
326
|
+
export interface TtsStreamController {
|
|
327
|
+
/** Cancel the ongoing TTS generation. */
|
|
328
|
+
cancel(): Promise<void>;
|
|
329
|
+
/** Remove event listeners (called automatically on end/error, or manually). */
|
|
330
|
+
unsubscribe(): void;
|
|
331
|
+
}
|
|
332
|
+
|
|
316
333
|
/**
|
|
317
334
|
* Handlers for TTS streaming generation (chunk, end, error).
|
|
318
335
|
*/
|
|
@@ -323,7 +340,9 @@ export interface TtsStreamHandlers {
|
|
|
323
340
|
}
|
|
324
341
|
|
|
325
342
|
/**
|
|
326
|
-
* Instance-based TTS engine returned by createTTS().
|
|
343
|
+
* Instance-based batch TTS engine returned by createTTS().
|
|
344
|
+
* Use for one-shot synthesis (generateSpeech, generateSpeechWithTimestamps).
|
|
345
|
+
* For streaming, use createStreamingTTS() and StreamingTtsEngine instead.
|
|
327
346
|
* Call destroy() when done to free native resources.
|
|
328
347
|
*/
|
|
329
348
|
export interface TtsEngine {
|
|
@@ -336,15 +355,6 @@ export interface TtsEngine {
|
|
|
336
355
|
text: string,
|
|
337
356
|
options?: TtsGenerationOptions
|
|
338
357
|
): Promise<GeneratedAudioWithTimestamps>;
|
|
339
|
-
generateSpeechStream(
|
|
340
|
-
text: string,
|
|
341
|
-
options: TtsGenerationOptions | undefined,
|
|
342
|
-
handlers: TtsStreamHandlers
|
|
343
|
-
): Promise<() => void>;
|
|
344
|
-
cancelSpeechStream(): Promise<void>;
|
|
345
|
-
startPcmPlayer(sampleRate: number, channels: number): Promise<void>;
|
|
346
|
-
writePcmChunk(samples: number[]): Promise<void>;
|
|
347
|
-
stopPcmPlayer(): Promise<void>;
|
|
348
358
|
updateParams(options: TtsUpdateOptions): Promise<{
|
|
349
359
|
success: boolean;
|
|
350
360
|
detectedModels: Array<{ type: string; modelDir: string }>;
|
package/android/codegen.gradle
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// With includesGeneratedCode=false, RNGP does not run codegen for this library when it
|
|
2
|
-
// is built as a dependency (e.g. example app). Run codegen whenever this module is built
|
|
3
|
-
// (standalone AAR or as part of an app) so that NativeSherpaOnnxSpec exists for Kotlin compile.
|
|
4
|
-
// Uses Exec + node (no Node Gradle plugin) to avoid classloader/DSL issues in applied script.
|
|
5
|
-
def libraryRoot = project.projectDir.parentFile
|
|
6
|
-
def outDir = file("${project.buildDir}/generated/source/codegen")
|
|
7
|
-
def codegenJavaDir = file("${project.buildDir}/generated/source/codegen/java")
|
|
8
|
-
|
|
9
|
-
codegenJavaDir.mkdirs()
|
|
10
|
-
|
|
11
|
-
def codegenScript = file("${libraryRoot}/node_modules/react-native/scripts/generate-codegen-artifacts.js")
|
|
12
|
-
def outDirPath = outDir.absolutePath.replace("\\", "/")
|
|
13
|
-
def libraryRootPath = libraryRoot.absolutePath.replace("\\", "/")
|
|
14
|
-
|
|
15
|
-
tasks.register("generateCodegenSpecNode", Exec) {
|
|
16
|
-
onlyIf { !project.hasProperty('useNpx') || project.property('useNpx') != 'true' }
|
|
17
|
-
workingDir = libraryRoot
|
|
18
|
-
inputs.dir(file("${libraryRoot}/src"))
|
|
19
|
-
inputs.file(file("${libraryRoot}/package.json"))
|
|
20
|
-
outputs.dir(codegenJavaDir)
|
|
21
|
-
|
|
22
|
-
doFirst {
|
|
23
|
-
if (!file("${libraryRoot}/node_modules/react-native").exists()) {
|
|
24
|
-
throw new RuntimeException(
|
|
25
|
-
"Codegen requires node_modules at library root. Run 'yarn install' (or npm install) in ${libraryRoot}, then rebuild."
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
outDir.mkdirs()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
commandLine(
|
|
32
|
-
'node',
|
|
33
|
-
codegenScript.absolutePath,
|
|
34
|
-
'-p', libraryRootPath,
|
|
35
|
-
'-t', 'android',
|
|
36
|
-
'-o', outDirPath,
|
|
37
|
-
'-s', 'library'
|
|
38
|
-
)
|
|
39
|
-
environment 'CI': 'true'
|
|
40
|
-
|
|
41
|
-
doLast {
|
|
42
|
-
def nestedJava = file("${outDir}/android/app/build/generated/source/codegen/java")
|
|
43
|
-
if (nestedJava.exists()) {
|
|
44
|
-
project.copy {
|
|
45
|
-
from nestedJava
|
|
46
|
-
into codegenJavaDir
|
|
47
|
-
}
|
|
48
|
-
println "[codegen] Normalised spec output -> ${codegenJavaDir}"
|
|
49
|
-
} else {
|
|
50
|
-
println "[codegen] WARNING: expected nested output at ${nestedJava} not found"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
tasks.register('generateCodegenSpec') {
|
|
56
|
-
dependsOn tasks.named('generateCodegenSpecNode')
|
|
57
|
-
}
|