概览
飞马语音合成接收一段说话人参考音频、合成文本和可选情感参数,成功时直接返回 WAV 二进制数据。客户端无需接触上游服务地址或凭据。
https://server.fmode.cn/api/voice/indextts2鉴权
公开语音 API 使用 Fmode API Token。Token 必须通过 Bearer 请求头发送,不能放入查询参数、表单字段或文件名。
Authorization: Bearer FMODE_API_TOKEN使用 Fmode API 控制台创建的 sk- Token。Fmode 网站登录会话用于账户管理,不作为公开语音 API 的计费凭据。
合成接口
/api/voice/indextts2根据说话人参考音频和 payload 生成单个 WAV 文件。请求开始前检查余额,上游成功后扣费。
请求头
| 名称 | 必填 | 说明 |
|---|---|---|
Authorization | 是 | Bearer FMODE_API_TOKEN |
Idempotency-Key | 建议 | 8-128 位字母、数字或 ._:-,用于避免超时重试重复扣费。 |
Content-Type | 自动 | 由 HTTP 客户端为 multipart 自动生成并携带 boundary,不要手工固定。 |
Multipart 字段
| 字段 | 类型 | 必填 | 限制 |
|---|---|---|---|
spk_audio_file | File | 是 | 说话人参考音频,仅 MP3 / WAV,单文件最大 -- MB。 |
emo_audio_file | File | 条件 | emo_control_method=1 时必填,限制同上。 |
payload | JSON string | 是 | 将下方 JSON 对象序列化为字符串后作为普通表单字段提交。 |
服务端会同时校验扩展名、MIME 类型和文件内容签名,修改扩展名不能绕过音频格式检查。
Payload 字段
| 字段 | 类型 | 默认值 | 约束 |
|---|---|---|---|
input | string | - | 必填;去除首尾空白后不能为空;最多 -- 个 Unicode code point。 |
emo_control_method | 0 | 1 | 2 | 3 | 0 | 选择情感控制方式。 |
emo_alpha | number | - | 可选;0-1。 |
emo_vec | number[8] | - | 模式 2 必填;每项 0-1.2,总和不超过 1.5。 |
emo_text | string | - | 模式 3 必填;最多 200 个 Unicode code point。 |
use_random | boolean | false | 仅严格布尔值 true 会启用;生产请求建议固定为 false。 |
{
"input": "非常感谢您的耐心等待。",
"emo_control_method": 3,
"emo_alpha": 0.45,
"emo_text": "自然、友好、耐心,表达清晰",
"use_random": false
}四种情感模式
自然表达
只使用说话人参考音频,不附加情感控制字段。
情感参考音频
额外上传 emo_audio_file,从第二段音频中提取情感表达。
八维情感向量
通过 emo_vec 传入八个数值。接口只固定维度和数值边界,不替客户端猜测向量语义。
情感描述文本
通过 emo_text 描述期望语气,可配合 emo_alpha 调整强度。
WAV 二进制响应
成功响应状态为 200,Content-Type 固定为 audio/wav,响应体是音频二进制。不要对成功响应调用 JSON 解析。
Content-Type: audio/wavCache-Control: no-store浏览器中可用 response.blob() 创建试听 URL;Node.js 使用 response.arrayBuffer() 写入文件;Python 直接写入 response.content。
计费与余额
- 字符数按 Unicode code point 计算,表情符号按一个字符计费。
- 每次请求独立换算 quota 并向上取整,最低扣除 1 quota。
- 上游成功返回有效 WAV 后才扣费;上游失败、超时或响应格式错误不扣费。
- 语音合成与其他 Fmode API 共用账户余额,不建立语音专属钱包或包月订阅。
余额查询
https://server.fmode.cn/api/voice/product/balance携带同一个 Bearer Token,返回 balanceCny 和按当前语音价格估算的 estimatedCharacters。该估算受逐请求取整影响,仅用于展示。
代码示例
示例均从本地读取参考音频,通过环境变量或占位值使用 Token,并将响应保存为 speech.wav。
curl
curl --request POST \
'https://server.fmode.cn/api/voice/indextts2' \
--header 'Authorization: Bearer FMODE_API_TOKEN' \
--header 'Idempotency-Key: voice-20260804-example-001' \
--form 'spk_audio_file=@./speaker.wav;type=audio/wav' \
--form 'payload={"input":"您好,欢迎使用飞马语音合成。","emo_control_method":0,"use_random":false}' \
--output speech.wavNode.js 18+
import { readFile, writeFile } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';
const token = process.env.FMODE_API_TOKEN;
const reference = await readFile('./speaker.wav');
const form = new FormData();
form.append(
'spk_audio_file',
new Blob([reference], { type: 'audio/wav' }),
'speaker.wav',
);
form.append('payload', JSON.stringify({
input: '您好,欢迎使用飞马语音合成。',
emo_control_method: 0,
use_random: false,
}));
const response = await fetch(
'https://server.fmode.cn/api/voice/indextts2',
{
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
'Idempotency-Key': 'voice-' + randomUUID(),
},
body: form,
},
);
if (!response.ok) {
throw new Error(await response.text());
}
await writeFile('./speech.wav', Buffer.from(await response.arrayBuffer()));Python 3
import os
import uuid
import requests
token = os.environ['FMODE_API_TOKEN']
payload = {
'input': '您好,欢迎使用飞马语音合成。',
'emo_control_method': 0,
'use_random': False,
}
with open('./speaker.wav', 'rb') as speaker:
response = requests.post(
'https://server.fmode.cn/api/voice/indextts2',
headers={
'Authorization': f'Bearer {token}',
'Idempotency-Key': f'voice-{uuid.uuid4()}',
},
files={'spk_audio_file': ('speaker.wav', speaker, 'audio/wav')},
data={'payload': __import__('json').dumps(payload, ensure_ascii=False)},
timeout=240,
)
response.raise_for_status()
with open('./speech.wav', 'wb') as output:
output.write(response.content)错误码
失败响应为 JSON:{"code": 400, "mess": "错误说明"}。
| HTTP | 含义 | 处理建议 |
|---|---|---|
400 | payload、情感参数或音频格式无效 | 根据 mess 修正请求,不要原样重试。 |
401 | Token 缺失、无效或过期 | 检查 Bearer Token,必要时创建新 Token。 |
402 | 用户或 Token 可用额度不足 | 充值共享余额,或调整 Token 的额度限制。 |
403 | 使用了网站登录会话而非 API Token | 改用 sk- Fmode API Token。 |
409 | Idempotency-Key 已用于不同输入或金额 | 保持原请求不变,或为新输入生成新键。 |
413 | 上传文件超过大小限制 | 压缩或裁剪参考音频。 |
502 / 503 | 语音服务响应无效、暂不可用或繁忙 | 使用同一幂等键退避重试。 |
504 | 语音合成超时 | 使用同一幂等键重试并延长客户端超时。 |
幂等与重试
每个逻辑合成请求应生成一个唯一的 Idempotency-Key,并在网络超时或响应丢失时复用同一个键。输入指纹覆盖文本、情感与输出参数、说话人参考音频内容以及可选情感音频内容。
允许恢复。服务会重新生成 WAV,但识别已有消费记录,不重复扣客户余额。
返回 409。即使字符数和费用相同,也不能复用原键。
当前版本不持久化缓存 WAV 二进制。若已成功扣费但客户端丢失响应,复用同键会重新合成并返回一份新的 WAV,客户侧不会产生第二次扣费。
安全建议
- Token 存放在服务端环境变量或密钥管理系统,不提交到 Git。
- 浏览器直调仅用于人工 Demo;正式应用通过自己的服务端转发,避免向最终用户暴露 Token。
- 按应用创建独立 Token,设置额度、有效期,并定期轮换。
- 不要把 Token 写入 URL、文件名、日志、埋点、错误上报或截图。
- 只上传有权使用的声音参考音频,并为声音档案提供删除和访问控制。