让你的 Agent
开口说话。
发送文字与音色描述,持续接收语音数据。面向服务端、桌面客户端与 Agent 的中文 / 英文语音合成接口。
POST
https://tts.heang.top/v1/audio/speech24 kHz单声道 PCM
流式返回边生成 · 边播放
Token 认证Cloudflare Access
01 / START
快速接入
先向服务提供方领取 Client ID 和 Client Secret,将它们放入运行环境变量,再执行以下请求。文档公开不代表 API 可匿名使用。
下面是 Bash / zsh 语法;Windows 可使用 WSL,或直接运行下方 Python 示例并配置环境变量。
export TTS_CLIENT_ID="由服务提供方单独提供"
export TTS_CLIENT_SECRET="由服务提供方单独提供"
curl --fail-with-body --no-buffer https://tts.heang.top/v1/audio/speech \
-H "CF-Access-Client-Id: $TTS_CLIENT_ID" \
-H "CF-Access-Client-Secret: $TTS_CLIENT_SECRET" \
-H "User-Agent: BreezeAgent/1.0" \
-H "Content-Type: application/json" \
-d '{"input":"你好,欢迎来到我的直播间。","instructions":"温柔自然的成年女性,用普通话亲切地说话。","response_format":"pcm","cfg_scale":1,"max_new_tokens":500}' \
--output speech.pcm
# 可选:使用 ffplay 边接收边播放时,将 --output speech.pcm 替换为:
# --output - | ffplay -nodisp -autoexit -f s16le -ar 24000 -ac 1 -i -02 / AUTH
每次请求都携带认证头
| 请求头 | 填写内容 |
|---|---|
CF-Access-Client-Id | 服务提供方分配的 Client ID |
CF-Access-Client-Secret | 对应的 Client Secret |
User-Agent | BreezeAgent/1.0;避免默认 Python 标识被站点检查拒绝 |
Content-Type | 合成请求使用 application/json |
密钥只放在服务端或可信客户端。不要放进公开网页 JavaScript、Git 仓库或分享链接。浏览器产品应由自己的后端转发;当前接口不提供跨域浏览器直连。
检查可用性
curl --fail-with-body https://tts.heang.top/health \
-H "User-Agent: BreezeAgent/1.0" \
-H "CF-Access-Client-Id: $TTS_CLIENT_ID" \
-H "CF-Access-Client-Secret: $TTS_CLIENT_SECRET"
# 正常响应包含:{"status":"ok","sample_rate":24000}03 / REQUEST
用文字描述声音
请求体使用 UTF-8 JSON。input 是必填字段。当前没有预注册的音色 ID;请省略 voice,通过 instructions 设计声音。
| 字段 | 类型 | 说明 |
|---|---|---|
input | string · 必填 | 要朗读的文字;建议从短句开始。 |
instructions | string | 音色和表达描述,例如“成年女性,中低音,温柔自然,用普通话聊天”。描述语言尽量与正文一致。 |
response_format | string | 实时建议显式指定 pcm;也支持 wav、mp3。 |
cfg_scale | number | 建议 1,音质和速度较均衡;提高可强化指令,但可能更刺耳。 |
seed | integer | 随机种子;相同文本与配置下便于复现,不保证跨文本保持完全相同的音色。 |
max_new_tokens | integer | 生成帧数上限,示例为 500;每帧约 80 ms,上限不是固定输出时长。 |
temperature | number | 可选采样温度,模型默认约 0.9,先保持默认。 |
split_chars | integer | 可选长文本分段预算,当前默认 200;中文按加权预算计数。 |
model | string | 兼容字段,当前忽略;不用填写。 |
接口兼容 OpenAI Speech 风格的部分字段,并非所有 OpenAI 参数或内置声音均可用;不要传入 alloy、nova 等默认音色名称。
04 / STREAM
收到一块,播放一块
文字→语音合成→PCM 数据块→播放队列
- PCM 为 24,000 Hz / 单声道 / signed 16-bit little-endian,每秒 48,000 字节,无文件头。
- 按字节流持续读取,别先等待完整文件。网络数据块不等于一句话,也可能不对齐 2 字节采样边界;缓存不足一个采样的尾部。
- 播放端用短缓冲队列吸收网络抖动。收到第一块不代表可以完全无缓冲连续播放。
- 保存 WAV 时请正确封装 PCM。流式 WAV 的文件头可能使用未知长度标记;推荐按 Python 示例自行写入。
- 取消 HTTP 请求不等于完整的对话打断协议;此接口不提供全双工语音会话。
一次公网测试:6.48 秒语音约 5.63 秒生成,首个音频块约 1.88 秒。数据包含当时的网络链路,仅供参考,不是延迟承诺。
05 / EXAMPLES
复制代码,接进你的项目
Python · 流式接收并保存 WAV
# Python 3.10+,仅使用标准库。密钥先设置到环境变量。
import json, os, urllib.request, wave
request = urllib.request.Request(
"https://tts.heang.top/v1/audio/speech",
data=json.dumps({
"input": "你好,这是流式语音合成测试。",
"instructions": "自然、清晰、温暖的成年女声。",
"response_format": "pcm",
"cfg_scale": 1,
"max_new_tokens": 500,
}).encode("utf-8"),
headers={
"Content-Type": "application/json",
"User-Agent": "BreezeAgent/1.0",
"CF-Access-Client-Id": os.environ["TTS_CLIENT_ID"],
"CF-Access-Client-Secret": os.environ["TTS_CLIENT_SECRET"],
},
)
with urllib.request.urlopen(request, timeout=180) as response:
if "audio" not in response.headers.get("Content-Type", ""):
raise RuntimeError("服务未返回音频,请检查认证和响应内容")
with wave.open("speech.wav", "wb") as audio:
audio.setnchannels(1)
audio.setsampwidth(2)
audio.setframerate(24000)
while chunk := response.read1(4096):
# 实时播放:将 chunk 交给 PCM 播放队列,勿等待整个请求结束。
audio.writeframesraw(chunk)
# wave 自动写入正确的文件长度;不要把 .pcm 直接改名为 .wav。Node.js · 流式保存 PCM
// Node.js 22+;保存为 speech.mjs 后执行 node speech.mjs。
import { createWriteStream } from "node:fs";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
const response = await fetch("https://tts.heang.top/v1/audio/speech", {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": "BreezeAgent/1.0",
"CF-Access-Client-Id": process.env.TTS_CLIENT_ID,
"CF-Access-Client-Secret": process.env.TTS_CLIENT_SECRET,
},
body: JSON.stringify({
input: "你好,我正在实时生成语音。",
instructions: "温暖、自然的成年男声。",
response_format: "pcm", cfg_scale: 1, max_new_tokens: 500,
}),
signal: AbortSignal.timeout(180000),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
if (!response.headers.get("content-type")?.includes("audio")) {
throw new Error("Expected audio response");
}
await pipeline(Readable.fromWeb(response.body), createWriteStream("speech.pcm"));
// 实时应用可逐块读取 response.body,并交给播放器。06 / ERRORS
错误与重试
| 状态 | 处理方式 |
|---|---|
| 400 | 检查字段、JSON 格式和文本内容;不要对同一错误请求盲目重试。 |
| 403 / 登录页面 | 检查两项认证头、Token 有效期、User-Agent。认证响应可能是 HTML 或纯文本。 |
| 404 | 仅开放 /health 和 /v1/audio/speech;根目录不提供网页。 |
| 413 | 请求体超过 1 MiB;拆小请求。 |
| 429 | 合成忙碌。等待约 5 秒,并加入随机抖动后重试;不要同时提交多个任务。 |
| 5xx / 超时 | 检查健康接口,退避重试;持续失败请联系服务提供方。 |
读取音频前先验证 HTTP 状态和 Content-Type。流中断后应停止或清空对应播放任务;重新请求可能再次生成整段音频。
07 / NOTES
接入前需要知道
- 当前为单路合成服务,并发上限为 1;可用性依赖提供方的设备与网络,不提供 SLA。
- 支持文字设计音色;公网未开放音色注册、文件上传管理、Live2D 或 WebSocket 会话接口。
- 公网音频不自动应用试听文件的离线高频柔化。
- 模型及本地生成结果受上游研究与非商业许可证约束;商用前需确认相应授权。