第一次合成
这一页覆盖完整生命周期:创建 client、加载模型、合成、查看结果、清理。
创建 client
ts
import { createTts } from "@optimalai/react-native";
import { kokoroZhInt8 } from "./optimalai-models";
const tts = createTts({
model: kokoroZhInt8,
voiceId: "zf_001",
numThreads: 4,
});createTts 开销很小 —— 它只是保存选项,在 load() 之前不会发生任何原生调用。
| 选项 | 必需 | 含义 |
|---|---|---|
model | 是 | @optimalai/model-tools 生成的对象。 |
voiceId | 是 | 该 client 的默认音色。 |
numThreads | 否 | ONNX 的 intra-op 线程数,按设备调优。 |
加载模型
ts
await tts.load();这是开销最大的一步。它会解析资产 ID 为真实文件 URI、校验文件大小、解析 token 表与 两份词典、打开 ONNX session,并校验音色布局。冷启动时会有可感知的耗时 —— 请在 真机上测量,不要用模拟器。
重复到达 ready 是无害的;client 已就绪时 load() 会立即返回。
合成
ts
const result = await tts.synthesizeToFile({
text: "你好,Hello world.",
voiceId: "af_maple",
speed: 1.0,
});| 字段 | 必需 | 含义 |
|---|---|---|
text | 是 | 中文、英文或混合文本,上限 2000 个 code point。 |
voiceId | 否 | 仅本次覆盖 client 默认音色,不重载模型。 |
outputPath | 否 | 输出路径,缺省时在缓存目录生成文件名。 |
speed | 否 | 语速,0.5–2.0,默认 1.0。 |
返回结果
ts
result.fileUri; // "file:///.../optimalai-1726000000000.wav"
result.sampleRate; // 24000
result.channels; // 1
result.voiceId; // "af_maple"
result.durationMs; // 生成音频的时长
result.modelId; // "kokoro-82m-v1.1-zh-int8"
result.modelVersion; // "1.1-zh-int8"
result.elapsedMs; // 合成耗时,不含加载注意 elapsedMs 不含模型加载,它只度量合成调用本身。因此调优 numThreads 时应该盯这个数。
播放 WAV
SDK 只写文件,到此为止。播放由你负责:
tsx
import { useAudioPlayer, setAudioModeAsync } from "expo-audio";
import { useEffect } from "react";
const player = useAudioPlayer(null);
// iOS 下不打开这个开关会静音。
useEffect(() => {
void setAudioModeAsync({ playsInSilentMode: true });
}, []);
async function synthesizeAndPlay() {
const result = await tts.synthesizeToFile({ text: "你好,Hello world." });
player.replace(result.fileUri);
player.play();
}清理
ts
await tts.reset();reset() 会关闭 ONNX session 并把 client 退回 unloaded。在页面卸载时调用它, 或者在丢弃 client 之前调用。遭遇致命错误后,必须 reset() 才能再次 load() —— 见错误处理。
文件生命周期
SDK 不会下载、播放、缓存、上传或删除它产出的 WAV。每个生成的文件都会留在磁盘上, 直到你删掉它。如果你的 App 会反复合成,请自行管理该目录。
复用路径
如果你传入的 outputPath 已存在,合成会以 OUTPUT_EXISTS 失败,而不是静默覆盖。 请先删除文件,或让 SDK 生成新名字。
一次跑完
ts
const tts = createTts({
model: kokoroZhInt8,
voiceId: "zf_001",
numThreads: 4,
});
await tts.load();
const first = await tts.synthesizeToFile({ text: "第一句。" });
const second = await tts.synthesizeToFile({
text: "Second line.",
voiceId: "af_maple",
speed: 1.15,
});
await tts.reset();两次合成,一次模型加载,中间没有重载。