Skip to main content
发起一次 /audio/speech 调用很简单。朗读一篇真实文章则会带出各种有趣的问题:该端点每次请求最多接受 4096 个字符,每个音色都属于特定的模型,音频格式因模型而异,而写来阅读的文本与写来朗读的文本相差甚远。 在本教程中,我们会一一解决这四个问题。最终得到的脚本可以将一个 URL 转换为单个音频文件:
我们会:
  1. 选择适合长篇朗读的模型和音色
  2. 发起一次语音请求并保存音频
  3. 将长文章切分为符合字符上限的片段
  4. 将合成的片段无缝拼接为一个文件
  5. 将文章改写为值得朗读的内容
  6. 组合各部分,然后了解适用于交互场景的流式传输

环境准备

你需要 Python 3.9 或更新版本、requests 包,以及一个 Venice API 密钥。

1. 选择模型和音色

音色隶属于模型。将某个家族的音色发送给另一家族的模型,是最常见的初次犯错。所以先列出每个模型实际支持的音色:
model_spec.voices 是某个模型音色列表的权威来源,而 supported_formats 会告诉你它接受哪些 response_format 值。去掉查询中的 | length 就可以打印出音色名称本身。 我们将使用 tts-xai-v1 搭配音色 eve。它支持 pcm,这正是让第 4 节中拼接片段变得简单的关键。
不同 TTS 模型之间的合成速度差距远大于输出质量的差距,而这个差距足以改变你的架构。在决定选用之前,用一次真实请求对两三个候选者进行计时。同一段片段在一个模型里几秒钟返回,在另一个模型里可能要几分钟。

2. 发起一次请求

响应体是原始音频而非 JSON,因此直接将字节写入文件。
不匹配的组合会在生成任何音频之前就被拒绝,错误信息会告诉你哪些值本可以使用:

3. 在 4096 字符上限处切分文本

input 字段最多接受 4096 个字符。更长的文本会被直接拒绝,而不会静默截断:
因此我们要先切分文章。按句子边界切分很重要,因为在句子中间结束的片段会在拼接处产生明显的顿挫。创建 narrate.py
对一段 5362 字符的脚本运行后会产生四个片段,每个都以句子结尾:
max_chars 的默认值是 1500,而不是接近 4096 的上限,这是有意为之。合成时间随输入长度增加,因此较小的片段返回更快,而由于它们是并行的,整个任务也完成得更快。它们还让失败时的重试成本更低。

4. 将片段拼接为一个文件

拼接已编码音频(例如 MP3)并不可靠,因为每个片段都自带帧头。请求 pcm 可以完全避免这个问题。PCM 是没有容器的原始采样,因此拼接就只是追加字节,Python 标准库的 wave 模块会替我们写入文件头。
相较于生成的音频时长,单个片段的返回速度很快:
这次请求大约用 13 秒生成了 89 秒的语音。并发运行这四个片段是让总耗时保持合理的关键:下面完整朗读的实际耗时仅为 15 秒。 ThreadPoolExecutor.map 会按输入的提交顺序返回结果,因此即便这些片段是同时合成的,也会按阅读顺序落位。
原始 PCM 不携带采样率,因此在写入 WAV 头时必须自行提供正确的采样率,且该值因模型而异。tts-xai-v1 返回 24 kHz,而 tts-gradium-v1 返回 48 kHz。如果猜错,朗读的速度和音高都会错。
要查找任意模型的采样率,只需请求一小段 wav 音频并读取其文件头:

5. 准备听起来自然的文本

抓取到的 Markdown 若逐字朗读几乎无法听。URL 就是最典型的例子。语音模型会一个字符一个字符地把它们念出来,因此 https://docs.venice.ai/llms.txt 会被念成:
h t t p s 冒号 斜杠 斜杠 docs 点 venice 点 a i l l m s 点 t x t
标题、项目符号、表格和代码块都会引发类似但程度较轻的问题。与其用正则表达式与 Markdown 搏斗,不如让聊天模型将文章改写成适合朗读的内容。创建 article_to_audio.py
URL_PATTERN 替换作为兜底保留,以防模型偶尔遗留下链接。

6. 把它们串联起来

入口会执行抓取、写脚本、保存并朗读:
script.txt 与音频一起保存下来非常值得多写这两行代码。当朗读听起来有问题时,脚本几乎总能告诉你原因,你可以在无需再次付费合成的情况下修复它。
近六分钟的朗读,大约在十五秒内生成。脚本现在以正文开头,而不是导航元素:
Venice is built on a simple but powerful principle. User privacy comes first. The platform’s entire architecture flows from this philosophical commitment.
想在不完整听完的情况下核对一段朗读,可以将音频通过 /audio/transcriptions 再发送回去,然后将转录结果与 script.txt 对照。转录最后二十秒是快速确认片段拼接顺序是否正确的方式,也能在几秒内发现丢失的片段或被念出来的 URL。

用于交互场景的流式传输

批量朗读优化的是总耗时。语音交互界面的优先级正好相反:越早发出第一段音频越好。将 streaming 设为 true,响应体会随生成过程逐句返回,因此播放大约可以在一秒内开始,而不必等待完整片段。
当你将音频直接送入浏览器的 Web Audio API 或音频设备时,优先选择 pcm 而非 mp3,因为它不需要解码。

值得了解的请求参数

错误

由于各片段相互独立,一次失败最多只会损失其中一个,对该片段重试 synthesize 总是安全的。

后续步骤

从这里出发的一些自然扩展:
  • 以文本、音色和模型的哈希为键缓存音频,这样未更改的段落就不会被反复合成。
  • 借助 语音克隆 换用克隆的音色,让朗读使用你自己的声音。
  • 使用 基于网页搜索的带引用回答 生成源文本,而不是抓取。
  • 使用 音乐与音效 加入片头或背景音。

文本转语音

语音端点及其参数的参考文档。

语音克隆

使用自定义音色而非预设音色进行朗读。

基于网页搜索的带引用回答

生成本工具要朗读的文本。

语音转文本

对音频进行转录,用于端到端验证朗读效果。