# -*- coding:utf-8 -*-
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
#本demo示例是单次上传文本的示例,如果用在对时效要求高的交互场景,需要流式上传文本
# 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)
# 在这里通过res_id 来设置通过哪个音库合成
self.CommonArgs = {"app_id": self.APPID,"status":2}
# 业务参数(business),更多个性化参数可在官网查看
self.BusinessArgs = {
"tts": {
"vcn": "x5_lingyuzhao_flow", # 发音人参数,更换不同的发音人会有不同的音色效果
"volume": 50, #设置音量大小
"rhy": 0, #是否返回拼音标注 0:不返回拼音, 1:返回拼音(纯文本格式,utf8编码)
"speed": 50, #设置合成语速,值越大,语速越快
"pitch": 50, #设置振幅高低,可通过该参数调整效果
"bgs": 0, #背景音 0:无背景音, 1:内置背景音1, 2:内置背景音2
"reg": 0, #英文发音方式 0:自动判断处理,如果不确定将按照英文词语拼写处理(缺省), 1:所有英文按字母发音, 2:自动判断处理,如果不确定将按照字母朗读
"rdn": 0, #合成音频数字发音方式 0:自动判断, 1:完全数值, 2:完全字符串, 3:字符串优先
"audio": {
"encoding": "lame", #合成音频格式, lame 合成音频格式为mp3
"sample_rate": 24000, #合成音频采样率, 16000, 8000, 24000
"channels": 1, # 音频声道数
"bit_depth": 16, #合成音频位深 :16, 8
"frame_size": 0
}
}
}
self.Data = {
"text": {
"encoding": "utf8",
"compress": "raw",
"format": "plain",
"status": 2,
"seq": 0,
"text": str(base64.b64encode(self.Text.encode('utf-8')), "UTF8") # 待合成文本base64格式
}
}
class AssembleHeaderException(Exception):
def __init__(self, msg):
self.message = msg
class Url:
def __init__(this, host, path, schema):
this.host = host
this.path = path
this.schema = schema
pass
# calculate sha256 and encode to base64
def sha256base64(data):
sha256 = hashlib.sha256()
sha256.update(data)
digest = base64.b64encode(sha256.digest()).decode(encoding='utf-8')
return digest
def parse_url(requset_url):
stidx = requset_url.index("://")
host = requset_url[stidx + 3:]
schema = requset_url[:stidx + 3]
edidx = host.index("/")
if edidx <= 0:
raise AssembleHeaderException("invalid request url:" + requset_url)
path = host[edidx:]
host = host[:edidx]
u = Url(host, path, schema)
return u
# build websocket auth request url
def assemble_ws_auth_url(requset_url, method="GET", api_key="", api_secret=""):
u = parse_url(requset_url)
host = u.host
path = u.path
now = datetime.now()
date = format_date_time(mktime(now.timetuple()))
print(date)
# date = "Thu, 12 Dec 2019 01:57:27 GMT"
signature_origin = "host: {}\ndate: {}\n{} {} HTTP/1.1".format(host, date, method, path)
# print(signature_origin)
signature_sha = hmac.new(api_secret.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\"" % (
api_key, "hmac-sha256", "host date request-line", signature_sha)
authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
# print(authorization_origin)
values = {
"host": host,
"date": date,
"authorization": authorization
}
return requset_url + "?" + urlencode(values)
def on_message(ws, message):
try:
print(message)
# data = json.dumps(message)
message = json.loads(message)
# print(message)
# message =json.loads(message)
code = message["header"]["code"]
sid = message["header"]["sid"]
if("payload" in message):
audio = message["payload"]["audio"]['audio']
audio = base64.b64decode(audio)
status = message["payload"]['audio']["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.mp3', 'ab') as f: # 这里文件后缀名,需要和业务参数audio.encoding 对应
f.write(audio)
except Exception as e:
print("receive msg,but parse exception:", e)
# 收到websocket错误的处理
def on_error(ws, error):
# return 0
print("### error:", error)
# 收到websocket关闭的处理
def on_close(ws,ts,end):
return 0
# print("### closed ###")
# 收到websocket连接建立的处理
def on_open(ws):
def run(*args):
d = {"header": wsParam.CommonArgs,
"parameter": wsParam.BusinessArgs,
"payload": wsParam.Data,
}
d = json.dumps(d)
print("------>开始发送文本数据")
ws.send(d)
if os.path.exists('./demo.mp3'):
os.remove('./demo.mp3')
thread.start_new_thread(run, ())
if __name__ == "__main__":
# 从控制台页面获取以下密钥信息,控制台地址:https://console.xfyun.cn/app/myapp
appid = '你的APPID'
apisecret = '你的APISecret'
apikey = '你的APIKey'
wsParam = Ws_Param(APPID=appid, APISecret=apisecret,
APIKey=apikey,
Text="合肥明天天气状况为晴,温度范围在1℃~16℃,风向风力为西北风转南风微风。建议适时调整着装以应对温度变化。具体天气情况如下:天气:晴最高温度:16℃。最低温度:1℃。 日出时间:06:45。 ")
websocket.enableTrace(False)
# wsUrl = wsParam.create_url()
requrl = 'wss://cbm01.cn-huabei-1.xf-yun.com/v1/private/mcd9m97e6'
wsUrl = assemble_ws_auth_url(requrl,"GET",apikey,apisecret)
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})