Files
aistream-test/python/test/super smart-tts.py
T
2025-12-01 03:42:34 +08:00

205 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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_lingxiaoxuan_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": "raw", #合成音频格式, 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 = 'c2ab263d'
apisecret = 'NTMyYzIxOGRlOTNhZjNhYjk4ZTcxMTMw'
apikey = 'af0eba3a3bf668d69fc0ccf4b05a4823'
wsParam = Ws_Param(APPID=appid, APISecret=apisecret,
APIKey=apikey,
Text="全红婵,2007年3月28日出生于广东省湛江市,中国国家跳水队女运动员,主项为女子10米跳台。")
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})