From 1976034f12ca328df72e298736ee12faeb539147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=8D=8E=E4=BE=A8?= Date: Sat, 3 May 2025 22:21:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=99=BE=E5=BA=A6asr?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 13 +++ .../core/providers/asr/baidu.py | 103 ++++++++++++++++++ main/xiaozhi-server/requirements.txt | 4 +- 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 main/xiaozhi-server/core/providers/asr/baidu.py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index ed8908f1..6135f0d7 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -258,6 +258,19 @@ ASR: access_key_id: 你的阿里云账号access_key_id access_key_secret: 你的阿里云账号access_key_secret output_dir: tmp/ + BaiduASR: + # 可以在这里申请百度语音技术的AppID、API Key、Secret Key + # https://console.bce.baidu.com/ai-engine/old/#/ai/speech/app/list + # 启用前需要先安装依赖包: + # pip install baidu-aip==4.16.13 + # pip install chardet==5.2.0 + type: baidu + app_id: 你的百度语音技术AppID + api_key: 你的百度语音技术APIKey + secret_key: 你的百度语音技术SecretKey + # 语言参数,1537为普通话,具体参考:https://ai.baidu.com/ai-doc/SPEECH/0lbxfnc9b + dev_pid: 1537 + output_dir: tmp/ VAD: SileroVAD: diff --git a/main/xiaozhi-server/core/providers/asr/baidu.py b/main/xiaozhi-server/core/providers/asr/baidu.py new file mode 100644 index 00000000..6edfe128 --- /dev/null +++ b/main/xiaozhi-server/core/providers/asr/baidu.py @@ -0,0 +1,103 @@ +import base64 +import hashlib +import hmac +import json +import time +from datetime import datetime, timezone +import os +import uuid +from typing import Optional, Tuple, List +import wave +import opuslib_next + +from aip import AipSpeech +from core.providers.asr.base import ASRProviderBase +from config.logger import setup_logging + +TAG = __name__ +logger = setup_logging() + +class ASRProvider(ASRProviderBase): + def __init__(self, config: dict, delete_audio_file: bool = True): + self.app_id = config.get("app_id") + self.api_key = config.get("api_key") + self.secret_key = config.get("secret_key") + self.dev_pid = config.get("dev_pid") + self.output_dir = config.get("output_dir") + self.delete_audio_file = delete_audio_file + + self.client = AipSpeech(str(self.app_id), self.api_key, self.secret_key) + + # 确保输出目录存在 + os.makedirs(self.output_dir, exist_ok=True) + + def save_audio_to_file(self, pcm_data: List[bytes], session_id: str) -> str: + """PCM数据保存为WAV文件""" + module_name = __name__.split(".")[-1] + file_name = f"asr_{module_name}_{session_id}_{uuid.uuid4()}.wav" + file_path = os.path.join(self.output_dir, file_name) + + with wave.open(file_path, "wb") as wf: + wf.setnchannels(1) + wf.setsampwidth(2) # 2 bytes = 16-bit + wf.setframerate(16000) + wf.writeframes(b"".join(pcm_data)) + + return file_path + + @staticmethod + def decode_opus(opus_data: List[bytes]) -> bytes: + """将Opus音频数据解码为PCM数据""" + + decoder = opuslib_next.Decoder(16000, 1) # 16kHz, 单声道 + pcm_data = [] + + for opus_packet in opus_data: + try: + pcm_frame = decoder.decode(opus_packet, 960) # 960 samples = 60ms + pcm_data.append(pcm_frame) + except opuslib_next.OpusError as e: + logger.bind(tag=TAG).error(f"Opus解码错误: {e}", exc_info=True) + + return pcm_data + + async def speech_to_text(self, opus_data: List[bytes], session_id: str) -> Tuple[Optional[str], Optional[str]]: + """将语音数据转换为文本""" + if not opus_data: + logger.bind(tag=TAG).warn("音频数据为空!") + return None, None + + file_path = None + try: + # 检查配置是否已设置 + if not self.app_id or not self.api_key or not self.secret_key: + logger.bind(tag=TAG).error("百度语音识别配置未设置,无法进行识别") + return None, file_path + + # 将Opus音频数据解码为PCM + pcm_data = self.decode_opus(opus_data) + combined_pcm_data = b"".join(pcm_data) + + # 判断是否保存为WAV文件 + if self.delete_audio_file: + pass + else: + self.save_audio_to_file(pcm_data, session_id) + + start_time = time.time() + # 识别本地文件 + result = self.client.asr(combined_pcm_data, 'pcm', 16000, { + 'dev_pid': str(self.dev_pid), + }) + + if result and result["err_no"] == 0: + logger.bind(tag=TAG).debug(f"百度语音识别耗时: {time.time() - start_time:.3f}s | 结果: {result}") + result = result["result"][0] + return result, file_path + else: + raise Exception(f"百度语音识别失败,错误码: {result['err_no']},错误信息: {result['err_msg']}") + return None, file_path + + except Exception as e: + logger.bind(tag=TAG).error(f"处理音频时发生错误!{e}", exc_info=True) + return None, file_path diff --git a/main/xiaozhi-server/requirements.txt b/main/xiaozhi-server/requirements.txt index 7da31b6f..e26fec18 100755 --- a/main/xiaozhi-server/requirements.txt +++ b/main/xiaozhi-server/requirements.txt @@ -25,4 +25,6 @@ sherpa_onnx==1.11.0 mcp==1.4.1 cnlunar==0.2.0 PySocks==1.7.1 -dashscope==1.23.1 \ No newline at end of file +dashscope==1.23.1 +baidu-aip==4.16.13 +chardet==5.2.0 \ No newline at end of file