指南

在 Rust 中离线实现文字转语音

Rust 有封装操作系统语音引擎的 crate,用于无障碍功能没问题,但声音机械且各平台不同;还有按字收费的云端 SDK。对于需要在各处使用同一个自然声音的游戏、机器人或命令行工具,两者都不理想。请注意:Supertonic 3 暂不支持中文,可朗读英语、日语、韩语等 31 种语言。

本指南介绍如何通过本地 HTTP 服务器从 Rust 调用免费的 SuperClonica 引擎:声音自然,WAV 文件留在你手里,用 reqwest 几行即可。

从 Rust 发声的方式

屏幕阅读器式的输出用系统声音就好;产品或游戏的声音请用引擎。

  • 封装系统声音的 crate:离线、轻量,但声音机械,在 Windows、macOS 和 Linux 上各不相同。
  • 云端 SDK:声音自然,但需要账号、密钥、按字计费,文字还会存到对方服务器上。
  • SuperClonica:本地 HTTP 服务器,支持自然声音、角色声音和自定义声音,只要能运行 Docker 就能离线使用。
  • 它支持 31 种语言,暂不支持中文。

免费试听声音

写代码之前,先打开任意声音页面,输入 30 到 100 个字符的句子,选择语言并点击生成按钮。几秒后就能听到你的应用可以拥有的声音,无需注册。

启动语音服务器

有 NVIDIA 显卡时用 cuda 镜像会更快,但不是必需的。

  • 在任何 CPU 上启动服务器:docker run -d -p 8000:8000 -v superclonica-models:/models superclonica/superclonica:cpu。
  • 首次启动会把约 385 MB 的 Supertonic 3 模型下载到卷中;GET /health 可以告诉你何时就绪。
  • POST /synthesize 接收包含 text、voice、lang、seed 和 normalize 的 JSON,返回 16 位单声道 WAV。
  • voice 可以是 M1–M5、F1–F5 等标准名称,也可以是声音文件的 JSON 内容。

从 Rust 调用

借助 reqwest crate 及其 blocking 特性,在返回 Result 的函数中几行代码就能发出请求:let voice = std::fs::read_to_string("voice.json")?; let body = format!(r#"{{"text": "Hello from Rust!", "lang": "en", "seed": 7, "voice": {voice}}}"#); let wav = reqwest::blocking::Client::new().post("http://localhost:8000/synthesize").header("Content-Type", "application/json").body(body).send()?.bytes()?; std::fs::write("hello.wav", wav)?; 在异步代码中,以同样方式使用 reqwest 的异步客户端。使用标准声音时,发送 "voice": "F1" 代替文件。同样的文字、声音和 seed 总是得到同样的音频,所以请缓存重复短语的文件;较大的请求可以用 serde_json 保持整洁。

上线前的好习惯

复用一个 reqwest 客户端并设置超时。不要把 8000 端口原样暴露到互联网:设置 SUPERCLONICA_API_KEY 并以 Bearer 令牌发送,或把服务器放在私有网络中。如果同时到达的请求很多,用 SUPERCLONICA_LANES 运行更多模型副本,并在你的硬件上实测:在我们的测试中,短句在 RTX 5070 Ti 上耗时 0.12 秒,在四个 CPU 线程上耗时 0.43 秒。