外观
HTTP API
API
服务对外暴露以下端点:
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /health | 服务健康检查与当前模型能力信息 |
| POST | /tts | 提交合成请求 |
| POST | /tts-stream | 创建流式 TTS 播放链接 |
| GET | /tts-stream/<id>.mp3 | 拉取流式 TTS MP3 音频 |
| HEAD | /tts-stream/<id>.mp3 | 检查流式链接是否存在 |
| GET | /wav/<file> | 下载已合成的 wav |
| HEAD | /wav/<file> | 检查 wav 是否存在 / 可读 |
所有响应均带 Connection: close,错误响应统一使用 JSON 体:
json
{
"error": "<message>"
}GET /health
返回服务基础信息和当前已加载 TTS 模型能力。前端可使用 model.numSpeakers > 1 或 model.supportsMultipleSpeakers=true 判断是否展示多个 speakerId 试听项。
成功响应(HTTP 200):
json
{
"status": "ok",
"server": "sherpa-tts-server",
"listenHost": "0.0.0.0",
"listenPort": 9080,
"publicBaseUrl": "http://127.0.0.1:9080",
"sampleRate": 24000,
"workerThreads": 8,
"ttsPoolSize": 4,
"activeStreams": 0,
"maxActiveStreams": 24,
"queuedRequests": 0,
"maxQueuedRequests": 256,
"socketReadTimeoutMs": 5000,
"socketSendTimeoutMs": 15000,
"ffmpegExitTimeoutMs": 5000,
"model": {
"id": "kokoro-multi-lang-v1_1",
"backend": "kokoro",
"type": "kokoro",
"dir": "kokoro-multi-lang-v1_1",
"numSpeakers": 103,
"supportsMultipleSpeakers": true,
"activeFiles": {
"model": "model.onnx",
"voices": "voices.bin",
"tokens": "tokens.txt",
"lexicon": "lexicon-us-en.txt,lexicon-zh.txt",
"dataDir": "espeak-ng-data",
"dictDir": "dict",
"lang": "zh"
},
"speakers": [
{ "id": 0, "name": "af_maple" },
{ "id": 1, "name": "af_sol" }
]
}
}POST /tts
合成一段文本并落盘 wav,返回可访问的 URL。
请求头
text
Content-Type: application/json请求体
json
{
"text": "欢迎进入会议,正在等待其他参会人加入。",
"speakerId": 0,
"speed": 1.0
}| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
text | string | 是 | - | 要合成的文本,trim 后不能为空 |
speakerId | int | 否 | 0 | 多说话人模型的说话人编号;若超过 NumSpeakers() 会自动回退为 0。默认 Matcha 模型只有 1 个说话人,因此该字段主要用于兼容现有调用 |
speed | float | 否 | 1.0 | 合成语速倍率,必须 > 0,否则 400 |
成功响应(HTTP 200)
json
{
"wavUrl": "http://127.0.0.1:9080/wav/tts-3a1c7b9eaf0c1d72.wav",
"fileName": "tts-3a1c7b9eaf0c1d72.wav",
"sampleRate": 24000,
"cached": false
}| 字段 | 类型 | 说明 |
|---|---|---|
wavUrl | string | 由 publicBaseUrl + /wav/ + fileName 拼接 |
fileName | string | wav 文件名,格式 tts-<FNV-1a 16hex>.wav |
sampleRate | int | 模型采样率 |
cached | bool | true 表示命中已有 wav,未触发新合成;false 表示当前请求实际生成 |
错误响应
| HTTP 状态 | 触发条件 | 响应体示例 |
|---|---|---|
| 400 Bad Request | 非 JSON、缺少 text、text 为空、speed <= 0、Content-Length 超限、解析失败 | {"error":"Missing text"} |
| 404 Not Found | 路径不是 /tts 或 /wav/* | {"error":"Not found"} |
| 500 Internal Server Error | TTS 生成失败、wav 写盘失败 | {"error":"TTS generated empty audio"} |
| 503 Service Unavailable | 等待队列已满(受 maxQueuedRequests 控制) | {"error":"Server busy"} |
缓存行为
服务按 <modelId>:<backend>:<modelDir>|sid=<speakerId>|speed=<speed.fixed3>|text=<text> 组合做 FNV-1a 64bit 哈希,得到 tts-<16hex>.wav 文件名。同样的 {text, speakerId, speed} 永远命中同一文件:
- 进程内已知 → 直接返回
cached=true - 合成进行中 → 共享同一个 future,等待其他请求完成后一起返回
- 磁盘上已有同名 wav → 补回索引并返回
cached=true - 否则 → 当前请求成为 owner,调用
OfflineTtsWorker实际生成
服务按 server.maxCacheEntries 执行进程内 O(1) LRU 淘汰,超限时批量收敛到 90% 水位; 正在 /wav 下载或供流式播放复用的条目持有读取租约,不参与删除。0 表示不限制缓存条数。 当前没有 TTL 清理;外部删除缓存文件不会破坏服务,下一次相同请求会重新生成。
GET /wav/<fileName>
下载已生成的 wav。
请求
text
GET /wav/tts-3a1c7b9eaf0c1d72.wav HTTP/1.1响应
text
HTTP/1.1 200 OK
Content-Type: audio/wav
Content-Length: <bytes>
Connection: close
<wav binary stream,按 wavSendChunkBytes 分块发送>约束
fileName只取path::filename,禁止..,否则返回 400- 文件不存在或不在缓存索引中返回 404
- 仅返回
Content-Length头,不支持Range/ 多段断点续传
HEAD /wav/<fileName>
与 GET 完全一致的状态码和头部,但不返回响应体。FreeSWITCH 通过 playback 拉取 wav URL 时通常会先做 HEAD 检查,文件不存在时直接放弃,避免下载半截。
POST /tts-stream
创建一个流式 TTS 播放链接并立即返回,不等待完整 wav 合成。请求体与 POST /tts 相同:
json
{
"text": "欢迎进入会议,正在等待其他参会人加入。",
"speakerId": 0,
"speed": 1.0
}成功响应:
json
{
"streamUrl": "http://127.0.0.1:9080/tts-stream/3a1c7b9eaf0c1d72.mp3",
"streamPath": "/tts-stream/3a1c7b9eaf0c1d72.mp3",
"sampleRate": 24000,
"estimatedDurationMs": null,
"cached": false
}GET /tts-stream/<id>.mp3 会在同一个 HTTP 连接中以 Transfer-Encoding: chunked 返回 audio/mpeg。 如果同一 {text, speakerId, speed} 的 wav 已经存在,服务会直接读取缓存 wav,经 ffmpeg 转成 MP3 后写入 HTTP 响应,不再调用 sherpa TTS。 未命中缓存时,服务会调用 sherpa TTS 的增量回调,把新生成的 float PCM 样本送入 ffmpeg 实时编码为 MP3 并写入 HTTP 响应;完整生成后仍会把 wav 结果落盘到 public/wav/,供后续 /tts 和 /tts-stream 命中缓存。 缓存 wav 和实时合成两条路径使用同一套 mp3BitrateKbps / mp3VolumeGainDb 配置,避免首次播放与缓存播放的 MP3 听感不一致。
响应字段 cached=true 表示创建播放链接时已发现可复用的 wav 缓存。 estimatedDurationMs 只在已命中 WAV 缓存时可预先返回;首次实时合成前无法 可靠知道最终音频样本数,因此返回 null。缓存命中时, HEAD/GET /tts-stream/<id>.mp3 也会返回 X-TTS-Estimated-Duration-Ms 响应头。
HEAD /tts-stream/<id>.mp3 只校验链接是否存在,不触发合成。