猴哥的第 186 期分享,欢迎追看
前面分享了,小智 AI 接入 TTS 的系列文章:
- 两家云厂商的 TTS 服务:
- 本地部署 TTS+语音克隆 服务:
低延迟小智AI服务端搭建-本地TTS篇:FishSpeech流式推理
低延迟小智AI服务端搭建-本地TTS篇:CosyVoice流式推理
SparkTTS 音色克隆 + vLLM INT8量化加速推理
低延迟小智AI服务端搭建-本地TTS篇:IndexTTS+vLLM加速推理
下表总结了,以上所有 TTS 的首包延时:
| TTS服务来源 | 模型名称 | 延迟时间(s) | | --- | --- | --- | | 阿里百炼 | CosyVoice | 0.4-0.5 | | 火山引擎 | 大模型语音合成 | 0.3-0.4 | | 本地部署 | FishSpeech1.5 | 2.0 | | 本地部署 | CosyVoice2.0 | 1.3 | | 本地部署 | SparkTTS | 0.5 | | 本地部署 | IndexTTS | 0.4-0.5 |
为了进一步降低本地音色克隆的首包延时,前文给出了音色缓存的两种思路:
低延迟小智AI服务端搭建-本地TTS篇:音色缓存
可以看到,音色克隆,我们采用的都是本地部署方案。
有什么缺点:依赖 GPU 推理,成本高,不太适合项目早期阶段。
最近看到,火山引擎上线了声音复刻2.0,支持流式输出。
今日分享:
如何接入火山引擎的声音复刻大模型,并对延时进行实测!
- 火山引擎-声音复刻简介
从 api 上推测,火山引擎的声音复刻2.0,其语音大模型就是前段时间开源的:MegaTTS。
模型架构和数据处理逻辑,和其它TTS语音大模型基本类似。
模型输入需要两部分:
- 参考音频:10-15s即可,用于提取音频特征,实现声音复刻
- 合成文本:不超过 60s,正常语速下小于300字
为此,API 接入上,需要完成两部分工作:
- 创建音色;
- 调用语音合成;
正式接入之前,还需要完成以下准备工作。
- 接入准备
控制台创建应用,然后勾选声音复刻大模型:
在应用中,获取以下 4 个接口参数:
创建完成,免费赠送一个音色,能够进行10次训练以及合成5000字符。
PS:这里压根不会执行任何训练,上传音频,是为了提取音频特征,作为大模型的 prompt 输入;如果多次上传,则对所有音频特征取平均?
如需新的音色,需进行购买:
- 声音克隆 API 接入
3.1 创建音色
拿到上面的应用参数,即可通过调用接口完成音频上传:
def voice\_upload():
file\_path = '1.wav'
with open(file\_path, 'rb') as audio\_file:
audio\_data = audio\_file.read()
encoded\_data = str(base64.b64encode(audio\_data), "utf-8")
audio\_format = os.path.splitext(file\_path)[1][1:] # 获取文件扩展名作为音频格式
url ="https://openspeech.bytedance.com/api/v1/mega\_tts/audio/upload"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer;" + 'token',
"Resource-Id": "volc.megatts.voiceclone",
}
audios = [{"audio\_bytes": encoded\_data, "audio\_format": audio\_format}]
data = {"appid": '41xx', "speaker\_id": 'S\_xx', "audios": audios, "source": 2,"language": 0, "model\_type": 1}
response = requests.post(url, json=data, headers=headers)
print("headers = ", response.headers)
print(response.json())
音频上传成功后,调用接口,查看云端是否准备就绪:
def get\_voice\_status():
url = "https://openspeech.bytedance.com/api/v1/mega\_tts/status"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer;" + 'token',
"Resource-Id": "volc.megatts.voiceclone",
}
body = {"appid": '4166067500', "speaker\_id": 'S\_lKZjXSAt1',}
response = requests.post(url, headers=headers, json=body)
if response.status\_code == 200:
status = response.json().get('status', 0)
# 0: 未发现 1: 训练中 2: 训练完成 3: 训练失败 4: active
print(f"voice status: {status}")
回到控制台查看状态:
此外,状态查看接口,还会返回如下字段:
{
"BaseResp": {"StatusCode": 0, "StatusMessage": ""},
"create\_time": 1751449452000,
"demo\_audio": , # 会有过期时间
"icl\_speaker\_id": "ICL\_5dc70e6ea3ca", # 文档中提到用于双向流式API,但现在似乎弃用了
"language": 0,
"speaker\_id": "S\_lKZjXSAt1", # 用于声音复刻API
"status": 2,
}
音色准备就绪,接下来就可以尝试调用 TTS 接口,使用该音色,合成音频。
接口提供 websocket/HTTP 两种协议。
不过,流式输出,得采用 websocket。
3.2 语音合成-websocket
WebSocket 协议下,提供 submit 和 query 两种操作类型:
- submit
- 用途:你发送文本内容,服务端会异步处理并返回音频流。
- 场景:用于实时合成、需要立即拿到音频的场景。
- query
- 用途:只需提供任务的 reqid,服务端会返回该任务的音频(如果已完成)。
- 场景:适合异步任务、轮询获取结果,或者断点续传等场景。
值得注意的是,接口不支持双向流式 :每个 WebSocket 连接只能处理一次合成(一次 submit)。
以 python 代码为例,大致流程如下:
async with websockets.connect(api\_url, extra\_headers=header, ping\_interval=None) as ws:
await ws.send(full\_client\_request) # 发送合成请求
while True:
res = await ws.recv() # 等待服务端响应
done = parse\_response(res, file\_to\_save)
if done: # 收到最后一个音频包
file\_to\_save.close()
break
print("\nclosing the connection...")
如果要合成多句文本:
- 必须每次都新建 WebSocket 连接,
- 合成结束后(收到最后一包音频),关闭连接
- 新建连接进行下一次合成。
- 接入小智 AI
4.1 实现思路
在小智 AI 对话应用中,LLM 生成的文本,是一句一句发过来的。
为此,可以建立一个 TtsSession 会话来统一处理。
那么,如何在一个session中实现多句文本合成呢?
下面给出一种思路:
- 维护一个队列:write(text) 只入队,不处理;
- 循环处理队列:每次建立一个 ws 连接,合成一条,收到 done 后再处理下一句;
- 所有文本合成完:触发 'finished' 事件,反馈给上游。
以下是调用流程图:
4.2 延时实测
服务端是 node.js 写的,以下是实测数据:
可以看到,首包延时高达 0.6-0.8s ,其中建立 ws 连接就要 0.1s!
显然,这么搞,不合理啊!
不过,就在本文完成前,
笔者看到火山的双向流式API也支持声音复刻2.0了。
于是动手改造了一番,实测延时如下:
你看,首包延时 0.4-0.5s ,比没有声音克隆的 tts 略慢,符合预期!
写在最后
本文分享了小智AI服务端接入火山引擎-音色克隆的实现,并对流式推理的延时进行了实测。
如果对你有帮助,欢迎点赞收藏 备用。
为方便大家交流,新建了一个 AI 交流群,公众号后台「联系我」,拉你进群。
👇 关注猴哥,快速入门AI工具
# AI 工具:
盘点9家免费且靠谱的AI大模型 API,统一封装,任性调用!
免费GPU算力本地跑DeepSeek R1,无惧官方服务繁忙!
# AI应用** :**
弃坑 Coze,我把 Dify 接入了个人微信,AI小助理太强了
我把「FLUX」接入了「小爱」,微信直接出图,告别一切绘画软件!
