ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PaddleSpeech 在线流式 TTS 服务引擎(tts_engine)深度解析:从 Python 动态图推理到分块流式合成

PaddleSpeech 在线流式 TTS 服务引擎(tts_engine)深度解析:从 Python 动态图推理到分块流式合成 PaddleSpeech 在线流式 TTS 服务引擎tts_engine深度解析从 Python 动态图推理到分块流式合成【免费下载链接】PaddleSpeechEasy-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.项目地址: https://gitcode.com/paddlepaddle/PaddleSpeechPaddleSpeech 的流式语音合成Streaming TTS服务由 paddlespeech/server/engine/tts/online/python/tts_engine.py 这一引擎模块承担它被 docs/source/api/paddlespeech.server.engine.tts.online.python.tts_engine.rst 作为 API 文档入口引用。该模块定义了TTSServerExecutor、TTSEngine与PaddleTTSConnectionHandler三个核心类完成从文本前端Frontend到声学模型AM再到声码器Vocoder的流式推理全链路并通过 HTTP / WebSocket 对外提供音频分片输出。读完本文你将掌握流式 TTS 服务的模块划分、配置项语义、分块chunk与去填充depadding原理以及如何启动服务并用客户端验证。一、模块定位与整体架构1.1 引擎在整个服务架构中的位置PaddleSpeech 的服务端采用「引擎池 路由」结构服务启动时根据配置中的engine_list加载对应引擎实例paddlespeech/server/engine/base_engine.py 定义了所有引擎的抽象基类BaseEngine基于 Singleton 元类提供init、run、postprocess等标准接口。流式 TTS 引擎TTSEngine即继承自BaseEngine其实现文件位于paddlespeech/server/engine/tts/online/python/tts_engine.py本文主体Python 动态图推理paddlespeech/server/engine/tts/online/onnx/onnxruntime 推理版本tts_online-onnx引擎网络协议层由 paddlespeech/server/ws/tts_api.py 提供WebSocket 端点/paddlespeech/tts/streaming会根据tts_engine.engine_type动态选择加载python.tts_engine或onnx.tts_engine中的PaddleTTSConnectionHandler随后将收到的文本逐包交给 handler 处理并把生成器yield出来的每一段 base64 音频实时回传客户端。┌─────────────────────────────┐ │ paddlespeech_server start │ └─────────────┬───────────────┘ ▼ ┌──────────────────┐ ┌───────────────────────────────────────────┐ │ engine_pool │──────▶│ TTSEngine (BaseEngine 子类) │ └──────────────────┘ │ ├─ TTSServerExecutor(TTSExecutor) │ ▲ │ │ ├─ frontend (zh/en) │ │ │ │ ├─ am_inference am_mu/am_std │ ┌─────────┴─────────┐ │ │ └─ voc_inference voc_mu/voc_std │ │ ws/tts_api.py │ │ └─ PaddleTTSConnectionHandler │ │ /tts/streaming │ │ ├─ infer()流式生成器 │ └─────────┬─────────┘ │ └─ run()后处理 base64 │ ▼ └───────────────────────────────────────────┘ 客户端(HTTP/WebSocket)1.2 模块的三个核心类类职责TTSServerExecutor继承自 paddlespeech/cli/tts/infer.py 的TTSExecutor负责模型资源下载、加载 AM/Voc 权重与统计量、初始化前端构建推理用封装模型TTSEngine服务引擎读取 YAML 配置、校验模型组合与 chunk 参数、设置 device持有executor并暴露sample_rate、am_block/am_pad、voc_block/voc_pad等流式参数PaddleTTSConnectionHandler每次连接/会话的处理器实现infer()流式推理生成器与run()后处理float32→PCM16→base64并统计首包响应时间与 RTF模块对外通过__all__ [TTSEngine, PaddleTTSConnectionHandler]暴露两个公共符号TTSServerExecutor供引擎内部使用。二、配置解析tts_online_application.yaml流式 TTS 服务的完整配置见 paddlespeech/server/conf/tts_online_application.yamldemo 使用的副本位于 demos/streaming_tts_server/conf/tts_online_application.yaml。2.1 服务级配置host: 0.0.0.0 port: 8092 protocol: http # 可选 websocket、http engine_list: [tts_online-onnx] # 可选 tts_online、tts_online-onnxengine_list中条目的命名格式为speech task_engine type其中tts_online对应本文介绍的 Python 动态图引擎tts_online-onnx对应 ONNX Runtime 推理引擎推理速度更快。protocol决定服务以 HTTP 还是 WebSocket 方式对外提供接口WebSocket 场景下由 paddlespeech/server/ws/tts_api.py 的/paddlespeech/tts/streaming端点承载。若在容器中启动服务后客户端访问 IP 不可达可尝试将host改为本机实际 IP。2.2 tts_onlinePython 动态图引擎配置tts_online: # 声学模型可选 [fastspeech2_csmsc, fastspeech2_cnndecoder_csmsc] am: fastspeech2_csmsc am_config: am_ckpt: am_stat: phones_dict: tones_dict: speaker_dict: spk_id: 0 # 声码器可选 [mb_melgan_csmsc, hifigan_csmsc] voc: mb_melgan_csmsc voc_config: voc_ckpt: voc_stat: # 其它参数 lang: zh device: cpu # 或 gpu:id am_block: 72 am_pad: 12 voc_block: 36 voc_pad: 14各字段说明参数含义am/voc声学模型与声码器标识。fastspeech2_cnndecoder_csmsc支持流式 AM 推理mb_melgan_csmsc与hifigan_csmsc均支持流式 Voc 推理am_config/am_ckpt/am_stat、voc_config/voc_ckpt/voc_stat若留空则使用CommonTaskResource自动下载的预训练模型若指定则使用本地文件此时phones_dict必填phones_dict音素字典声学模型前端的必要输入lang语言zh使用paddlespeech.t2s.frontend.zh_frontend.Frontenden使用paddlespeech.t2s.frontend.en_frontend.Englishdevice推理设备cpu或gpu:id不配置时回退到paddle.get_device()am_block/am_pad流式 AM 推理的 chunk 有效帧数与前后填充帧数。仅对fastspeech2_cnndecoder_csmsc生效voc_block/voc_pad流式 Voc 推理的 chunk 有效帧数与前后填充帧数2.3 关键约束与断言在TTSEngine.init()中配置会经过三重校验对应 tts_engine.py模型组合断言am必须是fastspeech2_csmsc或fastspeech2_cnndecoder_csmscvoc必须是hifigan_csmsc或mb_melgan_csmsc否则启动报错。chunk 参数断言voc_block与voc_pad必须大于 0。采样率一致性断言self.executor.am_config.fs必须等于self.executor.voc_config.fs并以此作为服务对外输出的sample_rate。同时self.voc_upsample self.executor.voc_config.n_shift默认 300表示每帧 mel 上采样到 300 个采样点用于后续去填充时的帧-采样换算。三、TTSServerExecutor模型与资源初始化TTSServerExecutor在构造时创建CommonTaskResource(tasktts, model_formatdynamic, inference_modeonline)用于管理预训练模型资源。其核心方法如下。3.1 get_model_info加载 AM/Voc 模型与统计量model_class self.task_resource.get_model_class(model_name) if field am: odim self.am_config.n_mels model model_class(idimself.vocab_size, odimodim, **self.am_config[model]) model.set_state_dict(paddle.load(ckpt)[main_params]) elif field voc: model model_class(**self.voc_config[generator_params]) model.set_state_dict(paddle.load(ckpt)[generator_params]) model.remove_weight_norm() ... model_mu, model_std np.load(stat) # 归一化统计量 return model, model_mu, model_std要点AM 侧从配置读取n_mels作为输出维度从phones_dict得到vocab_size作为输入维度权重取自 checkpoint 的main_params。Voc 侧使用generator_params构造生成器加载generator_params权重并调用remove_weight_norm()去掉权重归一化推理阶段不再需要。stat文件同时包含mu均值与std标准差二者会被包装成paddle.Tensor返回作为 Z-Score 归一化器的参数。3.2 _init_from_path完整初始化流程def _init_from_path(self, amfastspeech2_csmsc, am_configNone, am_ckptNone, am_statNone, phones_dictNone, tones_dictNone, speaker_dictNone, vocmb_melgan_csmsc, voc_configNone, voc_ckptNone, voc_statNone, langzh):初始化逻辑tts_engine.py幂等保护若am_inference与voc_inference已存在则直接返回避免重复加载。AM 资源若am_ckpt/am_config/am_stat/phones_dict任一为空则走预训练下载分支use_pretrained_amTrue否则使用用户指定的本地文件路径模型 tag 为am - lang。Voc 资源同样的下载/本地判断逻辑模型 tag 为voc - lang。读取配置与字典将 AM/Voc 的 YAML 配置解析为CfgNode读取phones_dict得到vocab_size。初始化前端zh语言用Frontend支持phone_vocab_path与tone_vocab_pathen语言用English仅phone_vocab_path。构建 AM 推理封装模型名取am[:am.rindex(_)]如fastspeech2_csmsc→fastspeech2若为fastspeech2_cnndecoder则直接使用底层模型其推理在 handler 中按 encoder/decoder/postnet 分步执行否则用ZScore归一化器 *_inference封装类构建self.am_inference。构建 Voc 推理封装ZScore*_inference封装类得到self.voc_inference。四、TTSEngine服务引擎与流式参数class TTSEngine(BaseEngine): def init(self, config: dict) - bool: self.executor TTSServerExecutor() self.config config self.lang self.config.lang self.engine_type online ... self.sample_rate self.executor.am_config.fs 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初始化成功后打印Initialize TTS server engine successfully on device: %s.失败则记录日志并返回False。其中am_upsample 1AM 帧不额外上采样voc_upsample n_shift通常 300两个上采样倍数会被传给PaddleTTSConnectionHandler用于把 mel 帧数换算成音频采样点数。五、PaddleTTSConnectionHandler流式推理的核心实现5.1 构造与参数传递class PaddleTTSConnectionHandler: def __init__(self, tts_engine): self.tts_engine tts_engine self.executor self.tts_engine.executor self.config self.tts_engine.config self.am_block self.tts_engine.am_block ...该 handler 在 WebSocket 端点收到signal start时由 paddlespeech/server/ws/tts_api.py 创建每次会话一个实例与每个连接的生命周期绑定。5.2 depadding流式推理的误差消除def depadding(self, data, chunk_num, chunk_id, block, pad, upsample): front_pad min(chunk_id * block, pad) if chunk_id 0: # 首块保留前半段 data data[:block * upsample] elif chunk_id chunk_num - 1: # 末块去掉前填充 data data[front_pad * upsample:] else: # 中间块去掉前后填充 data data[front_pad * upsample:(front_pad block) * upsample] return data由于分块推理时每个 chunk 前后都会额外带上pad帧上下文用于抵消边界效应合成结果中必须把这些填充帧对应的音频剪掉才能无缝拼接出与整句推理一致的结果。首块、中间块、末块三种情况分别按block * upsample与front_pad * upsample进行裁剪其中upsample是 mel 帧到采样点的放大倍数AM 侧为 1Voc 侧为n_shift。5.3 infer两条流式推理路径infer()是一个被paddle.no_grad()装饰的生成器其流程如下文本前端zh用frontend.get_input_ids(text, merge_sentencesFalse, get_tone_idsFalse)得到phone_idsen类似但不含 tone。前端耗时记录到self.frontend_time。逐句循环对每个句子的音素序列执行 AM→Voc 的流式合成first_flag标记首个音频块以统计首包延迟。路径 Afastspeech2_csmsc整体 AM 流式 Vocmel self.executor.am_inference(part_phone_ids) # 整句合成 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) # 分块 voc 推理 sub_wav self.depadding(sub_wav, voc_chunk_num, i, self.voc_block, self.voc_pad, self.voc_upsample) yield sub_wav这里 AM 一次性合成整句 mel只有声码器做流式分块因此该模式不支持流式 AMam_block/am_pad对它无效。路径 Bfastspeech2_cnndecoder_csmscAM、Voc 双流式orig_hs self.executor.am_inference.encoder_infer(part_phone_ids) # encoder 一次前向 mel_len orig_hs.shape[1] voc_chunk_num math.ceil(mel_len / self.voc_block) hss get_chunks(orig_hs, self.am_block, self.am_pad, am) # 对 encoder 输出分块 for i, hs in enumerate(hss): before_outs self.executor.am_inference.decoder(hs) # 逐块 decoder after_outs before_outs self.executor.am_inference.postnet( before_outs.transpose((0, 2, 1))).transpose((0, 2, 1)) # postnet 残差 normalized_mel after_outs[0] sub_mel denorm(normalized_mel, self.executor.am_mu, self.executor.am_std) sub_mel self.depadding(sub_mel, am_chunk_num, i, self.am_block, self.am_pad, self.am_upsample) mel_streaming np.concatenate((mel_streaming, sub_mel), axis0) # 累积 mel 流 while (mel_streaming.shape[0] end and voc_chunk_id voc_chunk_num): voc_chunk mel_streaming[start:end, :] sub_wav self.executor.voc_inference(voc_chunk) sub_wav self.depadding(sub_wav, voc_chunk_num, voc_chunk_id, self.voc_block, self.voc_pad, self.voc_upsample) yield sub_wav voc_chunk_id 1 start max(0, voc_chunk_id * self.voc_block - self.voc_pad) end min((voc_chunk_id 1) * self.voc_block self.voc_pad, mel_len)该模式将 encoder 一次性前向hidden states 不含填充随后 decoder postnet 按am_block分块流式产出 mel 帧denormdata * std mean定义于 paddlespeech/server/utils/util.py将归一化后的 mel 还原为真实值。累积的mel_streaming一旦达到voc_block voc_pad的窗口长度就触发一轮流式 Voc 推理并yield一段音频实现 AM 与 Voc 两级流水线式的低延迟输出。5.4 get_chunks 与窗口滑动分块工具函数get_chunks也在 paddlespeech/server/utils/util.py 中def get_chunks(data, block_size, pad_size, step): if block_size -1: return [data] # 不分块 data_len data.shape[1] if step am else data.shape[0] chunks [] n math.ceil(data_len / block_size) for i in range(n): start max(0, i * block_size - pad_size) end min((i 1) * block_size pad_size, data_len) ... return chunks每个 chunk 的窗口为[i*block - pad, (i1)*block pad)相邻 chunk 之间有2*pad的重叠区域这正是流式推理消除边界误差的来源。step参数区分 AM按时间维shape[1]切分与 Voc按 mel 帧维shape[0]切分。5.5 run后处理与指标统计def run(self, sentence, spk_id0): wav_list [] for wav in self.infer(textsentence, langself.config.lang, amself.config.am, spk_idspk_id): wav float2pcm(wav) # float32 - int16 wav_bytes wav.tobytes() wav_base64 base64.b64encode(wav_bytes).decode(utf8) wav_list.append(wav) yield wav_base64 ... logger.info(fThe durations of audio is: {duration} s) logger.info(ffirst response time: {self.first_response_time} s) logger.info(ffinal response time: {self.final_response_time} s) logger.info(fRTF: {self.final_response_time / duration})float2pcm位于 paddlespeech/server/utils/audio_process.py将范围在 [-1, 1] 的 float32 音频缩放到 int16 PCM并通过base64.b64encode编码后逐块yieldWebSocket 端收到后以{status: 1, audio: base64}逐包下发最后以{status: 2}标记合成完成。指标统计first_response_time首包响应时间从前端开始到首个 Voc 输出、final_response_time整句完成时间、RTFReal-Time Factor合成耗时/音频时长。服务端日志可用count_engine见 paddlespeech/server/utils/util.py批量统计平均首包、平均尾包与平均 RTF。六、流式 chunk 参数的工程调优chunk 参数直接决定延迟与音质的平衡仓库文档与配置注释给出的经验值如下参数推荐值说明am_block72流式 AM 每次推理的有效帧数仅fastspeech2_cnndecoder生效am_pad12流式 AM 前后填充帧数am_pad12时流式合成音频与非流式一致voc_block36流式 Voc 每次推理的有效 mel 帧数voc_padmb_melgan14与非流式一致最小可设 7听感正常小于 7 会出现听感异常voc_padhifigan19与非流式一致设为 14 听感正常调参原则block越小、pad越小首包延迟越低但过小的pad会引入拼接误差与音质劣化反之则音质更接近整句推理但延迟上升。Voc 模型的 pad 计算方法可参考配置注释中提到的 AIStudio 教程思路PaddleSpeech 流式声码器 Pad 计算。此外voc_upsample应与 voc 配置中的n_shift一致默认 300保证去填充的采样点换算正确。七、服务启动与客户端调用7.1 启动服务命令行方式推荐paddlespeech_server start --config_file ./conf/tts_online_application.yaml其中--config_file默认为./conf/tts_online_application.yaml--log_file默认为./log/paddlespeech.log。启动成功后日志会出现Uvicorn running on http://0.0.0.0:8092并伴随若干次 warm-up 的首包响应时间输出。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 协议paddlespeech_client tts_online --server_ip 127.0.0.1 --port 8092 --protocol http --input 您好欢迎使用百度飞桨语音合成服务。 --output output.wavWebSocket 协议paddlespeech_client tts_online --server_ip 127.0.0.1 --port 8092 --protocol websocket --input 您好欢迎使用百度飞桨语音合成服务。 --output output.wav客户端常用参数--server_ip默认 127.0.0.1、--port默认 8092、--protocol可选 http/websocket默认 http、--input待合成文本、--output输出音频文件。若127.0.0.1不可访问应使用实际服务 IP。完整的服务端/客户端使用步骤还可参考 demos/streaming_tts_server/README_cn.md。八、测试验证与实现佐证仓库在 tests/unit/server/online/tts/check_server/ 提供了流式 TTS 服务的自动化验证脚本 test.sh遍历tts_online引擎在fastspeech2_cnndecoder_csmsc/fastspeech2_csmsc×mb_melgan_csmsc/hifigan_csmsc四种 AM/Voc 组合下的 HTTP 服务启动与客户端合成再通过修改配置中的protocol为 websocket重复四组组合的 WebSocket 验证启动阶段以Uvicorn running on http://判断服务就绪以Failed to warm up on tts engine.或AssertionError判断启动失败HTTP 侧以200 OK计数、WebSocket 侧以Complete the synthesis of the audio streams计数判定测试通过。该测试脚本使用的客户端命令与配置亦与本文第 2、7 节描述的字段一一对应可作为引擎行为正确性的直接验证依据。九、小结paddlespeech.server.engine.tts.online.python.tts_engine是 PaddleSpeech 流式语音合成服务的 Python 动态图实现核心TTSServerExecutor负责模型与前端装配TTSEngine负责配置校验与流式参数提取PaddleTTSConnectionHandler通过get_chunks分块、depadding去填充、生成器逐块输出实现了「文本→音素→mel→PCM16→base64」的低延迟流式链路并配套首包响应时间与 RTF 指标。理解该模块的 chunk 窗口与去填充机制是调优流式 TTS 延迟-音质平衡、以及为线上服务选型tts_online/tts_online-onnx引擎的前提。【免费下载链接】PaddleSpeechEasy-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.项目地址: https://gitcode.com/paddlepaddle/PaddleSpeech创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表