给我儿子做个自动读故事的机器
前言
今天演示的例子是在将文字传送到讯飞的云平台进行语音合成,合成完毕后,返回语音,在pynq平台上将语音转化为合适的格式进行播放。这个东西可以给我儿子做个自动读书器,将来老子就不用天天给他讲故事了。哈哈哈,想想就很六,不过残酷的现实是得先找到孩子他妈。
不扯闲话了,开始正式分析这个问题怎么做。
将文字传送到讯飞的云平台很容易,只需要现在云端申请一个账号,建立一个应用,获取到key 和 id之后,按照官方开发文档并参照python demo就可以完成了。
先打一波广告!
讯飞的开放平台很好用,每天500次免费的使用,还算可以了,申请账号很方便。
再黑一波竞争对手
不像国内某SB搜索公司的人工智能平台,申请开发者账号的时候要填一堆信息,比美国签证还要麻烦,中间还强行要我安装他们的手机搜索APP进行人脸识别验证,一堆SB流程走完让我等审核的结果。结果尼玛,三个月了还是没给我。美国签证也只是Administrative Processing一下我,最终还是放我过去了啊。
一、讯飞云语音合成的实现
首先需要在讯飞开放平台上申请一个账户并建立一个应用,针对本设计我建立一个叫“电子书在线播放”的应用。在这里你可以看到APPID和APIKey这两个关键信息。
image.png
获取语音的代码如下:
import base64
import json
import time
import hashlib
import urllib.request
import urllib.parse
# API请求地址、API KEY、APP ID等参数,提前填好备用
api_url = "http://api.xfyun.cn/v1/service/v1/tts"
API_KEY = "****************************************"# use your key
APP_ID = "***********" # use your ID
OUTPUT_FILE = "output.wav" # 输出音频的保存路径,请根据自己的情况替换
TEXT = "江山如画,一时多少豪杰"
# Set Audio parameters
Param = {
"auf": "audio/L16;rate=16000", #音频采样率
"aue": "raw", #音频编码,raw(生成wav)或lame(生成mp3)
"voice_name": "xiaoyan",
"speed": "50", #语速[0,100]
"volume": "77", #音量[0,100]
"pitch": "50", #音高[0,100]
"engine_type": "aisound" #引擎类型。aisound(普通效果),intp65(中文),intp65_en(英文)
}
# 配置参数编码为base64字符串,过程:字典→明文字符串→utf8编码→base64(bytes)→base64字符串
Param_str = json.dumps(Param) #得到明文字符串
Param_utf8 = Param_str.encode('utf8') #得到utf8编码(bytes类型)
Param_b64 = base64.b64encode(Param_utf8) #得到base64编码(bytes类型)
Param_b64str = Param_b64.decode('utf8') #得到base64字符串
# Build HTTP request header
time_now = str(int(time.time()))
checksum = (API_KEY + time_now + Param_b64str).encode('utf8')
checksum_md5 = hashlib.md5(checksum).hexdigest()
header = {
"X-Appid": APP_ID,
"X-CurTime": time_now,
"X-Param": Param_b64str,
"X-CheckSum": checksum_md5
}
# build HTTP Body
body = {
"text": TEXT
}
body_urlencode = urllib.parse.urlencode(body)
body_utf8 = body_urlencode.encode('utf8')
#Send HTTP POST request
req = urllib.request.Request(api_url, data=body_utf8, headers=header)
response = urllib.request.urlopen(req)
# Get Result
response_head = response.headers['Content-Type']
if(response_head == "audio/mpeg"):
out_file = open(OUTPUT_FILE, 'wb')
data = response.read() # a 'bytes' object
out_file.write(data)
out_file.close()
print('Get translation sucessfully,output file is ' + OUTPUT_FILE)
else:
print(response.read().decode('utf8'))
说明:
1.这段代码是在网上一个博客里面找的,参考链接如下:
https://segmentfault.com/a/1190000013953185
具体细节原文已经讲的比较清楚了。
2.需要在应用的IP白名单里面将你的IP放进去,好像一般要等几分钟才能正式生效。只有白名单的IP才可正常连接讯飞开发平台获取数据。
3.语音文件的格式要和我的保持一致,否则下面的代码有可能无法正确运行并得到结果
二、对语音文件进行格式转化
PYNQ的库只能支持4.8k采样率24bit 精度双通道wav格式的播放(好像什么pdm格式也行,没太研究)这里需要自己将讯飞云平台返回的wav格式文件进行转化,才能进行播放。
根据前面的语音参数,我们获取到的音频是1.6k采样率,单声道,16bit。
先将数据转化成24bit。
import soundfile
data, samplerate = soundfile.read('output.wav')
soundfile.write('output_24bit.wav', data, samplerate, subtype='PCM_24')#Generate 24bit wave file
这部分代码需要安装soundfile库到python3.6(PYNQ 用的就是3.6,不要装错)
使用下面指令安装pip3
sudo apt update
sudo apt install python3-pip
pip3.6 install --upgrade pip
再安装soundfile库
pip3.6 install soundfile
如果运行pip3.6的时候返回找不到main。参考下面链接
https://blog.csdn.net/accumulate_zhang/article/details/80269313
对 /usr/bin/pip3进行下面修改即可
from pip import __main__
if __name__ == '__main__':
sys.exit(__main__._main())
转换完成后,再补齐成双通道,并且每个数据插入两次0,将采样率提升为4.8k。同时将文件头修改成正确的格式。代码如下:
import wave
import numpy as np
import pylab as plt
import struct
#open file
fin = wave.open("output_24bit.wav","rb")
#read wave file heater get wave information
params = fin.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
print(params)
#read wave date
str_data = fin.readframes(nframes)
buf = b''
barr = bytearray(buf)
for i in range(len(str_data)):
barr.append(str_data[i])
barr.append(str_data[i])
barr.append(0)
barr.append(0)
barr.append(0)
barr.append(0)
p = bytes(barr)
fout = wave.open("output_24bit_2channel_4k8.wav","wb")
fout.setparams(params)
fout.setframerate(4800)# set rate as 4k8
fout.setnchannels(2)# set as 2channel wave
fout.setnframes(nframes+int(len(str_data)/2)*5)#modify frame length to 4k8 2channel
fout.writeframes(p)
print('Generate wav file ', "output_24bit_2channel_4k8.wav")
fout.close()
现在这个使用简单插值的方式来增加采样率,噪音比较大,音量比较小,后面需要改进一下。
三、语音播放
接下来的工作就简单了使用base overlay 里面的audiomm模块就可以播放了。代码如下:
from pynq.overlays.base import BaseOverlay
base = BaseOverlay("base.bit")
pAudio = base.audio
pAudio.select_microphone()# use microphne
path ="/home/xilinx/jupyter_notebooks/base/mydesign/"+ "output_24bit_2channel_4k8.wav"
print(pAudio.info(path))
pAudio.load(path)
pAudio.play()
后记
使用PYNQ开发很方面,而且很多代码可以直接在PC上直接调试好,在复制到PYNQ上执行,加快调试速度。这篇文章里面的语音获取和文件格式修改都是在PC上完成的,只有最后一部分需要硬件,才在PYNQ上调试了一下。
这个设计只是一个demo 为我们演示如何迅速将底层硬件和云端进行连接,迅速完成以前我们需要花很长时间才能玩的设计。将云端和硬件连接起来有好多事情可以做的,可以极大地拓展我们的应用。
现在这个代码里面语音格式转换太耗费时间,将其代码用C实现或者用逻辑资源来实现,才是最大限度发挥PYNQ的优势,后面有时间再搞吧。毕竟还不知道孩子他妈在哪儿呢!