react-native-nobodywho 2.4.0 → 3.0.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/generated/cpp/nobodywho.cpp +902 -328
- package/generated/cpp/nobodywho.hpp +106 -27
- package/generated/ts/nobodywho-ffi.ts +48 -29
- package/generated/ts/nobodywho.ts +693 -164
- package/package.json +1 -1
- package/src/chat.ts +19 -0
- package/src/encoder.ts +5 -0
- package/src/model.ts +6 -1
- package/src/{stt.ts → speech_to_text.ts} +17 -9
- package/src/text_to_speech.ts +87 -0
- package/src/vad.ts +104 -0
- package/src/wrapper.ts +8 -5
- package/src/tts.ts +0 -78
package/package.json
CHANGED
package/src/chat.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RustChat,
|
|
3
3
|
SamplerConfig,
|
|
4
|
+
MtpConfig,
|
|
4
5
|
type ChatStats,
|
|
5
6
|
} from "../generated/ts/nobodywho";
|
|
6
7
|
import { Model } from "./model";
|
|
@@ -31,6 +32,12 @@ export class Chat {
|
|
|
31
32
|
/** @internal */
|
|
32
33
|
private readonly _inner: RustChat;
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* `threadCount` is the number of CPU threads used for inference. Omit it to detect the
|
|
37
|
+
* device's physical core count (performance cores only, on Apple silicon) — hyperthreads
|
|
38
|
+
* and efficiency cores make inference slower, not faster. Lower it to leave CPU headroom
|
|
39
|
+
* for the rest of the app.
|
|
40
|
+
*/
|
|
34
41
|
constructor(opts: {
|
|
35
42
|
model: Model;
|
|
36
43
|
systemPrompt?: string;
|
|
@@ -38,6 +45,8 @@ export class Chat {
|
|
|
38
45
|
templateVariables?: Record<string, boolean>;
|
|
39
46
|
tools?: Tool[];
|
|
40
47
|
sampler?: SamplerConfig;
|
|
48
|
+
mtp?: Partial<MtpConfig>;
|
|
49
|
+
threadCount?: number;
|
|
41
50
|
}) {
|
|
42
51
|
this._inner = new RustChat(
|
|
43
52
|
opts.model._inner,
|
|
@@ -46,6 +55,8 @@ export class Chat {
|
|
|
46
55
|
opts.templateVariables ? new Map(Object.entries(opts.templateVariables)) : undefined,
|
|
47
56
|
opts.tools?.map((t) => t._inner) ?? undefined,
|
|
48
57
|
opts.sampler ?? undefined,
|
|
58
|
+
opts.mtp !== undefined ? MtpConfig.create(opts.mtp) : undefined,
|
|
59
|
+
opts.threadCount ?? undefined,
|
|
49
60
|
);
|
|
50
61
|
}
|
|
51
62
|
|
|
@@ -65,17 +76,21 @@ export class Chat {
|
|
|
65
76
|
modelPath: string;
|
|
66
77
|
useGpu?: boolean;
|
|
67
78
|
projectionModelPath?: string;
|
|
79
|
+
draftModelPath?: string;
|
|
68
80
|
systemPrompt?: string;
|
|
69
81
|
contextSize?: number;
|
|
70
82
|
templateVariables?: Record<string, boolean>;
|
|
71
83
|
tools?: Tool[];
|
|
72
84
|
sampler?: SamplerConfig;
|
|
85
|
+
mtp?: Partial<MtpConfig>;
|
|
86
|
+
threadCount?: number;
|
|
73
87
|
onDownloadProgress?: (downloaded: number, total: number) => void;
|
|
74
88
|
}): Promise<Chat> {
|
|
75
89
|
const model = await Model.load({
|
|
76
90
|
modelPath: opts.modelPath,
|
|
77
91
|
useGpu: opts.useGpu,
|
|
78
92
|
projectionModelPath: opts.projectionModelPath,
|
|
93
|
+
draftModelPath: opts.draftModelPath,
|
|
79
94
|
onDownloadProgress: opts.onDownloadProgress,
|
|
80
95
|
});
|
|
81
96
|
return new Chat({ model, ...opts });
|
|
@@ -173,6 +188,10 @@ export class Chat {
|
|
|
173
188
|
return this._inner.getStats();
|
|
174
189
|
}
|
|
175
190
|
|
|
191
|
+
async mtpAcceptanceRate(): Promise<number | undefined> {
|
|
192
|
+
return this._inner.mtpAcceptanceRate();
|
|
193
|
+
}
|
|
194
|
+
|
|
176
195
|
/**
|
|
177
196
|
* Immediately free the underlying Rust resources (model context, KV cache, etc.).
|
|
178
197
|
* After calling this, the Chat instance is no longer usable.
|
package/src/encoder.ts
CHANGED
|
@@ -46,6 +46,11 @@ export class Encoder {
|
|
|
46
46
|
return this._inner.encode(text);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/** Encode multiple texts, preserving input order. */
|
|
50
|
+
async encodeBatch(texts: string[]): Promise<number[][]> {
|
|
51
|
+
return this._inner.encodeBatch(texts);
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
/**
|
|
50
55
|
* Immediately free the underlying Rust resources.
|
|
51
56
|
* After calling this, the Encoder instance is no longer usable.
|
package/src/model.ts
CHANGED
|
@@ -36,15 +36,19 @@ export class Model {
|
|
|
36
36
|
/**
|
|
37
37
|
* Load a GGUF model from a local path or remote URL.
|
|
38
38
|
*
|
|
39
|
-
* @param opts.modelPath -
|
|
39
|
+
* @param opts.modelPath - Local path, `hf://` or `https://` URL, or `auto` for memory-based model selection
|
|
40
40
|
* @param opts.useGpu - Use GPU acceleration if available (default: true)
|
|
41
41
|
* @param opts.projectionModelPath - Path to a multimodal projector file for vision models
|
|
42
|
+
* @param opts.draftModelPath - Optional MTP draft-heads gguf. Loading it lets `Chat`
|
|
43
|
+
* instances opt into MTP speculative decoding via `new Chat({ ..., mtp: true })`.
|
|
44
|
+
* Adds around 5% to VRAM usage.
|
|
42
45
|
* @param opts.onDownloadProgress - Invoked with `(downloaded, total)` byte counts while a remote model is being downloaded. Throttled to ~10 Hz with a guaranteed final emit on completion. Not invoked for cached/local files.
|
|
43
46
|
*/
|
|
44
47
|
static async load(opts: {
|
|
45
48
|
modelPath: string;
|
|
46
49
|
useGpu?: boolean;
|
|
47
50
|
projectionModelPath?: string;
|
|
51
|
+
draftModelPath?: string;
|
|
48
52
|
onDownloadProgress?: (downloaded: number, total: number) => void;
|
|
49
53
|
}): Promise<Model> {
|
|
50
54
|
const cb = opts.onDownloadProgress;
|
|
@@ -55,6 +59,7 @@ export class Model {
|
|
|
55
59
|
opts.modelPath,
|
|
56
60
|
opts.useGpu ?? true,
|
|
57
61
|
opts.projectionModelPath,
|
|
62
|
+
opts.draftModelPath,
|
|
58
63
|
progressCallback,
|
|
59
64
|
);
|
|
60
65
|
return new Model(inner);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RustSpeechToTextInterface } from "../generated/ts/nobodywho";
|
|
2
2
|
import * as nobodywho from "../generated/ts/nobodywho";
|
|
3
3
|
import { TokenStream } from "./streaming";
|
|
4
4
|
|
|
5
|
-
export type
|
|
5
|
+
export type SpeechToTextOptions = {
|
|
6
6
|
source: string;
|
|
7
7
|
language?: string;
|
|
8
8
|
quantization?: string;
|
|
@@ -13,7 +13,7 @@ export type SttOptions = {
|
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```typescript
|
|
16
|
-
* const stt =
|
|
16
|
+
* const stt = await SpeechToText.load({
|
|
17
17
|
* source: "hf://onnx-community/whisper-base",
|
|
18
18
|
* language: "en",
|
|
19
19
|
* });
|
|
@@ -26,24 +26,32 @@ export type SttOptions = {
|
|
|
26
26
|
* const text = await stream.completed();
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
|
-
export class
|
|
29
|
+
export class SpeechToText {
|
|
30
30
|
/** @internal */
|
|
31
|
-
private readonly _inner:
|
|
31
|
+
private readonly _inner: RustSpeechToTextInterface;
|
|
32
|
+
|
|
33
|
+
/** @internal */
|
|
34
|
+
private constructor(inner: RustSpeechToTextInterface) {
|
|
35
|
+
this._inner = inner;
|
|
36
|
+
}
|
|
32
37
|
|
|
33
38
|
/**
|
|
34
|
-
*
|
|
39
|
+
* Load a Whisper SpeechToText model.
|
|
40
|
+
*
|
|
41
|
+
* @param opts - See {@link SpeechToTextOptions}.
|
|
35
42
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
static async load(opts: SpeechToTextOptions): Promise<SpeechToText> {
|
|
44
|
+
const inner = await nobodywho.loadSpeechToText(
|
|
38
45
|
opts.source,
|
|
39
46
|
opts.language,
|
|
40
47
|
opts.quantization,
|
|
41
48
|
);
|
|
49
|
+
return new SpeechToText(inner);
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
/**
|
|
45
53
|
* Transcribe an audio file (WAV / MP3).
|
|
46
|
-
* Returns an `
|
|
54
|
+
* Returns an `SpeechToTextStream` to consume tokens as they arrive.
|
|
47
55
|
*/
|
|
48
56
|
transcribeFile(path: string): TokenStream {
|
|
49
57
|
return new TokenStream(this._inner.transcribeFile(path));
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RustTextToSpeech,
|
|
3
|
+
loadTextToSpeech,
|
|
4
|
+
type RustTextToSpeechInterface,
|
|
5
|
+
} from "../generated/ts/nobodywho";
|
|
6
|
+
|
|
7
|
+
export type TextToSpeechArchitecture = "kokoro" | "pocket-tts" | "supertonic";
|
|
8
|
+
export type TextToSpeechDevice = "auto" | "cpu" | "cuda";
|
|
9
|
+
|
|
10
|
+
export type TextToSpeechOptions = {
|
|
11
|
+
source: string;
|
|
12
|
+
architecture?: TextToSpeechArchitecture;
|
|
13
|
+
voice?: string;
|
|
14
|
+
language?: string;
|
|
15
|
+
speed?: number;
|
|
16
|
+
steps?: number;
|
|
17
|
+
silenceDuration?: number;
|
|
18
|
+
precision?: "int8" | "fp32";
|
|
19
|
+
temperature?: number;
|
|
20
|
+
huggingfaceToken?: string;
|
|
21
|
+
device?: TextToSpeechDevice;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Text-to-speech synthesizer that returns WAV bytes. */
|
|
25
|
+
export class TextToSpeech {
|
|
26
|
+
/** @internal */
|
|
27
|
+
private _inner: RustTextToSpeechInterface;
|
|
28
|
+
|
|
29
|
+
/** Create a TextToSpeech synthesizer synchronously. */
|
|
30
|
+
constructor(opts: TextToSpeechOptions) {
|
|
31
|
+
this._inner = new RustTextToSpeech(
|
|
32
|
+
opts.source,
|
|
33
|
+
opts.architecture,
|
|
34
|
+
opts.voice,
|
|
35
|
+
opts.language,
|
|
36
|
+
opts.speed,
|
|
37
|
+
opts.steps,
|
|
38
|
+
opts.silenceDuration,
|
|
39
|
+
opts.precision,
|
|
40
|
+
opts.temperature,
|
|
41
|
+
opts.huggingfaceToken,
|
|
42
|
+
opts.device ?? "auto",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private static fromInner(inner: RustTextToSpeechInterface): TextToSpeech {
|
|
47
|
+
const tts = Object.create(TextToSpeech.prototype) as TextToSpeech;
|
|
48
|
+
tts._inner = inner;
|
|
49
|
+
return tts;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Create a TextToSpeech synthesizer asynchronously. */
|
|
53
|
+
static async load(opts: TextToSpeechOptions): Promise<TextToSpeech> {
|
|
54
|
+
const inner = await loadTextToSpeech(
|
|
55
|
+
opts.source,
|
|
56
|
+
opts.architecture,
|
|
57
|
+
opts.voice,
|
|
58
|
+
opts.language,
|
|
59
|
+
opts.speed,
|
|
60
|
+
opts.steps,
|
|
61
|
+
opts.silenceDuration,
|
|
62
|
+
opts.precision,
|
|
63
|
+
opts.temperature,
|
|
64
|
+
opts.huggingfaceToken,
|
|
65
|
+
opts.device ?? "auto",
|
|
66
|
+
);
|
|
67
|
+
return TextToSpeech.fromInner(inner);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Synthesize text and return WAV bytes. */
|
|
71
|
+
async synthesize(text: string): Promise<Uint8Array> {
|
|
72
|
+
return new Uint8Array(await this._inner.synthesizeAsync(text));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Synthesize text synchronously and return WAV bytes. */
|
|
76
|
+
synthesizeSync(text: string): Uint8Array {
|
|
77
|
+
return new Uint8Array(this._inner.synthesize(text));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Immediately free the underlying Rust resources.
|
|
82
|
+
* After calling this, the TextToSpeech instance is no longer usable.
|
|
83
|
+
*/
|
|
84
|
+
destroy(): void {
|
|
85
|
+
(this._inner as any).uniffiDestroy();
|
|
86
|
+
}
|
|
87
|
+
}
|
package/src/vad.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { RustVoiceActivityDetectionInterface, VoiceActivityDetectionEvent } from "../generated/ts/nobodywho";
|
|
2
|
+
import * as nobodywho from "../generated/ts/nobodywho";
|
|
3
|
+
|
|
4
|
+
export { VoiceActivityDetectionEvent } from "../generated/ts/nobodywho";
|
|
5
|
+
|
|
6
|
+
export type VoiceActivityDetectionOptions = {
|
|
7
|
+
source?: string;
|
|
8
|
+
sampleRate: number;
|
|
9
|
+
threshold?: number;
|
|
10
|
+
minSilenceDurationMs?: number;
|
|
11
|
+
minSpeechDurationMs?: number;
|
|
12
|
+
prerollDurationMs?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Voice activity detection from live, streaming audio, backed by Silero VAD.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const vad = await VoiceActivityDetection.load({ sampleRate: 16000 });
|
|
21
|
+
*
|
|
22
|
+
* // Feed each newest chunk as it arrives (not the whole buffer — VoiceActivityDetection
|
|
23
|
+
* // tracks the current turn internally).
|
|
24
|
+
* if (vad.push(chunk) === VoiceActivityDetectionEvent.SpeechEnded) {
|
|
25
|
+
* const audio = vad.finish();
|
|
26
|
+
* // audio: Int16Array-like number[] spanning SpeechStarted (with a
|
|
27
|
+
* // small pre-roll) through SpeechEnded.
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class VoiceActivityDetection {
|
|
32
|
+
/** @internal */
|
|
33
|
+
private readonly _inner: RustVoiceActivityDetectionInterface;
|
|
34
|
+
|
|
35
|
+
/** @internal */
|
|
36
|
+
private constructor(inner: RustVoiceActivityDetectionInterface) {
|
|
37
|
+
this._inner = inner;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load a voice activity detector.
|
|
42
|
+
*
|
|
43
|
+
* @param opts - See {@link VoiceActivityDetectionOptions}.
|
|
44
|
+
*/
|
|
45
|
+
static async load(opts: VoiceActivityDetectionOptions): Promise<VoiceActivityDetection> {
|
|
46
|
+
const inner = await nobodywho.loadVoiceActivityDetection(
|
|
47
|
+
opts.source,
|
|
48
|
+
opts.sampleRate,
|
|
49
|
+
opts.threshold,
|
|
50
|
+
opts.minSilenceDurationMs,
|
|
51
|
+
opts.minSpeechDurationMs,
|
|
52
|
+
opts.prerollDurationMs,
|
|
53
|
+
undefined,
|
|
54
|
+
);
|
|
55
|
+
return new VoiceActivityDetection(inner);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Feed the newest chunk of audio (not the whole accumulated buffer —
|
|
60
|
+
* `VoiceActivityDetection` tracks the current turn internally). Always
|
|
61
|
+
* returns the current confirmed state: `Speech`/`Silence` if unchanged
|
|
62
|
+
* since the last call, or `SpeechStarted`/`SpeechEnded` on the call that
|
|
63
|
+
* confirmed the transition.
|
|
64
|
+
*
|
|
65
|
+
* @param chunk - Flat array of signed 16-bit samples (mono).
|
|
66
|
+
*/
|
|
67
|
+
push(chunk: Int16Array | number[]): VoiceActivityDetectionEvent {
|
|
68
|
+
const arr = chunk instanceof Int16Array ? Array.from(chunk) : chunk;
|
|
69
|
+
return this._inner.push(arr);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Return the current turn's captured audio (from the confirmed
|
|
74
|
+
* `SpeechStarted`, including a small pre-roll, through to `SpeechEnded`)
|
|
75
|
+
* and reset internal state for the next turn. Empty if speech was never
|
|
76
|
+
* confirmed.
|
|
77
|
+
*/
|
|
78
|
+
finish(): number[] {
|
|
79
|
+
return this._inner.finish();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Detect every speech segment in a complete audio buffer, returning
|
|
84
|
+
* each segment's audio (with a short pre-roll) in order. Unlike `push`,
|
|
85
|
+
* correctly finds every segment regardless of buffer size — use this
|
|
86
|
+
* for offline/batch processing instead of live streaming.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* for (const audio of vad.segment(fullRecording)) {
|
|
91
|
+
* transcribe(audio);
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
segment(samples: Int16Array | number[]): number[][] {
|
|
96
|
+
const arr = samples instanceof Int16Array ? Array.from(samples) : samples;
|
|
97
|
+
return this._inner.segment(arr);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Release native resources. Call when done with this handle. */
|
|
101
|
+
destroy(): void {
|
|
102
|
+
(this._inner as any).uniffiDestroy?.();
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/wrapper.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* NobodyWho — React Native bindings for local LLM inference.
|
|
3
3
|
*
|
|
4
4
|
* This is the public entry point. It re-exports the public API:
|
|
5
|
-
* - Wrapper classes: Model, Chat, Encoder, CrossEncoder,
|
|
5
|
+
* - Wrapper classes: Model, Chat, Encoder, CrossEncoder, TextToSpeech, Tool, TokenStream, Prompt
|
|
6
6
|
* - Direct re-exports: SamplerBuilder, SamplerConfig
|
|
7
7
|
* - Types: Message, Asset, ToolCall
|
|
8
8
|
* - Utilities: SamplerPresets, cosineSimilarity, downloadModel
|
|
@@ -57,10 +57,11 @@ export async function downloadModel(opts: {
|
|
|
57
57
|
// Wrapper classes
|
|
58
58
|
export { Model } from "./model";
|
|
59
59
|
export { Chat } from "./chat";
|
|
60
|
-
export {
|
|
60
|
+
export { SpeechToText } from "./speech_to_text";
|
|
61
|
+
export { VoiceActivityDetection } from "./vad";
|
|
61
62
|
export { Encoder } from "./encoder";
|
|
62
63
|
export { CrossEncoder } from "./cross_encoder";
|
|
63
|
-
export {
|
|
64
|
+
export { TextToSpeech } from "./text_to_speech";
|
|
64
65
|
export { Prompt } from "./prompt";
|
|
65
66
|
export { Tool } from "./tool";
|
|
66
67
|
export { TokenStream } from "./streaming";
|
|
@@ -85,8 +86,10 @@ export type {
|
|
|
85
86
|
CachedModel,
|
|
86
87
|
ChatStats,
|
|
87
88
|
} from "../generated/ts/nobodywho";
|
|
88
|
-
export type {
|
|
89
|
-
export type {
|
|
89
|
+
export type { TextToSpeechArchitecture, TextToSpeechDevice, TextToSpeechOptions } from "./text_to_speech";
|
|
90
|
+
export type { SpeechToTextOptions } from "./speech_to_text";
|
|
91
|
+
export { VoiceActivityDetectionEvent } from "./vad";
|
|
92
|
+
export type { VoiceActivityDetectionOptions } from "./vad";
|
|
90
93
|
|
|
91
94
|
// Ergonomic wrapper additions.
|
|
92
95
|
export { SamplerPresets } from "./sampler_presets";
|
package/src/tts.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RustTts,
|
|
3
|
-
loadTts,
|
|
4
|
-
type RustTtsInterface,
|
|
5
|
-
} from "../generated/ts/nobodywho";
|
|
6
|
-
|
|
7
|
-
export type TtsArchitecture = "kokoro" | "supertonic";
|
|
8
|
-
export type TtsDevice = "auto" | "cpu" | "cuda";
|
|
9
|
-
|
|
10
|
-
export type TtsOptions = {
|
|
11
|
-
source: string;
|
|
12
|
-
architecture?: TtsArchitecture;
|
|
13
|
-
voice?: string;
|
|
14
|
-
language?: string;
|
|
15
|
-
speed?: number;
|
|
16
|
-
steps?: number;
|
|
17
|
-
silenceDuration?: number;
|
|
18
|
-
device?: TtsDevice;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/** Text-to-speech synthesizer that returns WAV bytes. */
|
|
22
|
-
export class Tts {
|
|
23
|
-
/** @internal */
|
|
24
|
-
private _inner: RustTtsInterface;
|
|
25
|
-
|
|
26
|
-
/** Create a TTS synthesizer synchronously. */
|
|
27
|
-
constructor(opts: TtsOptions) {
|
|
28
|
-
this._inner = new RustTts(
|
|
29
|
-
opts.source,
|
|
30
|
-
opts.architecture,
|
|
31
|
-
opts.voice,
|
|
32
|
-
opts.language,
|
|
33
|
-
opts.speed,
|
|
34
|
-
opts.steps,
|
|
35
|
-
opts.silenceDuration,
|
|
36
|
-
opts.device ?? "auto",
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
private static fromInner(inner: RustTtsInterface): Tts {
|
|
41
|
-
const tts = Object.create(Tts.prototype) as Tts;
|
|
42
|
-
tts._inner = inner;
|
|
43
|
-
return tts;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Create a TTS synthesizer asynchronously. */
|
|
47
|
-
static async load(opts: TtsOptions): Promise<Tts> {
|
|
48
|
-
const inner = await loadTts(
|
|
49
|
-
opts.source,
|
|
50
|
-
opts.architecture,
|
|
51
|
-
opts.voice,
|
|
52
|
-
opts.language,
|
|
53
|
-
opts.speed,
|
|
54
|
-
opts.steps,
|
|
55
|
-
opts.silenceDuration,
|
|
56
|
-
opts.device ?? "auto",
|
|
57
|
-
);
|
|
58
|
-
return Tts.fromInner(inner);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Synthesize text and return WAV bytes. */
|
|
62
|
-
async synthesize(text: string): Promise<Uint8Array> {
|
|
63
|
-
return new Uint8Array(await this._inner.synthesizeAsync(text));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Synthesize text synchronously and return WAV bytes. */
|
|
67
|
-
synthesizeSync(text: string): Uint8Array {
|
|
68
|
-
return new Uint8Array(this._inner.synthesize(text));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Immediately free the underlying Rust resources.
|
|
73
|
-
* After calling this, the Tts instance is no longer usable.
|
|
74
|
-
*/
|
|
75
|
-
destroy(): void {
|
|
76
|
-
(this._inner as any).uniffiDestroy();
|
|
77
|
-
}
|
|
78
|
-
}
|