【RUST AI】把 TTS 搬进浏览器:kokoroi-rs 的 WASM 实践

📅 2026/7/22 11:46:16
【RUST AI】把 TTS 搬进浏览器:kokoroi-rs 的 WASM 实践
隐私风险文本内容会经过第三方服务器对于涉及个人隐私或商业机密的场景这是一个硬伤。网络依赖需要稳定的互联网连接弱网环境下体验打折扣。成本商用 API 按字符计费大规模使用不是小数目。那么有没有可能让 TTS 完全在浏览器本地运行WebAssembly 的出现让这种想法成为可能。kokoroi-rs 的 WASM 模块正是这样一个尝试——将 Kokoro TTS 的核心能力编译为 WebAssembly让高质量语音合成直接运行在用户的浏览器中无需任何后端服务器。本文将深入拆解 kokoroi-rs 的 WASM 架构、两种推理后端、JavaScript API 设计以及实际使用中的性能表现。一、整体架构WASM 模块 推理后端kokoroi-rs 的 WASM 模块将 Rust 代码编译为 wasm32-unknown-unknown 目标通过 wasm-bindgen 生成 JavaScript 胶水代码最终交付一个约 3MB 的 WASM 二进制文件。整个系统的核心功能分两层G2P 引擎将中文文本转换为 Bopomofo注音符号音素序列——这是模型输入的前置步骤完全在 Rust 中实现。ONNX 推理引擎将音素序列 发音人风格嵌入 → PCM 音频采样。推理部分提供了两种后端选择开发者可以根据需求灵活切换数据文件浏览器环境WASM 内部功能phonemizesynthesizeInferenceSessionBopomofoBopomofoPCMPCMWAV Uint8加载加载fetchinitJavaScript 调用层ChineseG2P字素转音素WAV 编码器oxionnx 推理(纯 Rust)ONNX Runtime Web(CDN 加载)ONNX 模型文件(~80MB)发音人嵌入(*.bin)这两个后端各有侧重ONNX Runtime Web 是微软官方方案稳定性好、优化成熟而 oxionnx 是纯 Rust 实现与 WASM 集成更紧密无需额外加载推理库。二、G2P 引擎中文处理的基石文本到音素的转换是 TTS 的第一步也是中文场景中最复杂的环节之一。WASM 模块中的 ChineseG2P 引擎与 Native 版本共享同一套代码保证了处理逻辑的一致性和准确性。核心流程如下输入中文文本文本规范化jieba-rs 分词 POS 标注多音字消歧基于上下文规则变调处理三声变调 / 一不变调拼音 → Bopomofo 映射输出音素序列几个关键点jieba-rs 分词WASM 版本同样使用 jieba-rs词典数据以压缩形式嵌入 WASM 二进制中首次调用时解压约数百毫秒一次性开销之后常驻内存。多音字消歧PolyphonicDisambiguator 维护了一套基于规则的上文消歧表例如“了”在“了解”中读 liǎo在“好了”中读 le”。变调处理实现了经典的三声变调规则两个三声相连前变二声以及“一”、“不”的变调规律。最终输出 Bopomofo注音符号序列这是 Kokoro 模型原生训练使用的音素表示准确度优于 IPA。三、JavaScript API简洁易用WASM 模块通过 wasm-bindgen 导出清晰的 JavaScript 接口开发者可以像使用普通 JS 库一样调用。初始化import init, { KokoroWASM, pcm_samples_to_wav_data } from ‘./wasm-pkg/kokoros.js’;// 加载 WASM 运行时await init();// 创建 TTS 实例const kokoro new KokoroWASM({ usePolyphonic: true });实战示例从文本输入到音频播放/下载下面是一个完整的 HTML 页面示例展示如何将文本合成为语音并播放或下载包含完善的错误处理和进度提示kokoroi-rs TTS 演示kokoroi-rs 浏览器 TTS你好欢迎体验浏览器端语音合成。 播放 ⬇️ 下载 WAV就绪代码说明初始化阶段通过 init() 加载 WASM 运行时创建 KokoroWASM 实例然后 fetch 下载 ONNX 模型并调用 loadModel() 加载。每一步都有进度提示和错误捕获。合成流程synthesize() 函数先获取默认发音人再调用 synthesize(text, voiceId, speed) 执行推理最后用 pcm_samples_to_wav_data() 将 PCM 数据编码为 WAV 格式。播放与下载handlePlay() 将 WAV 转为 Blob URL 后通过 Audio 播放handleDownload() 创建 标签触发下载。错误处理网络错误fetch 失败、模型加载失败、无可用发音人等场景均有 try/catch 捕获并显示友好提示。进度提示使用 元素和状态栏实时反馈加载与合成进度。核心方法一览方法 说明 返回值phonemize(text) 文本 → Bopomofo 音素 PromisephonemizeIPA(text) 文本 → IPA 音素 Promisetokenize(phonemes) 音素 → token ID 数组 PromiseloadModel(bytes) 加载 ONNX 模型oxionnx 模式 Promisesynthesize(text, style, speed) 文本 → 语音oxionnx 模式 PromisegetVoices() 获取内置发音人列表 PromiseVoiceInfo[]类型安全项目提供了完整的 TypeScript 类型声明kokoros.d.ts在 IDE 中可以获得智能提示interface SynthesisResult {phonemes: string; // Bopomofo 音素序列phonemesDisplay: string; // 可读注音带声调符号text: string; // 原始文本audio: Float32Array; // PCM 音频数据24000Hz 单声道sampleRate: number; // 固定 24000}辅助函数 pcm_samples_to_wav_data(samples) 将 Float32Array 转换为标准 WAV 格式的 Uint8Array方便播放或下载。四、两种推理模式深度对比WASM 模块最有趣的设计是提供了两套推理路径开发者可根据场景选择。模式一ONNX Runtime Web混合架构ONNX Runtime WebRust WASM (G2P)JSONNX Runtime WebRust WASM (G2P)JSphonemize(text)Bopomofo 音素tokenize 构造 TensorInferenceSession.run(feeds)PCM Float32Array编码为 WAVG2P 部分由 Rust WASM 完成高效、小巧模型推理使用微软官方的 onnxruntime-web 库从 CDN 加载 ort.min.js 及其 WASM 后端发音人嵌入由 JavaScript 通过 fetch 加载后传入适合场景对推理稳定性和算子覆盖度要求较高的生产应用。模式二纯 Rust WASM oxionnx全栈 RustRust WASM (G2P oxionnx)JSRust WASM (G2P oxionnx)JSloadModel(modelBytes)synthesize(text, style, speed)G2P → Tokenize → oxionnx 推理PCM Float32Array 音素信息编码为 WAV全部逻辑G2P、推理、WAV 编码都在 Rust WASM 内部完成推理使用 oxionnx——一个纯 Rust 实现的 ONNX 推理库无需加载任何外部 JavaScript 推理库零 CDN 依赖适合场景离线应用、对隐私要求极高、希望最小化外部依赖的场景。对比维度 ONNX Runtime Web oxionnx纯 Rust推理库来源 微软官方 CDN 编译进 WASMWASM 体积 ~3MB仅 G2P ~3MB含推理额外 JS 加载 ort.min.js (~200KB) 无算子覆盖度 广泛 核心算子持续完善中优化选项 丰富图优化、量化等 基础硬件加速 WebGL / WASM SIMD WASM SIMD离线使用 需缓存 CDN 资源 完全离线可用五、构建与 Demo从源码到浏览器构建 WASM 模块安装 WASM 目标rustup target add wasm32-unknown-unknown安装 wasm-packcargo install wasm-pack一键构建推荐./scripts/build_wasm.sh或手动构建wasm-pack build crates/kokoros-core–target web–out-dir …/…/static/wasm-pkg––features wasm–no-default-features构建产物位于 static/wasm-pkg/ 目录下static/wasm-pkg/├── kokoros_bg.wasm # WASM 二进制 (~3MB)├── kokoros.js # 胶水代码 (~28KB)├── kokoros.d.ts # TypeScript 类型声明└── package.jsonDemo 页面项目提供了三个可直接运行的演示页面位于 static/ 目录wasm_demo.html — 使用 ONNX Runtime Web 后端展示完整的 G2P 推理流程。browser_demo.html — 流式合成体验更接近实时对话场景。rust_wasm_demo.html — 纯 Rust WASMoxionnx后端展示零外部依赖的全离线方案。运行方式cd staticpython3 -m http.server 8080或npx serve .打开浏览器访问 http://localhost:8080/wasm_demo.html 即可体验。六、隐私与安全数据不出设备这是 WASM 版本最吸引人的特性之一。对比云 TTS 方案传统云 TTS用户文本 ──(网络)──→ 云服务器 ──(网络)──→ 返回音频文本离开用户设备存在隐私泄露风险kokoroi-rs WASM用户文本 ──(本地)──→ WASM 模块 ──(本地)──→ 播放音频文本始终在浏览器沙箱内永不离开用户设备对于一些敏感场景如医疗咨询、金融信息、个人日记等本地 TTS 有着天然的优势。同时由于无需网络请求弱网环境下也能稳定工作。七、性能与局限性能表现维度 数据G2P 处理速度 1ms / 字符模型加载80MB 首次约 1-3 秒取决于网络推理实时率 约 1-2 倍实时受 WASM 引擎限制内存占用 约 100-200MBWASM 体积 ~3MB含 G2P oxionnx当前局限单线程推理WASM 不支持 std::thread无法利用多核并行处理长文本分片。对于超长文本目前的方案是整体推理可能会造成 UI 卡顿可通过 Web Worker 缓解。模型下载体积ONNX 模型约 80MB发音人嵌入约 150MB首次加载需要一定时间。后续可以利用浏览器缓存或 OPFS 减少重复下载。仅 WAV 输出受限于 WASM 下的音频编码库支持目前只支持 WAV 格式PCM 16-bit。MP3/Opus 等压缩格式暂不支持。浏览器兼容性需要支持 WebAssembly 和 SharedArrayBuffer部分旧浏览器不支持。八、未来方向WASM 模块的潜力远不止于此团队已经在规划几个有意思的方向Web Worker 并行利用多个 Web Worker 各加载一个 WASM 实例实现分片并行推理弥补单线程的不足。OPFS 模型缓存利用 Origin Private File System 将下载的模型持久化在本地第二次访问时几乎秒开。流式合成将 Native 版本的 SSE 流式机制移植到 WASM实现“边合成边播放”的低延迟体验。