野生流动开发者文档
语音合成调用(TTS)

IndexTTS-2.5 语音合成

选择音色,提交文字,下载配音。包含固定音色 ID、调用示例和语速说明。

输入文字和音色 ID,即可生成 WAV 配音。支持中文、英文、日文、西班牙文和阿拉伯文。 当前供团队内部使用,暂不扣零售余额;使用现有账号的 API Key。

选择音色

音色voice_id使用范围
王立群参考音色(AI 合成)wangliqun可调用本模型的所有账号;内置已选试听配置
官方示例音色 01official-voice-01-v1可调用本模型的账号

使用 wangliqun,只需传文稿和这个 ID,无需上传录音或重复填写参数。 它固定绑定 wangliqun-v1 参考录音、默认生成参数和最终音频停顿处理;显式传入的生成参数可以覆盖默认值。 普通官方或自建音色 ID 固定的是参考录音。 更换同一账号的 API Key 不会改变音色 ID;其他账号不能直接使用该账号的自建音色。

王立群参考音色的内容账号选项已保存为「王立群老师」。这是参考录音生成的 AI 音色,合成内容不是本人录音。 下面的示例可供其他有本模型权限的账号直接使用;原先个人账号保存的 UUID 仍保持原内容及账号范围。

先完成下文「设置 API Key」,再查询当前账号能使用的音色:

curl --fail-with-body --silent --show-error \
  https://api.wildflow.cn/v1/voices \
  -H "Authorization: Bearer $WILDFLOW_API_KEY"

从响应的 data 中复制 voice_id。列表包含 11 个官方人声音色、2 个情绪参考、共享 wangliqun,以及当前账号的自建音色。 如果返回 next_cursor,用 ?after=next_cursor 查询下一页。

生成一段配音

下面的命令适用于 macOS / Linux,需要 Python 3 和 cURL。只需修改文稿;如果更换音色,再修改 voice_id。

1. 设置 API Key

export WILDFLOW_API_KEY="替换为控制台生成的密钥"
export WILDFLOW_IDEMPOTENCY_KEY="$(python3 -c 'import uuid; print(uuid.uuid4())')"

每次生成新配音时创建一个新的幂等键。网络中断后重试同一次生成,保留原键。

2. 提交文稿

curl --fail-with-body --silent --show-error \
  -X POST https://api.wildflow.cn/v1/jobs \
  -H "Authorization: Bearer $WILDFLOW_API_KEY" \
  -H "Idempotency-Key: $WILDFLOW_IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "IndexTTS-2.5",
    "parameters": {
      "text": "你好,欢迎使用野生流动。",
      "voice_id": "wangliqun"
    }
  }' \
  --output operation.json

响应保存到 operation.json。首次提交通常返回 HTTP 202,表示任务已接收;还需要查询完成状态并下载文件。

3. 等待完成并下载

export RESULT_FILE="result.wav"
export RESULT_MEDIA_TYPE="audio/wav"

下面的命令使用 Python 3,读取上一步保存的 operation.json,等待任务完成后下载结果。等待超过两小时会停止;再次执行本段会继续查询原任务,不会重新提交。

python3 - <<'PY'
import hashlib
import json
import os
import time
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from pathlib import Path
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import Request, urlopen

base = "https://api.wildflow.cn"
headers = {"Authorization": "Bearer " + os.environ["WILDFLOW_API_KEY"]}
operation = json.loads(Path("operation.json").read_text())
operation_id = operation["id"]
status_url = base + "/v1/jobs/" + quote(operation_id, safe="")
output = Path(os.environ["RESULT_FILE"])
expected_media_type = os.environ["RESULT_MEDIA_TYPE"]
deadline = time.monotonic() + 7200

def pause(value):
    try:
        seconds = float(value)
    except (TypeError, ValueError):
        try:
            seconds = (parsedate_to_datetime(value) - datetime.now(timezone.utc)).total_seconds()
        except (TypeError, ValueError, OverflowError):
            seconds = 3
    seconds = max(1, seconds)
    if time.monotonic() + seconds >= deadline:
        raise SystemExit("等待超时。保留 operation.json,稍后重新执行本段继续查询。")
    time.sleep(seconds)

while time.monotonic() < deadline:
    try:
        with urlopen(Request(status_url, headers=headers), timeout=60) as response:
            status = json.load(response)
            retry_after = response.headers.get("Retry-After", "3")
    except HTTPError as error:
        if error.code in (429, 503):
            pause(error.headers.get("Retry-After", "3"))
            continue
        raise SystemExit(f"查询失败:HTTP {error.code}。保留原任务,检查错误后继续查询。")

    state = status["state"]
    print("任务状态:", state, flush=True)
    if state == "succeeded":
        artifacts = status.get("artifacts", [])
        if not artifacts:
            raise SystemExit("任务成功但缺少结果文件,请保留任务 ID 并联系支持。")
        artifact = artifacts[0]
        if artifact["media_type"] != expected_media_type:
            raise SystemExit("结果格式与预期不同,请检查任务和模型。")
        download_url = base + "/v1/artifacts/" + quote(artifact["id"], safe="") + "/content"
        temporary = output.with_name(output.name + ".part")
        digest = hashlib.sha256()
        size = 0
        with urlopen(Request(download_url, headers=headers), timeout=60) as response:
            with temporary.open("wb") as file:
                while chunk := response.read(1024 * 1024):
                    file.write(chunk)
                    digest.update(chunk)
                    size += len(chunk)
        if size != artifact["size_bytes"] or digest.hexdigest() != artifact["sha256"]:
            raise SystemExit("文件校验失败,保留原任务并重新执行本段下载。")
        temporary.replace(output)
        print("已保存:", output)
        break
    if state not in ("queued", "running", "submitting"):
        raise SystemExit(f"任务已停止:{state}。请保留任务 ID;不要换幂等键重复提交。")
    pause(retry_after)
else:
    raise SystemExit("等待超时。保留 operation.json,稍后继续查询原任务。")
PY

网络中断时可重新执行本段。failed、cancelled、recovery_required 或未知状态会停止下载;需要帮助时查看任务恢复说明。

下载完成后,播放当前目录下的 result.wav。完整文件下载失败时可以重试下载,不需要重新生成。

调整语速和停顿

wangliqun 默认采用 duration_factor=1.1、interval_silence=350、 max_text_tokens_per_segment=80、use_random=false、use_emo_text=false。 这些默认值只作用于该说话人 ID;需要调整时在 parameters 中显式填写。

你想调整什么参数怎么调整
说话速度duration_factor1 为模型默认,1.1 稍慢,1.2 更慢。它是生成时长因子,不是播放器倍速,也不是音色相似度
模型分段之间额外加入的静音interval_silence单位毫秒;系统默认 200,上述试听使用 350。它不控制模型产生的所有字间、句间停顿
每次生成的文本段长度max_text_tokens_per_segment系统默认 120,上述试听使用 80;较短的段落会增加分段机会,需结合听感调整

同样的参数仍可能生成不同的节奏,最终时长不保证按时长因子线性变化。

wangliqun 的最终 WAV 已包含停顿处理。 仅将中间连续至少 450 毫秒、 所有声道低于约 -48 dB 的近静音段缩短至约 350 毫秒;保留其他 PCM 样本、首尾停顿,不做全局变速。 这是按音量判断的固定处理,并不理解停顿语义。返回的 voice_profile_version 为 wangliqun-v1, pause_removed_ms 表示缩短的毫秒数。其他音色不启用此处理。

提交前可以在朗读稿中去掉不需要发声的排版符号,把数字写成希望听到的读法。 展示稿可以保留书名号,例如 《中华文化一千问》;朗读稿可以写成 中华文化一千问。 这些是文稿准备建议,不表示后台会自动改写输入。

上传自己的音色

已有可用的音色 ID 时跳过本节。首次保存音色时,准备 1–15 秒的 PCM16 WAV 录音,命名为 reference.wav。 支持 8–192 kHz、单声道或双声道,文件最大 12 MiB,无需填写参考文本。

curl --fail-with-body --silent --show-error \
  https://api.wildflow.cn/v1/voices \
  -H "Authorization: Bearer $WILDFLOW_API_KEY" \
  -H "Content-Type: audio/wav" \
  -H "X-WildFlow-Voice-Name: Book-account-v1" \
  -H "X-WildFlow-Content-SHA256: $(shasum -a 256 reference.wav | cut -d ' ' -f 1)" \
  --data-binary @reference.wav --output voice.json

export WILDFLOW_VOICE_ID="$(python3 -c 'import json; print(json.load(open("voice.json"))["voice_id"])')"

curl --fail-with-body --silent --show-error \
  "https://api.wildflow.cn/v1/voices/$WILDFLOW_VOICE_ID" \
  -H "Authorization: Bearer $WILDFLOW_API_KEY"

curl --fail-with-body --silent --show-error \
  https://api.wildflow.cn/v1/voices \
  -H "Authorization: Bearer $WILDFLOW_API_KEY"

保存返回的 voice_id,以后每次合成直接传入。更换参考录音时创建新 ID,已有音色内容不能覆盖。 自建音色保存在当前账号下,目前没有自动过期时间;上传的录音应在你有权使用的范围内。

在网页中使用

在个人设置的「音色工作室」中选择音色、输入文稿并生成配音。自建音色也可以在这里上传和试听。 如果需要为多个内容账号保存不同音色,输入内容账号名称后保存选择。 脚本调用时仍需显式传 voice_id,以便确定每批内容使用哪个音色。

通过 API 保存某个内容账号的选择:

PUT /v1/voice-preference
Content-Type: application/json
Authorization: Bearer <你的 API Key>
{
  "content_account": "王立群老师",
  "voice_id": "wangliqun"
}

GET /v1/voice-preference 查看已保存的选择。省略或留空 content_account 时,PUT 保存通用默认音色。

哪些参数值得调整

没有显式传入的参数会使用默认值。例如 text_normalization=true、do_sample=true、top_p=0.8、 top_k=30、temperature=0.8、repetition_penalty=10 已经生效,不需要为了启用而重复填写。

你遇到的问题优先尝试如何判断
整体说得太快或太慢duration_factor同一文稿、同一音色,只改这个值对比。它也可能改变节奏,不保证只改变总时长
分段处额外空白偏长interval_silence可以把试听中的 350 改回默认 200 做对比;模型自身生成的停顿仍可能保留
切段频繁、衔接不顺max_text_tokens_per_segment先比较 80 与默认 120。段更长不保证一定更自然
需要明确的开心、悲伤等表达use_emo_text 与 emo_text单独做短句试听;不需要明显情绪的讲述可以保持关闭
已有满意的情绪参考录音emotion_voice_id 与 emo_alpha用参考录音控制表达,强度单独比较;它不替代说话人的 voice_id
需要细调情绪构成emo_vector八维情绪组合需要试听,不能当作通用的音质提升参数
想比较生成表达的变化temperature、top_p、top_k、do_sample属于生成采样控制,调低或关闭采样不等于质量更高。先保留默认值,每次只改一项
输出出现明显重复repetition_penalty先检查文稿和切段;必要时单独测试,不因名称而一味调大
希望更早开始播放stream=true改变结果获取和播放方式,不是提高音色相似度或发音质量的开关

use_random=false 只关闭随机情绪风格,不等于关闭所有生成采样。 情绪参考、情绪向量和文本情绪互斥,不要把三种方式一起启用。 是否采用某组参数,应以同音色、同文稿的试听结果为准;参数表本身不能证明哪一组听感更好。

全部生成参数

所有合成参数位于 parameters,未知字段会被拒绝。下表是通用默认值; wangliqun 的语速与分段默认值按上文覆盖。

参数默认值允许值与说明
text必填1–8,192 个 UTF-8 字节,首尾不能含空白
streamfalsetrue 时保存原生生成片段,可边生成边听;完整 WAV 仍由最终 Artifact 下载
voice_id原服务端音色共享 wangliqun、官方或自建音色 ID
langzhzh / en / ja / es / ar
duration_factor10.5–2,越大越慢;控制生成时长,不是音色相似度,最终秒数不保证线性变化
emotion_voice_id无参考音频的音色 ID,用于情绪而非说话人身份
emo_alpha10–1,情绪参考强度
emo_vector无8 个 0–1 数字,依次为高兴、愤怒、悲伤、恐惧、厌恶、低落、惊讶、平静
use_emo_textfalse从文稿自动判断情绪;传 emo_text 可明确描述
emo_text无非空且不超过 2,048 UTF-8 字节,要求 use_emo_text=true
use_randomfalse随机情绪风格,可能降低音色一致性
interval_silence200分段静音间隔,整数 0–2000 毫秒
max_text_tokens_per_segment120每段 Token 上限,整数 20–200
text_normalizationtrue文本规范化
do_sampletrue语音 Token 采样
top_p / top_k0.8 / 300.01–1 / 整数 1–100
temperature0.80.1–2
repetition_penalty100.1–20

情绪音频、情绪向量和文本情绪三种方式互斥。发音标注可直接写进文稿,例如 <行|XING2>。 固定音色 ID 固定的是参考录音;随机采样和文本内容仍会影响每次声学输出,不能保证文件逐字节相同。 不要自行设置 input_artifact_ids 或传原始参考音频到 Job;服务端根据音色 ID 绑定输入。

边生成边听

提交 Job 时设置 parameters.stream=true。网页勾选「边生成边听」,提交后点击同名播放按钮, 即可连续播放已保存的片段;浏览器要求一次点击才能开启音频播放。

API 客户端可以每 0.5–1 秒查询 GET /v1/jobs/{operation_id}/audio-segments。返回 data 数组, 每项包含 segment_id、attempt_id、sequence、media_type、size_bytes 和 sha256。 按 sequence 顺序读取 GET /v1/jobs/{operation_id}/audio-segments/{segment_id}/content, 每段是独立可解码的 PCM16 WAV。片段在持久化且通过完整性校验后才可读取,访问仍使用原账号 API Key。

客户端对同一 segment_id 只播放一次;若恢复后的 attempt_id 改变,清空旧播放队列,避免混合两次生成。 任务终止后再查询一次片段列表,避免遗漏最后一段。片段包含模型原生分段静音,播放时无需另加间隔。 每段最多 16 MiB,每次尝试最多 1,024 段、合计 320 MiB;当前片段没有自动过期时间。

音频片段不表示任务已经成功,也不触发计费。生成中断时,已保存片段可继续读取,但可能不完整; recovery_required 按任务恢复规则处理,不自动重新合成。 只有完整 WAV 持久化后才返回 succeeded,最终交付与视频制作应使用任务的完整 Artifact。 wangliqun 的流式片段是未经停顿处理的原生预览;最终 Artifact 包含停顿处理,时长可能更短, 请基于最终文件制作字幕。

错误与恢复

  • 400 invalid_request:检查文本长度、参数范围、音色 ID 和情绪模式是否互斥;
  • 401 / 403:检查 API Key,并用 GET /v1/models 确认这把 Key 能看到 IndexTTS-2.5;
  • 409 idempotency_conflict:同一幂等键已经绑定了不同请求;
  • 429 / 503:按 Retry-After 等待后有限重试;
  • recovery_required:停止自动补发,保留 Operation ID 并联系平台恢复。

网络中断后使用相同请求正文和相同幂等键恢复原 Operation,不要换键重复创建任务。不要在日志、前端 代码或问题截图中记录完整 API Key,也不要记录不必要的敏感文本。

模型与兼容说明

模型来源为 IndexTeam/IndexTTS-2.5,目录版本为 indextts-2.5@0b328234。 当前 team_trial 内部计费金额为 ¥0,上传、查询和保存音色偏好也不收费。 公共渠道为单并发;批量任务通过 Job 队列提交并轮询。

旧调用如果只传 text,仍采用 server_fixed 原服务端音色,不会自动使用网页中保存的默认音色。 该旧录音的来源记录不完整;需要固定音色时请显式传入已保存的 voice_id。

官方示例来自 IndexTeam/IndexTTS-2-Demo。模型许可与参考录音的使用范围是两回事,示例音色不代表取得说话人的商业声音授权。

相关文档

获取 API Key · 价格与计费 · 常见问题