播报与声音库

列出音色并选择兼容组合

GET /v1/audio/voices

发起合成前先列出音色。请选择 available: true 的音色,并检查各模型的 compatibility 列表;音色可用并不代表适用于所有合成模型。以下响应节选展示了一个用户音色,ID 仅为示意。

请求示例:

Shell
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/voices"

输出示例:

JSON
{
  "voices": [
    {
      "id": "user:00000000-0000-4000-8000-000000000001",
      "names": {
        "en-US": "My voice"
      },
      "descriptions": {},
      "supported_languages": [
        "en-US"
      ],
      "origin": "cloned",
      "compatibility": [],
      "available": true,
      "created_by_user": true
    }
  ]
}

名称与描述按 locale 索引,并非单个 name 字符串。示例中的空兼容列表不能证明支持任何模型,发起后续请求时请填入实际兼容的模型与音色组合。

生成 WAV 播报音频

POST /v1/audio/speech

JSON 请求必填字段:model、voice、input(或非空 segments[])。language 可选,省略或传 auto 由服务自动选择;各模型支持的语言见播报语言。下列模型与音色组合仅为示例,请先确认在模型目录中存在且兼容。响应格式为 WAV。

请求示例:

Shell
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/speech" \
  -H "Content-Type: application/json" \
  -d '{"model":"Qwen/Qwen3-TTS-0.6B-Base","voice":"builtin:bright-girl","input":"Hello world.","response_format":"wav"}' \
  -o speech.wav
file speech.wav

输出示例:

speech.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 24000 Hz

此处为 file 命令的输出示例,并非 JSON。HTTP 响应体为 audio/wav 二进制数据,默认流式输出。原生采样率取决于模型,请省略 sample_rate。桌面网关流式接口仅接受与原生采样率一致的显式值,Headless 服务则会拒绝显式 sample_rate。请求失败时 --fail-with-body 会以非零状态退出,应检查保存的错误内容而非直接播放文件。参阅各模型支持的语音控制参数。

通过 SSE 接收播报音频

需要事件流时传入 stream_format: "sse",并使用 curl -N 禁用缓冲。每个 delta 事件中的音频数据均为 Base64 编码,第一段解码后包含 WAV 文件头,按顺序拼接解码后的字节即可还原音频。

请求示例:

Shell
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/speech" \
  -H "Content-Type: application/json" \
  -d '{"model":"Qwen/Qwen3-TTS-0.6B-Base","voice":"builtin:bright-girl","input":"Hello world.","stream_format":"sse","response_format":"wav"}' \
  -N

输出示例:

event: speech.audio.delta
data: {"type":"speech.audio.delta","audio":"<base64 WAV header and PCM bytes>"}

event: speech.audio.done
data: {"type":"speech.audio.done","diagnostics":{},"usage":{"input_tokens":0,"output_tokens":0,"total_tokens":0}}

上述事件节选省略了 diagnostics 内部字段,音频内容为占位符。合成成功以 speech.audio.done 事件结束;客户端同时需要处理 error 事件以及完成前的异常断线。此处 usage 中的 token 数为零,不能用来估算音频时长。

风格指令、克隆档位与声音设计

以下字段都放在 POST /v1/audio/speech 的 JSON 顶层,segments[] 的每一段也可以单独给 instructions 或 disable_style;段内两者都不写(含 ""、null、false)就继承顶层风格。

参数说明
instructionsOpenAI 同名参数,字符串。不传、传 "" 或 null 沿用音色保存的风格;传非空文本则本次改用这段风格。只有 /v1/models 中 features 含 instruct 的模型接受非空文本,其他模型返回 400,不会静默忽略。voice: "builtin:design" 时它是声音描述,必填。FireRedTeam/FireRedTTS3-Instruct 与 k2-fsa/OmniVoice 的描述只在 voice: "builtin:design" 下生效:选了具体音色(预设音色、已保存音色或 builtin:auto)再给非空文本返回 400,不会悄悄换掉所选音色;不填则照常用所选音色。
disable_style布尔。true 表示本次不用任何风格;false 或不传无作用。与非空 instructions 同时出现返回 400 conflicting_speech_instructions。
clone_mode克隆档位:quick 起播更快,ultimate 相似度更好,所有克隆模型共用这一个参数。省略时用模型缺省档。点名了兑现不了的档位返回 400,不会静默回落到另一档。

各模型支持的克隆档位列在 /v1/models 中:有档位的播报模型带 clone_mode.values(可选档位)与 clone_mode.default_value(缺省档),没有档位的模型不带 clone_mode 字段。目前 Qwen3-TTS Base(0.6B 与 1.7B)和 openbmb/VoxCPM2 两档都有,缺省档为 quick。

JSON
{"id":"openbmb/VoxCPM2","clone_mode":{"values":["quick","ultimate"],"default_value":"quick"}}

上例为模型目录条目的节选,省略了其他字段。

按描述设计声音

voice: "builtin:design" 不挂任何内置或用户音色,instructions 里的描述就是声音:

  • 支持的模型:openbmb/VoxCPM2、Qwen/Qwen3-TTS-1.7B-VoiceDesign、BreezeBlue/Breeze-TTS-2、FireRedTeam/FireRedTTS3-Instruct、k2-fsa/OmniVoice。其他模型直接拒绝,不会换成某个内置音色。
  • 描述必填,上限 120 字;FireRedTeam/FireRedTTS3-Instruct 上限 1000 字,k2-fsa/OmniVoice 上限 80 字。
  • FireRedTeam/FireRedTTS3-Instruct 与 k2-fsa/OmniVoice 只能这样用描述:选了具体音色再给非空 instructions 返回 400,见下方错误表。
  • 不接受 clone_mode。
  • Realtime 会话不接受 builtin:design,见请求语音回复。

k2-fsa/OmniVoice 的描述由属性短语组成,用逗号分隔(中文用全角逗号),每类至多一项:性别(男 / 女,male / female)、年龄(儿童、少年、青年、中年、老年;child、teenager、young adult、middle-aged、elderly)、音高(极低音调 … 极高音调;very low pitch … very high pitch)、耳语(耳语,whisper)、英语口音(如 british accent,仅英文)、汉语方言(如 四川话,仅中文)。声音设计只支持中文和英语,其他语言可能不稳定。

桌面 App 的播报页填了描述会自动改用 builtin:design,所选音色本次不生效。

Shell
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/speech" \
  -H "Content-Type: application/json" \
  -d '{"model":"FireRedTeam/FireRedTTS3-Instruct","voice":"builtin:design","instructions":"沉稳、低沉的男声旁白","input":"你好,这里是 EdgeSpeak。","response_format":"wav"}' \
  -o designed.wav
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/speech" \
  -H "Content-Type: application/json" \
  -d '{"model":"k2-fsa/OmniVoice","voice":"builtin:design","instructions":"女,青年,低音调","input":"你好,这里是 EdgeSpeak。","response_format":"wav"}' \
  -o omnivoice-designed.wav

风格与档位错误

错误响应使用统一信封,按 error.code 分支,不要匹配 message 文案:

JSON
{"error":{"message":"...","type":"invalid_request_error","code":"conflicting_speech_instructions"}}
情形error.code原因位置
disable_style: true 与非空 instructions 同时出现conflicting_speech_instructionscode 本身
请求里出现已删除的 style_instruction(顶层或 segments[] 内,任何取值)bad_requestmessage 提示改用 instructions / disable_style
非空 instructions 遇到不支持风格指令的模型(含 FireRedTeam/FireRedTTS3-Instruct、k2-fsa/OmniVoice 选了具体音色)/ 描述必填但缺失 / 描述超长bad_requestmessage 中的稳定键 errors.broadcast.instructUnsupported、errors.broadcast.instructRequired、errors.broadcast.instructTooLong
模型没有点名的克隆档位 / 模型有这一档但本次音色没有bad_requestmessage 以 clone_mode_unsupported_by_model 或 clone_mode_unsupported_by_voice 开头,并列出支持的档位

这些都是请求本身的问题,需要改请求,原样重试不会成功。

查询模型专属播报示例

GET /v1/audio/speech/examples

可选查询参数:model、mode、language、id、recipe。先查询完整目录获取有效的 ID 与模式;传入无效过滤条件将返回 400。以下命令先保存完整目录,再通过 jq 展示可选模式。

请求示例:

Shell
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/speech/examples" \
  -o speech-examples.json
jq '.examples | map(.mode) | unique' speech-examples.json

输出示例:

JSON
[
  "emotion-vector",
  "style-control",
  "voice-design"
]

这里展示的是 jq 处理后的示例输出。保存的响应包含 schema_version、catalog_version、verified_at、resolved_language、models[] 和 examples[]。每个示例均包含 applicable 与 reason,调用时应仅使用当前模型支持的控制选项。例如追加 ?model=voxcpm2&mode=voice-design&language=en-US 即可按条件筛选。

创建可复用的用户音色

POST /v1/audio/voices

将声音保存到服务主机的声音库。使用 multipart 格式提交参数:

参数说明
audio_sample必填,最多 10 MiB;使用简短清晰的参考音频。
ref_text / name必填,分别对应参考音频文稿与音色名称。
consent必填,固定为 true。
language可选,默认 zh-CN;其他语言需显式设置。以 en-US 这类 locale 形式保存,见语言代码。
speaker_description可选,说话人描述。

不要提供 model。

请求示例:

Shell
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/voices" \
  -F audio_sample=@reference.wav -F ref_text="Hello, this is my reference recording." \
  -F name="My voice" -F language=en-US -F consent=true \
  -o created-voice.json
cat created-voice.json

输出示例:

JSON
{
  "success": true,
  "voice": {
    "id": "user:00000000-0000-4000-8000-000000000001",
    "names": {
      "en-US": "My voice"
    },
    "descriptions": {},
    "supported_languages": [
      "en-US"
    ],
    "origin": "cloned",
    "compatibility": [],
    "available": true,
    "created_by_user": true
  }
}

后续合成或删除时请使用实际返回的 voice.id,不要使用示意 UUID。发起合成前请检查返回的兼容性信息。

删除用户音色

DELETE /v1/audio/voices/:voice_id

从声音库中删除上一步创建的音色,仅在确认不再需要时执行。内置音色不可删除。以下示例使用 jq 读取保存的响应以获取音色 ID。

请求示例:

Shell
VOICE_ID=$(jq -er '.voice.id' created-voice.json)
curl --fail-with-body "$EDGESPEAK_BASE_URL/audio/voices/$VOICE_ID" \
  -X DELETE

输出示例:

JSON
{
  "success": true,
  "deleted": {
    "id": "user:00000000-0000-4000-8000-000000000001",
    "name": "My voice"
  }
}

桌面请求排队

桌面端的本机播报请求共用同一个队列。客户端可以并发提交,模型按需依次执行,排队等待期间不会提前占用模型。模型休眠设置只决定空闲回收,不等于多个播报模型同时驻留。

请求格式保持不变。若前面已有播报请求,本次首包时间将包含排队耗时:

Shell
curl --no-buffer http://127.0.0.1:1117/v1/audio/speech \
  -H "Content-Type: application/json" \
  -d '{"model":"openbmb/VoxCPM2","voice":"builtin:bright-girl","input":"你好。","response_format":"wav","stream_format":"sse"}'

桌面网关启用鉴权时需添加 Bearer 凭据。请求成功时同样通过 speech.audio.delta 与 speech.audio.done 发送 SSE 事件;音频传输开始后的异常仍按现有流内错误处理。

Headless 模型队列

CLI 与 Headless 服务里,REST 播报、Realtime 播报、MCP 合成、声音准备和 TTS 预加载共用同一个模型执行队列。请求依次执行;调大 --speech-concurrency 也不会在前一个请求准备或合成期间提前切换模型。排队中取消请求,服务端不会继续加载模型;运行中的请求被取消时,清理流程会等原生任务结束后再释放模型占用。原生故障或清理超时仍会返回错误。Realtime 握手达到并发上限时同样排队等待,详见会话排队。

直接使用上文的 WAV 或 SSE 请求示例即可,排队不新增请求字段、SSE 事件或响应字段。客户端超时需计入排队、模型加载与推理耗时,详见 CLI 并发参数。

生成计时与 IndexTTS 流式

diagnostics.infer_seconds 表示原生合成阶段的墙钟时间,包含声码器计算及阶段内等待,但并非纯 GPU 计算时间,也不包含模型加载耗时,其中含非流式长文的批量预生成时间。客户端首音延迟从发起请求开始计时,可能包含排队、加载和声音准备耗时。

以下请求沿用现有协议;将 input 替换为实际长文,并从模型目录中选择 IndexTTS 2.5。仅当长文切分为多段时才走流式,短句可能只返回一个音频事件。

JSON
{"model":"IndexTeam/IndexTTS-2.5","voice":"builtin:bright-girl","input":"你好,这是一条语音速度测试。","language":"zh-CN","response_format":"wav","stream_format":"sse"}

成功终态的诊断字段节选如下(数值仅为示意;实际终态还含其他字段):

JSON
{"type":"speech.audio.done","diagnostics":{"infer_seconds":6.0,"duration_seconds":3.0}}

该例生成的 RTF 为 6.0 / 3.0 = 2.0;总 RTF 需使用客户端完整的请求耗时单独计算。单次请求的模型加载耗时目前没有公开字段,不能用「总耗时减生成耗时」来充当加载时间。

继续阅读:语言与视觉 · API 索引