ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PaddleSpeech 流式 TTS 实战:基于 ONNX Runtime 的 stream_play_tts 模块源码解析与边合成边播放实现

2026/9/24 15:34:23 拓冰建站 浏览量
PaddleSpeech 流式 TTS 实战:基于 ONNX Runtime 的 stream_play_tts 模块源码解析与边合成边播放实现 PaddleSpeech 流式 TTS 实战基于 ONNX Runtime 的 stream_play_tts 模块源码解析与边合成边播放实现【免费下载链接】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/gh_mirrors/pa/PaddleSpeech本篇文章以 PaddleSpeech 仓库中paddlespeech.t2s.exps.stream_play_tts模块对应 docs/source/api/paddlespeech.t2s.exps.stream_play_tts.rst 文档条目为主体深入讲解其底层实现 stream_play_tts.py 的完整运行机制从模型下载、前端文本转音素、流式声学模型FastSpeech2 CNNDecoder与流式声码器MB-MelGAN的 ONNX 分块推理到去重叠depadding、PyAudio 实时播放与 WAV 导出。读完本文你将掌握 PaddleSpeech 端到端流式语音合成的最小可运行实现理解am_block / am_pad / voc_block / voc_pad等核心参数的意义与取值依据并能基于源码自行复现文本输入 → 首包语音快速响应 → 边合成边播放的完整链路。模块定位API 文档背后的流式 TTS 播放脚本在 docs/source/api/paddlespeech.t2s.exps.stream_play_tts.rst 中该模块以 Sphinxautomodule指令自动提取 docstring 的方式生成 API 文档被收录于paddlespeech.t2s.exps包见 paddlespeech.t2s.exps.rst与ort_predict、ort_predict_streaming、synthesize_streaming等推理脚本并列。它对应的唯一源码文件是 paddlespeech/t2s/exps/stream_play_tts.py属于 TTS 实验exps目录下的一个独立可执行脚本。该脚本的核心目标是演示流式语音合成 实时播放使用 ONNX Runtime 加载流式声学模型FastSpeech2 CNNDecoder与流式声码器MB-MelGAN将文本经中文前端paddlespeech/t2s/frontend/zh_frontend.py转换为音素 ID声学模型按块chunk推理出归一化梅尔谱声码器按块合成语音片段通过 PyAudio 边合成边播放实现类似流式输出的用户体验同时把所有片段拼接导出为demo_stream.wav文件。从源码结构看这个脚本是流式推理 播放的最小可运行样例它复用了paddlespeech.server.utils中的通用工具函数可作为独立脚本直接运行也是理解demos/streaming_tts_server服务端流式 TTS 原理的极佳起点。运行前准备依赖安装与模型下载脚本顶部注释明确要求首次执行前先在执行目录下载并解压模型涉及两个模型包wget https://paddlespeech.cdn.bcebos.com/Parakeet/released_models/fastspeech2/fastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0.zip wget https://paddlespeech.cdn.bcebos.com/Parakeet/released_models/mb_melgan/mb_melgan_csmsc_onnx_0.2.0.zip unzip fastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0.zip unzip mb_melgan_csmsc_onnx_0.2.0.zip两个模型包解压后分别包含文件作用fastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0/phone_id_map.txt音素到 ID 的映射表供前端 Frontend 加载fastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0/speech_stats.npy梅尔谱归一化统计量均值am_mu、标准差am_stdfastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0/fastspeech2_csmsc_am_encoder_infer.onnx声学模型编码器文本 → hidden statefastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0/fastspeech2_csmsc_am_decoder.onnx声学模型解码器hidden state → 归一化梅尔谱fastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0/fastspeech2_csmsc_am_postnet.onnx声学模型 Postnet 后处理网络mb_melgan_csmsc_onnx_0.2.0/mb_melgan_csmsc.onnxMB-MelGAN 流式声码器梅尔谱 → 波形脚本依赖的外部 Python 库包括numpy、onnxruntimeONNX 推理、pyaudio实时音频播放、soundfileWAV 落盘以及仓库内的paddlespeech.server.utils.audio_process、paddlespeech.server.utils.util与paddlespeech.t2s.frontend.zh_frontend模块。核心超参数块大小block与填充pad流式合成的关键是把长序列切块、逐块推理并拼回而块与块之间的上下文衔接依赖填充pad机制。脚本在 stream_play_tts.py 顶部定义了四个核心参数voc_block 36 # 声码器每次推理的梅尔帧数 voc_pad 14 # 声码器每块前后填充的梅尔帧数 am_block 72 # 声学模型每次推理的帧数 am_pad 12 # 声学模型每块前后填充的帧数 voc_upsample 300 # MB-MelGAN 的上采样倍数每帧梅尔谱对应 300 个采样点这些参数与流式 TTS 服务端配置完全一致。在 demos/streaming_tts_server/conf/tts_online_application.yaml 中tts_online引擎engine_type: online与tts_online-onnx引擎engine_type: online-onnx均配置了相同取值并且配置文件给出了权威注释可作为参数选择依据am_block: 72、am_pad: 12仅用于fastspeech2_cnndecoder系列模型做流式声学推理当am_pad设为 12 时流式合成的音频与非流式合成完全一致voc_block: 36、voc_pad: 14用于流式声码器推理当声码器为mb_melgan_csmsc时voc_pad设为 14 可与非流式结果一致最小值可降至 7 而听感正常当声码器换为hifigan_csmsc时voc_pad需设为 19 才能与非流式一致设为 14 则听感正常。这组参数直观体现了流式合成的质量-延迟权衡pad 越大块与块之间的上下文越完整、合成质量越接近非流式但首包等待与计算量也会相应增加。前端与模型加载从文本到音素、从 ONNX 文件到 Session中文前端 Frontend脚本使用Frontend(phone_vocab_pathphones_dict, tone_vocab_pathNone)构建中文前端其中phone_vocab_path指向模型包内的phone_id_map.txttone_vocab_path传None表示该流式模型不区分声调。前端类定义于 paddlespeech/t2s/frontend/zh_frontend.py其get_input_ids方法见同文件 L645-L697接收merge_sentences、get_tone_ids、robot、add_blank等参数内部先做文本正则化与字音转换得到音素序列再查表映射为phone_ids/tone_ids张量返回形如{phone_ids: [Tensor, ...]}的字典。脚本中的调用方式input_ids frontend.get_input_ids( text, merge_sentencesFalse, get_tone_idsFalse) phone_ids input_ids[phone_ids]merge_sentencesFalse表示按句子切分不合并get_tone_idsFalse表示不输出声调 ID——与前端初始化时tone_vocab_pathNone的设置保持一致最终取phone_ids作为声学模型编码器的输入。归一化统计量am_stat_path fastspeech2_cnndecoder_csmsc_streaming_onnx_1.0.0/speech_stats.npy am_mu, am_std np.load(am_stat_path)speech_stats.npy保存训练时梅尔谱的均值与标准差用于推理后将模型输出的归一化梅尔谱反归一化还原为真实梅尔谱对应下文denorm步骤。创建 ONNX Runtime Session脚本显式指定providers [CPUExecutionProvider]进行 CPU 推理并统一使用默认SessionOptions创建四个 Sessionam_encoder_infer_sess ort.InferenceSession(onnx_am_encoder, providersproviders, sess_optionssess_options) am_decoder_sess ort.InferenceSession(onnx_am_decoder, providersproviders, sess_optionssess_options) am_postnet_sess ort.InferenceSession(onnx_am_postnet, providersproviders, sess_optionssess_options) voc_melgan_sess ort.InferenceSession(onnx_voc_melgan, providersproviders, sess_optionssess_options)从源码结构看FastSpeech2 CNNDecoder 被拆分为 Encoder / Decoder / Postnet 三个 ONNX 子模型分别负责文本 → hidden state、hidden state → 归一化梅尔谱与梅尔谱精修三者串行调用构成完整的声学模型MB-MelGAN 则作为单独的声码器模型。流式推理核心逻辑inference_stream 生成器inference_stream(text)是一个 Python 生成器函数通过yield逐段输出语音波形这是边合成边播放的关键——主程序可以每拿到一个sub_wav立即写入音频设备而无需等待整段合成完毕。其完整流水线如下文本 → 音素 ID调用frontend.get_input_ids得到每句话的phone_idsAM Encoder 整句推理将整句音素 ID 一次送入am_encoder_infer_sess得到完整 hidden stateorig_hs形状为[1, mel_len, hidden]规划声码器块根据梅尔帧总数mel_len计算声码器分块数voc_chunk_num ceil(mel_len / voc_block)并初始化当前块的start/endAM 流式分块调用get_chunks(orig_hs, am_block, am_pad, am)把 hidden state 切成带 pad 的块逐块送入 Decoder 与 Postnet相加得到归一化梅尔谱再经denorm反归一化AM 去重叠对每个 AM 块调用depadding(...)去掉因 pad 引入的重叠帧拼接为完整或当前已合成部分的梅尔谱mel_streaming声码器流式触发当mel_streaming的帧数满足mel_streaming.shape[0] end且未达到voc_chunk_num时取mel_streaming[start:end, :]送入voc_melgan_sess对输出波形再执行一次depaddingyield出去滑动窗口推进voc_chunk_id 1并按start max(0, voc_chunk_id * voc_block - voc_pad)、end min((voc_chunk_id 1) * voc_block voc_pad, mel_len)更新窗口。通过上述AM 整句编码 分块解码 声码器按需消费的流水线Mel 谱一旦累积到声码器所需的块长度即可立刻合成并输出一段波形从而在整句文本尚未全部解码完成前就开始播放语音。去重叠函数 depadding 详解分块推理时每个块都会在首尾多算pad帧以保证上下文连续因此拼接前必须把重叠区域去掉。depadding 按块位置分三种情况处理def depadding(data, chunk_num, chunk_id, block, pad, upsample): front_pad min(chunk_id * block, pad) # 第一块只保留前 block 帧 if chunk_id 0: data data[:block * upsample] # 最后一块去掉前面累积的填充帧 elif chunk_id chunk_num - 1: data data[front_pad * upsample:] # 中间块去掉首尾填充只保留中间 block 帧 else: data data[front_pad * upsample:(front_pad block) * upsample] return data要点在于第chunk_id块的有效内容位于该块[chunk_id * block, (chunk_id 1) * block)区间块首的重叠量等于min(chunk_id * block, pad)前几块不足 pad 时重叠量更小upsample参数把帧维度换算为采样点维度——AM 阶段去重叠时upsample1处理梅尔帧声码器阶段去重叠时upsamplevoc_upsample300处理波形采样点。首块特殊处理是因为没有前文填充直接截取前block * upsample即可。声学模型分块get_chunks 工具函数AM 分块调用的是仓库通用工具 paddlespeech/server/utils/util.py 中的get_chunks(data, block_size, pad_size, step)def get_chunks(data, block_size, pad_size, step): if block_size -1: return [data] if step am: data_len data.shape[1] # 对 hidden state取时间维 elif step voc: data_len 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) if step am: chunks.append(data[:, start:end, :]) elif step voc: chunks.append(data[start:end, :]) return chunks该函数按block_size等分数据并在每个块前后各扩展pad_size帧首尾用max/min收窄step参数决定沿哪个维度切分以及返回的形状AM 阶段传入的三维张量按中间时间维切成[1, block2*pad, hidden]Voc 阶段按第一维切成[block2*pad, feat]。它同时也是流式 TTS 服务端分块的基础设施体现了分块-填充-去重叠这一流式推理通用范式的完整闭环。主程序warmup、实时播放、计时与导出脚本的__main__入口stream_play_tts.py演示了完整的端到端使用流程1. 预热Warmupfor sub_wav in inference_stream(text哈哈哈哈): continue源码注释明确说明onnxruntime 第一次时间会长一些建议先 warmup 一下。预热的意义在于让 ONNX Runtime 完成算子初始化与线程池预热避免首次推理耗时污染后续的响应时间统计。2. PyAudio 实时播放p pyaudio.PyAudio() stream p.open( formatp.get_format_from_width(2), # int16 channels1, rate24000, outputTrue)播放参数为单声道、采样率 24000 Hz、16-bit PCM。注意rate24000与voc_upsample 300的内在关系MB-MelGAN 每帧梅尔谱对应 300 个采样点若其输入梅尔谱帧率hop为 80 Hz则300 × 80 24000 Hz与播放采样率完全吻合——这是声码器输出能直接送声卡播放的前提。3. 逐块播放与计时t1 time.time() for sub_wav in inference_stream(text): print(响应时间, time.time() - t1) t1 time.time() wavs.append(sub_wav.flatten()) wav float2pcm(sub_wav) # float32 → int16 wav_bytes wav.tobytes() stream.write(wav_bytes) # 立即写入音频设备每拿到一个语音块就打印该块的响应时间、通过float2pcm转为 16-bit PCM 并立即写入声卡实现边合成边播放。float2pcm定义于 paddlespeech/server/utils/audio_process.py它要求输入为浮点数组先校验 dtype再按目标整数位深缩放并 clip 到有效范围默认目标类型int16返回值可直接tobytes()交给音频设备。4. 结果导出stream.stop_stream(); stream.close(); p.terminate() wav np.concatenate(wavs) print(wav.shape) sf.write(demo_stream.wav, datawav, samplerate24000)播放结束后将所有sub_wav拼接为完整波形用soundfile以 24000 Hz 采样率写为demo_stream.wav方便离线核对流式合成结果与整句合成是否一致配合前述am_pad12、voc_pad14的质量配置。归一化与反归一化denorm 的桥梁作用流式声学模型输出的是归一化梅尔谱训练时以(mel - mu) / std归一化因此推理后必须反归一化才能交给声码器。脚本调用仓库工具 paddlespeech/server/utils/util.py 中的denormdef denorm(data, mean, std): return data * std mean即mel normalized_mel * std mean其中mean/std来自前文加载的speech_stats.npy。该函数同样被流式 TTS 服务端引擎复用见paddlespeech/server/engine/tts/相关实现是模型输出 → 可听波形之间不可缺少的一环。与其他模块的关联从单机脚本到流式服务stream_play_tts.py并非孤立存在它与仓库内的流式 TTS 能力一脉相承paddlespeech/t2s/exps/ort_predict_streaming.py 提供了面向文件输出的 ONNX 流式推理脚本不含播放stream_play_tts可视为其实时播放版服务端流式 TTS 的配置 demos/streaming_tts_server/conf/tts_online_application.yaml 与 tts_online_ws_application.yaml 使用了完全相同的am_block / am_pad / voc_block / voc_pad参数体系脚本中写死的四个常量即是这些配置项的脚本内等价物分块工具get_chunks、反归一化denorm、浮点转 PCM 的float2pcm均来自 paddlespeech/server/utils说明该脚本与paddlespeech.server服务端共享同一套流式推理基础设施。因此理解stream_play_tts.py就等于掌握了 PaddleSpeech 流式 TTS 从模型推理到音频输出的最小核心链路无论是将其改造为 WebSocket 实时播放、嵌入式 TTS还是在此基础上做首包延迟优化都能以此为起点。小结流式 TTS 播放脚本的关键要点架构拆分FastSpeech2 CNNDecoder 以 Encoder / Decoder / Postnet 三个 ONNX 子模型串行推理MB-MelGAN 作为流式声码器独立消费梅尔谱块参数即质量开关am_pad12、voc_pad14可使流式结果与非流式一致voc_pad最小可降至 7 以换听感可接受的低延迟分块范式get_chunks负责带 pad 切块depadding负责去除重叠区AM 阶段upsample1、声码器阶段upsample300二者构成完整的流式拼接闭环实时体验生成器 PyAudio 边合成边播放24000 Hz 采样率与声码器上采样倍数严格对应首次推理建议 warmup 以消除初始化开销落盘验证所有音频块拼接后以 24000 Hz 写入demo_stream.wav便于质量对比与结果复现。如果需要在此基础上构建真正的流式 TTS 服务可直接参考 demos/streaming_tts_server 的在线/在线 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/gh_mirrors/pa/PaddleSpeech创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考