跳到正文

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 > 1model.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
}
字段类型必填默认说明
textstring-要合成的文本,trim 后不能为空
speakerIdint0多说话人模型的说话人编号;若超过 NumSpeakers() 会自动回退为 0。默认 Matcha 模型只有 1 个说话人,因此该字段主要用于兼容现有调用
speedfloat1.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
}
字段类型说明
wavUrlstringpublicBaseUrl + /wav/ + fileName 拼接
fileNamestringwav 文件名,格式 tts-<FNV-1a 16hex>.wav
sampleRateint模型采样率
cachedbooltrue 表示命中已有 wav,未触发新合成;false 表示当前请求实际生成

错误响应

HTTP 状态触发条件响应体示例
400 Bad Request非 JSON、缺少 texttext 为空、speed <= 0、Content-Length 超限、解析失败{"error":"Missing text"}
404 Not Found路径不是 /tts/wav/*{"error":"Not found"}
500 Internal Server ErrorTTS 生成失败、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} 永远命中同一文件:

  1. 进程内已知 → 直接返回 cached=true
  2. 合成进行中 → 共享同一个 future,等待其他请求完成后一起返回
  3. 磁盘上已有同名 wav → 补回索引并返回 cached=true
  4. 否则 → 当前请求成为 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 只校验链接是否存在,不触发合成。

文档与代码在同一仓库维护,现有 Markdown 是唯一内容源。