语音合成的维护记录:IndexTTS2 和 MAI-Voice
这是生成语音背后的维护记录。用接口时不用看,接口出问题或者要换方案时再看。前三节是方案一 IndexTTS2,后面依次是方案二 MAI-Voice、试过又换掉的 Gemini 和克隆的备选。
接口从哪来
- 工作流页面:autodl.art/large-model/comfyui/indextts2-v11。页面是单页应用,curl 拿不到正文,接口是从前端 JS 和下面这个元数据接口里找到的。
- 元数据接口:
GET /api/v1/comfyui/workflows/indextts2-v1,不用鉴权。里面有参数定义input_rules、两个端点endpoint_info、计费billing_config、示例输入输出,以及官方文档地址/docs/comfyui_api/2。 - 鉴权:令牌要在「令牌管理」里建,分组选 ComfyUI。官方的
Authorization头里直接放令牌本身,不带Bearer。中转把它包了一层:Agent 发Bearer <网关 token>,中转换成真实令牌再转发。
input_rules 里值得记的几点(2026-10-01 抓取):
- 必填三项:
prompt_text(1–2048 字)、prompt_simple(音色参考,audio/mpeg或audio/wav)、emo_control_method。 emo_control_method有三个选项:「与音色参考音频相同」「使用情感参考音频」(配合emo_ref_audio)、「使用情感向量控制」(配合emo_happy、emo_calm等,取值 0–1.4)。emo_surprised被定义成只有"0"这一个选项的枚举,所以「惊讶」这个情绪没法调。- 计费:
price_type是audio_sec_cost,按生成音频的秒数计费;billing_config里写的是price 1、minimum_price 10。数字除以 1000 是元,也就是每秒 0.001 元、最低 0.01 元,换算是后来查视频生成时从前端代码里看到的。 - 页面上的示例输出写的是
"status": "completed",那是 2026-04 的旧样例。实际返回的是SUCCESS,和官方文档一致。
实测
2026-10-02 经中转实测四次,2026-10-08 又跑了一次:
| 参考音频 | 排队 | 运行 | 结果 |
|---|---|---|---|
| 页面示例里的 soundhelix MP3 URL(6 分钟纯音乐) | 12 分 24 秒 | 1 秒 | FAILED,msg 列了显存不足、模型加载失败、资源链接失效三种可能 |
| 8 秒人声 wav,用 data URI 传(从 Wikimedia Commons 上孙中山演讲录音里截的) | 1 秒 | 7 秒 | SUCCESS,得到 4.5 秒 22050 Hz 的 wav |
| 大米 14 秒 wav(48 kHz 立体声,2.7 MB),用 data URI 传,请求体 3.5 MB | 5 秒 | 18 秒 | SUCCESS,得到 10 秒的 wav |
| 同一个文件部署到 COS 后,填 URL(中文文件名按百分号编码) | 1 秒 | 15 秒 | SUCCESS,得到 11 秒的 wav |
| 2026-10-08,大米 COS URL,54 字 | 7 秒 | 41 秒 | SUCCESS,得到 15 秒的 wav;用 MAI-Transcribe-2 转回文字,字都对,句末问号转成了句号 |
- 第一次失败的原因没分开:可能是冷启动,也可能是纯音乐或者那个 URL 本身有问题。用 URL 传参考音频本身没问题,COS 上的大米音频就能用。抓取那天这个工作流
usage_count_7d是 0,没什么人用,冷启动的可能性大。 - 大米音频放在
skills/agent-io/assets/下,跟着部署上 COS,返回的Content-Type是audio/x-wav。用 URL 就不用每次传 3.5 MB 的请求体。 - 结果链接在火山引擎 TOS 上(
cg-comfyui-prod.tos-cn-beijing.volces.com),是带签名的 URL,X-Tos-Expires=86400,也就是 24 小时有效。 - 中转:带
Bearer返回 200;不带Bearer、直接放令牌返回 401,被中转拦下。元数据接口走中转也能用。
没做的
- 情绪控制的两种方式(情感参考音频、情感向量)都没测。
- 没对过账单。
- AutoDL 的 ComfyUI 工作流里只有这一个是语音的(2026-10-02 查)。2026-10-08 拿同一段参考音频跑过 OpenRouter 上的两个克隆模型,见克隆的备选,像不像原声还没比。
方案二:微软 MAI-Voice-2.1-Flash
用户说不用自己的音色时的方案,2026-10-08 接入,音色 zh-CN-Mei:MAI-Voice-2.1-Flash。
为什么选它
- 同一天先接的是 Gemini 的 Kore,但 Gemini 经 OpenRouter 只出 pcm,Agent 得自己补文件头才能播——用户不想让 Agent 做任何转换,要直接拿到 mp3 或 wav。
- 于是把 OpenRouter 上能直接出 mp3、有中文的 9 个模型,用同一段 168 字的晚间播报各生成一份,用户听完选了 MAI-Voice-2.1-Flash 的 Mei。
- 走现成的 OpenRouter 海外中转,中转配置不用动。
9 个模型的对比(response_format: "mp3",都没带风格指令;每分钟花费按字数单价 × 168 字 ÷ 音频时长算):
| 模型 / 音色 | 出结果 | 音频 | 每分钟 | 备注 |
|---|---|---|---|---|
microsoft/mai-voice-2.1-flash / zh-CN-Mei(选中) | 6 秒 | 42.9 秒 | 0.0035 美元 | 最便宜,语速偏慢 |
microsoft/mai-voice-2.1 / zh-CN-Mei | 9 秒 | 37.4 秒 | 0.0059 美元 | 账单 0.003696 美元 = 168 字 × 每百万字 22 美元,按字计费对得上 |
qwen/qwen-audio-3.0-tts-flash / longanhuan_v3.6 | 6 秒 | 33.3 秒 | 0.0045 美元 | 每个模型只开放 2 个音色 |
qwen/qwen-audio-3.0-tts-plus / longanlingxin | 8 秒 | 32.8 秒 | 0.0061 美元 | 同上 |
minimax/speech-2.8-turbo / Chinese (Mandarin)_Radio_Host | 5 秒 | 30.9 秒 | 0.0196 美元 | 列表里只有英文音色,但收任意 MiniMax 音色 ID |
minimax/speech-2.8-hd / 同上 | 5 秒 | 31.4 秒 | 0.0321 美元 | 最贵 |
elevenlabs/eleven-v3 / alice | 15 秒 | 36.5 秒 | 0.011 美元 | 最慢 |
x-ai/grok-voice-tts-1.0 / eve | 8 秒 | 35.7 秒 | 0.0042 美元 | 只有 5 个音色 |
fish-audio/s2.1-pro / 默认音色 | 10 秒 | 29.7 秒 | 0.0051 美元 | 同一个模型也能克隆 |
- 9 段用 MAI-Transcribe-2 转回文字都和原文一致,差别只在标点和同音的「小结 / 小节」——准确度分不出高下,选型全凭听感。
- OpenRouter 上 TTS 的
instructions只有 OpenAI 和 Gemini 认,所以这 9 段都是照原文读,没调语气。
接口从哪来
- 用法和 Gemini 一样是 OpenRouter TTS 文档3的
POST /api/v1/audio/speech;模型页 openrouter.ai/microsoft/mai-voice-2.1-flash4 只写了model、input、voice、response_format四个字段。 - 模型元数据:
supported_voices有 97 个,按<语言>-<名字>:MAI-Voice-2.1-Flash命名,中文 6 个(Bo、Grant、Harper、Lan、Mei、Wei);描述写的是 23 种语言。/endpoints显示上游是 Azure,context_length是 0,也就是没标长度上限,supports_voice_cloning是 false。 - 价格:每百万字 15 美元,只按输入字数收。
实测
2026-10-08 经海外中转:
- 168 字播报:200,
audio/mpeg,160 kbps、24 kHz 单声道的 mp3,42.9 秒音频,耗时 6 秒,转录逐字一致。 - 同一句 33 字(「今天的天气有点反复……出门记得带伞。」),Mei,mp3,看参数对时长有没有影响:
| 参数 | 音频时长 |
|---|---|
| 不加 | 9.1 秒、7.3 秒、7.1 秒 |
"speed": 1.3 | 5.7 秒、6.2 秒 |
instructions「非常兴奋、语速很快」 | 8.3 秒、8.0 秒 |
instructions「非常难过、语速很慢」 | 8.9 秒 |
- 同一个请求时长能差 2 秒。
speed1.3 两次都最短,生效;instructions落在不加参数的波动范围里,「很快」也没变快,和文档说的只有 OpenAI、Gemini 认一致。
没做的
- 另外 5 个中文音色没听。
- 一次能读多长没测。
- 几段 mp3 直接
cat拼起来能不能正常播,没测。
试过又换掉的:Gemini 3.8 Flash Lite TTS
2026-10-08 先当方案二接入,音色 Kore,同一天换成了 MAI-Voice——经 OpenRouter 只能拿到 pcm,Agent 得自己补文件头。下面的记录留着,以后要 Kore、语气控制或者多人对话时再看。
当初为什么选它
- 走现成的 OpenRouter 海外中转,中转配置不用动——
/api/v1/audio/speech原样转发就能用。 - Google 2026-09-23 发布,101 种语言,列了简体中文、繁体中文和粤语,见 Google 模型文档5。Google 公布的盲测语言里没有中文,中文好不好只能靠自己听。
- 价格:输入每百万 token 0.5 美元,输出(音频)每百万 token 6 美元;音频每秒 25 token,折合每分钟约 0.009 美元。Google 价格页6写明这是 2026-12-31 前的价格,2027-01-01 起输入输出都翻倍。OpenRouter 标价和 Google 一样。
- 同系列的
google/gemini-3.8-flash-tts输出贵 50%,130 种语言,表演更细,请求格式完全一样,只换模型名。没拿来比。 - 音色和指令是用户听出来的:2026-10-08 生成了一段 40 秒的 Kore,
instructions写「像电台主播一样,亲切自然,语速适中」;又生成了 6 个音色、6 种情绪各一段短句对比。用户听完觉得 Kore 那段最好,定为默认。
接口从哪来
- 用法:OpenRouter TTS 文档3。OpenAI 兼容的
POST /api/v1/audio/speech,返回体是音频字节,不是 JSON。 - 模型元数据:
GET https://openrouter.ai/api/v1/models?output_modalities=speech,不用鉴权,里面有supported_voices(30 个)和价格;GET /api/v1/models/google/gemini-3.8-flash-lite-tts/endpoints能看到上游是 Google AI Studio 和 Vertex 两家,上下文 32768,max_prompt_tokens8192,max_completion_tokens16384,supports_voice_cloning是 false。 - 文档里写的 Gemini 专属规矩:风格写进顶层
instructions,OpenRouter 把它转成上游的 style 字段,写进input的指令会被念出来;input可以是分段列表,做多人对话。
实测
2026-10-08 经海外中转:
| 请求 | 结果 |
|---|---|
response_format: "mp3" | 400:Gemini TTS only supports response_format="pcm". Got "mp3".——OpenRouter 文档说支持 mp3,Gemini 实际不支持 |
response_format 填 wav、opus、aac、flac | 都是 400,ZodError,OpenRouter 的参数校验只认 mp3 和 pcm,到不了 Gemini |
pcm,Kore,54 字中文,带 instructions | 200,Content-Type: audio/pcm;rate=24000;channels=1,9.7 秒音频,总耗时 8 秒;MAI-Transcribe-2 转回文字逐字一致 |
input 写成两段,Kore 和 Puck | 200,9 秒音频;MAI-Transcribe-2 开说话人分离,分出两个说话人,文字也对 |
正文插 <laugh>、<sigh> | 转录里没有出现这两个单词 |
| 40 秒左右的整段,Kore,电台主播指令,跑了两次 | 每次总耗时约 21 秒 |
| 6 个音色、6 种情绪各一句,5–8 秒 | 每段耗时 5–11 秒;同一句话指令写「生气」读成 5.1 秒,写「难过」读成 7.9 秒,指令至少改了语速 |
- 计费:用
GET /api/v1/generation?id=<X-Generation-Id>查到 9.7 秒那次是 0.0014765 美元,输入 25 token、输出 244 token,和每秒 25 token 对得上;用户在 OpenRouter 后台看到的逐条费用也对得上。 - 上游:这几次都路由到 Google AI Studio。
- 转格式:起初写的是 ffmpeg 转 mp3,用户嫌要装 ffmpeg;改成 Python 的
wave模块,用户又担心 Agent 只有命令行、没有 Python——最后改成 shell 自带的printf写 44 字节文件头再接上 pcm。拿 39 秒那段验证过:bash、dash、zsh、busybox sh 都能跑,file认出是 24 kHz 单声道 16 位 wav,Pythonwave严格解析通过,文件头和wave模块写的逐字节相同,后面的数据和原 pcm 一致;macOS 的wc -c输出带前导空格,算术展开照样算对。没在 Windows 上试过。 - 13 段对比加两段 40 秒的 demo,Gemini 合成一共花了约 0.03 美元。
没做的
- 30 个音色只听了 7 个(Kore、Puck、Charon、Fenrir、Leda、Sulafat、Gacrux)。
- 那段 40 秒的比短句好听,是因为文字长还是因为指令写得具体,没拆开测。
speed参数、超长文本切段都没测。- 没和
gemini-3.8-flash-tts比。
克隆的备选
方案二的 MAI-Voice 和试过的 Gemini 都不能克隆,所以要特定人声仍然用方案一。2026-10-08 查了 OpenRouter 全部 39 个 TTS 端点,supports_voice_cloning 为 true 的只有三个:fish-audio/s2.1-pro、它的免费版 fish-audio/s2.1-pro-free:free、bytedance-seed/seed-audio-1-0。
参考音频放在请求的 input_references 里:{"type": "input_audio", "input_audio": {"data": "data:audio/wav;base64,…"}},也可以把 data 换成 url;Fish 还收一段 {"type": "text", "text": "参考音频的文字稿"},Seed 会忽略文字稿。Seed 不能同时传 voice 和 input_references。这两家都能直接出 mp3。
同一段大米参考音频、同一段 54 字文字,和方案一一起实测:
| 方案 | 出结果 | 花费 | 转录核对 |
|---|---|---|---|
| Fish Audio s2.1-pro | 27 秒,同步 | 0.0024 美元(13.8 秒音频,约每分钟 0.01 美元) | 逐字一致 |
| Fish Audio s2.1-pro 免费版 | 31 秒,同步 | 0 | 逐字一致 |
| Seed Audio 1.0 | 46 秒,同步 | 0.039 美元(15.7 秒音频,每秒 0.0025 美元) | 「一段语音」转成了「一段1」 |
| IndexTTS2(方案一) | 48 秒,要轮询 | 没对账单 | 字都对,句末问号转成句号 |
- 哪个最像原声还没比,默认仍是 IndexTTS2。
- Fish 免费版的次数上限和数据政策没查。
-
autodl.art/large-model/comfyui/indextts2-v1,2026-10-02 读。 ↩
-
/docs/comfyui_api/,2026-10-02 读。 ↩ -
OpenRouter TTS 文档,2026-10-15 读。 ↩↩
-
openrouter.ai/microsoft/mai-voice-2.1-flash,2026-10-15 读。 ↩
-
Google 模型文档,2026-10-15 读。 ↩
-
Google 价格页,2026-10-15 读。 ↩