51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 846|回复: 0
打印 上一主题 下一主题

[转贴] 利用Python打造一个语音合成系统

[复制链接]
  • TA的每日心情
    无聊
    前天 09:05
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-7-22 09:44:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    背景
      一直对语音合成系统比较感兴趣,总想能给自己合成一点内容,比如说合成小说,把我下载的电子书播报给我听等等。
      语音合成系统
      其实就是一个基于语音合成的工具,但是这个东西由于很多厂家都提供了API的形式,因此开发难度大大降低,只需要调用几个API即可实现属于自己的语音合成工具;麻雀虽小,五脏俱全。往大了说,这就是一个小型的语音合成系统。
      准备工作
      首先我们电脑上需要安装:
      ·Anaconda
      · Python 3.7
      · visual studio code
      步骤
      这里我们选用讯飞开放平台的WebAPI接口。
      https://www.xfyun.cn/doc/tts/online_tts/API.html
      首先我们到控制台创建一个应用。

    创建好了之后,点击该应用进入,有该应用的详细栏目。
      点击左侧的语音合成,再到下一级在线语音合成(流式版)

    在右上侧,我们需要拿到3个东西:
      · APPID
      · APISecret
      · APIKey
      代码实现
      好了接下来进行代码实现了,首先安装我们需要的两个库。
    1.  pip install websocket-client
    2.   pip install playsound
    复制代码
    接下来我们定义一个类play,包含4个函数。

    1.  class play:
    2.    def __init__(self): #初始化函数
    3.    def play_sound(self):#播放音频函数
    4.    def select_vcn(self,*arg):#选择下拉框设置发音人
    5.    def xfyun_tts(self):#进行语音合成
    复制代码
    在这里,大家需要填上刚才从讯飞开放平台控制台获取到的appid、appkey以及appsecret。

    1. def __init__(self):
    2.          self.APP_ID = 'xxx'   #请填上自己的appid
    3.          self.API_KEY = 'xxx'  #请填上自己的appkey
    4.          self.SECRET_KEY = 'xxx' #请填上自己的appsecret
    5.          self.root=tk.Tk() #初始化窗口
    6.          self.root.title("语音合成系统") #窗口名称
    7.          self.root.geometry("600x550") #设置窗口大小
    8.          self.root.resizable(0,0)
    9.          #self.root.resizable(width=True,height=True)#设置窗口是否可变,宽不可变,高可变,默认为True
    10.          self.lb=tk.Label(self.root,text='请选择语音发音人')#标签
    11.          self.tt=tk.Text(self.root,width=77,height=30) #多行文本框
    12.          self.cb=ttk.Combobox(self.root, width=12)  #下拉列表框
    13.           #设置下拉列表框的内容   
    14.          self.cb['values']=("甜美女声-小燕","亲切男声-许久","知性女声-小萍", "可爱童声-许小宝","亲切女声-小婧")
    15.          self.cb.current(0)    #将当前选择状态置为0,也就是第一项
    16.          self.cb.bind("<<ComboboxSelected>>", self.select_vcn)
    17.          self.tk_tts_file=tk.Label(self.root,text='生成文件名')
    18.          self.b1=tk.Button(self.root, text='进行语音合成', width=10,height=1,command=self.xfyun_tts) #按钮
    19.          self.tk_play=tk.Button(self.root, text='播放', width=10,height=1,command=self.play_sound) #按钮
    20.          #各个组件的位置
    21.          self.tk_tts_file.place(x=30,y=500)
    22.          self.b1.place(x=300,y=500)
    23.          self.tk_play.place(x=400,y=500)
    24.          self.lb.place(x=30,y=30)
    25.          self.cb.place(x=154,y=30)
    26.          self.tt.place(x=30,y=60)
    27.          self.root.mainloop()
    复制代码
    当选择了下拉列表,设置对应的发音人

    1. def select_vcn(self,*arg):
    2.          if self.cb.get()=='甜美女声-小燕':
    3.              self.vcn="xiaoyan"
    4.          elif self.cb.get()=='亲切男声-许久':
    5.              self.vcn="aisjiuxu"
    6.          elif self.cb.get()=='知性女声-小萍':
    7.              self.vcn="aisxping"
    8.          elif self.cb.get()=='可爱童声-许小宝':
    9.              self.vcn="aisbabyxu"
    10.          elif self.cb.get()=='亲切女声-小婧':
    11.              self.vcn="aisjinger"
    12.          print(self.vcn)
    复制代码
    接下来我们来魔改讯飞自带的Python demo为从而更加方便的来使用。另外,搜索公众号程序员小乐后台回复“赚钱”,获取一份惊喜礼包。

    1. # -*- coding:utf-8 -*-
    2.   #
    3.   #   author: iflytek
    4.   #
    5.   #  本demo测试时运行的环境为:Windows + Python3.7
    6.   #  本demo测试成功运行时所安装的第三方库及其版本如下:
    7.   #   cffi==1.12.3
    8.   #   gevent==1.4.0
    9.   #   greenlet==0.4.15
    10.   #   pycparser==2.19
    11.   #   six==1.12.0
    12.   #   websocket==0.2.1
    13.   #   websocket-client==0.56.0
    14.   #   合成小语种需要传输小语种文本、使用小语种发音人vcn、tte=unicode以及修改文本编码方式
    15.   #  错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
    16.   # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    17.   import websocket
    18.   import datetime
    19.   import hashlib
    20.   import base64
    21.   import hmac
    22.   import json
    23.   from urllib.parse import urlencode
    24.   import time
    25.   import ssl
    26.   from wsgiref.handlers import format_date_time
    27.   from datetime import datetime
    28.   from time import mktime
    29.   import _thread as thread
    30.   import os
    31.   import wave
    32.   STATUS_FIRST_FRAME = 0  # 第一帧的标识
    33.   STATUS_CONTINUE_FRAME = 1  # 中间帧标识
    34.   STATUS_LAST_FRAME = 2  # 最后一帧的标识
    35.   PCM_PATH = "./demo.pcm"
    36.   class Ws_Param(object):
    37.      # 初始化
    38.      def __init__(self):
    39.          pass
    40.      def set_tts_params(self, text, vcn):
    41.              if text != "":
    42.                  self.Text = text
    43.              if vcn != "":
    44.                  self.vcn = vcn
    45.                  # 业务参数(business),更多个性化参数可在官网查看
    46.                  self.BusinessArgs = {"bgs":1,"aue": "raw", "auf": "audio/L16;rate=16000", "vcn": self.vcn, "tte": "utf8"}
    47.              #使用小语种须使用以下方式,此处的unicode指的是 utf16小端的编码方式,即"UTF-16LE"”
    48.              #self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-16')), "UTF8")}
    49.              self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-8')), "UTF8")}
    50.      def set_params(self, appid, apiSecret, apiKey):
    51.          if appid != "":
    52.              self.APPID = appid
    53.              # 公共参数(common)
    54.              self.CommonArgs = {"app_id": self.APPID}
    55.          if apiKey != "":
    56.              self.APIKey = apiKey
    57.          if apiSecret != "":
    58.              self.APISecret = apiSecret
    59.      # 生成url
    60.      def create_url(self):
    61.          url = 'wss://tts-api.xfyun.cn/v2/tts'
    62.          # 生成RFC1123格式的时间戳
    63.          now = datetime.now()
    64.          date = format_date_time(mktime(now.timetuple()))
    65.          # 拼接字符串
    66.          signature_origin = "host: " + "ws-api.xfyun.cn" + "\n"
    67.          signature_origin += "date: " + date + "\n"
    68.          signature_origin += "GET " + "/v2/tts " + "HTTP/1.1"
    69.          # 进行hmac-sha256进行加密
    70.          signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
    71.                                   digestmod=hashlib.sha256).digest()
    72.          signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
    73.          authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
    74.              self.APIKey, "hmac-sha256", "host date request-line", signature_sha)
    75.          authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
    76.          # 将请求的鉴权参数组合为字典
    77.          v = {
    78.              "authorization": authorization,
    79.              "date": date,
    80.              "host": "ws-api.xfyun.cn"
    81.          }
    82.          url = url + '?' + urlencode(v)
    83.          return url
    84.   def on_message(ws, message):
    85.      try:
    86.          #print(message)
    87.          try:
    88.              message =json.loads(message)
    89.          except Exception as e:
    90.              print("111",e)
    91.          code = message["code"]
    92.          sid = message["sid"]
    93.          audio = message["data"]["audio"]
    94.          audio = base64.b64decode(audio)
    95.          status = message["data"]["status"]
    96.          print(code, sid, status)
    97.          if status == 2:
    98.              print("ws is closed")
    99.              ws.close()
    100.          if code != 0:
    101.              errMsg = message["message"]
    102.              print("sid:%s call error:%s code is:%s" % (sid, errMsg, code))
    103.          else:
    104.              with open(PCM_PATH, 'ab') as f:
    105.                  f.write(audio)
    106.      except Exception as e:
    107.          print("receive msg,but parse exception:", e)
    108.   # 收到websocket错误的处理
    109.   def on_error(ws, error):
    110.      print("### error:", error)
    111.   # 收到websocket关闭的处理
    112.   def on_close(ws):
    113.      print("### closed ###")
    114.   # 收到websocket连接建立的处理
    115.   def on_open(ws):
    116.      def run(*args):
    117.          d = {"common": wsParam.CommonArgs,
    118.               "business": wsParam.BusinessArgs,
    119.               "data": wsParam.Data,
    120.               }
    121.          d = json.dumps(d)
    122.          print("------>开始发送文本数据")
    123.          ws.send(d)
    124.          if os.path.exists(PCM_PATH):
    125.              os.remove(PCM_PATH)
    126.      thread.start_new_thread(run, ())
    127.   def text2pcm(appid, apiSecret, apiKey, text, vcn, fname):
    128.      wsParam.set_params(appid, apiSecret, apiKey)
    129.      wsParam.set_tts_params(text, vcn)
    130.      websocket.enableTrace(False)
    131.      wsUrl = wsParam.create_url()
    132.      ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
    133.      ws.on_open = on_open
    134.      ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
    135.      pcm2wav(PCM_PATH, fname)
    136.   def pcm2wav(fname, dstname):
    137.      with open(fname, 'rb') as pcmfile:
    138.          pcmdata = pcmfile.read()
    139.          print(len(pcmdata))
    140.      with wave.open(dstname, "wb") as wavfile:
    141.          wavfile.setparams((1, 2, 16000, 0, 'NONE', 'NONE'))
    142.          wavfile.writeframes(pcmdata)
    143.   wsParam = Ws_Param()
    复制代码
    最终一个语音合成系统就这样实现了。


    当前,各种云计算、云服务迅速发展,各大公司提供了丰富的资源,大大降低了人工智能开发的门槛,不需要懂语音合成的原理,竟然可以快速开发出一个语音合成工具出来!




    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-23 11:49 , Processed in 0.065368 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表