Skip to content

IndexTTS2 情绪控制

网关地址: https://ai.ospreyai.cn(公网) 模型: IndexTTS2 无需后端 Bearer Token

概述

使用 IndexTTS2 模型做音色克隆 + 情绪控制语音合成。参考音频定音色,文本定内容,8 维情绪向量定语气。任务异步执行:提交工作流 → 轮询状态 → 下载结果。任务状态查询、结果下载等通用接口见 任务管理

前置条件: 需先通过 /api/v1/upload 上传参考音频。

bash
export GW="https://ai.ospreyai.cn"
export API_KEY="sk-your-api-key"

接口清单

用途方法路径
上传参考音频POST/api/v1/upload
提交任务POST/api/v1/ai/audio/generate
查询任务状态GET/api/v1/ai/tasks/{prompt_id}
下载结果文件GET/api/v1/ai/image/view/
查询队列GET/api/v1/ai/queue

音频提交端点 /ai/audio/generate,与 image/video/generate 一样转发到 ComfyUI 工作流引擎,区别在于工作流含音频节点,结果在 outputs[].audio 字段(不是 images/gifs)。

提交任务

POST /api/v1/ai/audio/generate
Content-Type: application/json
Authorization: Bearer sk-xxx

1. 上传参考音频

bash
curl -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/upload" \
  -F "image=@/path/to/ref.wav" \
  -F "type=input"
# 返回: {"name": "ref.wav", "subfolder": "", "type": "input"}

2. 提交工作流

bash
curl -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/ai/audio/generate" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": {
      "11": {"inputs": {"top_k": 30, "top_p": 0.8, "temperature": 0.8, "num_beams": 3, "max_mel_tokens": 1500, "max_text_tokens_per_sentence": 120, "custom_cuda_kernel": false, "deepspeed": true, "unload_model": false, "emo_alpha": 1, "emo_vector": ["16", 0], "use_emo_text": false, "emo_text": "", "use_random": false, "emo_alpha_s2": 1, "emo_vector_s2": "", "use_emo_text_s2": false, "emo_text_s2": "", "use_random_s2": false, "audio": ["14", 0], "text": ["13", 0]}, "class_type": "IndexTTS2Run"},
      "13": {"inputs": {"multi_line_prompt": "今天天气真好,我们去爬山吧!"}, "class_type": "MultiLinePromptIndex"},
      "14": {"inputs": {"audio": "ref.wav", "audioUI": ""}, "class_type": "LoadAudio"},
      "16": {"inputs": {"happy": 1.5, "angry": 0, "sad": 0, "fear": 0, "hate": 0, "low": 0, "surprise": 0, "neutral": 0}, "class_type": "Emotional Control"},
      "20": {"inputs": {"filename_prefix": "IN会话", "quality": "V0", "audioUI": "", "audio": ["11", 0]}, "class_type": "SaveAudioMP3"}
    },
    "extra_data": {}
  }'

响应:

json
{"prompt_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "number": 1, "node_errors": {}}

关键节点参数

节点字段说明推荐值
13multi_line_prompt要合成的文本
14audio参考音频文件名(需先上传)上传返回的 name
16happy/angry/sad/...情绪向量(8 维,一次设一个)0~2
20filename_prefix输出文件名前缀IN会话

8 种情绪

happy / angry / sad / fear / hate / low(低落)/ surprise / neutral。强度 -2~2,正值加强,负值反向。一次只设一种,其余置 0。

下载结果

音频在 outputs[].audio 字段,IndexTTS2 输出在 output 根目录(type=outputsubfolder="")。

bash
curl -H "Authorization: Bearer $API_KEY" \
  "$GW/api/v1/ai/image/view/?filename=IN会话_00001.mp3&type=output&subfolder=" \
  -o result.mp3

输出: MP3 音频。

缓存坑:若提交的工作流与历史完全相同(尤其 seed 相同),ComfyUI 会命中 execution_cached 直接返回,outputs 可能为空。提交时换个文本或参考音频可避免。

Python 调用示例

python
import json, time, urllib.request

GW = "https://ai.ospreyai.cn"
API_KEY = "sk-your-api-key"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

# 1. 上传参考音频(参考「任务管理」页上传示例)

# 2. 提交工作流
nodes = {
    "11": {"inputs": {"top_k": 30, "top_p": 0.8, "temperature": 0.8, "num_beams": 3, "max_mel_tokens": 1500, "max_text_tokens_per_sentence": 120, "custom_cuda_kernel": false, "deepspeed": true, "unload_model": false, "emo_alpha": 1, "emo_vector": ["16", 0], "use_emo_text": false, "emo_text": "", "use_random": false, "emo_alpha_s2": 1, "emo_vector_s2": "", "use_emo_text_s2": false, "emo_text_s2": "", "use_random_s2": false, "audio": ["14", 0], "text": ["13", 0]}, "class_type": "IndexTTS2Run"},
    # ...其余节点同上 curl 示例...
}
nodes["13"]["inputs"]["multi_line_prompt"] = "今天天气真好,我们去爬山吧!"
nodes["14"]["inputs"]["audio"] = "ref.wav"
nodes["16"]["inputs"] = {"happy": 1.5, "angry": 0, "sad": 0, "fear": 0,
                         "hate": 0, "low": 0, "surprise": 0, "neutral": 0}

req = urllib.request.Request(
    f"{GW}/api/v1/ai/audio/generate",
    data=json.dumps({"prompt": nodes, "extra_data": {}}).encode(),
    headers=HEADERS, method="POST",
)
prompt_id = json.loads(urllib.request.urlopen(req, timeout=30).read())["prompt_id"]

# 3. 轮询
while True:
    r = urllib.request.Request(f"{GW}/api/v1/ai/tasks/{prompt_id}", headers=HEADERS)
    task = json.loads(urllib.request.urlopen(r, timeout=15).read())[prompt_id]
    if task["status"].get("completed"):
        break
    time.sleep(4)

# 4. 下载(音频在 outputs 的 audio 字段)
for out in task["outputs"].values():
    for a in out.get("audio", []):
        url = f"{GW}/api/v1/ai/image/view/?filename={a['filename']}&type={a['type']}&subfolder={a.get('subfolder','')}"
        req = urllib.request.Request(url, headers=HEADERS)
        open(a["filename"], "wb").write(urllib.request.urlopen(req, timeout=120).read())

常见问题

问题现象可能原因解决方案
401 UnauthorizedAPI Key 错误/未传检查 Authorization: Bearer sk-xxx
node_errors 非空参考音频未上传/文件名错POST /api/v1/upload(字段名 imagetype=input),文件名一致
任务 success 但 outputs 为空命中 ComfyUI 缓存换文本/参考音频重提
下载 404type 错IndexTTS2 用 type=output
outputs 找不到音频找错字段音频在 outputs[].audio,不是 images/gifs

AI API Gateway Documentation