Files
dsProject/dsLightRag/KeDaXunFei/XunFeiAudioEvaluator.py

290 lines
11 KiB
Python
Raw Normal View History

2025-09-05 20:28:18 +08:00
import base64
import datetime
import hashlib
import hmac
import json
import ssl
import time
import xml.etree.ElementTree as ET
2025-09-06 08:38:04 +08:00
import base64
2025-09-05 20:28:18 +08:00
from datetime import datetime
from time import mktime
from urllib.parse import urlencode
from wsgiref.handlers import format_date_time
import websocket
2025-09-05 21:01:11 +08:00
from Config.Config import XF_APPID, XF_APISECRET, XF_APIKEY
2025-09-05 20:28:18 +08:00
class XunFeiAudioEvaluator:
"""讯飞语音评测类"""
2025-09-06 08:56:25 +08:00
def __init__(self, appid, api_key, api_secret, audio_file, language, txt):
2025-09-05 20:28:18 +08:00
self.appid = appid
self.api_key = api_key
self.api_secret = api_secret
self.audio_file = audio_file
2025-09-05 21:31:14 +08:00
self.language = language
self.host_url = "wss://ise-api.xfyun.cn/v2/open-ise"
2025-09-05 20:28:18 +08:00
self.websocket_url = ""
self.evaluation_results = {}
2025-09-06 08:56:25 +08:00
self.txt = txt
2025-09-05 20:28:18 +08:00
def generate_auth_url(self):
"""生成鉴权URL"""
now_time = datetime.now()
now_date = format_date_time(mktime(now_time.timetuple()))
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
origin_base = "host: " + "ise-api.xfyun.cn" + "\n"
origin_base += "date: " + now_date + "\n"
origin_base += "GET " + "/v2/open-ise " + "HTTP/1.1"
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
# SHA256加密
signature_sha = hmac.new(
2025-09-06 08:56:25 +08:00
self.api_secret.encode('utf-8'),
2025-09-05 20:28:18 +08:00
origin_base.encode('utf-8'),
digestmod=hashlib.sha256
).digest()
signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
authorization_origin = f'api_key="{self.api_key}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha}"'
authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
dict_data = {
"authorization": authorization,
"date": now_date,
"host": "ise-api.xfyun.cn"
}
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
return self.host_url + '?' + urlencode(dict_data)
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
def on_message(self, ws, message):
"""WebSocket消息处理"""
print(f"Received message: {message}")
data = json.loads(message)
2025-09-06 08:56:25 +08:00
2025-09-05 21:36:42 +08:00
# 检查是否存在错误码
if data.get("code") != 0:
print(f"API错误: {data.get('message', '未知错误')}")
ws.close()
return
2025-09-06 08:56:25 +08:00
2025-09-05 21:36:42 +08:00
# 安全获取data字段
response_data = data.get("data", {})
status = response_data.get("status")
2025-09-06 08:56:25 +08:00
2025-09-05 21:36:42 +08:00
if status == 2 and response_data.get("data"):
2025-09-05 20:28:18 +08:00
# 解析评测结果
2025-09-05 21:36:42 +08:00
xml_data = base64.b64decode(response_data["data"])
2025-09-05 20:28:18 +08:00
xml_content = xml_data.decode("utf-8")
self.parse_evaluation_results(xml_content)
ws.close()
2025-09-05 21:31:14 +08:00
2025-09-05 20:28:18 +08:00
def on_error(self, ws, error):
"""错误处理"""
print(f"Error: {error},{ws}")
2025-09-05 21:31:14 +08:00
2025-09-05 20:28:18 +08:00
def on_close(self, ws, reason, res):
"""连接关闭处理"""
print(f"WebSocket connection closed,{ws}")
2025-09-05 21:31:14 +08:00
2025-09-05 20:28:18 +08:00
def on_open(self, ws):
"""连接建立处理"""
print(f"WebSocket connection opened,{ws},ws连接建立成功...")
2025-09-05 21:31:14 +08:00
2025-09-05 20:28:18 +08:00
# 发送初始参数
send_dict = {
"common": {
"app_id": self.appid
},
"business": {
"category": "read_sentence",
"rstcd": "utf8",
"sub": "ise",
"group": "pupil",
2025-09-06 09:50:55 +08:00
"ent": "cn_vip" if self.language == "chinese" else "en_vip",
2025-09-05 20:28:18 +08:00
"tte": "utf-8",
"cmd": "ssb",
"auf": "audio/L16;rate=16000",
"aue": "lame",
2025-09-06 08:56:25 +08:00
"text": '\uFEFF' + "[content]\n" + self.txt
2025-09-05 20:28:18 +08:00
},
"data": {
"status": 0,
"data": ""
}
}
ws.send(json.dumps(send_dict))
2025-09-05 21:31:14 +08:00
2025-09-05 20:28:18 +08:00
# 发送音频数据
with open(self.audio_file, "rb") as file_flag:
2025-09-05 21:36:42 +08:00
total_sent = 0
2025-09-05 20:28:18 +08:00
while True:
buffer = file_flag.read(1280)
if not buffer:
2025-09-05 21:36:42 +08:00
# 发送最后一帧(仅在有数据发送时)
if total_sent > 0:
my_dict = {
"business": {
2025-09-06 08:56:25 +08:00
"cmd": "auw",
"aus": 4,
2025-09-05 21:36:42 +08:00
"aue": "lame"
},
"data": {
2025-09-06 08:56:25 +08:00
"status": 2,
2025-09-05 21:36:42 +08:00
"data": "" # 空字符串表示结束
}
2025-09-05 20:28:18 +08:00
}
2025-09-05 21:36:42 +08:00
ws.send(json.dumps(my_dict))
time.sleep(1)
2025-09-05 20:28:18 +08:00
break
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
# 发送中间音频帧
send_dict = {
"business": {
"cmd": "auw",
"aus": 1,
"aue": "lame"
},
"data": {
"status": 1,
"data": str(base64.b64encode(buffer).decode()),
"data_type": 1,
"encoding": "raw"
}
}
ws.send(json.dumps(send_dict))
2025-09-05 21:36:42 +08:00
total_sent += len(buffer)
2025-09-05 20:28:18 +08:00
time.sleep(0.04)
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
def parse_evaluation_results(self, xml_content):
"""解析评测结果XML并提取得分信息"""
try:
root = ET.fromstring(xml_content)
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
# 查找read_chapter节点
read_chapter = root.find('.//read_chapter')
if read_chapter is not None:
2025-09-06 08:38:04 +08:00
# 保持字段名一致使用completeness_score
2025-09-05 20:28:18 +08:00
self.evaluation_results = {
'accuracy_score': float(read_chapter.get('accuracy_score', 0)),
'fluency_score': float(read_chapter.get('fluency_score', 0)),
2025-09-06 08:38:04 +08:00
'completeness_score': float(read_chapter.get('integrity_score', 0)),
2025-09-05 20:28:18 +08:00
'standard_score': float(read_chapter.get('standard_score', 0)),
'total_score': float(read_chapter.get('total_score', 0)),
'word_count': int(read_chapter.get('word_count', 0)),
'is_rejected': read_chapter.get('is_rejected', 'false') == 'true'
}
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
# 提取句子级别得分
sentence = read_chapter.find('.//sentence')
if sentence is not None:
self.evaluation_results['sentence'] = {
'accuracy_score': float(sentence.get('accuracy_score', 0)),
'fluency_score': float(sentence.get('fluency_score', 0)),
'total_score': float(sentence.get('total_score', 0))
}
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
# 提取单词级别得分
words = []
for word in read_chapter.findall('.//word'):
word_data = {
'content': word.get('content', ''),
'total_score': float(word.get('total_score', 0)),
'dp_message': int(word.get('dp_message', 0))
}
words.append(word_data)
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
self.evaluation_results['words'] = words
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
except ET.ParseError as e:
print(f"XML解析错误: {e}")
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
def get_evaluation_summary(self):
"""获取评测结果摘要"""
if not self.evaluation_results:
return "暂无评测结果"
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
summary = "=== 语音评测结果摘要 ===\n"
summary += f"总得分: {self.evaluation_results.get('total_score', 0):.4f}\n"
summary += f"准确度得分: {self.evaluation_results.get('accuracy_score', 0):.4f}\n"
summary += f"流畅度得分: {self.evaluation_results.get('fluency_score', 0):.4f}\n"
2025-09-06 08:38:04 +08:00
# 修复这里使用completeness_score
summary += f"完整度得分: {self.evaluation_results.get('completeness_score', 0):.4f}\n"
2025-09-05 20:28:18 +08:00
summary += f"标准度得分: {self.evaluation_results.get('standard_score', 0):.4f}\n"
summary += f"单词数量: {self.evaluation_results.get('word_count', 0)}\n"
summary += f"是否被拒绝: {'' if self.evaluation_results.get('is_rejected', False) else ''}\n"
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
if 'sentence' in self.evaluation_results:
sentence = self.evaluation_results['sentence']
summary += f"\n=== 句子级别得分 ===\n"
summary += f"句子准确度: {sentence.get('accuracy_score', 0):.4f}\n"
summary += f"句子流畅度: {sentence.get('fluency_score', 0):.4f}\n"
summary += f"句子总分: {sentence.get('total_score', 0):.4f}\n"
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
if 'words' in self.evaluation_results:
summary += f"\n=== 单词级别得分 ===\n"
for i, word in enumerate(self.evaluation_results['words']):
dp_msg = self._get_dp_message_description(word['dp_message'])
2025-09-06 08:56:25 +08:00
summary += f"{i + 1}. {word['content']}: {word['total_score']:.4f} ({dp_msg})\n"
2025-09-05 20:28:18 +08:00
return summary
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
def _get_dp_message_description(self, dp_message):
"""获取dp_message描述"""
descriptions = {
0: "正常",
16: "漏读",
2025-09-06 08:56:25 +08:00
32: "增读",
2025-09-05 20:28:18 +08:00
64: "回读",
128: "替换"
}
return descriptions.get(dp_message, f"未知({dp_message})")
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
def run_evaluation(self):
"""运行评测"""
start_time = datetime.now()
websocket.enableTrace(False)
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
ws_url = self.generate_auth_url()
ws_entity = websocket.WebSocketApp(
2025-09-06 08:56:25 +08:00
ws_url,
on_message=self.on_message,
on_error=self.on_error,
2025-09-05 20:28:18 +08:00
on_close=self.on_close,
on_open=self.on_open
)
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
ws_entity.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
end_time = datetime.now()
evaluation_time = end_time - start_time
print(f"评测耗时: {evaluation_time}")
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
return self.evaluation_results, evaluation_time
# 使用示例
if __name__ == '__main__':
# 配置参数
2025-09-05 21:01:11 +08:00
# appid = "5b83f8d6"
# api_secret = "604fa6cb9c5ab664a0d153fe0ccc6802"
# api_key = "5beb887923204000bfcb402046bb05a6"
appid = XF_APPID
api_secret = XF_APISECRET
api_key = XF_APIKEY
2025-09-06 08:56:25 +08:00
# audio_file = "./1.mp3"
2025-09-06 09:19:46 +08:00
audio_file = r'D:\dsWork\dsProject\dsLightRag\static\audio\audio_afc0a96e382c428cba2f00e3f71e4e8f.mp3'
2025-09-05 20:28:18 +08:00
# 创建评测器实例
2025-09-06 09:19:46 +08:00
txt="Hello everyone! Nice to meet you. Today is a beautiful day. I am learning English pronunciation with this tool."
evaluator = XunFeiAudioEvaluator(appid, api_key, api_secret, audio_file, "english",txt)
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
# 运行评测
results, eval_time = evaluator.run_evaluation()
2025-09-06 08:56:25 +08:00
2025-09-05 20:28:18 +08:00
# 输出评测结果摘要
2025-09-06 08:56:25 +08:00
print("\n" + "=" * 50)
2025-09-05 20:28:18 +08:00
print(evaluator.get_evaluation_summary())
print(f"总评测时间: {eval_time}")
2025-09-06 08:56:25 +08:00
print("=" * 50)