吕声辉,飞桨开发者技能专家(PPDE),某网络科技公司研发工程师。首要研讨方向为图像识别,自然语言处理等。
AI Studio主页
aistudio.baidu.com/aistudio/pe…

自制有声书阅读器:用PaddleSpeech打开读书新方式

项目布景

随着互联网的发展,普通用户对于书本展现形式的需求已由纯文字变成了图文、语音、视频等多种形式,因此将文本书本转换为有声读物具有很大的市场需求。本文以飞桨语音模型库PaddleSpeech供给的语音组成技能为核心,经过音色克隆、语速设置、音量调整等附加功用,展现有声书本的技能可行计划。

自制有声书阅读器:用PaddleSpeech打开读书新方式

  • 终究出现效果如

player.bilibili.com/player.html?bvid=BV1x84y1V7SR

  • 网页体验拜访地址

book.weixin12306.com/

自制有声书阅读器:用PaddleSpeech打开读书新方式
环境预备

PaddleSpeech 是根据飞桨的语音方向开源模型库,用于语音和音频中的各种关键使命的开发,包含许多根据深度学习的前沿和有影响力的模型。首先进行PaddleSpeech装置环境的装备,装备如下:

#留意假如之前运转过这步下次就不用再运转了,这个目录重启项目也不会清空的
#下载解压说话人编码器
!wget-Pdatahttps://bj.bcebos.com/paddlespeech/Parakeet/released_models/ge2e/ge2e_ckpt_0.3.zip
!unzip-o-dworkdata/ge2e_ckpt_0.3.zip
#下载解压声码器
!wget-Pdatahttps://paddlespeech.bj.bcebos.com/Parakeet/released_models/pwgan/pwg_aishell3_ckpt_0.5.zip
!unzip-o-dworkdata/pwg_aishell3_ckpt_0.5.zip
#下载解压声学模型
!wget-Pdatahttps://paddlespeech.bj.bcebos.com/Parakeet/released_models/fastspeech2/fastspeech2_nosil_aishell3_vc1_ckpt_0.5.zip
!unzip-o-dworkdata/fastspeech2_nosil_aishell3_vc1_ckpt_0.5.zip
#下载解压nltk包
!wget-Pdatahttps://paddlespeech.bj.bcebos.com/Parakeet/tools/nltk_data.tar.gz
!tarzxvfdata/nltk_data.tar.gz
#装置PaddleSpeech
!pipinstallpytest-runner
!pipinstallpaddlespeech
#将nltk_data拷贝到/home/aistudio目录
!cp-r/home/aistudio/work/nltk_data/home/aistudio
#装置moviepy
!pipinstallmoviepy==1.0.3
数据处理

每本书的内容均以json格式存放在txt文本中,路径为

/work/books/inputs/bookname.txt。为便利演示,这里以三国演义为例。

{
“name”:“三国演义”,
“lists”:[{
“title”:“第一回宴桃园好汉三结义斩黄巾英豪首建功”
“content”:“滚滚长江东逝水,浪花淘尽英豪。是非胜败回头空。青山依
},{
“title”:“第二回张翼德怒鞭督邮何国舅谋诛宦竖”,
“content”:“且说董卓字仲颖,陇西临洮人也,官拜河东太守,自来自豪
}]
}

自制有声书阅读器:用PaddleSpeech打开读书新方式
音频组成
自制有声书阅读器:用PaddleSpeech打开读书新方式
阶段语句分割

以换行符”\n”分割为阶段,以”。”分割为语句。

#阶段和语句分割
deflists(self,lists):
results=[]
foriinrange(len(lists)):
item=lists[i]
title=item['title']
content=item['content']
sections=[]
sentences=[]
contents=content.split('\n')
forcitemincontents:
iflen(citem)>1:
sections.append(citem)
sentenceIndex=0
forsitemsinsections:
sitems_=[]
fortmpinsitems.split('。'):
iflen(tmp)>1:
sitems_.append(tmp)
forjinrange(len(sitems_)):
sentence={
'id':sentenceIndex,
'sentence':sitems_[j],
'end':0ifj<len(sitems_)-1else1
}
sentences.append(sentence)
sentenceIndex+=1
result={
'id':i,
'title':title,
'sentences':sentences
}
results.append(result)
returnresults

自制有声书阅读器:用PaddleSpeech打开读书新方式
特别字符处理

在国学书本中,有可能出现许多生僻字或许特别符号,这里需求做针对性的替换。

#特别处理示例,工程化最好用字典主动判别替换
defdealText(self,text):
text=text.replace('-','')
text=text.replace('','')
text=text.replace('’','')
text=text.replace('﨑','崎')
text=text.replace("[",'')
text=text.replace("]",'')
text=text.replace(' ','')
text=text.replace(",]","")
text=text.replace("1","1")
text=text.replace("2",'2')
text=text.replace("6","6")
text=text.replace("〔","")
text=text.replace("─","")
text=text.replace("┬","")
text=text.replace("┼","")
text=text.replace("┴","")
text=text.replace("〖","")
text=text.replace("〗","")
text=text.replace("礻殳","祋")
returntext

自制有声书阅读器:用PaddleSpeech打开读书新方式
音频组成

依据分割的ID,保存到对应位置。

#音频组成
defaudio(self,contents):
self.tts=TTSExecutor()
foriinrange(len(contents['lists'])):
item=contents['lists'][i]
basePath=self.bookPathOutput+'/'+self.bookname+'/'+str(i)
ifos.path.exists(basePath)isFalse:
os.makedirs(r''+basePath)
#生成每回标题音频
self.text2audio(item['title'],basePath+'/title.wav')
#生成每句内容音频
forjinrange(len(item['sentences'])):
sitem=item['sentences'][j]
self.text2audio(sitem['sentence'],basePath+'/'+str(sitem['id'])+'.wav')
deftext2audio(self,text,path):
text=self.dealText(text)
self.voice_cloning(text,path)
#self.tts(text=text,output=path)

自制有声书阅读器:用PaddleSpeech打开读书新方式
音色克隆

可以事先将不同音色音频放置在/work/sounds 目录下。此处音色克隆部分的功用首要参考自PaddleSpeech语音克隆项目。

  • 项目链接

aistudio.baidu.com/aistudio/pr…

defclone_pre(self):
#Initbody.
withopen(self.am_config)asf:
am_config=CfgNode(yaml.safe_load(f))
self.am_config_=am_config
withopen(self.voc_config)asf:
voc_config=CfgNode(yaml.safe_load(f))
#speakerencoder
p=SpeakerVerificationPreprocessor(
sampling_rate=16000,
audio_norm_target_dBFS=-30,
vad_window_length=30,
vad_moving_average_width=8,
vad_max_silence_length=6,
mel_window_length=25,
mel_window_step=10,
n_mels=40,
partial_n_frames=160,
min_pad_coverage=0.75,
partial_overlap_ratio=0.5)
print("AudioProcessorDone!")
self.p=p
speaker_encoder=LSTMSpeakerEncoder(
n_mels=40,num_layers=3,hidden_size=256,output_size=256)
speaker_encoder.set_state_dict(paddle.load(self.ge2e_params_path))
speaker_encoder.eval()
self.speaker_encoder=speaker_encoder
print("GE2EDone!")
withopen(self.phones_dict,"r")asf:
phn_id=[line.strip().split()forlineinf.readlines()]
vocab_size=len(phn_id)
print("vocab_size:",vocab_size)
#acousticmodel
odim=am_config.n_mels
#model:{model_name}_{dataset}
am_name=self.am[:self.am.rindex('_')]
am_dataset=self.am[self.am.rindex('_')+1:]
am_class=dynamic_import(am_name,self.model_alias)
am_inference_class=dynamic_import(
am_name+'_inference',self.model_alias)
ifam_name=='fastspeech2':
am=am_class(
idim=vocab_size,odim=odim,spk_num=None,**am_config["model"])
elifam_name=='tacotron2':
am=am_class(idim=vocab_size,odim=odim,**am_config["model"])
am.set_state_dict(paddle.load(self.am_ckpt)["main_params"])
am.eval()
am_mu,am_std=np.load(self.am_stat)
am_mu=paddle.to_tensor(am_mu)
am_std=paddle.to_tensor(am_std)
am_normalizer=ZScore(am_mu,am_std)
am_inference=am_inference_class(am_normalizer,am)
am_inference.eval()
self.am_inference=am_inference
print("acousticmodeldone!")
#vocoder
#model:{model_name}_{dataset}
voc_name=self.voc[:self.voc.rindex('_')]
voc_class=dynamic_import(voc_name,self.model_alias)
voc_inference_class=dynamic_import(
voc_name+'_inference',self.model_alias)
voc=voc_class(**voc_config["generator_params"])
voc.set_state_dict(paddle.load(self.voc_ckpt)["generator_params"])
voc.remove_weight_norm()
voc.eval()
voc_mu,voc_std=np.load(self.voc_stat)
voc_mu=paddle.to_tensor(voc_mu)
voc_std=paddle.to_tensor(voc_std)
voc_normalizer=ZScore(voc_mu,voc_std)
voc_inference=voc_inference_class(voc_normalizer,voc)
voc_inference.eval()
self.voc_inference=voc_inference
print("vocdone!")
self.frontend=Frontend(phone_vocab_path=self.phones_dict)
print("frontenddone!")
#获取音色
ref_audio_path=self.soundsInput+'/'+str(self.sound)+'.mp3'
mel_sequences=self.p.extract_mel_partials(self.p.preprocess_wav(ref_audio_path))
#print("mel_sequences:",mel_sequences.shape)
withpaddle.no_grad():
spk_emb=self.speaker_encoder.embed_utterance(paddle.to_tensor(mel_sequences))
#print("spk_embshape:",spk_emb.shape)
self.spk_emb=spk_emb
defvoice_cloning(self,text,path):
input_ids=self.frontend.get_input_ids(text,merge_sentences=True)
phone_ids=input_ids["phone_ids"][0]
withpaddle.no_grad():
wav=self.voc_inference(self.am_inference(phone_ids,spk_emb=self.spk_emb))
sf.write(path,wav.numpy(),samplerate=self.am_config_.fs)
self.post_del(path)

自制有声书阅读器:用PaddleSpeech打开读书新方式
语速和音量调整

defpost_del(self,path):
old_au=AudioFileClip(path)
new_au=old_au.fl_time(lambdat:self.speed*t,apply_to=['mask','audio'])
new_au=new_au.set_duration(old_au.duration/self.speed)
new_au=(new_au.fx(afx.volumex,self.volumex))
final_path=path.replace('outputs','final')
print(path,final_path)
new_au.write_audiofile(final_path)
print('^^^^^^')

音色、语速和音量需求在 main.py 的头部中设置。

classMain(object):
def__init__(self):
self.bookPathInput='./books/inputs'#书本输入目录
self.bookPathOutput='./books/outputs'#常规输出目录
self.bookPathFinal='./books/final'#终究输出目录
self.bookname='sanguoyanyi'
self.tts=None
self.soundsInput='./sounds'#音色文件存放目录
self.sound='001'#音色编号
self.speed=1.0#语速
self.volumex=1.1#音量
#音频组成,一键命令
%cd/home/aistudio/work/
!pythonmain.py

自制有声书阅读器:用PaddleSpeech打开读书新方式
检查生成成果

终究切分好的数据在

/work/outputs/sanguoyanyi目录下,原始语速和音量音频在outputs目录下,指定语速和音量音频在final目录下。其中的outputs.txt为切分内容,而音频会按照每个章节以及每个章节的语句索引排序好。

以下为outputs.txt 内容:

{
“name”:“三国演义”,
“lists”:[{
“id”:0,
“title”:“第一回宴桃园好汉三结义斩黄巾英豪首建功”,
“sentence”:[{
“id”:0
“sentence”:“滚滚长江东逝水,浪花淘尽英豪”,
“end”:0
},{
“id”:1,
“sentence”:“是否胜败回头空”,
“end”:0
},{
“id”:2,
“sentence”:“青山依旧在,几度夕阳红”,
“end”:0
},{
“id”:3,
“sentence”:“青丝渔樵江渚上,惯看秋月春风”,
“end”:0
},{
“id”:4,
“sentence”:“一壶浊酒喜相逢”,
“end”:0
},{

以下为第一回的每个语句wav格式音频。

自制有声书阅读器:用PaddleSpeech打开读书新方式

自制有声书阅读器:用PaddleSpeech打开读书新方式
客户端展现

输出第三部分生成好的内容和音频。这里用H5页面简略展现一下有声书阅读的效果,包含内容展现和逐句朗读高亮两种功用。

  • H5的具体代码已放在GitHub 上,我们可在下方链接中检查

github.com/lvsh2012/bo…

  • 手机或许PC也可直接体验

book.weixin12306.com/

自制有声书阅读器:用PaddleSpeech打开读书新方式
总结

经过PaddleSpeech可以简略快速地实现语音组成功用,轻松实现书本有声化。使用者在这里需求重视下,当以H5展现播放效果时,需求留意内容和音频的对应关系。除了语音组成功用外,PaddleSpeech还供给了包含语音识别、声纹提取、标点康复等其他功用。相信我们根据PaddleSpeech可以在该范畴挖掘出更多的可能性!