AngeVoice API Docs

API 调用文档与 MOSS 语音克隆示例

这里是给普通用户和接入方看的可复制 API 文档。默认服务端口请按部署画像替换: pip 为 8000,Docker CPU 为 8100,Docker GPU 为 8101, 老架构 GPU 为 8102

鉴权状态检测中 HTTP: /v1/audio/speech、/api/tts WebSocket: /ws/v1/tts

快速开始

页面中的示例默认使用 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用途是否支持参考音频克隆
kokoroKokoro v1.1 中文,默认启动,资源占用低否,只支持 .pt 音色
mossMOSS-TTS-Nano 克隆引擎;CPU/CUDA 由服务运行策略决定
zipvoiceZipVoice 高质量克隆与长文本后端是,支持保存 Voice Profile 或 multipart 临时参考音频
旧客户端仍可提交 moss-nano-cpumoss-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_voiceogg_opusm4amp3 仅适用于非流式 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

字段说明

字段说明
modelmoss
text要合成的文本
voiceMOSS 内置说话人名,例如 Junhao;有参考音频时仍作为 prompt 解析的 voice 参数
response_formatwavpcm,或启用 FFmpeg 后的 mp3ogg_opus/telegram_voicem4a
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.filenameprompt_audio.datadata 可以是纯 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_ratechannels 自行封装成 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_s16lewav 分包。

服务端默认参考音频

如果你想固定一个参考音频,让所有 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 能力 切换/指定 mosszipvoice
未检测到可合成的中文或英文文本 输入是 URL、代码、日志、JSON、纯符号,或清理后没有可进入 tokenizer 的文本 改成自然语言后重试;纯数字如 3.5 会自动转成自然读法
FFMPEG_DISABLED 请求了 mp3ogg_opus/telegram_voicem4a,但未启用 FFmpeg 转码 到管理后台启用 FFmpeg 转码,并确认服务器安装了 ffmpeg
FFMPEG_UNAVAILABLE 已启用转码,但服务环境找不到可执行的 ffmpeg 安装 ffmpeg,或在管理后台/环境变量中配置正确路径
FFMPEG_CONVERSION_FAILED ffmpeg 运行失败,常见原因是缺少 libopus / libmp3lame / aac 编码器 检查 ffmpeg 构建能力,或临时改用 wav
MOSS 模型不可选 MOSS runtime 没装,或当前画像没有启用该模型 查看 /v1/models,检查 ANGEVOICE_ENABLED_MODELSMOSS_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