- 人工智能
- 语音
- 音频
【免费下载链接】PaddleSpeech
Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.
PaddleSpeech 的流式语音合成(Streaming TTS)服务在 docs/source/api/paddlespeech.server.engine.tts.online.python.rst 中对外公开了paddlespeech.server.engine.tts.online.python包,该包是服务端 TTS 引擎的Python 动态图推理后端(对应引擎类型tts_online)。本文以该 API 文档指向的模块为骨架,结合其底层实现 tts_engine.py、服务配置 tts_online_application.yaml 与官方示例 streaming_tts_server,完整讲解流式 TTS 引擎的初始化流程、分块(chunk)推理原理、pad/depad 机制、配置项含义以及 HTTP/WebSocket 服务接入方法。读完本文,你将能够从源码层面理解流式 TTS 的端到端调用链,并独立部署、配置和调用 PaddleSpeech 流式语音合成服务。
1. 模块定位:从 API 文档入口看在线 TTS 引擎
docs/source/api/paddlespeech.server.engine.tts.online.python.rst是一份 Sphinxautomodule风格的 API 文档骨架,它声明了三个关键信息:
- 模块名:
paddlespeech.server.engine.tts.online.python; - 渲染方式:
:members:、:undoc-members:、:show-inheritance:,即自动提取模块内所有公开类、方法与继承关系; - 子模块:唯一的子模块
paddlespeech.server.engine.tts.online.python.tts_engine。
也就是说,该包的核心内容全部集中在 tts_engine.py 这一个文件中,它导出了__all__ = ['TTSEngine', 'PaddleTTSConnectionHandler']两个公开符号。从 online 包索引 可以看到,paddlespeech.server.engine.tts.online之下还存在一个onnx子包,二者分别对应配置中的引擎类型:
| 引擎类型 | 推理后端 | 模型名称后缀 | 说明 |
|---|---|---|---|
tts_online | Paddle 动态图(本包) | fastspeech2_csmsc/fastspeech2_cnndecoder_csmsc | Python 动态图推理 |
tts_online-onnx | ONNX Runtime | fastspeech2_csmsc_onnx/fastspeech2_cnndecoder_csmsc_onnx | 推理速度更快 |
本文聚焦于tts_online,即 Python 动态图后端。该引擎类层次上继承自 BaseEngine,BaseEngine使用Singleton元类保证全局只有一个引擎实例,并定义了init()、run()、postprocess()三个生命周期方法。
2. 引擎内部三大组件与整体调用链
从源码结构看,tts_engine.py内部由三个类协同工作:
TTSServerExecutor(继承自paddlespeech.cli.tts.infer.TTSExecutor):负责模型文件的解析、下载与加载,将 AM(声学模型)、Vocoder(声码器)、前端(frontend)初始化为可推理状态;TTSEngine(继承自BaseEngine):服务级引擎,负责读取 yaml 配置、校验模型组合与流式参数、设置推理设备,并持有唯一的TTSServerExecutor;PaddleTTSConnectionHandler:连接处理器,负责对每条合成请求执行流式分块推理、去 pad(depadding)、float32 转 PCM、base64 编码,并以生成器(generator)的方式逐块产出音频。
一次流式合成的调用链可以概括为:
WebSocket/HTTP 请求 │ ▼ paddlespeech/server/ws/tts_api.py (按 engine_type 选择 handler) │ ▼ PaddleTTSConnectionHandler.run(sentence, spk_id) │ ▼ PaddleTTSConnectionHandler.infer(...) -- 前端文本转音素 -> AM 推理 -> Mel 分块 -> Vocoder 推理 │ ▼ float2pcm -> base64 编码,逐块 yield3. 引擎初始化:模型解析、下载与加载(TTSServerExecutor)
TTSServerExecutor.__init__中创建了CommonTaskResource(task='tts', model_format='dynamic', inference_mode='online'),即按动态图格式、在线推理模式来定位 TTS 模型资源。
3.1_init_from_path:AM 与 Vocoder 的资源解析
_init_from_path是模型初始化的核心入口,签名如下:
def _init_from_path( self, am: str = 'fastspeech2_csmsc', am_config=None, am_ckpt=None, am_stat=None, phones_dict=None, tones_dict=None, speaker_dict=None, voc: str = 'mb_melgan_csmsc', voc_config=None, voc_ckpt=None, voc_stat=None, lang: str = 'zh'):其关键逻辑包括:
- 预训练模型自动下载:当
am_ckpt、am_config、am_stat、phones_dict任一为空时,use_pretrained_am = True,引擎会以am + '-' + lang(如fastspeech2_csmsc-zh)作为model_tag调用set_task_model(model_type=0)自动下载官方预训练模型,并从资源目录解析出config、ckpt、speech_stats、phones_dict等文件的真实路径;Vocoder 同理(model_type=1)。如果用户显式指定了所有路径,则skip_download=True,直接使用绝对路径加载本地模型。 - 词汇表构建:读取
phones_dict(音素字典)得到vocab_size,用于构造 AM 模型输入维度。 - 前端选择:
lang == 'zh'时使用 zh_frontend.Frontend(支持tone_vocab_path声调字典);lang == 'en'时使用 en_frontend.English。 - AM 名称归一化:
self.am_name = am[:am.rindex('_')],即fastspeech2_csmsc归一为fastspeech2、fastspeech2_cnndecoder_csmsc归一为fastspeech2_cnndecoder;随后通过get_model_class动态获取模型类。
3.2get_model_info:加载模型与归一化统计量
get_model_info(field, model_name, ckpt, stat)按field区分加载逻辑:
field == "am":以idim=self.vocab_size、odim=self.am_config.n_mels构造声学模型,加载paddle.load(ckpt)["main_params"];field == "voc":以**self.voc_config["generator_params"]构造声码器,加载paddle.load(ckpt)["generator_params"],并调用model.remove_weight_norm()移除权重归一化(推理加速);- 二者都会读取
stat(mean/std 统计量文件)并转为paddle.Tensor。
加载完成后,AM 会包一层ZScore归一化器并套上<am_name>_inference推理封装类(如fastspeech2_inference),Vocoder 同样以ZScore+<voc_name>_inference封装,二者均置为eval()模式。
3.3 采样率一致性校验
TTSEngine.init()中有一个容易被忽略但重要的断言:
assert self.executor.am_config.fs == self.executor.voc_config.fs, \ "The sample rate of AM and Vocoder model are different, please check model." self.sample_rate = self.executor.am_config.fs流式 TTS 要求 AM 与 Vocoder 的采样率一致(默认均为 24000 Hz),合成音频的采样率最终取自 AM 配置。同时在初始化完成后,voc_upsample = self.executor.voc_config.n_shift(默认 300),即声码器的 hop 长度,它决定 Mel 帧数到音频采样点数的放大倍数。
4. TTSEngine.init:配置校验与流式分块参数
TTSEngine.init(config)是服务启动时的入口,除了模型初始化外,还做了以下硬性校验(对应 tts_online_application.yaml 中tts_online一节):
- AM 模型只允许
fastspeech2_csmsc或fastspeech2_cnndecoder_csmsc(后者支持流式 AM 推理); - Vocoder只允许
hifigan_csmsc或mb_melgan_csmsc(二者均支持流式推理); voc_block > 0且voc_pad > 0;device参数:配置中可写cpu或gpu:id,未配置时回退到paddle.get_device(),随后paddle.set_device(device)生效;设备设置失败时记录 error 并返回False。
初始化成功后,引擎从配置中取出四个流式关键参数:
self.am_block = self.config.am_block self.am_pad = self.config.am_pad self.voc_block = self.config.voc_block self.voc_pad = self.config.voc_pad self.am_upsample = 1 self.voc_upsample = self.executor.voc_config.n_shift| 参数 | 含义 | 默认值 | 说明 |
|---|---|---|---|
am_block | AM 推理 chunk 的有效帧数 | 72 | 仅对fastspeech2_cnndecoder生效 |
am_pad | chunk 前后各叠加的帧数,用于消除流式误差 | 12 | 设为 12 时流式与非流式合成结果一致 |
voc_block | Vocoder 推理 chunk 的有效帧数 | 36 | — |
voc_pad | chunk 前后各叠加的帧数 | 14 | 见下文按模型区分的最小值 |
am_upsample | AM 帧放大倍数 | 1 | — |
voc_upsample | Mel 帧到采样点的放大倍数 | 300 | 取自 voc 配置n_shift |
5. 流式推理核心:PaddleTTSConnectionHandler
PaddleTTSConnectionHandler是流式合成的"心脏"。它的infer()方法使用@paddle.no_grad()装饰,以生成器方式逐块产出sub_wav(numpy 数组)。
5.1 前端处理与计时
对zh文本,调用executor.frontend.get_input_ids(text, merge_sentences=False, get_tone_ids=False)得到phone_ids(音素 id 序列);对en文本走English前端。随后记录frontend_time,并针对每一条音素序列执行 AM + Vocoder 推理。
5.2 模式一:fastspeech2_csmsc(AM 一次性 + Vocoder 流式)
mel = self.executor.am_inference(part_phone_ids) # AM 一次性产出全部 Mel mel_chunks = get_chunks(mel, self.voc_block, self.voc_pad, "voc") for i, mel_chunk in enumerate(mel_chunks): sub_wav = self.executor.voc_inference(mel_chunk) # 声码器逐块推理 sub_wav = self.depadding(sub_wav, voc_chunk_num, i, self.voc_block, self.voc_pad, self.voc_upsample) yield sub_wav这种模式下 AM 不流式,只有 Vocoder 按voc_block分块,实现"首包低延迟"的流式听感。
5.3 模式二:fastspeech2_cnndecoder_csmsc(AM + Vocoder 双流式)
该模式真正实现了 AM 与 Vocoder 的双重流式:
- 先用
am_inference.encoder_infer(part_phone_ids)一次性得到 encoder 隐层orig_hs; - 计算 Mel 总长
mel_len与 vocoder chunk 数量voc_chunk_num = ceil(mel_len / voc_block); - 用
get_chunks(orig_hs, self.am_block, self.am_pad, "am")将隐层切块,逐块调用decoder(hs)与postnet(...)得到归一化 Mel; - 通过
denorm(normalized_mel, am_mu, am_std)(见 util.py 中的denorm(data, mean, std) = data * std + mean)反归一化; depadding去除每块的前后 pad,并np.concatenate累积到mel_streaming;- 当累积的 Mel 帧数足够(
mel_streaming.shape[0] >= end)时,按滑动窗口start:end交给 vocoder 推理并 yield 音频块,随后更新start、end滑窗位置。
这里start = max(0, voc_chunk_id * voc_block - voc_pad)、end = min((voc_chunk_id+1) * voc_block + voc_pad, mel_len),即相邻 vocoder chunk 之间有voc_pad帧的重叠,重叠部分在depadding时被裁剪,从而避免流式推理在块边界产生音质跳变。
5.4 depadding:流式去重叠原理
depadding(data, chunk_num, chunk_id, block, pad, upsample)的实现按 chunk 位置分三种情况:
- 首块(
chunk_id == 0):只保留前block * upsample个采样点(去掉尾部 pad); - 末块(
chunk_id == chunk_num - 1):去掉头部front_pad * upsample个采样点; - 中间块:取
[front_pad * upsample : (front_pad + block) * upsample],同时去掉头、尾 pad。
其中front_pad = min(chunk_id * block, pad)保证首块之后的重叠量不超过pad。乘以upsample是因为 pad 的单位是 Mel 帧,需要放大为采样点数(vocoder 阶段upsample = voc_upsample,AM 阶段am_upsample = 1)。
与之配套的切块工具是 util.py 中的get_chunks(data, block_size, pad_size, step):它以ceil(data_len / block_size)决定 chunk 数量,每块取[i*block - pad, (i+1)*block + pad]的闭区间(含边界),step参数决定按哪个维度切分("am"按data.shape[1],"voc"按data.shape[0])。
5.5 时序指标与 RTF
infer()内部埋点统计了三个关键时序:
first_am_infer:首次 AM 推理耗时(从前端结束起算);first_voc_infer:首次 Vocoder 推理耗时(从首次 AM 结束起算);first_response_time:首包响应时间(从前端开始到首段音频产出),这是流式 TTS 最核心的体验指标。
run()方法完成推理后的后处理与统计:
wav = float2pcm(wav) # float32 -> int16 wav_bytes = wav.tobytes() # -> bytes wav_base64 = base64.b64encode(wav_bytes).decode('utf8') yield wav_base64其中float2pcm实现在 audio_process.py,将范围 [-1, 1] 的浮点信号缩放到 int16 整数域并裁剪。全部 chunk 产完后,run()计算音频总时长duration = len(wav_all) / sample_rate,并输出日志:
sentence: ... The durations of audio is: X s first response time: X s final response time: X s RTF: final_response_time / duration Other info: front time, first am infer time, first voc infer timeRTF(Real-Time Factor)即合成耗时与音频时长之比,是评估流式合成性能的关键指标。仓库还提供了 util.py 中的count_engine(logfile)工具,可直接解析nohup.out日志批量统计平均首包响应、平均尾包响应、平均时长与整体 RTF。
6. 服务配置文件详解:tts_online_application.yaml
流式 TTS 服务使用 paddlespeech/server/conf/tts_online_application.yaml 作为默认配置,demo 目录 demos/streaming_tts_server/conf/tts_online_application.yaml 中也有同款可运行版本。配置分三大部分:
6.1 SERVER SETTING(服务设置)
host: 0.0.0.0 port: 8092 protocol: 'http' # 可选 ['websocket', 'http'] engine_list: ['tts_online-onnx'] # 可选 ['tts_online', 'tts_online-onnx']protocol:服务使用的网络协议,目前支持http与websocket;engine_list:服务包含的引擎列表,格式为<语音任务>_<引擎类型>;流式 TTS 使用tts_online(Python 动态图)或tts_online-onnx(ONNX Runtime,速度更快);- 注意:若在容器内可正常启动服务但客户端访问 IP 不可达,可将
host改为本地实际 IP。
6.2 tts_online 引擎配置(本包对应部分)
tts_online: am: 'fastspeech2_csmsc' # 可选 fastspeech2_csmsc / fastspeech2_cnndecoder_csmsc am_config: am_ckpt: am_stat: phones_dict: tones_dict: speaker_dict: spk_id: 0 voc: 'mb_melgan_csmsc' # 可选 mb_melgan_csmsc / hifigan_csmsc voc_config: voc_ckpt: voc_stat: lang: 'zh' device: 'cpu' # 可选 'gpu:id' 或 'cpu' am_block: 72 am_pad: 12 voc_block: 36 voc_pad: 14所有模型路径字段(am_config、am_ckpt等)留空时引擎会自动下载官方预训练模型。am_block/am_pad仅对fastspeech2_cnndecoder_csmsc生效;voc_pad的推荐取值与模型相关,详见下一节。
6.3 pad 参数选取的工程经验
根据 demo 文档 与配置文件注释,pad 的取值直接影响流式合成音质:
am_pad = 12时,流式 AM 合成音频与非流式完全一致;mb_melgan_csmsc:voc_pad = 14时流式与非流式一致;最小可设为 7(听感正常),小于 7 听感异常;hifigan_csmsc:voc_pad = 19时流式与非流式一致;设为 14 时听感正常;- 推理速度:
mb_melgan > hifigan;音频质量:mb_melgan < hifigan。
仓库中 tts_online_application.yaml 的
tts_online-onnx一节还展示了 ONNX 后端的扩展字段:am_ckpt为模型列表(cnndecoder 时按 [encoder, decoder, postnet] 顺序)、am_sess_conf/voc_sess_conf(含use_trt、cpu_threads)以及voc_upsample(须与 voc 配置的n_shift一致)。
7. 服务部署与客户端调用
7.1 服务端启动
命令行方式(推荐):
paddlespeech_server start --config_file ./conf/tts_online_application.yamlconfig_file:服务配置文件,默认./conf/tts_online_application.yaml;log_file:日志文件,默认./log/paddlespeech.log。
启动成功后日志会先打印 3 次 warm up 的首包响应时间,随后出现Uvicorn running on http://0.0.0.0:8092,表明 HTTP 服务已在 8092 端口就绪。若将protocol改为websocket,则启动同样的命令即可提供 WebSocket 流式接口。
Python API 方式:
from paddlespeech.server.bin.paddlespeech_server import ServerExecutor server_executor = ServerExecutor() server_executor( config_file="./conf/tts_online_application.yaml", log_file="./log/paddlespeech.log")7.2 HTTP 协议客户端
命令行(若127.0.0.1不可达,替换为实际服务 IP):
paddlespeech_client tts_online --server_ip 127.0.0.1 --port 8092 --protocol http --input "您好,欢迎使用百度飞桨语音合成服务。" --output output.wav客户端参数一览:
| 参数 | 含义 | 默认值 |
|---|---|---|
server_ip | 服务端 IP | 127.0.0.1 |
port | 服务端口 | 8092 |
protocol | 服务协议,可选 http / websocket | http |
input | 待合成文本(必填) | — |
spk_id | 说话人 id(多说话人场景) | 0 |
output | 输出音频路径,None 表示不保存 | None |
play | 是否边合成边播放(依赖 pyaudio) | False |
Python API 方式:
from paddlespeech.server.bin.paddlespeech_client import TTSOnlineClientExecutor executor = TTSOnlineClientExecutor() executor( input="您好,欢迎使用百度飞桨语音合成服务。", server_ip="127.0.0.1", port=8092, protocol="http", spk_id=0, output="./output.wav", play=False)客户端成功输出示例:
tts http client start 句子:您好,欢迎使用百度飞桨语音合成服务。 首包响应:0.18863153457641602 s 尾包响应:3.1427218914031982 s 音频时长:3.825 s RTF: 0.8216266382753459 音频保存至:output.wav7.3 WebSocket 协议客户端
将配置中protocol改为websocket后重启服务,客户端命令只需更换协议参数:
paddlespeech_client tts_online --server_ip 127.0.0.1 --port 8092 --protocol websocket --input "您好,欢迎使用百度飞桨语音合成服务。" --output output.wav7.4 WebSocket 服务端协议流程
WebSocket 端点在 tts_api.py 的/paddlespeech/tts/streaming路由中实现,协议分三类消息:
- start 信号:客户端发送
{"signal": "start"},服务端创建PaddleTTSConnectionHandler(按tts_engine.engine_type动态导入 python 或 onnx 版本),返回{"status": 0, "signal": "server ready", "session": <uuid>}; - 合成请求:客户端发送
{"text": "...", "spk_id": 0},服务端调用connection_handler.run(...)并逐块返回{"status": 1, "audio": <base64>};合成完毕返回{"status": 2, "audio": ''},出错返回{"status": -1, "audio": ''}; - end 信号:客户端发送
{"signal": "end"},服务端关闭连接并返回{"status": 0, "signal": "connection will be closed"}。
这就是流式 TTS 能够"边合成边推送"的协议基础:每一段音频(约一个voc_block对应的时长)独立编码为 base64 后通过 WebSocket 帧即时下发。
8. 使用限制与注意事项
- 说话人:当前代码只支持单说话人模型,
spk_id的选择并不生效; - 不支持的能力:流式 TTS 不支持更换采样率、变速、变音量等功能;
- 模型组合:流式引擎仅支持
fastspeech2/fastspeech2_cnndecoder(AM)与hifigan/mb_melgan(Vocoder)的组合,且 AM 与 Vocoder 采样率必须一致; - 依赖版本:官方 demo 推荐使用 paddlepaddle 2.4rc 及以上版本;若使用简单模式安装,需要自行参考 conf 目录 下的 yaml 文件准备配置;
- 设备占用:若设置
device失败,请检查该设备是否已被占用以及 yaml 中device参数格式(cpu或gpu:id)。
9. 进一步阅读
- 流式 TTS 在线引擎实现:paddlespeech/server/engine/tts/online/python/tts_engine.py
- ONNX 版在线引擎(对比参考):paddlespeech/server/engine/tts/online/onnx/tts_engine.py
- 引擎基类:paddlespeech/server/engine/base_engine.py
- 服务配置:paddlespeech/server/conf/tts_online_application.yaml
- 分块与统计工具:paddlespeech/server/utils/util.py
- WebSocket 端点:paddlespeech/server/ws/tts_api.py
- 完整部署示例:demos/streaming_tts_server/README_cn.md
- 人工智能
- 语音
- 音频
【免费下载链接】PaddleSpeech
Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.
相关推荐
PaddleSpeech 在线 ASR 引擎(Python 版)源码级解析:从流式解码到端点检测
PaddleSpeech 在线 ASR 引擎(Python 版)源码级解析:从流式解码到端点检测 导读 本文以 paddlespeech.server.engi
人工智能语音音频PaddleSpeech 在线流式 TTS 服务引擎(tts_engine)深度解析:从 Python 动态图推理到分块流式合成
PaddleSpeech 在线流式 TTS 服务引擎(tts_engine)深度解析:从 Python 动态图推理到分块流式合成 PaddleSpeech 的流
人工智能语音音频NLP媒体生成PaddleSpeech 在线 ASR 引擎 Python 实现:asr_engine 模块 API 与流式解码源码解析
PaddleSpeech 在线 ASR 引擎 Python 实现:asr_engine 模块 API 与流式解码源码解析 导读 本文以 PaddleSpeech
人工智能语音音频
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考