diff --git a/main/xiaozhi-server/core/handle/reportHandle.py b/main/xiaozhi-server/core/handle/reportHandle.py index ea621f74..15beb472 100644 --- a/main/xiaozhi-server/core/handle/reportHandle.py +++ b/main/xiaozhi-server/core/handle/reportHandle.py @@ -22,39 +22,45 @@ from config.manage_api_client import report as manage_report TAG = __name__ -async def report(conn: "ConnectionHandler", type, text, opus_data, report_time): +async def report(conn: "ConnectionHandler", chat_type, text, audio_data, report_time): """执行聊天记录上报操作 Args: conn: 连接对象 - type: 上报类型,1为用户,2为智能体,3为工具调用 + chat_type: 上报类型,1为用户(ASR/PCM),2为智能体(TTS/Opus),3为工具调用 text: 合成文本 - opus_data: opus音频数据 + audio_data: 音频数据(chat_type=1时为PCM格式,chat_type=2时为Opus格式) report_time: 上报时间 """ try: - if opus_data: - audio_data = opus_to_wav(conn, opus_data) + if audio_data: + + if chat_type == 1: + wav_data = pcm_to_wav(conn, audio_data) + elif chat_type == 2: + wav_data = opus_to_wav(conn, audio_data) + else: + wav_data = None else: - audio_data = None + wav_data = None # 执行异步上报 await manage_report( mac_address=conn.device_id, session_id=conn.session_id, - chat_type=type, + chat_type=chat_type, content=text, - audio=audio_data, + audio=wav_data, report_time=report_time, ) except Exception as e: conn.logger.bind(tag=TAG).error(f"聊天记录上报失败: {e}") -def opus_to_wav(conn: "ConnectionHandler", pcm_data): +def pcm_to_wav(conn: "ConnectionHandler", pcm_data): """将PCM数据转换为WAV格式的字节流 Args: - output_dir: 输出目录(保留参数以保持接口兼容) + conn: 连接对象 pcm_data: PCM音频数据(可能是列表或bytes) Returns: @@ -96,11 +102,62 @@ def opus_to_wav(conn: "ConnectionHandler", pcm_data): raise +def opus_to_wav(conn: "ConnectionHandler", opus_data): + """将Opus数据转换为WAV格式的字节流 + + Args: + conn: 连接对象 + opus_data: Opus音频数据(可能是列表或bytes) + + Returns: + bytes: WAV格式的音频数据 + """ + decoder = None + try: + decoder = opuslib_next.Decoder(16000, 1) + pcm_data = [] + + if isinstance(opus_data, list): + for opus_packet in opus_data: + try: + pcm_frame = decoder.decode(opus_packet, 960) + pcm_data.append(pcm_frame) + except opuslib_next.OpusError as e: + conn.logger.bind(tag=TAG).error(f"Opus解码错误: {e}", exc_info=True) + elif isinstance(opus_data, bytes): + pcm_frame = decoder.decode(opus_data, 960) + pcm_data.append(pcm_frame) + + if not pcm_data: + raise ValueError("没有有效的音频数据") + + pcm_data_bytes = b"".join(pcm_data) + + wav_header = bytearray() + wav_header.extend(b"RIFF") + wav_header.extend((36 + len(pcm_data_bytes)).to_bytes(4, "little")) + wav_header.extend(b"WAVE") + wav_header.extend(b"fmt ") + wav_header.extend((16).to_bytes(4, "little")) + wav_header.extend((1).to_bytes(2, "little")) + wav_header.extend((1).to_bytes(2, "little")) + wav_header.extend((16000).to_bytes(4, "little")) + wav_header.extend((32000).to_bytes(4, "little")) + wav_header.extend((2).to_bytes(2, "little")) + wav_header.extend((16).to_bytes(2, "little")) + wav_header.extend(b"data") + wav_header.extend(len(pcm_data_bytes).to_bytes(4, "little")) + + return bytes(wav_header) + pcm_data_bytes + finally: + if decoder is not None: + try: + del decoder + except Exception as e: + conn.logger.bind(tag=TAG).debug(f"释放decoder资源时出错: {e}") + + def enqueue_tts_report(conn: "ConnectionHandler", text, opus_data): - if not conn.read_config_from_api or conn.need_bind or not conn.report_tts_enable: - return - if conn.chat_history_conf == 0: - return """将TTS数据加入上报队列 Args: @@ -108,6 +165,10 @@ def enqueue_tts_report(conn: "ConnectionHandler", text, opus_data): text: 合成文本 opus_data: opus音频数据 """ + if not conn.read_config_from_api or conn.need_bind or not conn.report_tts_enable: + return + if conn.chat_history_conf == 0: + return try: # 使用连接对象的队列,传入文本和二进制数据而非文件路径 if conn.chat_history_conf == 2: @@ -164,10 +225,6 @@ def enqueue_tool_report(conn: "ConnectionHandler", tool_name: str, tool_input: d def enqueue_asr_report(conn: "ConnectionHandler", text, opus_data): - if not conn.read_config_from_api or conn.need_bind or not conn.report_asr_enable: - return - if conn.chat_history_conf == 0: - return """将ASR数据加入上报队列 Args: @@ -175,6 +232,10 @@ def enqueue_asr_report(conn: "ConnectionHandler", text, opus_data): text: 合成文本 opus_data: opus音频数据 """ + if not conn.read_config_from_api or conn.need_bind or not conn.report_asr_enable: + return + if conn.chat_history_conf == 0: + return try: # 使用连接对象的队列,传入文本和二进制数据而非文件路径 if conn.chat_history_conf == 2: