前面第 15 篇和第 16 篇,我们已经分析了 Pixelle-Video 的 AI 视频片段生成,以及图生视频、动作迁移、数字人口播这类复杂视频能力。
这一篇继续往下看一个非常关键的模块:
TTS 语音生成。
在 Pixelle-Video 中,TTS 不只是"把文字读出来"这么简单。它实际上决定了每个视频片段的节奏、时长和音画同步。
因为 Pixelle-Video 的每个分镜都是这样处理的:
text
narration
↓
TTS 生成 audio_path
↓
读取音频时长 frame.duration
↓
生成图片或视频素材
↓
HTML 模板合成字幕画面
↓
音频 + 画面合成视频片段
所以,TTS 是连接"文本内容"和"视频节奏"的核心环节。
这一篇我们重点分析:
text
Edge-TTS 如何本地接入?
Index-TTS 这类声音克隆方案如何通过 ComfyUI workflow 接入?
TTS 生成的 audio_path 如何影响后续视频时长?
一、TTS 在 Pixelle-Video 中负责什么?
Pixelle-Video 的完整视频生成流程可以简化成:
text
文案生成
↓
分镜规划
↓
TTS 配音
↓
图片 / 视频素材生成
↓
模板渲染
↓
视频片段合成
↓
最终拼接
其中 TTS 负责把每个 StoryboardFrame 中的 narration 转成音频文件。
源码中 FrameProcessor 的注释明确说明,它处理单个 frame 的完整流程:TTS、图像生成、画面合成、视频片段生成;并且特别强调一个关键特性:TTS 音频时长会传给视频生成 workflow,用来保证音频和视频同步。
这说明 TTS 在 Pixelle-Video 里有两个作用:
text
第一,生成旁白音频。
第二,决定当前分镜的视频时长。
如果没有 TTS,Pixelle-Video 仍然可以生成图片,但很难自动决定每个片段应该持续多久。
二、TTSService:语音生成的统一入口
Pixelle-Video 的 TTS 核心服务是:
text
pixelle_video/services/tts_service.py
源码文件开头写得很清楚:TTSService 支持 local 和 ComfyUI 两种推理方式。local 模式主要走 Edge TTS;ComfyUI 模式则通过工作流执行,比如 Edge-TTS workflow、Index-TTS workflow 或其他自定义 TTS workflow。
它的核心调用入口是 __call__():
python
async def __call__(
self,
text: str,
workflow: Optional[str] = None,
comfyui_url: Optional[str] = None,
runninghub_api_key: Optional[str] = None,
voice: Optional[str] = None,
speed: Optional[float] = None,
inference_mode: Optional[str] = None,
output_path: Optional[str] = None,
**params
) -> str:
...
这个接口说明,TTSService 支持的核心参数包括:
text
text:要转语音的文本
workflow:ComfyUI TTS 工作流
voice:音色
speed:语速
inference_mode:local 或 comfyui
output_path:音频输出路径
params:额外 workflow 参数
所以,上层 FrameProcessor 不需要知道当前用的是 Edge-TTS 还是 Index-TTS。
它只需要调用:
python
audio_path = await self.core.tts(**tts_params)
最后拿到一个音频路径即可。
这就是 TTSService 的价值:
把不同语音方案统一封装成 text → audio_path。
三、两种 TTS 路线:local 和 comfyui
Pixelle-Video 的 TTSService 里有一个非常关键的分支:
python
mode = inference_mode or self.config.get("inference_mode", "local")
if mode == "local":
return await self._call_local_tts(...)
else:
return await self._call_comfyui_workflow(...)
源码中就是通过 inference_mode 判断当前走本地 Edge TTS,还是走 ComfyUI workflow。
这意味着 Pixelle-Video 的 TTS 有两条路线:
text
local 模式:
text
↓
Edge-TTS
↓
output.mp3
comfyui 模式:
text
↓
TTS workflow
↓
ComfyKit
↓
ComfyUI / RunningHub
↓
output.mp3 / wav / flac
这两条路线最终都返回 audio_path。
所以从后续视频合成角度看,它们是统一的。
四、配置层:TTS 配置放在哪里?
Pixelle-Video 的 TTS 配置主要放在 comfyui.tts 下。
在 config.example.yaml 中,TTS 默认工作流是:
yaml
comfyui:
tts:
default_workflow: selfhost/tts_edge.json
同一个配置文件中还包含 comfyui_url、comfyui_api_key、runninghub_api_key、runninghub_concurrent_limit 等字段。也就是说,如果 TTS 走 ComfyUI 或 RunningHub workflow,就会复用前面几篇讲过的 ComfyKit / workflow 执行体系。
不过在 schema 中,TTS 配置已经被进一步拆成两部分:
python
class TTSLocalConfig(BaseModel):
voice: str = "zh-CN-YunjianNeural"
speed: float = 1.2
class TTSComfyUIConfig(BaseModel):
default_workflow: Optional[str] = None
class TTSSubConfig(BaseModel):
inference_mode: str = "local"
local: TTSLocalConfig
comfyui: TTSComfyUIConfig
源码中 TTSLocalConfig 定义了本地 Edge TTS 的默认音色和语速,TTSSubConfig 则定义了 inference_mode,取值思路是 local 或 comfyui。
这说明 Pixelle-Video 的 TTS 配置方向是:
text
local:
适合 Edge-TTS,配置 voice 和 speed。
comfyui:
适合 Index-TTS、声音克隆、自定义 TTS workflow。
五、Edge-TTS:最轻量的本地语音路线
先看 local 模式。
TTSService._call_local_tts() 的职责是调用 Edge TTS。源码中它会先读取本地配置:
python
local_config = self.config.get("local", {})
final_voice = voice or local_config.get("voice", "zh-CN-YunjianNeural")
final_speed = speed if speed is not None else local_config.get("speed", 1.2)
rate = speed_to_rate(final_speed)
然后调用 edge_tts(),把 text、voice、rate 和 output_path 传进去,最后返回生成的音频路径。
这条路线可以理解成:
text
frame.narration
↓
TTSService._call_local_tts()
↓
voice + speed
↓
speed_to_rate()
↓
edge_tts()
↓
output.mp3
Edge-TTS 的好处是简单。
它不需要你自己部署语音模型,也不需要本地显卡。
对普通用户来说,它是最容易跑通的 TTS 方案。
六、speed_to_rate:Pixelle-Video 如何处理语速?
Edge-TTS 使用的语速参数不是 1.2 这种倍速,而是 +20% 这种 rate 字符串。
所以 Pixelle-Video 在 tts_voices.py 中提供了一个工具函数:
python
def speed_to_rate(speed: float) -> str:
percentage = int((speed - 1.0) * 100)
sign = "+" if percentage >= 0 else ""
return f"{sign}{percentage}%"
它会把倍速转换成 Edge-TTS 能理解的 rate。源码注释中给出的例子是:1.0 → +0%,1.2 → +20%,0.8 → -20%。
所以:
text
tts_speed = 1.0 → rate = +0%
tts_speed = 1.2 → rate = +20%
tts_speed = 0.8 → rate = -20%
这个设计很实用。
因为对用户来说,1.2x 更容易理解;
对 Edge-TTS 来说,+20% 才是它需要的参数。
Pixelle-Video 在中间做了一层转换。
七、Edge-TTS 支持哪些音色?
Pixelle-Video 在 tts_voices.py 中内置了一批 Edge-TTS 音色配置。
其中包括中文、英文、韩语、法语、葡萄牙语、德语、俄语、土耳其语、西班牙语等音色。中文音色里包括 zh-CN-XiaoxiaoNeural、zh-CN-XiaoyiNeural、zh-CN-YunjianNeural、zh-CN-YunxiNeural、zh-CN-YunyangNeural 等;英文音色里包括 en-US-AriaNeural、en-US-JennyNeural、en-US-GuyNeural、en-GB-SoniaNeural、en-GB-RyanNeural 等。
这些音色配置的作用主要是给 WebUI 和配置系统使用。
可以理解成:
text
voice_id:
真正传给 TTS 的音色 ID。
label_key:
用于界面多语言显示。
locale:
语言地区。
gender:
男声或女声。
这对 Pixelle-Video 很重要。
因为它不是只做中文视频,也不是只做英文视频。
如果文案是中文,可以选择中文音色。
如果文案是英文,可以选择英文音色。
如果后续要支持更多语言,也可以继续扩展这个音色列表。
八、Edge-TTS 工具函数做了哪些容错?
真正调用 Edge-TTS 的函数在:
text
pixelle_video/utils/tts_util.py
这个文件中的 edge_tts() 会调用 edge_tts_sdk.Communicate(),收集音频流中的 audio chunk,然后把字节写入输出文件。源码注释中也说明,它返回 MP3 音频数据,并带有自动重试、指数退避、抖动、并发限制和请求间隔,用来处理 401、临时网络问题和 NoAudioReceived 等情况。
它的大致流程是:
text
创建 request semaphore
↓
请求前等待一个小随机延迟
↓
调用 edge_tts_sdk.Communicate()
↓
stream 收集 audio chunks
↓
合并 audio bytes
↓
写入 output_path
↓
返回 audio data
这里有两个值得注意的点。
第一,它做了并发限制。
源码中 _MAX_CONCURRENT_REQUESTS = 3,并且每次请求前会等待 _REQUEST_DELAY 加随机抖动。
第二,它对临时错误做了重试。
遇到 401、NoAudioReceived 等情况时,会用指数退避重试。
这说明 Pixelle-Video 对 Edge-TTS 的处理不是简单调一下接口,而是考虑了真实生成中可能遇到的不稳定问题。
九、ComfyUI TTS:Index-TTS 这类方案的接入口
再看 comfyui 模式。
Pixelle-Video 的 README 明确提到,TTS 工作流支持 Edge-TTS、Index-TTS 等方案;语音设置中可以从下拉菜单选择 TTS workflow,系统会扫描 workflows/ 文件夹中的 TTS 工作流,并且支持上传参考音频用于声音克隆,适合 Index-TTS 这类支持声音克隆的 TTS workflow。
这说明 Pixelle-Video 接入 Index-TTS 的方式,不是像 Edge-TTS 那样直接在 Python 里写死调用逻辑,而是通过 ComfyUI workflow。
也就是说,Index-TTS 的定位更像:
text
Pixelle-Video
↓
TTSService
↓
ComfyUI TTS workflow
↓
Index-TTS 节点 / 模型
↓
输出音频
这样做的好处是:
text
Pixelle-Video 不需要直接适配 Index-TTS 的所有内部参数。
Index-TTS 的模型、节点和推理细节交给 ComfyUI workflow。
Pixelle-Video 只负责传 text、voice、speed、ref_audio 等参数。
这和前面讲 ComfyUI 生图、RunningHub 工作流是同一种设计思想。
十、ComfyUI TTS 的执行流程
当 inference_mode != "local" 时,TTSService 会进入 _call_comfyui_workflow()。
源码中这一步会先通过 _resolve_workflow() 解析 workflow,然后构造:
python
workflow_params = {"text": text}
如果传入了 voice,就加入:
python
workflow_params["voice"] = voice
如果传入了非默认 speed,就加入:
python
workflow_params["speed"] = speed
然后把额外参数 params 合并进去。
所以 ComfyUI TTS 的参数结构可以理解成:
text
text:
要朗读的文本。
voice:
工作流支持的音色或说话人参数。
speed:
工作流支持的语速参数。
ref_audio:
参考音频,常用于声音克隆。
其他 params:
由具体 TTS workflow 决定。
在 FrameProcessor 中,如果 TTS 模式是 comfyui,它会把 tts_workflow、voice_id、tts_speed 和 ref_audio 都放入 tts_params,然后统一调用 self.core.tts(**tts_params)。
这就是 Index-TTS 一类 workflow 能接入的关键。
十一、ComfyUI / RunningHub TTS 如何真正执行?
在 _call_comfyui_workflow() 中,Pixelle-Video 会通过核心服务拿到共享的 ComfyKit 实例:
python
kit = await self.core._get_or_create_comfykit()
然后根据 workflow 来源决定传什么给 ComfyKit:
text
runninghub:
传 workflow_id
selfhost:
传 workflow 文件路径
源码中就是这样判断的:如果 workflow_info["source"] == "runninghub" 且包含 workflow_id,就把 workflow_id 作为 workflow_input;否则就把本地 workflow 文件路径作为 workflow_input。最后统一调用 kit.execute(workflow_input, workflow_params)。
所以 ComfyUI TTS 的完整执行链路是:
text
TTSService
↓
_resolve_workflow()
↓
workflow_info
↓
selfhost:workflow path
runninghub:workflow_id
↓
ComfyKit.execute()
↓
ComfyUI / RunningHub 执行 TTS workflow
↓
返回音频结果
这和图片、视频 workflow 的执行方式是统一的。
十二、TTS workflow 输出如何被识别?
TTS workflow 执行完成后,Pixelle-Video 需要从结果里找到音频文件。
源码中 _call_comfyui_workflow() 会依次检查:
text
result.audios
result.files
result.outputs
如果 result.audios 存在,就取第一项。
如果没有 audios,但有 files,就取第一项。
如果都没有,则会在 outputs 字典中查找以 .mp3、.wav、.flac 结尾的字符串。
如果仍然找不到,就抛出 No audio file generated by workflow。
这个设计说明 Pixelle-Video 对 ComfyUI TTS 的结果做了兼容处理。
因为不同 workflow、不同节点、不同平台返回音频的字段可能不同。
有的结果在 audios。
有的结果在 files。
有的结果藏在 outputs 里。
Pixelle-Video 不要求所有 workflow 返回完全相同结构,而是做了一层兜底查找。
十三、远程音频如何保存到本地?
如果 workflow 返回的是远程 URL,而调用时指定了 output_path,Pixelle-Video 会把音频下载到本地路径。
源码中 _call_comfyui_workflow() 判断:
python
if output_path and audio_path.startswith(("http://", "https://")):
...
然后用 httpx.AsyncClient() 下载音频,写入 output_path,最后返回本地文件路径。
这点很重要。
因为 RunningHub 这类云端 workflow 很可能返回的是 URL。
但后面的 FrameProcessor 和 VideoService 更适合处理本地文件路径。
所以 Pixelle-Video 做了一步转换:
text
云端音频 URL
↓
下载到当前任务目录
↓
本地 audio_path
这样后面的音频时长读取、视频合成都能统一处理。
十四、FrameProcessor 如何调用 TTS?
TTS 真正被触发的位置在 FrameProcessor._step_generate_audio()。
在处理每个 frame 时,FrameProcessor.__call__() 的第 1 步就是生成音频。如果 frame.audio_path 为空,就发出 action="audio" 的进度事件,然后调用 _step_generate_audio();如果已经有音频,则复用已有音频。
_step_generate_audio() 会构造 tts_params:
text
text = frame.narration
output_path = 当前 frame 的音频输出路径
inference_mode = config.tts_inference_mode
如果是 local 模式,就传入:
text
voice
speed
如果是 comfyui 模式,就传入:
text
workflow
voice
speed
ref_audio
然后调用:
python
audio_path = await self.core.tts(**tts_params)
最后写回:
python
frame.audio_path = audio_path
frame.duration = await self._get_audio_duration(audio_path)
源码中可以看到,音频生成后会立即读取音频时长,并写入 frame.duration。
这就是 Pixelle-Video 语音生成的核心闭环。
十五、为什么 TTS 要先于图片 / 视频生成?
从流程上看,Pixelle-Video 先生成音频,再生成媒体。
这不是偶然。
对于 image 模式来说,音频时长决定静态图片视频片段的持续时间。
对于 video 模式来说,音频时长还会传给视频生成 workflow,作为目标视频时长。
源码中 _step_generate_media() 会判断当前是 image 还是 video;如果是 video workflow,并且 frame.duration 已经存在,就把 duration 加入 media_params,日志中也说明这个 duration 来自 TTS audio。
所以顺序必须是:
text
先生成 TTS
↓
拿到音频时长
↓
再生成视频素材或视频片段
如果顺序反过来,视频模型就不知道应该生成多长。
这就是 Pixelle-Video 的"旁白驱动节奏"设计。
十六、audio_path 如何影响最终视频片段?
一个 frame 在生成音频后,会进入后续三步:
text
_step_generate_media()
_step_compose_frame()
_step_create_video_segment()
对于图片模式:
text
composed_image_path + audio_path
↓
create_video_from_image()
↓
video_segment_path
对于视频模式:
text
video_path + composed overlay + audio_path
↓
overlay_image_on_video()
↓
merge_audio_video(replace_audio=True)
↓
video_segment_path
第 15 篇已经讲过,视频模式下 Pixelle-Video 会用 TTS 旁白替换 AI 视频原音频。这样可以保证最终每个 segment 都使用当前 narration 对应的配音,而不是保留模型生成的杂音或无关声音。FrameProcessor 中的合成逻辑会根据 frame.media_type 使用图片或视频路径,并继续生成合成画面。
所以 audio_path 不是一个孤立文件,它会影响:
text
当前片段时长
视频生成 duration
图片转视频时长
最终 segment 的声音
后续 BGM 混音基础
十七、Edge-TTS 和 Index-TTS 的核心区别
从 Pixelle-Video 源码设计看,Edge-TTS 和 Index-TTS 的区别不是"哪个声音更好"这么简单,而是接入方式不同。
1. Edge-TTS
Edge-TTS 在 Pixelle-Video 中可以走 local 模式:
text
text
↓
TTSService._call_local_tts()
↓
edge_tts()
↓
mp3
特点是:
text
部署简单
不需要 ComfyUI workflow
参数主要是 voice 和 speed
适合普通旁白
成本低,适合快速跑通
2. Index-TTS
Index-TTS 更适合通过 ComfyUI workflow 接入:
text
text + ref_audio
↓
TTS workflow
↓
ComfyKit
↓
ComfyUI / RunningHub
↓
audio
特点是:
text
适合声音克隆
可能需要参考音频
依赖具体 ComfyUI workflow
参数由 workflow 决定
更适合需要特定音色、人声风格的场景
README 中也明确提到,语音设置支持选择 TTS workflow,并且参考音频适用于支持声音克隆的 TTS workflow,比如 Index-TTS。
所以可以简单总结:
text
Edge-TTS:
轻量旁白方案。
Index-TTS:
工作流式高级 TTS / 声音克隆方案。
十八、为什么 Index-TTS 更适合 workflow 方式?
Index-TTS 这类方案通常不是一个简单的文本接口。
它可能涉及:
text
模型加载
说话人参考音频
语音克隆参数
音频采样率
推理节点
后处理节点
输出格式
如果 Pixelle-Video 直接适配每一种 TTS 模型,代码会越来越复杂:
text
call_edge_tts()
call_index_tts()
call_chattts()
call_fish_audio()
call_custom_voice_clone()
而 workflow 方式可以把复杂度放到 ComfyUI 里。
Pixelle-Video 只需要统一传:
text
text
voice
speed
ref_audio
具体这些参数如何连接到 Index-TTS 节点,由 workflow 决定。
这种设计和前面的媒体生成是一致的:
text
Pixelle-Video:
负责 pipeline 和参数传递。
ComfyUI workflow:
负责具体模型和节点组合。
ComfyKit:
负责执行 workflow。
TTSService:
负责拿回 audio_path。
这就是扩展性的来源。
十九、参考音频 ref_audio 的作用
参考音频是声音克隆功能的核心输入。
在 FrameProcessor._step_generate_audio() 中,如果当前 TTS 模式是 comfyui,并且配置里有 ref_audio,就会把它加入 tts_params:
python
tts_params["ref_audio"] = config.ref_audio
然后这个参数会继续进入 TTSService._call_comfyui_workflow(),并通过 workflow_params.update(params) 合并到 workflow 参数中。
也就是说,ref_audio 的传递链路是:
text
StoryboardConfig.ref_audio
↓
FrameProcessor._step_generate_audio()
↓
tts_params["ref_audio"]
↓
TTSService.__call__()
↓
_call_comfyui_workflow()
↓
workflow_params
↓
ComfyKit.execute()
↓
Index-TTS / 声音克隆 workflow
这个设计很灵活。
Pixelle-Video 并不规定 ref_audio 在 workflow 里怎么使用。
它只负责把参数传下去。
如果你的 workflow 支持参考音频,它就能用。
如果 workflow 不支持,这个参数可能会被忽略或导致 workflow 报错。
二十、TTS 和数字人口播的关系
第 16 篇我们讲过数字人口播。
数字人口播通常需要:
text
人物参考图
+
语音音频
↓
数字人视频
Pixelle-Video 的 TTS 流程正好提供了这个音频基础。
每个 frame 先生成:
text
frame.audio_path
frame.duration
后续如果使用支持 audio-driven 或 digital human 的视频模型,就可以把这个 audio_path 作为驱动音频传给视频生成模型。
在复杂视频能力中,FrameProcessor._prepare_api_video_inputs() 和相关 API 视频参数可以使用 narration audio 作为 driving audio;前面第 16 篇也分析过,音频驱动图生视频、数字人口播都依赖这条链路。
所以 TTS 不只是给最终视频配音,也可能成为视频生成模型的输入。
对于数字人口播来说:
text
TTS 生成的不是"后期声音"
而是"驱动人物说话的输入"
这也是为什么 TTS 要尽早生成。
二十一、TTS 和 BGM 的关系
TTS 生成的是人声旁白。
BGM 是后期背景音乐。
这两者不要混在一起。
Pixelle-Video 的每个 frame 会先生成 TTS 音频,并用它生成 segment。
等所有 segment 拼接完成后,再根据用户选择添加 BGM。
所以声音层可以理解成两层:
text
第一层:
每个 frame 的 narration audio
第二层:
最终视频整体 BGM
这种设计比较合理。
因为旁白决定每个片段的长度和节奏。
BGM 是后期氛围增强,不应该反过来决定分镜时长。
二十二、常见问题:为什么 TTS 会失败?
理解源码后,TTS 问题可以按路线排查。
1. local Edge-TTS 失败
常见原因:
text
网络访问失败
Edge-TTS 服务临时不可用
请求过于频繁
voice_id 写错
SSL 或证书问题
没有生成音频数据
tts_util.py 中已经针对 401、NoAudioReceived、临时网络问题做了重试、请求间隔和并发限制,但这不代表永远不会失败。
2. comfyui TTS workflow 找不到
常见原因:
text
tts_workflow 配置错误
workflow 没有放到 workflows/ 目录
文件名或 key 写错
default_workflow 没有配置
TTSService 继承自 ComfyBaseService,workflow 前缀是 tts_,工作流目录是 workflows,因此 TTS workflow 通常需要遵守对应命名和目录约定。
3. Index-TTS 没有输出音频
常见原因:
text
ComfyUI 节点缺失
模型文件没有下载
ref_audio 格式不支持
workflow 输出节点不对
result.audios / result.files / result.outputs 中没有音频
源码中如果找不到 .mp3、.wav、.flac 结果,就会抛出 No audio file generated by workflow。
4. 远程音频下载失败
如果 RunningHub 或远程 workflow 返回 URL,Pixelle-Video 需要下载到本地 output_path。如果网络不通、URL 过期、权限不对,就可能失败。源码中这一步使用 httpx.AsyncClient() 下载音频。
5. 音频时长读取失败
Pixelle-Video 需要读取音频时长来设置 frame.duration。tts_util.py 中有一个 get_audio_duration() 工具函数,优先使用 ffmpeg.probe() 读取时长,失败时会按文件大小做粗略估算。
如果 ffmpeg 不可用或音频文件损坏,后续视频时长可能不准确。
二十三、二次开发:如何新增一个 TTS 方案?
如果你想给 Pixelle-Video 增加新的 TTS 能力,有两种路线。
1. 直接代码接入
适合像 Edge-TTS 这样接口简单的方案。
你可以增加:
text
_call_my_tts()
然后在 TTSService.__call__() 中增加新的 mode:
text
local_edge
local_my_tts
comfyui
但这种方式会让 TTSService 变复杂。
如果每个 TTS 模型都这么做,后期维护压力会很大。
2. workflow 接入
更推荐。
把新的 TTS 模型封装成 ComfyUI workflow,比如:
text
workflows/selfhost/tts_my_voice.json
workflows/runninghub/tts_my_voice.json
然后在配置中选择:
yaml
comfyui:
tts:
default_workflow: selfhost/tts_my_voice.json
Pixelle-Video 只负责传:
text
text
voice
speed
ref_audio
workflow 负责实际推理。
这条路线更适合:
text
Index-TTS
ChatTTS
CosyVoice
Fish Speech
声音克隆模型
数字人口播前置语音模型
也更符合 Pixelle-Video 的整体架构。
二十四、二次开发:TTS 模块可以优化什么?
当前 TTS 模块已经能支撑视频生成,但还有一些可优化方向。
1. 增加语音预览缓存
用户调音色和语速时,可能会反复点击试听。
可以根据:
text
text + voice + speed + ref_audio + workflow
生成 hash,缓存试听音频,避免重复生成。
2. 增加每段音频时长预览
在正式生成视频前,先显示:
text
第几帧
旁白文本
预计音频时长
音色
语速
这样用户能提前判断节奏是否太慢或太快。
3. 增加文本长度检查
如果某段 narration 太长,TTS 会变长,视频片段也会变长。
可以在生成音频前提示:
text
这一段旁白过长,建议拆成两段。
4. 增加音量标准化
不同 TTS workflow 输出音量可能不同。
可以在 audio_path 生成后加一步:
text
音量检测
↓
响度标准化
↓
统一输出音频
这样最终视频的人声更稳定。
5. 增加字幕断句
当前 narration 通常直接作为字幕文本。
但 TTS 朗读和字幕显示最好能做更细的断句。
可以进一步生成:
text
字幕分行
停顿点
关键词高亮
逐字字幕时间轴
这会明显提升短视频质感。
6. 增加数字人专用 TTS 模式
如果用于数字人口播,可以增加:
text
voice_clone_mode
reference_voice
speaker_id
lip_sync_safe_speed
确保输出音频更适合后续驱动数字人。
二十五、源码阅读建议
如果你要读 Pixelle-Video 的 TTS 相关源码,建议按这个顺序:
text
1. pixelle_video/services/frame_processor.py
看 _step_generate_audio() 如何把 frame.narration 送入 TTS。
2. pixelle_video/services/tts_service.py
看 TTSService 如何根据 inference_mode 分流 local 和 comfyui。
3. pixelle_video/utils/tts_util.py
看 Edge-TTS 如何真正生成音频、重试、限流和保存文件。
4. pixelle_video/tts_voices.py
看 Edge-TTS 音色列表和 speed_to_rate()。
5. pixelle_video/config/schema.py
看 TTSLocalConfig、TTSComfyUIConfig、TTSSubConfig。
6. config.example.yaml
看 comfyui.tts.default_workflow 和 ComfyUI / RunningHub 相关配置。
7. workflows/selfhost/ 或 workflows/runninghub/
看具体 TTS workflow 如何接收 text、voice、speed、ref_audio。
8. pixelle_video/services/video.py
看 audio_path 最后如何参与图片转视频、视频合成和音轨替换。
这样读,可以完整理解:
text
narration
↓
TTS 参数
↓
Edge-TTS 或 TTS workflow
↓
audio_path
↓
duration
↓
video_segment_path
二十六、总结
这一篇我们分析了 Pixelle-Video 的 TTS 语音生成流程。
它的核心链路是:
text
StoryboardFrame.narration
↓
FrameProcessor._step_generate_audio()
↓
构造 tts_params
↓
TTSService.__call__()
↓
local 模式:
Edge-TTS 生成 mp3
comfyui 模式:
ComfyKit 执行 TTS workflow
Edge-TTS / Index-TTS / 其他语音模型输出音频
↓
frame.audio_path
↓
读取音频时长 frame.duration
↓
后续媒体生成和视频片段合成
从设计上看,Pixelle-Video 的 TTS 模块有几个关键点:
text
Edge-TTS 适合轻量本地旁白。
Index-TTS 适合通过 ComfyUI workflow 做声音克隆。
TTSService 把不同语音方案统一成 text → audio_path。
FrameProcessor 会先生成音频,再生成媒体。
音频时长会写入 frame.duration,并影响后续视频长度。
ComfyUI TTS workflow 可以接收 ref_audio 等扩展参数。
RunningHub 返回的远程音频会被下载成本地文件。
一句话总结:
Pixelle-Video 的 TTS 设计,本质是把每段 narration 转换成可复用的 audio_path,并用音频时长驱动后续图片、视频和 segment 合成;Edge-TTS 走轻量本地路线,Index-TTS 这类高级语音方案则通过 ComfyUI / RunningHub workflow 接入。