# -*- coding:utf-8 -*- import sys import threading import wave from queue import Queue import pyaudio import websocket import datetime import hashlib import base64 import hmac import json from urllib.parse import urlencode import time from concurrent.futures import ThreadPoolExecutor import _thread as thread from wsgiref.handlers import format_date_time from datetime import datetime from time import mktime pool = ThreadPoolExecutor(max_workers=10) STATUS_FIRST_FRAME = 0 # 第一帧的标识 STATUS_CONTINUE_FRAME = 1 # 中间帧标识 STATUS_LAST_FRAME = 2 # 最后一帧的标识 CHUNK = 1024 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 16000 wsParam = None data = Queue(maxsize=2000) audio = None stream = None produce_end = False 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": "x4_lingxiaoyao_en", "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): global produce_end 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") produce_end = True ws.close() if code != 0: errMsg = message["message"] print("sid:%s call error:%s code is:%s" % (sid, errMsg, code)) else: if not data.full(): print("正在生产") data.put(audio) time.sleep(0.2) else: print("队列满了,正在等待 ") # time.sleep(2) except Exception as e: produce_end = True print("receive msg,but parse exception:", e) # 收到websocket错误的处理 def on_error(ws, error): print("### error:", error) # 收到websocket关闭的处理 def on_close(ws,code,resason): print("### closed ###") # 收到websocket连接建立的处理 def on_open(ws): global audio global stream global produce_end def run(*args): d = {"common": wsParam.CommonArgs, "business": wsParam.BusinessArgs, "data": wsParam.Data, } d = json.dumps(d) print("------>开始发送文本数据") ws.send(d) audio = pyaudio.PyAudio() stream = audio.open(format=FORMAT, channels=CHANNELS, rate=16000, output=True) produce_end = False run() def play(APPID, APISecret, APIKey, Text): global audio global stream global ws global produce_end # 只要队列不为空,就一直从队列里取出值 print("调用play()") thread_pool.pool.submit(text2speech,APPID, APISecret, APIKey, Text) # product = threading.Thread(target=text2speech, args=( # APPID, APISecret, APIKey, Text)) # product.start() while True: if not data.empty(): print('正在使用数据') d= data.get() stream.write(d) else: if produce_end == False: print("队列空了,正在等待 ") time.sleep(0.2) else: print(f"prodcut_end={produce_end}") print("生产结束,消费结束") stream.stop_stream() stream.close() audio.terminate() print(f"data.qsize() = {data.qsize()}") produce_end = False # ws.close() break ws = None consumer =None def text2speech(APPID, APISecret, APIKey, Text): global wsParam global ws global consumer wsParam = Ws_Param(APPID=APPID, APISecret=APISecret, APIKey=APIKey, Text=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() if __name__=='__main__': t = time.time() while True: text = "你好" play("5fcafeb8","NDU4YTc1YjA4ZmRkYWQ4Mzg4NjM1MjZm","8163783ac3d52d9cb017458242e82302",text) # text2speech("5fcafeb8","NDU4YTc1YjA4ZmRkYWQ4Mzg4NjM1MjZm","8163783ac3d52d9cb017458242e82302",text)