快速开始
页面中的示例默认使用 BASE_URL=http://localhost:8000。
Docker 部署时把端口替换成你的画像端口即可。
BASE_URL=http://localhost:8000
curl "$BASE_URL/health"
curl "$BASE_URL/v1/models"
curl "$BASE_URL/v1/audio/voices"
如果设置了 KOKORO_API_KEY,HTTP 请求要加
Authorization: Bearer YOUR_TOKEN;WebSocket 可在首包 JSON 里传
token。
模型与端点
| 模型 ID | 用途 | 是否支持参考音频克隆 |
kokoro | Kokoro v1.1 中文,默认启动,资源占用低 | 否,只支持 .pt 音色 |
moss | MOSS-TTS-Nano 克隆引擎;CPU/CUDA 由服务运行策略决定 | 是 |
zipvoice | ZipVoice 高质量克隆与长文本后端 | 是,支持保存 Voice Profile 或 multipart 临时参考音频 |
旧客户端仍可提交 moss-nano-cpu 或 moss-nano-cuda;新接入统一使用 moss。
切换到 MOSS
curl -X POST "$BASE_URL/v1/models/switch" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"model":"moss","unload_previous":true}'
OpenAI 兼容合成
/v1/audio/speech 用 JSON 调用,适合普通 TTS。它不走 multipart,
所以 MOSS/ZipVoice 临时参考音频上传请用 /api/tts。
curl -X POST "$BASE_URL/v1/audio/speech" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"model":"kokoro","input":"你好世界","voice":"zm_010","speed":1.0,"response_format":"wav"}' \
--output kokoro.wav
curl -X POST "$BASE_URL/v1/audio/speech" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"model":"moss","input":"这是 MOSS 预设音色测试。","voice":"Junhao","response_format":"wav"}' \
--output moss.wav
curl -X POST "$BASE_URL/v1/audio/speech" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"model":"zipvoice","input":"这是已保存 ZipVoice 音色测试。","voice":"voice_001","response_format":"telegram_voice"}' \
--output zipvoice.ogg
telegram_voice、ogg_opus、m4a 和 mp3 仅适用于非流式 HTTP,且需要在管理后台启用 FFmpeg 转码。
MOSS 克隆:参考音频到底放哪?
很多人会把 MOSS 的参考音频误放到 models/models--hexgrad--Kokoro-82M-v1.1-zh/voices。
这个目录是 Kokoro 的 .pt 音色目录,不是 MOSS 克隆参考音频目录。
| 使用方式 | 参考音频放置位置 | 适合场景 |
| HTTP multipart 上传 |
放在调用客户端本机任意路径,例如 ./reference.wav,curl 用 -F prompt_audio=@reference.wav 上传 |
最推荐,最直观,一次请求带一次参考音频 |
| WebSocket base64 |
前端/客户端读取本地文件,转成 base64,放进首个 JSON 的 prompt_audio.data |
流式克隆、网页上传、实时播放 |
| 服务端默认参考音频 |
把文件挂载到容器内,例如 /app/prompts/reference.wav,设置 MOSS_PROMPT_AUDIO_PATH |
固定一个默认克隆音色,客户端无需每次上传 |
推荐参考音频:3-10 秒、单人、清晰、少背景噪音。过长音频会被
MOSS_PROMPT_AUDIO_MAX_SECONDS 裁剪,也可能增加显存和延迟压力。
MOSS HTTP 参考音频克隆
使用 POST /api/tts 的 multipart 表单。字段名可以是
prompt_audio,也兼容 reference_audio。
BASE_URL=http://localhost:8000
curl -X POST "$BASE_URL/api/tts" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F model=moss \
-F text="这是参考音频克隆测试。" \
-F voice=Junhao \
-F response_format=wav \
-F prompt_audio=@reference.wav \
--output clone.wav
字段说明
| 字段 | 说明 |
model | moss |
text | 要合成的文本 |
voice | MOSS 内置说话人名,例如 Junhao;有参考音频时仍作为 prompt 解析的 voice 参数 |
response_format | wav、pcm,或启用 FFmpeg 后的 mp3、ogg_opus/telegram_voice、m4a |
prompt_audio | 参考音频文件上传字段,支持 wav/mp3/flac/ogg/m4a/aac |
Python requests 示例
import requests
base_url = "http://localhost:8000"
headers = {"Authorization": "Bearer YOUR_TOKEN"}
with open("reference.wav", "rb") as audio:
files = {"prompt_audio": ("reference.wav", audio, "audio/wav")}
data = {
"model": "moss",
"text": "这是 Python 上传参考音频克隆测试。",
"voice": "Junhao",
"response_format": "wav",
}
resp = requests.post(f"{base_url}/api/tts", headers=headers, data=data, files=files)
resp.raise_for_status()
with open("clone.wav", "wb") as out:
out.write(resp.content)
MOSS WebSocket 流式克隆
WebSocket 不能像 HTTP 一样用 multipart。参考音频需要在第一个 JSON 消息里传:
prompt_audio.filename 和 prompt_audio.data。
data 可以是纯 base64,也可以是浏览器 FileReader 生成的 data URL。
首包 JSON 结构
{
"model": "moss",
"text": "这是参考音频克隆的流式测试。",
"voice": "Junhao",
"format": "pcm_s16le",
"binary": false,
"prompt_audio": {
"filename": "reference.wav",
"data": "<base64-or-data-url>"
},
"token": "YOUR_TOKEN"
}
浏览器 FileReader 示例
async function fileToDataUrl(file) {
return await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
async function startMossCloneStream(file) {
const promptData = await fileToDataUrl(file);
const ws = new WebSocket("ws://localhost:8000/ws/v1/tts");
ws.onopen = () => {
ws.send(JSON.stringify({
model: "moss",
text: "这是网页端 MOSS 克隆流式测试。",
voice: "Junhao",
format: "pcm_s16le",
binary: false,
token: "YOUR_TOKEN",
prompt_audio: {
filename: file.name || "reference.wav",
data: promptData
}
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "started") {
console.log("stream started", msg);
}
if (msg.type === "audio") {
const bytes = Uint8Array.from(atob(msg.data), c => c.charCodeAt(0));
// bytes 是 PCM s16le;接到 AudioWorklet、播放器队列或后端转封装即可。
console.log("audio chunk", bytes.byteLength);
}
if (msg.type === "done") {
ws.close();
}
if (msg.type === "error" || msg.type === "segment_error") {
console.error(msg.message);
}
};
return ws;
}
Python websockets 示例
import asyncio
import base64
import json
import websockets
async def main():
with open("reference.wav", "rb") as f:
prompt_b64 = base64.b64encode(f.read()).decode("ascii")
async with websockets.connect("ws://localhost:8000/ws/v1/tts") as ws:
await ws.send(json.dumps({
"model": "moss",
"text": "这是 Python WebSocket 克隆流式测试。",
"voice": "Junhao",
"format": "pcm_s16le",
"binary": False,
"token": "YOUR_TOKEN",
"prompt_audio": {
"filename": "reference.wav",
"data": prompt_b64
}
}, ensure_ascii=False))
with open("stream.pcm", "wb") as out:
async for raw in ws:
msg = json.loads(raw)
if msg.get("type") == "audio":
out.write(base64.b64decode(msg["data"]))
elif msg.get("type") in {"done", "cancelled"}:
break
elif msg.get("type") in {"error", "segment_error"}:
raise RuntimeError(msg.get("message"))
asyncio.run(main())
上面的 Python 示例保存的是原始 PCM s16le。要直接得到 wav,可以请求
format:"wav",或者把 PCM 按返回的 sample_rate、
channels 自行封装成 wav。
ZipVoice HTTP 克隆调用
ZipVoice 可以使用已保存的 voice,也可以通过 POST /api/tts multipart 临时上传参考音频与参考文本。
prompt_text 必须是参考音频中真实朗读的自然语言文本。
curl -X POST "$BASE_URL/api/tts" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F model=zipvoice \
-F text="这是 ZipVoice 临时参考音频克隆测试。" \
-F voice=voice_001 \
-F prompt_text="这是一段参考音频中实际朗读的文字。" \
-F prompt_audio=@reference.wav \
-F response_format=wav \
--output zipvoice.wav
curl -X POST "$BASE_URL/api/tts" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F model=zipvoice \
-F text="这条语音将输出为 Telegram 可直接尝试的 OGG Opus 格式。" \
-F voice=voice_001 \
-F response_format=telegram_voice \
--output voice.ogg
WebSocket 流式暂不输出 OGG/MP3/M4A;流式仍使用 pcm_s16le 或 wav 分包。
服务端默认参考音频
如果你想固定一个参考音频,让所有 MOSS clone 请求默认使用它,可以设置
MOSS_PROMPT_AUDIO_PATH。这适合“单一默认克隆音色”的服务。
Docker Compose 挂载示例
volumes:
- ../../prompts:/app/prompts:ro
environment:
- MOSS_PROMPT_AUDIO_PATH=/app/prompts/reference.wav
- MOSS_PROMPT_AUDIO_MAX_SECONDS=8
- MOSS_PROMPT_CACHE_MAX_ITEMS=8
调用时不用再上传文件
curl -X POST "$BASE_URL/api/tts" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F model=moss \
-F text="这次请求会使用服务端默认参考音频。" \
-F voice=Junhao \
-F response_format=wav \
--output clone-default.wav
这种方式不会把音频保存到 models/models--hexgrad--Kokoro-82M-v1.1-zh/voices,也不会生成 Kokoro 的
.pt 音色。它只是 MOSS 每次推理时读取/缓存的一段 prompt audio。
常见错误
| 现象 | 原因 | 处理 |
当前模型不支持参考音频克隆 |
请求打到了 kokoro,或模型没有 voice_clone 能力 |
切换/指定 moss 或 zipvoice |
未检测到可合成的中文或英文文本 |
输入是 URL、代码、日志、JSON、纯符号,或清理后没有可进入 tokenizer 的文本 |
改成自然语言后重试;纯数字如 3.5 会自动转成自然读法 |
FFMPEG_DISABLED |
请求了 mp3、ogg_opus/telegram_voice 或 m4a,但未启用 FFmpeg 转码 |
到管理后台启用 FFmpeg 转码,并确认服务器安装了 ffmpeg |
FFMPEG_UNAVAILABLE |
已启用转码,但服务环境找不到可执行的 ffmpeg |
安装 ffmpeg,或在管理后台/环境变量中配置正确路径 |
FFMPEG_CONVERSION_FAILED |
ffmpeg 运行失败,常见原因是缺少 libopus / libmp3lame / aac 编码器 |
检查 ffmpeg 构建能力,或临时改用 wav |
| MOSS 模型不可选 |
MOSS runtime 没装,或当前画像没有启用该模型 |
查看 /v1/models,检查 ANGEVOICE_ENABLED_MODELS 和 MOSS_CUDA_ENABLED |
| WebSocket 连接成功但没音频 |
首包缺 text、鉴权 token、或代理没转发 WebSocket upgrade |
先本地直连 ws://host:port/ws/v1/tts 测试,再检查代理 |
| clone OOM / 爆音 / 失真 |
参考音频太长、CUDA provider 不稳定、显存紧张 |
把 MOSS_PROMPT_AUDIO_MAX_SECONDS 降到 5-8,先试 moss |
| 401 Unauthorized |
服务端设置了 KOKORO_API_KEY |
HTTP 加 Bearer;WebSocket 首包加 token |