【飞桨PaddleSpeech语音技术课程】— 流式语音合成技术揭秘与实践

1 流式语音合成服务的场景与产业应用

语音合成(Speech Sysnthesis),又称文本转语音(Text-to-Speech, TTS),指的是将一段文本按照一定需求转化成对应的音频的技术。

语音合成分为非流式合成和流式合成,两者在实时性上有所不同。非流式语音合成,一次性输入文字,一次性输出语音,注重语音合成系统的 整体运算速度,不适合做语音交互;流式语音合成,可以对输入文本进行分词断句、声学模型和声码器局部合成语音特征和音频,分段传回合成的音频,这种语音合成方式主要关注 首包响应时间,首包响应时间越短,用户就会越快收到响应,用户等待时间减少,就不会因为等待回应而失去耐心,因此整体体验感更好,更适合作为语音交互场景的语音合成方案。

目前,语音合成系统分为云端语音合成和离线语音合成。云端语音合成主要配套端到端或多层神经网络算法,语音输出质量高、算力强,但实时性更差,不适于语音交互;近年来,离线语音合成算法和算力得到逐步更新,一些参数化的合成方案质量也可达到一定的水平,适合于合成语音的交互类场景。

2 语音合成的基本流程

本教程主要讲解基于深度学习的语音合成技术,流水线包含 文本前端(Text Frontend)声学模型(Acoustic Model)声码器(Vocoder) 三个主要模块:

  • 文本前端模块将原始文本转换为字符/音素
  • 声学模型将字符/音素转换为声学特征,如线性频谱图、mel 频谱图、LPC 特征等
  • 声码器将声学特征转换为波形

语音合成基本流程图

文本前端模块主要包含: 分段(Text Segmentation)、文本正则化(Text Normalization, TN)、分词(Word Segmentation, 主要是在中文中)、词性标注(Part-of-Speech, PoS)、韵律预测(Prosody)和字音转换(Grapheme-to-Phoneme,G2P)等。

其中最重要的模块是 文本正则化 模块和 字音转换(TTS 中更常用 G2P 代指) 模块。

各模块输出示例:

• Text: 全国一共有112所211高校
• Text Normalization: 全国一共有一百一十二所二一一高校
• Word Segmentation: 全国/一共/有/一百一十二/所/二一一/高校/
• G2P(注意此句中"一"的读音):
    quan2 guo2 yi2 gong4 you3 yi4 bai3 yi1 shi2 er4 suo3 er4 yao1 yao1 gao1 xiao4
    (可以进一步把声母和韵母分开)
    q uan2 g uo2 y i2 g ong4 y ou3 y i4 b ai3 y i1 sh i2 er4 s uo3 er4 y ao1 y ao1 g ao1 x iao4
    (把音调和声韵母分开)
    q uan g uo y i g ong y ou y i b ai y i sh i er s uo er y ao y ao g ao x iao
    0 2 0 2 0 2 0 4 0 3 ...

• Prosody (prosodic words #1, prosodic phrases #2, intonation phrases #3, sentence #4):
    全国#2一共有#2一百#1一十二所#2二一一#1高校#4
    (分词的结果一般是固定的,但是不同人习惯不同,可能有不同的韵律)

声学模型将字符/音素转换为声学特征,如线性频谱图、mel 频谱图、LPC 特征等。声学特征以 “帧” 为单位,一般一帧是 10ms 左右,一个音素一般对应 5~20 帧左右。声学模型需要解决的是 “不等长序列间的映射问题”,”不等长”是指,同一个人发不同音素的持续时间不同,同一个人在不同时刻说同一句话的语速可能不同,对应各个音素的持续时间不同,不同人说话的特色不同,对应各个音素的持续时间不同。这是一个困难的 “一对多” 问题。

卡尔普陪外孙玩滑梯
000001|baker_corpus|sil 20 k 12 a2 4 er2 10 p 12 u3 12 p 9 ei2 9 uai4 15 s 11 uen1 12 uan2 14 h 10 ua2 11 t 15 i1 16 sil 20

声学模型主要分为自回归模型和非自回归模型。自回归模型在 t 时刻的预测需要依赖 t-1 时刻的输出作为输入,预测时间长,但是音质相对较好;非自回归模型不存在预测上的依赖关系,预测时间快,音质相对较差。

主流声学模型:

  • 自回归模型: Tacotron、Tacotron2 和 Transformer TTS 等
  • 非自回归模型: FastSpeech、SpeedySpeech、FastPitch 和 FastSpeech2 等

声码器将声学特征转换为波形,它需要解决的是 “信息缺失的补全问题”。信息缺失是指,在音频波形转换为频谱图时,存在相位信息的缺失;在频谱图转换为 mel 频谱图时,存在频域压缩导致的信息缺失。假设音频的采样率是 16kHz, 即 1s 的音频有 16000 个采样点,一帧的音频有 10ms,则 1s 中包含 100 帧,每一帧有 160 个采样点。声码器的作用就是将一个频谱帧变成音频波形的 160 个采样点,所以声码器中一般会包含上采样模块。

与声学模型类似,声码器也分为自回归模型和非自回归模型:

  • 自回归模型:WaveNet、WaveRNN 和 LPCNet 等
  • 非自回归模型:Parallel WaveGAN、Multi Band MelGAN、Style MelGAN 和 HiFiGAN 等

3 流式语音合成模型设计

语音合成的基本流程如下:

流式语音合成要求语音合成系统的实时率(Real Time Factor) RTF < 1,即合成 1s 的音频所需的时间要小于 1s,否则无法达到实时地流式合成。

为了使得语音合成系统的 RTF < 1,PaddleSpeech 选择的声学模型和声码器都是速度更快的非自回归模型,本教程以 FastSpeech2 和 HiFiGAN 为例搭建流式语音合成系统。

  • 若训练过程中没有使用韵律特征,可以以产生 sp、sil 等静音段处(对应文本输入的标点符号位置)作为子句的划分标准。
  • 若训练过程中使用了韵律特征,可以以四级韵律划分为例(#1, #2,#3, #4),以产生 #4 处作为子句的划分标准。

PaddleSpeech TTS 流式推理按照标点符号,将长文本切为短文本,分句处理输入文本,在保证模型推理时间的前提下,还能防止因输入文本过长导致的语音效果不佳的问题(FastSpeech2 是 Transformer 结构,虽然有 Positional Embedding,但是若输入文本过长,末尾的音频质量会明显差于开头的音频质量)。

Tacotron2 模型结构图

Tacotron2 结构主要分为 Encoder 和 Decoder :

  • Encoder:
  • Character Embedding
  • ConvLayers
  • Bidirectional LSTM

Encoder 计算量小,合成速度快。

  • Decoder:
  • LSTM Layers
  • Layer PreNet
  • Linear Projection
  • Conv Layer PostNet

Decoder 计算量大,PreNet 逐帧输出,所有的帧计算完 concat 到一起,再输入到 PostNet。

Tacotron2 模型在进行前向计算时,主要时间消耗在 Decoder 部分,流式合成系统在 Decoder 部分进行流式计算,对 Mel 进行逐帧输出。

流式合成思路:

方案一:修改模型结构,砍掉 PostNet。

  • PreNet 与 PostNet 使用的是相同的 Loss,PreNet 逐帧输出,累积到一定帧长后,输入到 Vocoder
  • 代价是合成的效果相对于原版会有所下降,需要考虑在实际测试过程,这个代价是否可以接受

方案二:多级缓存。

  • PreNet 输出的结果,先进入 Buffer 缓存,在达到了一定的长度之后,再输入到 PostNet
  • 进入 PostNet 前对特征添加一定长度的 padding,可保持流式推理与非流式推理的结果一致

此处我们主要介绍 方案二

流式合成步骤:

计算流程图如下图所示:

Tacotron2 流式合成结构图

FastSpeech2 模型由 Phoneme Embedding、Encoder、Variance adaptor 和 Decoder 等几个部分组成。其前向计算主要耗时集中在 Decoder 部分,因此我们选择对 Decoder 部分进行流式计算。

FastSpeech2 模型结构图

FastSpeech2 Encoder 和 Decoder 都是使用 FFT Block,FFT Block 中的 Multi-Head Attention 是全局依赖的,无法直接通过 chunk 的方式进行流式合成。

FFT Block 结构图

流式合成思路:

方案一: 用基于局部感受野的 Attention 替换依赖全局感受野的 Attention

方案二: 更换 FFT Block,用 局部感受野的模块替换,如,以 Covn1D 为主体的模块

方案一

常见于基于 Transformer/Conformer 结构的流式 ASR 结构。

将 Decoder 的 Attention 更换成基于 chunk 的 attention,chunk 内部不会依赖于右侧的数值;同时将 Conv1D 结构更换成因果卷积(Causal Convolution),避免右侧上下文对计算的影响。基于 chunk 的 Attention 有多种选择方式,既可以是固定大小的 chunk,也可以是动态大小;可以只关注自己 chunk 内的数值,也可以只关注从开头到自己 chunk 内的数值。 训练时使用基于动态 chunk 的 attention 结构,在推理过程中,可以直接使用切片后的 Encoder Feature 输入 Decoder 模块中解码,计算结果与整体合成的结果保持一致。

方案二

将 Decoder 的 FFT Block 替换成 Conv1D Residual Block(不限于图中结构),其模型结构如下图所示:

Conv1D Residual Block 结构图

Conv1D Residual Block 结构并不会依赖于整个序列,而是依赖于局部序列,因此 Decoder 部分可以通过切片的方式进行流式合成,只要保证对局部的 chunk 输入,padding 足够多的前后信息,就可以使拼接起来的局部输出与输入完整信息得到的输出在数值上一致。

流式合成步骤如下:

计算流程图如下图所示:

FastSpeech2 流式合成结构图

声码器流式合成以 HiFiGAN 模型为例进行说明。基于 GAN 的声码器流式合成的原理与 FastSpeech2 流式合成的方案二类似,因为 GAN Vocoder 的生成器主要是由卷积块组成的,只要保证对局部的 chunk 输入,padding 足够多的前后信息,就可以使拼接起来的局部输出与输入完整信息得到的输出在数值上一致。

基于 GAN 的 Vocoder 模型主体结构分为两个部分,生成器(Generator)与判别器(Discriminator)。

在推理过程中仅需要使用生成器模块,生成器主体部分由 Conv1D、Conv1DTranspose 和 Residual Block 等模块组成。

HiFiGAN 生成器结构图

语音合成的推理过程与 Vocoder 的判别器无关。

HiFiGAN 判别器结构图

声码器流式合成时,Mel Spectrogram(图中简写 M)通过 Vocoder 的生成器模块计算得到对应的 Wave(图中简写 W)。

声码器流式合成步骤如下:

计算流程图如下图所示:

Vocoder 流式合成结构图

不同模块 padding 的大小因 感受野不同而变化,目前 PaddleSpeech 提供的模型配置,与整体计算数值一致的 padding 值:

  • FastSpeech2_CNNDecoder: 12
  • Multi Band MelGAN: 14
  • HiFiGAN: 19

实际应用中,可根据推理速度上的需求,在不影响合成效果的前提下,适当降低 padding 值。

4 基于 ONNXRuntime 的语音合成推理引擎

ONNX 是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的深度学习框架(如 PaddlePaddle 、Pytorch、TensorFlow 等)可以采用相同格式存储模型数据。是一种便于在各个主流深度学习框架中迁移模型的中间表达格式。

ONNXRuntime 是微软推出的一款推理框架,用户可以非常便利的用其运行 ONNX 模型,它支持多种运行后端包括 CPU、GPU、TensorRT 和 DML 等。把深度学习框架训练好的模型转换成 ONNX 格式的模型,再利用 ONNXRuntime 引擎进行推理,会达到比用该深度学习框架原生推理引擎更快的速度,一般的交互式硬件只有质量一般的 CPU,并没有 GPU,利用 ONNXRuntime-CPU,可以多线程地运行神经网络推理流程, 大大加快语音合成模型的推理速度,更好地满足流式语音合成的要求。

ONNX 模型的通用性,使开发者可以把模型替换成在其他深度学习框架训练好的模型,而不需要对流式语音合成服务的代码进行大的修改。 极大地提升了语音合成服务代码的复用性

使用 ONNXRuntime 进行流式推理,需要经过如下步骤:

Paddle 模型实现 ONNXRuntime 推理流程图

PaddlePaddle 在模型开发时,推荐采用动态图编程。 可获得更好的编程体验、更易用的接口、更友好的调试交互机制。

run.shstage0 数据预处理和 stage1 训练

if [ ${stage} -le 0 ] && [ ${stop_stage} -ge 0 ]; then

    ./local/preprocess.sh ${conf_path} || exit -1
fi

if [ ${stage} -le 1 ] && [ ${stop_stage} -ge 1 ]; then

    CUDA_VISIBLE_DEVICES=${gpus} ./local/train.sh ${conf_path} ${train_output_path} || exit -1
fi

若要使用 PaddleInference 推理引擎或者将 Paddle 模型转换成通用的 ONNX 模型,需要对 Paddle 的动态图模型进行动转静。

run.shstage3

if [ ${stage} -le 3 ] && [ ${stop_stage} -ge 3 ]; then

    CUDA_VISIBLE_DEVICES=${gpus} ./local/synthesize_e2e.sh ${conf_path} ${train_output_path} ${ckpt_name} || exit -1
fi

run_cnndecoder.shstage3(完整推理) 和 stage5(流式推理)


if [ ${stage} -le 3 ] && [ ${stop_stage} -ge 3 ]; then

    CUDA_VISIBLE_DEVICES=${gpus} ./local/synthesize_e2e.sh ${conf_path} ${train_output_path} ${ckpt_name} || exit -1
fi

if [ ${stage} -le 5 ] && [ ${stop_stage} -ge 5 ]; then

    CUDA_VISIBLE_DEVICES=${gpus} ./local/synthesize_streaming.sh ${conf_path} ${train_output_path} ${ckpt_name} || exit -1
fi

注意,FastSpeech2_CNNDecoder 用于流式合成时,在动转静时需要导出 3 个静态模型,分别是:

静态图推理的目的是保证动转静导出的模型没问题,使用 PaddleInference 引擎进行推理。

run.shstage4

if [ ${stage} -le 4 ] && [ ${stop_stage} -ge 4 ]; then

    CUDA_VISIBLE_DEVICES=${gpus} ./local/inference.sh ${train_output_path} || exit -1
fi

run_cnndecoder.shstage4(完整推理) 和 stage6(流式推理)


if [ ${stage} -le 4 ] && [ ${stop_stage} -ge 4 ]; then

    CUDA_VISIBLE_DEVICES=${gpus} ./local/inference.sh ${train_output_path} || exit -1
fi

if [ ${stage} -le 6 ] && [ ${stop_stage} -ge 6 ]; then

    CUDA_VISIBLE_DEVICES=${gpus} ./local/inference_streaming.sh ${train_output_path} || exit -1
fi

run.shstage5


if [ ${stage} -le 5 ] && [ ${stop_stage} -ge 5 ]; then

    version=$(echo pip list |grep "paddle2onnx" |awk -F" " '{print $2}')
    if [[ -z "$version" || ${version} != '0.9.5' ]]; then
        pip install paddle2onnx==0.9.5
    fi
    ./local/paddle2onnx.sh ${train_output_path} inference inference_onnx fastspeech2_csmsc
    ./local/paddle2onnx.sh ${train_output_path} inference inference_onnx hifigan_csmsc
    ./local/paddle2onnx.sh ${train_output_path} inference inference_onnx mb_melgan_csmsc
fi

run_cnndecoder.shstage7(完整推理) 和 stage9(流式推理)


if [ ${stage} -le 7 ] && [ ${stop_stage} -ge 7 ]; then

    version=$(echo pip list |grep "paddle2onnx" |awk -F" " '{print $2}')
    if [[ -z "$version" || ${version} != '0.9.5' ]]; then
        pip install paddle2onnx==0.9.5
    fi
    ./local/paddle2onnx.sh ${train_output_path} inference inference_onnx fastspeech2_csmsc
    ./local/paddle2onnx.sh ${train_output_path} inference inference_onnx hifigan_csmsc
fi

if [ ${stage} -le 9 ] && [ ${stop_stage} -ge 9 ]; then

    version=$(echo pip list |grep "paddle2onnx" |awk -F" " '{print $2}')
    if [[ -z "$version" || ${version} != '0.9.5' ]]; then
        pip install paddle2onnx==0.9.5
    fi

    ./local/paddle2onnx.sh ${train_output_path} inference_streaming inference_onnx_streaming fastspeech2_csmsc_am_encoder_infer
    ./local/paddle2onnx.sh ${train_output_path} inference_streaming inference_onnx_streaming fastspeech2_csmsc_am_decoder
    ./local/paddle2onnx.sh ${train_output_path} inference_streaming inference_onnx_streaming fastspeech2_csmsc_am_postnet

    ./local/paddle2onnx.sh ${train_output_path} inference_streaming inference_onnx_streaming hifigan_csmsc
fi

导出 ONNX 格式的模型后,就可以用 ONNX 引擎或者 ONNXRuntime 进行推理啦,此处我们选择 ONNXRuntime 作为推理引擎。采用 ONNXRuntime 作为推理引擎,FastSpeech2 + HIFIGAN 即使在低压 CPU 上也可以达到实时,满足流式合成的要求。

run.shstage6


if [ ${stage} -le 6 ] && [ ${stop_stage} -ge 6 ]; then
    ./local/ort_predict.sh ${train_output_path}
fi

run_cnndecoder.shstage8(完整推理) 和 stage10(流式推理)


if [ ${stage} -le 8 ] && [ ${stop_stage} -ge 8 ]; then
    ./local/ort_predict.sh ${train_output_path}
fi

if [ ${stage} -le 10 ] && [ ${stop_stage} -ge 10 ]; then
    ./local/ort_predict_streaming.sh ${train_output_path}
fi

5 语音合成服务部署

启动及访问服务的流程如下图所示:

非流式 TTS 服务的启动与访问流程图(左:启动服务流程;右:访问服务流程)

启动服务步骤:

访问服务步骤:

首先我们需要准备一个服务相关的 yaml 配置文件(application.yaml),配置文件内容如下:

host: 0.0.0.0                # server ip
port: 8090                   # server port

protocol: 'http'             # only support http
engine_list: ['tts_python']  # speech task_engine type, can choose tts_python or tts_inference.

################### speech task: tts; engine_type: python #######################
tts_python:
    # am (acoustic model) choices=['speedyspeech_csmsc', 'fastspeech2_csmsc', 'fastspeech2_ljspeech', 'fastspeech2_aishell3', 'fastspeech2_vctk']
    am: 'fastspeech2_csmsc'
    am_config:
    am_ckpt:
    am_stat:
    phones_dict:
    tones_dict:
    speaker_dict:

    # voc (vocoder) choices=['pwgan_csmsc', 'pwgan_ljspeech', 'pwgan_aishell3', 'pwgan_vctk', 'mb_melgan_csmsc']
    voc: 'pwgan_csmsc'
    voc_config:
    voc_ckpt:
    voc_stat:

    # others
    lang: 'zh'
    device:                   # set 'gpu:id' or 'cpu'

################### speech task: tts; engine_type: inference #######################
tts_inference:
    # am (acoustic model) choices=['speedyspeech_csmsc', 'fastspeech2_csmsc']
    am: 'fastspeech2_csmsc'
    am_model: # the pdmodel file of your am static model (XX.pdmodel)
    am_params: # the pdiparams file of your am static model (XX.pdipparams)
    am_sample_rate: 24000
    phones_dict:
    tones_dict:
    speaker_dict:

    am_predictor_conf:
        device:               # set 'gpu:id' or 'cpu'
        switch_ir_optim: True
        glog_info: False      # True -> print glog
        summary: True         # False -> do not show predictor config

    # voc (vocoder) choices=['pwgan_csmsc', 'mb_melgan_csmsc','hifigan_csmsc']
    voc: 'pwgan_csmsc'
    voc_model:                # the pdmodel file of your vocoder static model (XX.pdmodel)
    voc_params:               # the pdiparams file of your vocoder static model (XX.pdipparams)
    voc_sample_rate: 24000

    voc_predictor_conf:
        device:               # set 'gpu:id' or 'cpu'
        switch_ir_optim: True
        glog_info: False      # True -> print glog
        summary: True         # False -> do not show predictor config

    # others
    lang: 'zh'

服务的配置文件(application.yaml)准备好后,执行下述命令可一键启动服务:

paddlespeech_server start --config_file application.yaml

显示如下表示启动服务成功:

[2022-02-23 11:17:32] [INFO] [server.py:64] Started server process [6384]
INFO:     Waiting for application startup.

[2022-02-23 11:17:32] [INFO] [on.py:26] Waiting for application startup.

INFO:     Application startup complete.

[2022-02-23 11:17:32] [INFO] [on.py:38] Application startup complete.

INFO:     Uvicorn running on http://0.0.0.0:8090 (Press CTRL+C to quit)
[2022-02-23 11:17:32] [INFO] [server.py:204] Uvicorn running on http://0.0.0.0:8090 (Press CTRL+C to quit)

使用如下指令可直接访问非流式 TTS 服务:

paddlespeech_client tts --server_ip 127.0.0.1 --port 8090 --input "您好,欢迎使用百度飞桨语音合成服务。" --output output.wav

结果显示如下表示访问服务成功:

[2022-02-23 15:20:37,875] [    INFO] - {'description': 'success.'}
[2022-02-23 15:20:37,875] [    INFO] - Save synthesized audio successfully on output.wav.

[2022-02-23 15:20:37,875] [    INFO] - Audio duration: 3.612500 s.

[2022-02-23 15:20:37,875] [    INFO] - Response time: 0.348050 s.

url: http://127.0.0.1:8090/paddlespeech/tts
请求方式:POST
请求示例如下,该示例包含所有字段:

{
    "text": "您好,欢迎使用百度飞桨语音合成服务。",
    "spk_id": 0,
    "speed": 1.0,
    "volume": 1.0,
    "sample_rate": 0,
    "save_path": "./output.wav",
}

成功返回示例如下:

{
    "success": true,
    "code": 0,
    "message": {"global": "success" }
    "result": {
        "lang": "zh",
        "spk_id": 0,
        "speed": 1.0,
        "volume": 1.0,
        "sample_rate": 24000,
        "duration": 3.6125,
        "save_path": "./output.wav",
        "audio": "LTI1OTIuNjI1OTUwMzQsOTk2OS41NDk4..."
    }
}

流式 TTS 服务支持 http 和 webscoket 两种协议。

http 支持流式返回,可以满足目前的流式 TTS 的方案,即请求一次,返回流式数据,响应返回结束会自动断开连接。

而 websocket 支持双工,适用于需要长连接的场景,也可应用于目前的流式 TTS 的方案,可以在一次连接中请求多次,相比 http 请求多次而言,可减少建立连接的次数。

除此之外,流式 ASR 使用的是 websocket 协议,使用 webscoket 协议启动服务,可以同时启动包含流式 ASR 和流式 TTS 的服务。

启动流式 TTS 服务的整体流程与启动非流式 TTS 服务的整体流程一致。

访问流式 TTS 服务流程如下图所示:

访问流式 TTS 服务流程图(左:访问 http 服务;右:访问 websocket 服务)

访问 http 流式服务步骤:

访问 websocket 流式服务步骤:

无论是 http 协议还是 websocket 协议,启动服务都需要有服务配置 yaml 文件(tts_online_application.yaml)。

若想启动 http 协议的服务,将 protocol 设置为 http ;若想启动 websocket 协议的服务,将 protocol 设置为 websocket,配置文件内容如下:

host: 0.0.0.0
port: 8092

protocol: 'http'
engine_list: ['tts_online-onnx']

################### speech task: tts; engine_type: online #######################
tts_online:
    # am (acoustic model) choices=['fastspeech2_csmsc', 'fastspeech2_cnndecoder_csmsc']
    am: 'fastspeech2_csmsc'
    am_config:
    am_ckpt:
    am_stat:
    phones_dict:
    tones_dict:
    speaker_dict:

    # voc (vocoder) choices=['mb_melgan_csmsc, hifigan_csmsc']
    voc: 'mb_melgan_csmsc'
    voc_config:
    voc_ckpt:
    voc_stat:

    # others
    lang: 'zh'
    device: 'cpu' # set 'gpu:id' or 'cpu'
    am_block: 72
    am_pad: 12
    voc_block: 36
    voc_pad: 14

################### speech task: tts; engine_type: online-onnx #######################
tts_online-onnx:
    # am (acoustic model) choices=['fastspeech2_csmsc_onnx', 'fastspeech2_cnndecoder_csmsc_onnx']
    am: 'fastspeech2_cnndecoder_csmsc_onnx'
    # am_ckpt is a list, if am is fastspeech2_cnndecoder_csmsc_onnx, am_ckpt = [encoder model, decoder model, postnet model];
    # if am is fastspeech2_csmsc_onnx, am_ckpt = [ckpt model];
    am_ckpt:          # list
    am_stat:
    phones_dict:
    tones_dict:
    speaker_dict:
    spk_id: 0
    am_sample_rate: 24000
    am_sess_conf:
        device: "cpu" # set 'gpu:id' or 'cpu'
        use_trt: False
        cpu_threads: 4

    # voc (vocoder) choices=['mb_melgan_csmsc_onnx, hifigan_csmsc_onnx']
    voc: 'hifigan_csmsc_onnx'
    voc_ckpt:
    voc_sample_rate: 24000
    voc_sess_conf:
        device: "cpu" # set 'gpu:id' or 'cpu'
        use_trt: False
        cpu_threads: 4

    # others
    lang: 'zh'
    am_block: 72
    am_pad: 12
    voc_block: 36
    voc_pad: 14
    voc_upsample: 300

服务的配置文件(application.yaml)准备好后,执行下述命令可一键启动服务:

paddlespeech_server start --config_file tts_online_application.yaml

显示如下表示启动服务成功:

[2022-04-24 21:00:17] [INFO] [server.py:75] Started server process [320]
INFO:     Waiting for application startup.

[2022-04-24 21:00:17] [INFO] [on.py:45] Waiting for application startup.

INFO:     Application startup complete.

[2022-04-24 21:00:17] [INFO] [on.py:59] Application startup complete.

INFO:     Uvicorn running on http://0.0.0.0:8092 (Press CTRL+C to quit)
[2022-04-24 21:00:17] [INFO] [server.py:211] Uvicorn running on http://0.0.0.0:8092 (Press CTRL+C to quit)

使用如下指令可一键式访问流式 TTS 服务:

访问 http 流式 TTS 服务 (–protocol http)

paddlespeech_client tts_online --server_ip 127.0.0.1 --port 8090 --protocol http --input "您好,欢迎使用百度飞桨语音合成服务。" --output output.wav

访问 websocket 流式 TTS 服务 (–protocol websocket)

paddlespeech_client tts_online --server_ip 127.0.0.1 --port 8090 --protocol websocket --input "您好,欢迎使用百度飞桨语音合成服务。" --output output.wav

结果显示如下表示访问服务成功:

[2022-04-24 21:08:21,702] [    INFO] - 句子:您好,欢迎使用百度飞桨语音合成服务。
[2022-04-24 21:08:21,703] [    INFO] - 首包响应:0.18863153457641602 s
[2022-04-24 21:08:21,704] [    INFO] - 尾包响应:3.1427218914031982 s
[2022-04-24 21:08:21,704] [    INFO] - 音频时长:3.825 s
[2022-04-24 21:08:21,704] [    INFO] - RTF: 0.8216266382753459
[2022-04-24 21:08:21,739] [    INFO] - 音频保存至:output.wav

  • 访问 http 流式 TTS 服务
    url: http://127.0.0.1:8092/paddlespeech/tts/stareaming
    请求方式:POST
    请求示例如下,该示例包含所有字段(目前流式 TTS 服务只使用到 text 字段,其他字段暂不生效):
{
    "text": "您好,欢迎使用百度飞桨语音合成服务。",
    "spk_id": 0,
    "speed": 1.0,
    "volume": 1.0,
    "sample_rate": 0,
    "save_path": "./output.wav",
}

返回为音频经过 base64 编码的 string,如下:

LTI1OTIuNjI1OTUwMzQsOTk2OS41NDk4...

  • 访问 websocket 流式 TTS 服务
    url: ws://127.0.0.1:8092/paddlespeech/tts/stareaming
{
    "task": "tts",
    "signal": "start"
}

成功响应开始请求的示例如下,其中 status 为 0 表示可以开始发送请求,signal 表示 server 是否准备好, session 为 40 位的随机字符串,用来表示此次连接的编号。

{
    "status": 0,
    "signal": "server ready",
    "session": "UloVFXg3xjb2nIP6xH58Ms8G98vnA1thHL6snKOy"
}

注意:流式 http request 直接传文本,websocket 传 base64 的 string,具体原因会在 FAQ 部分解释。

{
    "text": "5oKo5aW977yM5qyi6L+O5L2/55So55m+5bqm6aOe5qGo6K+t6Z+z5ZCI5oiQ5pyN5Yqh44CC",
}

成功响应示例如下,其中 status 字段表示音频片段是否为最后一段,status 为 2 表示该音频片段为最后一片段,status 为 1 表示该音频片段不是最后一片段;audio 字段表示音频片段经过 base64 编码后的 string。

{
    "status": 1,
    "audio": "LTI1OTIuNjI1OTUwMzQsOTk2OS41NDk4...",
    "session": "UloVFXg3xjb2nIP6xH58Ms8G98vnA1thHL6snKOy"
}
{
    "task": "tts",
    "signal": "end",
    "session": "UloVFXg3xjb2nIP6xH58Ms8G98vnA1thHL6snKOy",
}

成功响应示例如下:

{
    "status": 0,
    "signal": "connection will be closed",
    "session": "UloVFXg3xjb2nIP6xH58Ms8G98vnA1thHL6snKOy"
}

下例中,server 端和 client 端均运行在低配 Windows10 笔记本上,机器配置如下:

Python 版本:3.8.3

Paddle 版本:2.3.0rc

PaddleSpeech 版本:1.0.0

ONNXRuntime 版本:1.10.0

CPU:

  • Intel® Core™ i5-8250U CPU @ 1.60GHz
  • cpu 核数:4
  • 逻辑 cpu (线程):8

内存:8G

1. 为什么流式 http request 直接传文本,websocket 传 base64 的 string ?

因为 http 流式服务直接复用了非流式 TTS 服务的请求类,非流式请求类直接传文本是为了使请求看上去比较直观(直接显示文本),如果想改成传 base64 的 string 也可以(改下代码就行)。websocket 是考虑到网络中传 base64 可能会更快些,也可以直接传文本。(总体而言,传哪种类型并不是那么重要)

2. 为什么流式 TTS 服务既支持 http 协议又支持 websocket 协议 ?

http 支持流式返回,可以满足我们流式 TTS 的需求,比 websocket 建立连接要简单,比较直观,响应返回结束后会自动断开连接。

而 websocket 支持双工,可以一直建立连接,因此它不仅能支持目前的流式(一开始传入的就是确定好的文本,然后模型支持流式),也能支持分段输入的文本进行流式合成,例如一段文本返回一个响应(这样的情况模型可以不支持流式)。并且 websocket 可以支持流式语音识别,因此在我们的框架下,使用 websocket 协议,可以同时启动流式语音合成和流式语音识别的服务。

3. http 流式和 websocket 流式的区别和利弊 ?

http 适用于单次请求访问,每次请求之前都需要建立连接,相比 websocket 更直观,简洁,无需发送 start 请求和 end 请求。

websocket 适用于多次请求访问,它可以更好地支持长连接,相比 http 的多次请求而言,可以减少建立连接的次数,同时流式 ASR 也使用了 websocket 协议,因此使用 websocket 协议可以在一个服务中同时启动两个语音任务。仅针对流式 TTS 单请求而言,使用 http 和 websocket 差别不大。

Original: https://blog.csdn.net/qq_21275321/article/details/127619474
Author: 小湉湉
Title: 【飞桨PaddleSpeech语音技术课程】— 流式语音合成技术揭秘与实践

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/720484/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球