VoxCPM2 语音合成
使用耐久 Job API 稳定生成 VoxCPM2 语音,并正确处理长文本。
VoxCPM2 是野生流动的首方中文语音合成模型,精确运行版本为 openbmb/VoxCPM2。请求以文本作为
输入。当前生产 Artifact 为 MP3(audio/mpeg);当前目录价格为 ¥0.8 / 万字符,实时可用状态和最终
账单以模型广场与控制台为准。
语音生成可能经历排队、模型准备和生成,因此使用耐久 Job,而不是要求客户端一直保持同步连接。 下面的示例包含提交、等待和下载步骤。需要了解重试时,可查看任务与结果文件。
快速调用
以下命令适用于 macOS / Linux,需要 Python 3 和 cURL。每次新任务生成新的幂等键,重试同一任务时保留原键。
先获取 API Key,并为这次业务请求生成一个稳定且唯一的幂等键:
export WILDFLOW_API_KEY="替换为控制台生成的密钥"
export WILDFLOW_IDEMPOTENCY_KEY="$(python3 -c 'import uuid; print(uuid.uuid4())')"提交任务:
curl --fail-with-body --silent --show-error -D response-headers.txt -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": "VoxCPM2",
"parameters": {
"input": "你好,欢迎使用野生流动。",
"voice": "default",
"speed": 1.0
}
}' \
--output operation.json首次接收成功返回 HTTP 202,响应头 Location 指向 /v1/jobs/{operation_id}。重复提交相同 API Key、
幂等键和请求正文会返回原 Operation;同一幂等键配合不同请求会返回 409。
Windows CMD
Windows CMD 不要把 input、voice、speed 放在 JSON 顶层,也不要把提交 Job 的 JSON 响应直接保存为
speech.wav 或 speech.mp3。先把下面内容保存为 UTF-8 编码的 request.json:
{
"model": "VoxCPM2",
"parameters": {
"input": "你好,欢迎使用野生流动。",
"voice": "default",
"speed": 1.0
}
}在 CMD 中提交任务。--data-binary 会原样上传 UTF-8 文件;-D 把响应头单独保存,避免 -i 将响应头
混入 JSON 文件:
set "WILDFLOW_API_KEY=替换为控制台新生成的密钥"
set "WILDFLOW_IDEMPOTENCY_KEY=voxcpm2-request-001"
curl.exe --fail-with-body -sS -D response-headers.txt -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" ^
--data-binary "@request.json" ^
--output operation.json用 type operation.json 查看响应并复制 id。首次成功应为 202;后续用同一个幂等键和完全相同的
request.json 重放时可能返回 200,两者都不是音频响应:
set "OPERATION_ID=把 operation.json 中的 id 粘贴到这里"
curl.exe --fail-with-body -sS ^
-H "Authorization: Bearer %WILDFLOW_API_KEY%" ^
"https://api.wildflow.cn/v1/jobs/%OPERATION_ID%" ^
--output status.json当 status.json 中的 state 变为 succeeded 后,复制 artifacts[0].id 再下载真正的 MP3:
set "ARTIFACT_ID=把 status.json 中 artifacts[0].id 粘贴到这里"
curl.exe --fail-with-body -sS ^
-H "Authorization: Bearer %WILDFLOW_API_KEY%" ^
"https://api.wildflow.cn/v1/artifacts/%ARTIFACT_ID%/content" ^
--output result.mp3参数
| 参数 | 必需 | 允许值 | 说明 |
|---|---|---|---|
input 或 text | 是 | 1–20,000 个 Unicode 字符 | 二选一,不能同时传入;服务端自动按语义边界分段 |
voice | 是 | 见下表 | 标准模型名不会自动补默认音色 |
speed | 否 | 0.25–4 | 保持音高的语速倍率,默认 1.0 |
可用音色
voice | 名称 | 说明 |
|---|---|---|
shuoshuren | 说书人 | 沉稳男声,适合叙事内容 |
dabin | 大斌 | 磁性男声,适合故事讲述 |
tingting | 婷婷 | 温柔女声,适合自然表达 |
default | 默认 | VoxCPM2 无条件合成音色 |
wangliqun | 王立群 | 已配置的自有克隆音色 |
音色是同一个 VoxCPM2 模型的请求参数,不是独立模型。使用真人或克隆音色前,应取得相应授权,不得 用于冒充、欺诈或误导他人。
轮询与下载
先完成上面的提交步骤,确认已生成 operation.json。然后设置下载文件名:
export RESULT_FILE="result.mp3"
export RESULT_MEDIA_TYPE="audio/mpeg"下面的命令使用 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 或未知状态会停止下载;需要帮助时查看任务恢复说明。
下载后可核对 media_type=audio/mpeg、size_bytes 和 sha256。Artifact 的 metadata 还会提供
input_characters、completed_characters、segment_count、completed_segment_count、duration_ms、
codec、bitrate、sample_rate 和 channels。如果任务仍在排队或执行,请继续轮询,
不要更换幂等键重复扣费。
长文本 MP3
单个 Job 最多接受 20,000 个 Unicode 字符。服务端会在句号、问号、感叹号、逗号和换行等语义边界 自动拆成约 300 字的小段,在同一 Job 内按原文顺序生成、缓存和拼接,最后只编码一次 96 kbps、48 kHz、 单声道 MP3。调用方无需再把约 3,000 字的正文拆成多个请求。
每篇正文使用一个稳定且唯一的 Idempotency-Key。网络中断或轮询超时时,应以相同请求正文和相同键
恢复原 Operation;不要换键创建第二个付费任务。服务端只有在字符数、片段数、文件大小和 SHA-256
全部自洽后才将 Job 标记为 succeeded,不完整或结果不确定时进入失败或 recovery_required。
当前生产已经验收 36 字和 3,200 字请求;3,200 字结果为 12/12 片段完整、约 13.16 分钟 MP3。 20,000 字是接口硬上限,不代表所有内容、音色和速度组合都具有同样耗时。接近上限的任务应预留更长 轮询时间,并在正式批量生产前先用代表性文稿验证听感。
可用 ffprobe 检查实际产物:
ffprobe -v error \
-show_entries stream=codec_name,sample_rate,channels,bit_rate:format=duration \
-of json result.mp3预期为 MP3、48 kHz、单声道、96 kbps。自动完整性门禁用于检测技术性截断,不能代替内容验收;长文本 仍建议抽听开头、中间和结尾,并检查时长是否与文字量大致相符。
错误与恢复
统一错误和恢复原则见任务与结果文件。VoxCPM2 还应重点检查:
400 invalid_request:检查input、voice、speed是否放在parameters内,并检查文本、必传音色和速度范围;401/403:检查 API Key、额度和令牌允许的模型范围;409 idempotency_conflict:同一幂等键已经绑定了不同请求;429/503:按响应中的Retry-After等待后有限重试;recovery_required:结果或账务状态需要平台恢复,不要自动创建第二个付费任务。
不要在日志、前端代码或问题截图中记录完整 API Key,也不要记录不必要的敏感文本。
相关文档
获取 API Key · 价格与计费 · 常见问题