
☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython
一、引言
因亲友的诉求,需要一个支持动态文本转语音TTS的程序,来支持一些个性化的需求。老猿以前没研究过,认为这个很容易,具体实现时却遇到了如下难题:
- 本人的操作系统是ARM主机的统信操作系统,操作系统支持直接在文本编辑器中朗读中文,且语音清晰,但笔者没有找到相关命令或者API支持自己定制使用;
- 网上推荐的espeak,在统信系统上安装完之后只支持英文朗读,下载了中文数据之后,执行espeak–compile=="zh"报错,找不到规则库来执行,不知道在其他操作系统是否正常;
- marytts也是推荐比较多的,但下载下来之后要编译,而其相关库不支持ARM+UOS的组合,无法执行应用构建。
想起科大讯飞是国内语音处理的头部应用商,因此就决定使用科大讯飞试试,再开始想用离线语音合成方式,发现提供的库也不支持ARM+UOS的组合,因此最后只能选择科大讯飞在线语音合成的服务来支持。
二、订购科大讯飞在线语音合成服务
-
在科大讯飞官网注册用户,并进行实名制登记,具体操作在此不详细介绍;
-
重新回到科大讯飞官网,选择应用类型,TTS可以选择:语音合成->在线语音合成,如图:
点击在线语音合成,出现:
选择免费试用,出现:
选择:立即领取。
选择在线语音合成,点击立即领取。
选择:个人免费套餐,进入后选择自己已经命名的应用或点击下图的加号创建新应用:
下面以创建新应用来说明。
-
点击上图加号后出现应用创建页面,填写自己定的应用名(笔者前面已经注册了应用:中文TTS_ONLINE,这里随意填写应用名为:TTS),随意选择一个适合的分类,填写应用说明,如图:
点击提交。出现页面点击:确认下单,如图:
这样就创建了新的应用。 -
获取注册用户和应用的APPID等信息
通过官网首页点击右上角的控制台:
进入后出现在即的应用列表,如图:
选择自己的应用点击后进入如下界面:
点击旁边的更多服务信息查询,就在右侧出现:Websocket服务接口认证信息,如图:
这样就可以得到自己注册的应用信息,后续程序中需要使用。
在当前页面,在左侧的选项往下,可以看到:
选择在线语音合成,出现如下页面:
在右下部可以看到API文档,点击:文档,进入如下页面:
点击下载Demo。
三、关于下载的SDK
下载的SDK源码文件名为:tts_ws_python3_demo.py,代码如下:
# -*- coding:utf-8 -*-
#
# author: iflytek
#
# 本demo测试时运行的环境为:Windows + Python3.7
# 本demo测试成功运行时所安装的第三方库及其版本如下:
# cffi==1.12.3
# gevent==1.4.0
# greenlet==0.4.15
# pycparser==2.19
# six==1.12.0
# websocket==0.2.1
# websocket-client==0.56.0
# 合成小语种需要传输小语种文本、使用小语种发音人vcn、tte=unicode以及修改文本编码方式
# 错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
import websocket
import datetime
import hashlib
import base64
import hmac
import json
from urllib.parse import urlencode
import time
import ssl
from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime
import _thread as thread
import os
STATUS_FIRST_FRAME = 0 # 第一帧的标识
STATUS_CONTINUE_FRAME = 1 # 中间帧标识
STATUS_LAST_FRAME = 2 # 最后一帧的标识
class Ws_Param(object):
# 初始化
def __init__(self, APPID, APIKey, APISecret, Text):
self.APPID = APPID
self.APIKey = APIKey
self.APISecret = APISecret
self.Text = Text
# 公共参数(common)
self.CommonArgs = {"app_id": self.APPID}
# 业务参数(business),更多个性化参数可在官网查看
self.BusinessArgs = {"aue": "raw", "auf": "audio/L16;rate=16000", "vcn": "xiaoyan", "tte": "utf8"}
self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-8')), "UTF8")}
#使用小语种须使用以下方式,此处的unicode指的是 utf16小端的编码方式,即"UTF-16LE"”
#self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-16')), "UTF8")}
# 生成url
def create_url(self):
url = 'wss://tts-api.xfyun.cn/v2/tts'
# 生成RFC1123格式的时间戳
now = datetime.now()
date = format_date_time(mktime(now.timetuple()))
# 拼接字符串
signature_origin = "host: " + "ws-api.xfyun.cn" + "\n"
signature_origin += "date: " + date + "\n"
signature_origin += "GET " + "/v2/tts " + "HTTP/1.1"
# 进行hmac-sha256进行加密
signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
digestmod=hashlib.sha256).digest()
signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
self.APIKey, "hmac-sha256", "host date request-line", signature_sha)
authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
# 将请求的鉴权参数组合为字典
v = {
"authorization": authorization,
"date": date,
"host": "ws-api.xfyun.cn"
}
# 拼接鉴权参数,生成url
url = url + '?' + urlencode(v)
# print("date: ",date)
# print("v: ",v)
# 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
# print('websocket url :', url)
return url
def on_message(ws, message):
try:
message =json.loads(message)
code = message["code"]
sid = message["sid"]
audio = message["data"]["audio"]
audio = base64.b64decode(audio)
status = message["data"]["status"]
print(message)
if status == 2:
print("ws is closed")
ws.close()
if code != 0:
errMsg = message["message"]
print("sid:%s call error:%s code is:%s" % (sid, errMsg, code))
else:
with open('./demo.pcm', 'ab') as f:
f.write(audio)
except Exception as e:
print("receive msg,but parse exception:", e)
# 收到websocket错误的处理
def on_error(ws, error):
print("### error:", error)
# 收到websocket关闭的处理
def on_close(ws):
print("### closed ###")
# 收到websocket连接建立的处理
def on_open(ws):
def run(*args):
d = {"common": wsParam.CommonArgs,
"business": wsParam.BusinessArgs,
"data": wsParam.Data,
}
d = json.dumps(d)
print("------>开始发送文本数据")
ws.send(d)
if os.path.exists('./demo.pcm'):
os.remove('./demo.pcm')
thread.start_new_thread(run, ())
if __name__ == "__main__":
# 测试时候在此处正确填写相关信息即可运行
wsParam = Ws_Param(APPID=' ', APISecret=' ',
APIKey=' ',
Text="这是一个语音合成示例")
websocket.enableTrace(False)
wsUrl = wsParam.create_url()
ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
ws.on_open = on_open
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
关于该代码实际使用还是存在问题的,需要注意以下:
- import websocket的websocket库名并不是websocket,如果安装应该安装websocket-client,但要命的是确实有个websocket库,安装错了的话,要先卸载再安装websocket-client,导入语句还是import websocket不变,这二者的关系不展开介绍,但如果先安装了websocket库并执行过导入的话,卸载再安装后要退出Python环境(IDE或终端)再重新导入,否则同一个导入语句不会重新执行,导致websocket-client的类不认;
- on_close的函数参数定义不对,在demo中只定义了一个参数,实际上需要三个参数,应该定义为:
def on_close(ws,close_status_code, close_msg):
- 将APPID等信息用自己的数据进行替换:
wsParam = Ws_Param(APPID=' ', APISecret=' ', APIKey=' ', Text="这是一个语音合成示例")
的内容; - 该代码生成的音频保存在pcm文件内,并不是MP3,如果要转换为MP3还要再处理,笔者在付费专栏文章《封装科大讯飞语音在线合成服务快捷实现中文文本转语音TTS服务 https://blog.csdn.net/LaoYuanPython/article/details/144865924》提供了一种处理方式。
按照以上3点修改代码,就可以运行官方提供的代码生成PCM音频了,如果要转成MP3,还需要进一步处理。
四、小结
本文介绍了在科大讯飞创建文本转语音应用的详细步骤,并提供了官网平台demo下载的方法,但下载的代码存在3个影响运行的问题,老猿将其总结了下,并给出了完善建议,大家按照建议进行处理,就能真正完成一个中文文本转语音的示例了。
最后,给讯飞提一个建议:讯飞的能力是有免费试用期的,过了免费期要收费,这是应有之意,但上面的定价都是企业或大户才会订购的,能否给个人开发者提供一个订购的内容和价格,毕竟个人开发者并不需要大范围使用,一台机器按条收费就可以接受了,一般也不需要频繁调用,如果要有套餐价,以10元为起点的系列订购价可能更受欢迎。
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!
更多关于信创的内容请参考专栏《国产信创之光》的其他文章。
更多音视频剪辑的内容请参考专栏《PyQt+moviepy音视频剪辑实战》。
关于老猿的付费专栏
- 付费专栏《https://blog.csdn.net/laoyuanpython/category_9607725.html 使用PyQt开发图形界面Python应用》专门介绍基于Python的PyQt图形界面开发基础教程,对应文章目录为《 https://blog.csdn.net/LaoYuanPython/article/details/107580932 使用PyQt开发图形界面Python应用专栏目录》;
- 付费专栏《https://blog.csdn.net/laoyuanpython/category_10232926.html moviepy音视频开发专栏 )详细介绍moviepy音视频剪辑合成处理的类相关方法及使用相关方法进行相关剪辑合成场景的处理,对应文章目录为《https://blog.csdn.net/LaoYuanPython/article/details/107574583 moviepy音视频开发专栏文章目录》;
- 付费专栏《https://blog.csdn.net/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》为《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的伴生专栏,是笔者对OpenCV-Python图形图像处理学习中遇到的一些问题个人感悟的整合,相关资料基本上都是老猿反复研究的成果,有助于OpenCV-Python初学者比较深入地理解OpenCV,对应文章目录为《https://blog.csdn.net/LaoYuanPython/article/details/109713407 OpenCV-Python初学者疑难问题集专栏目录 》
- 付费专栏《https://blog.csdn.net/laoyuanpython/category_10762553.html Python爬虫入门 》站在一个互联网前端开发小白的角度介绍爬虫开发应知应会内容,包括爬虫入门的基础知识,以及爬取CSDN文章信息、博主信息、给文章点赞、评论等实战内容。
前两个专栏都适合有一定Python基础但无相关知识的小白读者学习,第三个专栏请大家结合《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的学习使用。
对于缺乏Python基础的同仁,可以通过老猿的免费专栏《https://blog.csdn.net/laoyuanpython/category_9831699.html 专栏:Python基础教程目录)从零开始学习Python。
如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。