parent
1a3c811f04
commit
82992b3ed6
@ -0,0 +1,100 @@
|
|||||||
|
009901 昨日,这名伤者与医生全部被警方依法刑事拘留。
|
||||||
|
009902 钱伟长想到上海来办学校是经过深思熟虑的。
|
||||||
|
009903 她见我一进门就骂,吃饭时也骂,骂得我抬不起头。
|
||||||
|
009904 李述德在离开之前,只说了一句柱驼杀父亲了。
|
||||||
|
009905 这种车票和保险单捆绑出售属于重复性购买。
|
||||||
|
009906 戴佩妮的男友西米露接唱情歌,让她非常开心。
|
||||||
|
009907 观大势,谋大局,出大策始终是该院的办院方针。
|
||||||
|
009908 他们骑着摩托回家,正好为农忙时的父母帮忙。
|
||||||
|
009909 但是因为还没到退休年龄,只能掰着指头捱日子。
|
||||||
|
009910 这几天雨水不断,人们恨不得待在家里不出门。
|
||||||
|
009911 没想到徐赟,张海翔两人就此玩起了人间蒸发。
|
||||||
|
009912 藤村此番发言可能是为了凸显野田的领导能力。
|
||||||
|
009913 程长庚,生在清王朝嘉庆年间,安徽的潜山小县。
|
||||||
|
009914 南海海域综合补给基地码头项目正在论证中。
|
||||||
|
009915 也就是说今晚成都市民极有可能再次看到飘雪。
|
||||||
|
009916 随着天气转热,各地的游泳场所开始人头攒动。
|
||||||
|
009917 更让徐先生纳闷的是,房客的手机也打不通了。
|
||||||
|
009918 遇到颠簸时,应听从乘务员的安全指令,回座位坐好。
|
||||||
|
009919 他在后面呆惯了,怕自己一插身后的人会不满,不敢排进去。
|
||||||
|
009920 傍晚七个小人回来了,白雪公主说,你们就是我命中的七个小矮人吧。
|
||||||
|
009921 他本想说,教育局管这个,他们是一路的,这样一管岂不是妓女起嫖客?
|
||||||
|
009922 一种表示商品所有权的财物证券,也称商品证券,如提货单,交货单。
|
||||||
|
009923 会有很丰富的东西留下来,说都说不完。
|
||||||
|
009924 这句话像从天而降,吓得四周一片寂静。
|
||||||
|
009925 记者所在的是受害人家属所在的右区。
|
||||||
|
009926 不管哈大爷去哪,它都一步不离地跟着。
|
||||||
|
009927 大家抬头望去,一只老鼠正趴在吊顶上。
|
||||||
|
009928 我决定过年就辞职,接手我爸的废品站!
|
||||||
|
009929 最终,中国男子乒乓球队获得此奖项。
|
||||||
|
009930 防汛抗旱两手抓,抗旱相对抓的不够。
|
||||||
|
009931 图们江下游地区开发开放的进展如何?
|
||||||
|
009932 这要求中国必须有一个坚强的政党领导。
|
||||||
|
009933 再说,关于利益上的事俺俩都不好开口。
|
||||||
|
009934 明代瓦剌,鞑靼入侵明境也是通过此地。
|
||||||
|
009935 咪咪舔着孩子,把它身上的毛舔干净。
|
||||||
|
009936 是否这次的国标修订被大企业绑架了?
|
||||||
|
009937 判决后,姚某妻子胡某不服,提起上诉。
|
||||||
|
009938 由此可以看出邯钢的经济效益来自何处。
|
||||||
|
009939 琳达说,是瑜伽改变了她和马儿的生活。
|
||||||
|
009940 楼下的保安告诉记者,这里不租也不卖。
|
||||||
|
009941 习近平说,中斯两国人民传统友谊深厚。
|
||||||
|
009942 传闻越来越多,后来连老汉儿自己都怕了。
|
||||||
|
009943 我怒吼一声冲上去,举起砖头砸了过去。
|
||||||
|
009944 我现在还不会,这就回去问问发明我的人。
|
||||||
|
009945 显然,洛阳性奴案不具备上述两个前提。
|
||||||
|
009946 另外,杰克逊有文唇线,眼线,眉毛的动作。
|
||||||
|
009947 昨晚,华西都市报记者电话采访了尹琪。
|
||||||
|
009948 涅拉季科未透露这些航空公司的名称。
|
||||||
|
009949 从运行轨迹上来说,它也不可能是星星。
|
||||||
|
009950 目前看,如果继续加息也存在两难问题。
|
||||||
|
009951 曾宝仪在节目录制现场大爆观众糗事。
|
||||||
|
009952 但任凭周某怎么叫,男子仍酣睡不醒。
|
||||||
|
009953 老大爷说,小子,你挡我财路了,知道不?
|
||||||
|
009954 没料到,闯下大头佛的阿伟还不知悔改。
|
||||||
|
009955 卡扎菲部落式统治已遭遇部落内讧。
|
||||||
|
009956 这个孩子的生命一半来源于另一位女士捐赠的冷冻卵子。
|
||||||
|
009957 出现这种泥鳅内阁的局面既是野田有意为之,也实属无奈。
|
||||||
|
009958 济青高速济南,华山,章丘,邹平,周村,淄博,临淄站。
|
||||||
|
009959 赵凌飞的话,反映了沈阳赛区所有奥运志愿者的共同心声。
|
||||||
|
009960 因为,我们所发出的力量必会因难度加大而减弱。
|
||||||
|
009961 发生事故的楼梯拐角处仍可看到血迹。
|
||||||
|
009962 想过进公安,可能身高不够,老汉儿也不让我进去。
|
||||||
|
009963 路上关卡很多,为了方便撤离,只好轻装前进。
|
||||||
|
009964 原来比尔盖茨就是美国微软公司联合创始人呀。
|
||||||
|
009965 之后他们一家三口将与双方父母往峇里岛旅游。
|
||||||
|
009966 谢谢总理,也感谢广大网友的参与,我们明年再见。
|
||||||
|
009967 事实上是,从来没有一个欺善怕恶的人能作出过稍大一点的成就。
|
||||||
|
009968 我会打开邮件,你可以从那里继续。
|
||||||
|
009969 美方对近期东海局势表示关切。
|
||||||
|
009970 据悉,奥巴马一家人对这座冬季白宫极为满意。
|
||||||
|
009971 打扫完你会很有成就感的,试一试,你就信了。
|
||||||
|
009972 诺曼站在滑板车上,各就各位,准备出发啦!
|
||||||
|
009973 塔河的寒夜,气温降到了零下三十多摄氏度。
|
||||||
|
009974 其间,连破六点六,六点五,六点四,六点三五等多个重要关口。
|
||||||
|
009975 算命其实只是人们的一种自我安慰和自我暗示而已,我们还是要相信科学才好。
|
||||||
|
009976 这一切都令人欢欣鼓舞,阿讷西没理由不坚持到最后。
|
||||||
|
009977 直至公元前一万一千年,它又再次出现。
|
||||||
|
009978 尽量少玩电脑,少看电视,少打游戏。
|
||||||
|
009979 从五到七,前后也就是六个月的时间。
|
||||||
|
009980 一进咖啡店,他就遇见一张熟悉的脸。
|
||||||
|
009981 好在众弟兄看到了把她追了回来。
|
||||||
|
009982 有一个人说,哥们儿我们跑过它才能活。
|
||||||
|
009983 捅了她以后,模糊记得她没咋动了。
|
||||||
|
009984 从小到大,葛启义没有收到过压岁钱。
|
||||||
|
009985 舞台下的你会对舞台上的你说什么?
|
||||||
|
009986 但考生普遍认为,试题的怪多过难。
|
||||||
|
009987 我希望每个人都能够尊重我们的隐私。
|
||||||
|
009988 漫天的红霞使劲给两人增添气氛。
|
||||||
|
009989 晚上加完班开车回家,太累了,迷迷糊糊开着车,走一半的时候,铛一声!
|
||||||
|
009990 该车将三人撞倒后,在大雾中逃窜。
|
||||||
|
009991 这人一哆嗦,方向盘也把不稳了,差点撞上了高速边道护栏。
|
||||||
|
009992 那女孩儿委屈的说,我一回头见你已经进去了我不敢进去啊!
|
||||||
|
009993 小明摇摇头说,不是,我只是美女看多了,想换个口味而已。
|
||||||
|
009994 接下来,红娘要求记者交费,记者表示不知表姐身份证号码。
|
||||||
|
009995 李东蓊表示,自己当时在法庭上发表了一次独特的公诉意见。
|
||||||
|
009996 另一男子扑了上来,手里拿着明晃晃的长刀,向他胸口直刺。
|
||||||
|
009997 今天,快递员拿着一个快递在办公室喊,秦王是哪个,有他快递?
|
||||||
|
009998 这场抗议活动究竟是如何发展演变的,又究竟是谁伤害了谁?
|
||||||
|
009999 因华国锋肖鸡,墓地设计根据其属相设计。
|
||||||
|
010000 在狱中,张明宝悔恨交加,写了一份忏悔书。
|
@ -0,0 +1,64 @@
|
|||||||
|
model_path=/home/users/liangyunming/.paddlespeech/models/
|
||||||
|
#am_model_dir=$model_path/fastspeech2_csmsc-zh/fastspeech2_nosil_baker_ckpt_0.4/ ## fastspeech2
|
||||||
|
am_model_dir=$model_path/fastspeech2_csmsc-zh/fastspeech2_cnndecoder_csmsc_ckpt_1.0.0/ ## fastspeech2_cnn
|
||||||
|
voc_model_dir=$model_path/hifigan_csmsc-zh/hifigan_csmsc_ckpt_0.1.1/ ## hifigan
|
||||||
|
#voc_model_dir=$model_path/mb_melgan_csmsc-zh/mb_melgan_csmsc_ckpt_0.1.1/ ## mb_melgan
|
||||||
|
|
||||||
|
if [[ $am_model_dir == *"fastspeech2_cnndecoder"* ]]; then
|
||||||
|
am_support_stream=True
|
||||||
|
else
|
||||||
|
am_support_stream=False
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get am file
|
||||||
|
for file in $(ls $am_model_dir)
|
||||||
|
do
|
||||||
|
if [[ $file == *"yaml"* ]]; then
|
||||||
|
am_config_file=$file
|
||||||
|
elif [[ $file == *"pdz"* ]]; then
|
||||||
|
am_ckpt_file=$file
|
||||||
|
elif [[ $file == *"stat"* ]]; then
|
||||||
|
am_stat_file=$file
|
||||||
|
elif [[ $file == *"phone"* ]]; then
|
||||||
|
phones_dict_file=$file
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
# get voc file
|
||||||
|
for file in $(ls $voc_model_dir)
|
||||||
|
do
|
||||||
|
if [[ $file == *"yaml"* ]]; then
|
||||||
|
voc_config_file=$file
|
||||||
|
elif [[ $file == *"pdz"* ]]; then
|
||||||
|
voc_ckpt_file=$file
|
||||||
|
elif [[ $file == *"stat"* ]]; then
|
||||||
|
voc_stat_file=$file
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
#run
|
||||||
|
python test_online_tts.py --am fastspeech2_csmsc \
|
||||||
|
--am_support_stream $am_support_stream \
|
||||||
|
--am_config $am_model_dir/$am_config_file \
|
||||||
|
--am_ckpt $am_model_dir/$am_ckpt_file \
|
||||||
|
--am_stat $am_model_dir/$am_stat_file \
|
||||||
|
--phones_dict $am_model_dir/$phones_dict_file \
|
||||||
|
--voc hifigan_csmsc \
|
||||||
|
--voc_config $voc_model_dir/$voc_config_file \
|
||||||
|
--voc_ckpt $voc_model_dir/$voc_ckpt_file \
|
||||||
|
--voc_stat $voc_model_dir/$voc_stat_file \
|
||||||
|
--lang zh \
|
||||||
|
--device cpu \
|
||||||
|
--text ./csmsc_test.txt \
|
||||||
|
--output_dir ./output \
|
||||||
|
--log_file ./result.log \
|
||||||
|
--am_streaming False \
|
||||||
|
--am_pad 12 \
|
||||||
|
--am_block 42 \
|
||||||
|
--voc_streaming True \
|
||||||
|
--voc_pad 14 \
|
||||||
|
--voc_block 14 \
|
||||||
|
|
@ -0,0 +1,650 @@
|
|||||||
|
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import paddle
|
||||||
|
import soundfile as sf
|
||||||
|
import yaml
|
||||||
|
from yacs.config import CfgNode
|
||||||
|
|
||||||
|
from paddlespeech.s2t.utils.dynamic_import import dynamic_import
|
||||||
|
from paddlespeech.t2s.exps.syn_utils import get_am_inference
|
||||||
|
from paddlespeech.t2s.exps.syn_utils import get_frontend
|
||||||
|
from paddlespeech.t2s.exps.syn_utils import get_sentences
|
||||||
|
from paddlespeech.t2s.exps.syn_utils import get_voc_inference
|
||||||
|
from paddlespeech.t2s.exps.syn_utils import model_alias
|
||||||
|
from paddlespeech.t2s.utils import str2bool
|
||||||
|
|
||||||
|
mel_streaming = None
|
||||||
|
wav_streaming = None
|
||||||
|
stream_first_time = 0.0
|
||||||
|
voc_stream_st = 0.0
|
||||||
|
sample_rate = 0
|
||||||
|
|
||||||
|
|
||||||
|
def denorm(data, mean, std):
|
||||||
|
return data * std + mean
|
||||||
|
|
||||||
|
|
||||||
|
def get_chunks(data, block_size, pad_size, step):
|
||||||
|
if step == "am":
|
||||||
|
data_len = data.shape[1]
|
||||||
|
elif step == "voc":
|
||||||
|
data_len = data.shape[0]
|
||||||
|
else:
|
||||||
|
print("Please set correct type to get chunks, am or voc")
|
||||||
|
|
||||||
|
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, :])
|
||||||
|
else:
|
||||||
|
print("Please set correct type to get chunks, am or voc")
|
||||||
|
return chunks
|
||||||
|
|
||||||
|
|
||||||
|
def get_stream_am_inference(args, am_config):
|
||||||
|
with open(args.phones_dict, "r") as f:
|
||||||
|
phn_id = [line.strip().split() for line in f.readlines()]
|
||||||
|
vocab_size = len(phn_id)
|
||||||
|
print("vocab_size:", vocab_size)
|
||||||
|
|
||||||
|
am_name = args.am[:args.am.rindex('_')]
|
||||||
|
am_dataset = args.am[args.am.rindex('_') + 1:]
|
||||||
|
odim = am_config.n_mels
|
||||||
|
|
||||||
|
am_class = dynamic_import(am_name, model_alias)
|
||||||
|
am = am_class(idim=vocab_size, odim=odim, **am_config["model"])
|
||||||
|
am.set_state_dict(paddle.load(args.am_ckpt)["main_params"])
|
||||||
|
am.eval()
|
||||||
|
am_mu, am_std = np.load(args.am_stat)
|
||||||
|
am_mu = paddle.to_tensor(am_mu)
|
||||||
|
am_std = paddle.to_tensor(am_std)
|
||||||
|
|
||||||
|
return am, am_mu, am_std
|
||||||
|
|
||||||
|
|
||||||
|
def init(args):
|
||||||
|
global sample_rate
|
||||||
|
# get config
|
||||||
|
with open(args.am_config) as f:
|
||||||
|
am_config = CfgNode(yaml.safe_load(f))
|
||||||
|
with open(args.voc_config) as f:
|
||||||
|
voc_config = CfgNode(yaml.safe_load(f))
|
||||||
|
|
||||||
|
sample_rate = am_config.fs
|
||||||
|
|
||||||
|
# frontend
|
||||||
|
frontend = get_frontend(args)
|
||||||
|
|
||||||
|
# acoustic model
|
||||||
|
if args.am_support_stream:
|
||||||
|
am, am_mu, am_std = get_stream_am_inference(args, am_config)
|
||||||
|
am_infer_info = [am, am_mu, am_std, am_config]
|
||||||
|
else:
|
||||||
|
am_inference, am_name, am_dataset = get_am_inference(args, am_config)
|
||||||
|
am_infer_info = [am_inference, am_name, am_dataset, am_config]
|
||||||
|
|
||||||
|
# vocoder
|
||||||
|
voc_inference = get_voc_inference(args, voc_config)
|
||||||
|
voc_infer_info = [voc_inference, voc_config]
|
||||||
|
|
||||||
|
return frontend, am_infer_info, voc_infer_info
|
||||||
|
|
||||||
|
|
||||||
|
def get_phone(args, frontend, sentence, merge_sentences, get_tone_ids):
|
||||||
|
am_name = args.am[:args.am.rindex('_')]
|
||||||
|
tone_ids = None
|
||||||
|
if am_name == 'speedyspeech':
|
||||||
|
get_tone_ids = True
|
||||||
|
|
||||||
|
if args.lang == 'zh':
|
||||||
|
input_ids = frontend.get_input_ids(
|
||||||
|
sentence,
|
||||||
|
merge_sentences=merge_sentences,
|
||||||
|
get_tone_ids=get_tone_ids)
|
||||||
|
phone_ids = input_ids["phone_ids"]
|
||||||
|
if get_tone_ids:
|
||||||
|
tone_ids = input_ids["tone_ids"]
|
||||||
|
elif args.lang == 'en':
|
||||||
|
input_ids = frontend.get_input_ids(
|
||||||
|
sentence, merge_sentences=merge_sentences)
|
||||||
|
phone_ids = input_ids["phone_ids"]
|
||||||
|
else:
|
||||||
|
print("lang should in {'zh', 'en'}!")
|
||||||
|
|
||||||
|
return phone_ids, tone_ids
|
||||||
|
|
||||||
|
|
||||||
|
@paddle.no_grad()
|
||||||
|
# 生成完整的mel
|
||||||
|
def gen_mel(args, am_infer_info, part_phone_ids, part_tone_ids):
|
||||||
|
# 如果是支持流式的AM模型
|
||||||
|
if args.am_support_stream:
|
||||||
|
am, am_mu, am_std, am_config = am_infer_info
|
||||||
|
orig_hs, h_masks = am.encoder_infer(part_phone_ids)
|
||||||
|
if args.am_streaming:
|
||||||
|
am_pad = args.am_pad
|
||||||
|
am_block = args.am_block
|
||||||
|
hss = get_chunks(orig_hs, am_block, am_pad, "am")
|
||||||
|
chunk_num = len(hss)
|
||||||
|
mel_list = []
|
||||||
|
for i, hs in enumerate(hss):
|
||||||
|
before_outs, _ = am.decoder(hs)
|
||||||
|
after_outs = before_outs + am.postnet(
|
||||||
|
before_outs.transpose((0, 2, 1))).transpose((0, 2, 1))
|
||||||
|
normalized_mel = after_outs[0]
|
||||||
|
sub_mel = denorm(normalized_mel, am_mu, am_std)
|
||||||
|
# clip output part of pad
|
||||||
|
if i == 0:
|
||||||
|
sub_mel = sub_mel[:-am_pad]
|
||||||
|
elif i == chunk_num - 1:
|
||||||
|
# 最后一块的右侧一定没有 pad 够
|
||||||
|
sub_mel = sub_mel[am_pad:]
|
||||||
|
else:
|
||||||
|
# 倒数几块的右侧也可能没有 pad 够
|
||||||
|
sub_mel = sub_mel[am_pad:(am_block + am_pad) -
|
||||||
|
sub_mel.shape[0]]
|
||||||
|
mel_list.append(sub_mel)
|
||||||
|
mel = paddle.concat(mel_list, axis=0)
|
||||||
|
|
||||||
|
else:
|
||||||
|
orig_hs, h_masks = am.encoder_infer(part_phone_ids)
|
||||||
|
before_outs, _ = am.decoder(orig_hs)
|
||||||
|
after_outs = before_outs + am.postnet(
|
||||||
|
before_outs.transpose((0, 2, 1))).transpose((0, 2, 1))
|
||||||
|
normalized_mel = after_outs[0]
|
||||||
|
mel = denorm(normalized_mel, am_mu, am_std)
|
||||||
|
|
||||||
|
else:
|
||||||
|
am_inference, am_name, am_dataset, am_config = am_infer_info
|
||||||
|
# acoustic model
|
||||||
|
if am_name == 'fastspeech2':
|
||||||
|
# multi speaker
|
||||||
|
if am_dataset in {"aishell3", "vctk"}:
|
||||||
|
spk_id = paddle.to_tensor(args.spk_id)
|
||||||
|
mel = am_inference(part_phone_ids, spk_id)
|
||||||
|
else:
|
||||||
|
mel = am_inference(part_phone_ids)
|
||||||
|
elif am_name == 'speedyspeech':
|
||||||
|
part_tone_ids = tone_ids[i]
|
||||||
|
if am_dataset in {"aishell3", "vctk"}:
|
||||||
|
spk_id = paddle.to_tensor(args.spk_id)
|
||||||
|
mel = am_inference(part_phone_ids, part_tone_ids, spk_id)
|
||||||
|
else:
|
||||||
|
mel = am_inference(part_phone_ids, part_tone_ids)
|
||||||
|
elif am_name == 'tacotron2':
|
||||||
|
mel = am_inference(part_phone_ids)
|
||||||
|
|
||||||
|
return mel
|
||||||
|
|
||||||
|
|
||||||
|
@paddle.no_grad()
|
||||||
|
def stream_voc_infer(args, voc_infer_info, mel_len):
|
||||||
|
global mel_streaming
|
||||||
|
global stream_first_time
|
||||||
|
global wav_streaming
|
||||||
|
voc_inference, voc_config = voc_infer_info
|
||||||
|
block = args.voc_block
|
||||||
|
pad = args.voc_pad
|
||||||
|
upsample = voc_config.n_shift
|
||||||
|
wav_list = []
|
||||||
|
flag = 1
|
||||||
|
|
||||||
|
valid_start = 0
|
||||||
|
valid_end = min(valid_start + block, mel_len)
|
||||||
|
actual_start = 0
|
||||||
|
actual_end = min(valid_end + pad, mel_len)
|
||||||
|
mel_chunk = mel_streaming[actual_start:actual_end, :]
|
||||||
|
|
||||||
|
while valid_end <= mel_len:
|
||||||
|
sub_wav = voc_inference(mel_chunk)
|
||||||
|
if flag == 1:
|
||||||
|
stream_first_time = time.time()
|
||||||
|
flag = 0
|
||||||
|
|
||||||
|
# get valid wav
|
||||||
|
start = valid_start - actual_start
|
||||||
|
if valid_end == mel_len:
|
||||||
|
sub_wav = sub_wav[start * upsample:]
|
||||||
|
wav_list.append(sub_wav)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
end = start + block
|
||||||
|
sub_wav = sub_wav[start * upsample:end * upsample]
|
||||||
|
wav_list.append(sub_wav)
|
||||||
|
|
||||||
|
# generate new mel chunk
|
||||||
|
valid_start = valid_end
|
||||||
|
valid_end = min(valid_start + block, mel_len)
|
||||||
|
if valid_start - pad < 0:
|
||||||
|
actual_start = 0
|
||||||
|
else:
|
||||||
|
actual_start = valid_start - pad
|
||||||
|
actual_end = min(valid_end + pad, mel_len)
|
||||||
|
mel_chunk = mel_streaming[actual_start:actual_end, :]
|
||||||
|
|
||||||
|
wav = paddle.concat(wav_list, axis=0)
|
||||||
|
wav_streaming = wav
|
||||||
|
|
||||||
|
|
||||||
|
@paddle.no_grad()
|
||||||
|
# 非流式AM / 流式AM + 非流式Voc
|
||||||
|
def am_nostream_voc(args, am_infer_info, voc_infer_info, part_phone_ids,
|
||||||
|
part_tone_ids):
|
||||||
|
mel = gen_mel(args, am_infer_info, part_phone_ids, part_tone_ids)
|
||||||
|
am_infer_time = time.time()
|
||||||
|
voc_inference, voc_config = voc_infer_info
|
||||||
|
wav = voc_inference(mel)
|
||||||
|
first_response_time = time.time()
|
||||||
|
final_response_time = first_response_time
|
||||||
|
voc_infer_time = first_response_time
|
||||||
|
|
||||||
|
return am_infer_time, voc_infer_time, first_response_time, final_response_time, wav
|
||||||
|
|
||||||
|
|
||||||
|
@paddle.no_grad()
|
||||||
|
# 非流式AM + 流式Voc
|
||||||
|
def nostream_am_stream_voc(args, am_infer_info, voc_infer_info, part_phone_ids,
|
||||||
|
part_tone_ids):
|
||||||
|
global mel_streaming
|
||||||
|
global stream_first_time
|
||||||
|
global wav_streaming
|
||||||
|
|
||||||
|
mel = gen_mel(args, am_infer_info, part_phone_ids, part_tone_ids)
|
||||||
|
am_infer_time = time.time()
|
||||||
|
|
||||||
|
# voc streaming
|
||||||
|
mel_streaming = mel
|
||||||
|
mel_len = mel.shape[0]
|
||||||
|
stream_voc_infer(args, voc_infer_info, mel_len)
|
||||||
|
first_response_time = stream_first_time
|
||||||
|
wav = wav_streaming
|
||||||
|
final_response_time = time.time()
|
||||||
|
voc_infer_time = final_response_time
|
||||||
|
|
||||||
|
return am_infer_time, voc_infer_time, first_response_time, final_response_time, wav
|
||||||
|
|
||||||
|
|
||||||
|
@paddle.no_grad()
|
||||||
|
# 流式AM + 流式 Voc
|
||||||
|
def stream_am_stream_voc(args, am_infer_info, voc_infer_info, part_phone_ids,
|
||||||
|
part_tone_ids):
|
||||||
|
global mel_streaming
|
||||||
|
global stream_first_time
|
||||||
|
global wav_streaming
|
||||||
|
global voc_stream_st
|
||||||
|
mel_streaming = None
|
||||||
|
flag = 1 #用来表示开启流式voc的线程
|
||||||
|
|
||||||
|
am, am_mu, am_std, am_config = am_infer_info
|
||||||
|
orig_hs, h_masks = am.encoder_infer(part_phone_ids)
|
||||||
|
mel_len = orig_hs.shape[1]
|
||||||
|
am_block = args.am_block
|
||||||
|
am_pad = args.am_pad
|
||||||
|
hss = get_chunks(orig_hs, am_block, am_pad, "am")
|
||||||
|
chunk_num = len(hss)
|
||||||
|
|
||||||
|
for i, hs in enumerate(hss):
|
||||||
|
before_outs, _ = am.decoder(hs)
|
||||||
|
after_outs = before_outs + am.postnet(
|
||||||
|
before_outs.transpose((0, 2, 1))).transpose((0, 2, 1))
|
||||||
|
normalized_mel = after_outs[0]
|
||||||
|
sub_mel = denorm(normalized_mel, am_mu, am_std)
|
||||||
|
# clip output part of pad
|
||||||
|
if i == 0:
|
||||||
|
sub_mel = sub_mel[:-am_pad]
|
||||||
|
mel_streaming = sub_mel
|
||||||
|
elif i == chunk_num - 1:
|
||||||
|
# 最后一块的右侧一定没有 pad 够
|
||||||
|
sub_mel = sub_mel[am_pad:]
|
||||||
|
mel_streaming = paddle.concat([mel_streaming, sub_mel])
|
||||||
|
am_infer_time = time.time()
|
||||||
|
else:
|
||||||
|
# 倒数几块的右侧也可能没有 pad 够
|
||||||
|
sub_mel = sub_mel[am_pad:(am_block + am_pad) - sub_mel.shape[0]]
|
||||||
|
mel_streaming = paddle.concat([mel_streaming, sub_mel])
|
||||||
|
|
||||||
|
if flag and mel_streaming.shape[0] > args.voc_block + args.voc_pad:
|
||||||
|
t = threading.Thread(
|
||||||
|
target=stream_voc_infer, args=(args, voc_infer_info, mel_len, ))
|
||||||
|
t.start()
|
||||||
|
voc_stream_st = time.time()
|
||||||
|
flag = 0
|
||||||
|
|
||||||
|
t.join()
|
||||||
|
final_response_time = time.time()
|
||||||
|
voc_infer_time = final_response_time
|
||||||
|
first_response_time = stream_first_time
|
||||||
|
wav = wav_streaming
|
||||||
|
|
||||||
|
return am_infer_time, voc_infer_time, first_response_time, final_response_time, wav
|
||||||
|
|
||||||
|
|
||||||
|
def try_infer(args, logger, frontend, am_infer_info, voc_infer_info):
|
||||||
|
global sample_rate
|
||||||
|
logger.info(
|
||||||
|
"Before the formal test, we test a few texts to make the inference speed more stable."
|
||||||
|
)
|
||||||
|
if args.lang == 'zh':
|
||||||
|
sentence = "您好,欢迎使用语音合成服务。"
|
||||||
|
if args.lang == 'en':
|
||||||
|
sentence = "Hello and welcome to the speech synthesis service."
|
||||||
|
|
||||||
|
if args.voc_streaming:
|
||||||
|
if args.am_streaming:
|
||||||
|
infer_func = stream_am_stream_voc
|
||||||
|
else:
|
||||||
|
infer_func = nostream_am_stream_voc
|
||||||
|
else:
|
||||||
|
infer_func = am_nostream_voc
|
||||||
|
|
||||||
|
merge_sentences = True
|
||||||
|
get_tone_ids = False
|
||||||
|
for i in range(3): # 推理3次
|
||||||
|
st = time.time()
|
||||||
|
phone_ids, tone_ids = get_phone(args, frontend, sentence,
|
||||||
|
merge_sentences, get_tone_ids)
|
||||||
|
part_phone_ids = phone_ids[0]
|
||||||
|
if tone_ids:
|
||||||
|
part_tone_ids = tone_ids[0]
|
||||||
|
else:
|
||||||
|
part_tone_ids = None
|
||||||
|
|
||||||
|
am_infer_time, voc_infer_time, first_response_time, final_response_time, wav = infer_func(
|
||||||
|
args, am_infer_info, voc_infer_info, part_phone_ids, part_tone_ids)
|
||||||
|
wav = wav.numpy()
|
||||||
|
duration = wav.size / sample_rate
|
||||||
|
logger.info(
|
||||||
|
f"sentence: {sentence}; duration: {duration} s; first response time: {first_response_time - st} s; final response time: {final_response_time - st} s"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate(args, logger, frontend, am_infer_info, voc_infer_info):
|
||||||
|
global sample_rate
|
||||||
|
sentences = get_sentences(args)
|
||||||
|
|
||||||
|
output_dir = Path(args.output_dir)
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
get_tone_ids = False
|
||||||
|
merge_sentences = True
|
||||||
|
|
||||||
|
# choose infer function
|
||||||
|
if args.voc_streaming:
|
||||||
|
if args.am_streaming:
|
||||||
|
infer_func = stream_am_stream_voc
|
||||||
|
else:
|
||||||
|
infer_func = nostream_am_stream_voc
|
||||||
|
else:
|
||||||
|
infer_func = am_nostream_voc
|
||||||
|
|
||||||
|
final_up_duration = 0.0
|
||||||
|
sentence_count = 0
|
||||||
|
front_time_list = []
|
||||||
|
am_time_list = []
|
||||||
|
voc_time_list = []
|
||||||
|
first_response_list = []
|
||||||
|
final_response_list = []
|
||||||
|
sentence_length_list = []
|
||||||
|
duration_list = []
|
||||||
|
|
||||||
|
for utt_id, sentence in sentences:
|
||||||
|
# front
|
||||||
|
front_st = time.time()
|
||||||
|
phone_ids, tone_ids = get_phone(args, frontend, sentence,
|
||||||
|
merge_sentences, get_tone_ids)
|
||||||
|
part_phone_ids = phone_ids[0]
|
||||||
|
if tone_ids:
|
||||||
|
part_tone_ids = tone_ids[0]
|
||||||
|
else:
|
||||||
|
part_tone_ids = None
|
||||||
|
front_et = time.time()
|
||||||
|
front_time = front_et - front_st
|
||||||
|
|
||||||
|
am_st = time.time()
|
||||||
|
am_infer_time, voc_infer_time, first_response_time, final_response_time, wav = infer_func(
|
||||||
|
args, am_infer_info, voc_infer_info, part_phone_ids, part_tone_ids)
|
||||||
|
am_time = am_infer_time - am_st
|
||||||
|
if args.voc_streaming and args.am_streaming:
|
||||||
|
voc_time = voc_infer_time - voc_stream_st
|
||||||
|
else:
|
||||||
|
voc_time = voc_infer_time - am_infer_time
|
||||||
|
|
||||||
|
first_response = first_response_time - front_st
|
||||||
|
final_response = final_response_time - front_st
|
||||||
|
|
||||||
|
wav = wav.numpy()
|
||||||
|
duration = wav.size / sample_rate
|
||||||
|
sf.write(
|
||||||
|
str(output_dir / (utt_id + ".wav")), wav, samplerate=sample_rate)
|
||||||
|
print(f"{utt_id} done!")
|
||||||
|
|
||||||
|
sentence_count += 1
|
||||||
|
front_time_list.append(front_time)
|
||||||
|
am_time_list.append(am_time)
|
||||||
|
voc_time_list.append(voc_time)
|
||||||
|
first_response_list.append(first_response)
|
||||||
|
final_response_list.append(final_response)
|
||||||
|
sentence_length_list.append(len(sentence))
|
||||||
|
duration_list.append(duration)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"uttid: {utt_id}; sentence: '{sentence}'; front time: {front_time} s; am time: {am_time} s; voc time: {voc_time} s; \
|
||||||
|
first response time: {first_response} s; final response time: {final_response} s; audio duration: {duration} s;"
|
||||||
|
)
|
||||||
|
|
||||||
|
if final_response > duration:
|
||||||
|
final_up_duration += 1
|
||||||
|
|
||||||
|
all_time_sum = sum(final_response_list)
|
||||||
|
front_rate = sum(front_time_list) / all_time_sum
|
||||||
|
am_rate = sum(am_time_list) / all_time_sum
|
||||||
|
voc_rate = sum(voc_time_list) / all_time_sum
|
||||||
|
rtf = all_time_sum / sum(duration_list)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"The length of test text information, test num: {sentence_count}; text num: {sum(sentence_length_list)}; min: {min(sentence_length_list)}; max: {max(sentence_length_list)}; avg: {sum(sentence_length_list)/len(sentence_length_list)}"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"duration information, min: {min(duration_list)}; max: {max(duration_list)}; avg: {sum(duration_list) / len(duration_list)}; sum: {sum(duration_list)}"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Front time information: min: {min(front_time_list)} s; max: {max(front_time_list)} s; avg: {sum(front_time_list)/len(front_time_list)} s; ratio: {front_rate * 100}%"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"AM time information: min: {min(am_time_list)} s; max: {max(am_time_list)} s; avg: {sum(am_time_list)/len(am_time_list)} s; ratio: {am_rate * 100}%"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Vocoder time information: min: {min(voc_time_list)} s, max: {max(voc_time_list)} s; avg: {sum(voc_time_list)/len(voc_time_list)} s; ratio: {voc_rate * 100}%"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"first response time information: min: {min(first_response_list)} s; max: {max(first_response_list)} s; avg: {sum(first_response_list)/len(first_response_list)} s"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"final response time information: min: {min(final_response_list)} s; max: {max(final_response_list)} s; avg: {sum(final_response_list)/len(final_response_list)} s"
|
||||||
|
)
|
||||||
|
logger.info(f"RTF is: {rtf}")
|
||||||
|
logger.info(
|
||||||
|
f"The number of final_response is greater than duration is {final_up_duration}, ratio: {final_up_duration / sentence_count}%"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
# parse args and config and redirect to train_sp
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Synthesize with acoustic model & vocoder")
|
||||||
|
# acoustic model
|
||||||
|
parser.add_argument(
|
||||||
|
'--am',
|
||||||
|
type=str,
|
||||||
|
default='fastspeech2_csmsc',
|
||||||
|
choices=[
|
||||||
|
'speedyspeech_csmsc', 'speedyspeech_aishell3', 'fastspeech2_csmsc',
|
||||||
|
'fastspeech2_ljspeech', 'fastspeech2_aishell3', 'fastspeech2_vctk',
|
||||||
|
'tacotron2_csmsc', 'tacotron2_ljspeech'
|
||||||
|
],
|
||||||
|
help='Choose acoustic model type of tts task.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--am_support_stream',
|
||||||
|
type=str2bool,
|
||||||
|
default=False,
|
||||||
|
help='if am model is fastspeech2_csmsc, specify whether it supports streaming'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--am_config',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='Config of acoustic model. Use deault config when it is None.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--am_ckpt',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='Checkpoint file of acoustic model.')
|
||||||
|
parser.add_argument(
|
||||||
|
"--am_stat",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="mean and standard deviation used to normalize spectrogram when training acoustic model."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--phones_dict", type=str, default=None, help="phone vocabulary file.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--tones_dict", type=str, default=None, help="tone vocabulary file.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--speaker_dict", type=str, default=None, help="speaker id map file.")
|
||||||
|
parser.add_argument(
|
||||||
|
'--spk_id',
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help='spk id for multi speaker acoustic model')
|
||||||
|
# vocoder
|
||||||
|
parser.add_argument(
|
||||||
|
'--voc',
|
||||||
|
type=str,
|
||||||
|
default='mb_melgan_csmsc',
|
||||||
|
choices=[
|
||||||
|
'pwgan_csmsc', 'pwgan_ljspeech', 'pwgan_aishell3', 'pwgan_vctk',
|
||||||
|
'mb_melgan_csmsc', 'style_melgan_csmsc', 'hifigan_csmsc',
|
||||||
|
'wavernn_csmsc'
|
||||||
|
],
|
||||||
|
help='Choose vocoder type of tts task.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--voc_config',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='Config of voc. Use deault config when it is None.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--voc_ckpt', type=str, default=None, help='Checkpoint file of voc.')
|
||||||
|
parser.add_argument(
|
||||||
|
"--voc_stat",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="mean and standard deviation used to normalize spectrogram when training voc."
|
||||||
|
)
|
||||||
|
# other
|
||||||
|
parser.add_argument(
|
||||||
|
'--lang',
|
||||||
|
type=str,
|
||||||
|
default='zh',
|
||||||
|
choices=['zh', 'en'],
|
||||||
|
help='Choose model language. zh or en')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--device", type=str, default='cpu', help="set cpu or gpu:id")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--text",
|
||||||
|
type=str,
|
||||||
|
default="./csmsc_test.txt",
|
||||||
|
help="text to synthesize, a 'utt_id sentence' pair per line.")
|
||||||
|
parser.add_argument("--output_dir", type=str, help="output dir.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--log_file", type=str, default="result.log", help="log file.")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--am_streaming",
|
||||||
|
type=str2bool,
|
||||||
|
default=False,
|
||||||
|
help="whether use streaming acoustic model")
|
||||||
|
|
||||||
|
parser.add_argument("--am_pad", type=int, default=12, help="am pad size.")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--am_block", type=int, default=42, help="am block size.")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--voc_streaming",
|
||||||
|
type=str2bool,
|
||||||
|
default=False,
|
||||||
|
help="whether use streaming vocoder model")
|
||||||
|
|
||||||
|
parser.add_argument("--voc_pad", type=int, default=14, help="voc pad size.")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--voc_block", type=int, default=14, help="voc block size.")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
paddle.set_device(args.device)
|
||||||
|
if args.am_support_stream:
|
||||||
|
assert (args.am == 'fastspeech2_csmsc')
|
||||||
|
if args.am_streaming:
|
||||||
|
assert (args.am_support_stream and args.am == 'fastspeech2_csmsc')
|
||||||
|
if args.voc_streaming:
|
||||||
|
assert (args.voc == 'mb_melgan_csmsc' or args.voc == 'hifigan_csmsc')
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
fhandler = logging.FileHandler(filename=args.log_file, mode='w')
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
'%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'
|
||||||
|
)
|
||||||
|
fhandler.setFormatter(formatter)
|
||||||
|
logger.addHandler(fhandler)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# set basic information
|
||||||
|
logger.info(
|
||||||
|
f"AM: {args.am}; Vocoder: {args.voc}; device: {args.device}; am streaming: {args.am_streaming}; voc streaming: {args.voc_streaming}"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"am pad size: {args.am_pad}; am block size: {args.am_block}; voc pad size: {args.voc_pad}; voc block size: {args.voc_block};"
|
||||||
|
)
|
||||||
|
|
||||||
|
# get information about model
|
||||||
|
frontend, am_infer_info, voc_infer_info = init(args)
|
||||||
|
logger.info(
|
||||||
|
"************************ try infer *********************************")
|
||||||
|
try_infer(args, logger, frontend, am_infer_info, voc_infer_info)
|
||||||
|
logger.info(
|
||||||
|
"************************ normal test *******************************")
|
||||||
|
evaluate(args, logger, frontend, am_infer_info, voc_infer_info)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in new issue