mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-26 09:03:54 +08:00
update:优化代码
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
from core.handle.sendAudioHandle import send_stt_message
|
||||
import time
|
||||
import json
|
||||
import asyncio
|
||||
from core.utils.util import audio_to_data
|
||||
from core.handle.abortHandle import handleAbortMessage
|
||||
from core.handle.intentHandler import handle_user_intent
|
||||
from core.utils.output_counter import check_device_output_limit
|
||||
from core.handle.abortHandle import handleAbortMessage
|
||||
import time
|
||||
import asyncio
|
||||
import json
|
||||
from core.handle.sendAudioHandle import SentenceType
|
||||
from core.utils.util import audio_to_data
|
||||
from core.handle.sendAudioHandle import send_stt_message, SentenceType
|
||||
|
||||
TAG = __name__
|
||||
|
||||
@@ -22,7 +21,6 @@ async def handleAudioMessage(conn, audio):
|
||||
if not hasattr(conn, "vad_resume_task") or conn.vad_resume_task.done():
|
||||
conn.vad_resume_task = asyncio.create_task(resume_vad_detection(conn))
|
||||
return
|
||||
|
||||
if have_voice:
|
||||
if conn.client_is_speaking:
|
||||
await handleAbortMessage(conn)
|
||||
@@ -42,22 +40,22 @@ async def startToChat(conn, text):
|
||||
# 检查输入是否是JSON格式(包含说话人信息)
|
||||
speaker_name = None
|
||||
actual_text = text
|
||||
|
||||
|
||||
try:
|
||||
# 尝试解析JSON格式的输入
|
||||
if text.strip().startswith('{') and text.strip().endswith('}'):
|
||||
if text.strip().startswith("{") and text.strip().endswith("}"):
|
||||
data = json.loads(text)
|
||||
if 'speaker' in data and 'content' in data:
|
||||
speaker_name = data['speaker']
|
||||
actual_text = data['content']
|
||||
if "speaker" in data and "content" in data:
|
||||
speaker_name = data["speaker"]
|
||||
actual_text = data["content"]
|
||||
conn.logger.bind(tag=TAG).info(f"解析到说话人信息: {speaker_name}")
|
||||
|
||||
|
||||
# 直接使用JSON格式的文本,不解析
|
||||
actual_text = text
|
||||
except (json.JSONDecodeError, KeyError):
|
||||
# 如果解析失败,继续使用原始文本
|
||||
pass
|
||||
|
||||
|
||||
# 保存说话人信息到连接对象
|
||||
if speaker_name:
|
||||
conn.current_speaker = speaker_name
|
||||
@@ -118,10 +116,12 @@ async def no_voice_close_connect(conn, have_voice):
|
||||
|
||||
|
||||
async def max_out_size(conn):
|
||||
# 播放超出最大输出字数的提示
|
||||
conn.client_abort = False
|
||||
text = "不好意思,我现在有点事情要忙,明天这个时候我们再聊,约好了哦!明天不见不散,拜拜!"
|
||||
await send_stt_message(conn, text)
|
||||
file_path = "config/assets/max_output_size.wav"
|
||||
opus_packets, _ = audio_to_data(file_path)
|
||||
opus_packets = audio_to_data(file_path)
|
||||
conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text))
|
||||
conn.close_after_chat = True
|
||||
|
||||
@@ -140,7 +140,7 @@ async def check_bind_device(conn):
|
||||
|
||||
# 播放提示音
|
||||
music_path = "config/assets/bind_code.wav"
|
||||
opus_packets, _ = audio_to_data(music_path)
|
||||
opus_packets = audio_to_data(music_path)
|
||||
conn.tts.tts_audio_queue.put((SentenceType.FIRST, opus_packets, text))
|
||||
|
||||
# 逐个播放数字
|
||||
@@ -148,15 +148,17 @@ async def check_bind_device(conn):
|
||||
try:
|
||||
digit = conn.bind_code[i]
|
||||
num_path = f"config/assets/bind_code/{digit}.wav"
|
||||
num_packets, _ = audio_to_data(num_path)
|
||||
num_packets = audio_to_data(num_path)
|
||||
conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, num_packets, None))
|
||||
except Exception as e:
|
||||
conn.logger.bind(tag=TAG).error(f"播放数字音频失败: {e}")
|
||||
continue
|
||||
conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None))
|
||||
else:
|
||||
# 播放未绑定提示
|
||||
conn.client_abort = False
|
||||
text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。"
|
||||
await send_stt_message(conn, text)
|
||||
music_path = "config/assets/bind_not_found.wav"
|
||||
opus_packets, _ = audio_to_data(music_path)
|
||||
opus_packets = audio_to_data(music_path)
|
||||
conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text))
|
||||
|
||||
@@ -30,6 +30,28 @@ async def sendAudioMessage(conn, sentenceType, audios, text):
|
||||
await conn.close()
|
||||
|
||||
|
||||
async def _send_to_mqtt_gateway(conn, opus_packet, timestamp, sequence):
|
||||
"""
|
||||
发送带16字节头部的opus数据包给mqtt_gateway
|
||||
Args:
|
||||
conn: 连接对象
|
||||
opus_packet: opus数据包
|
||||
timestamp: 时间戳
|
||||
sequence: 序列号
|
||||
"""
|
||||
# 为opus数据包添加16字节头部
|
||||
header = bytearray(16)
|
||||
header[0] = 1 # type
|
||||
header[2:4] = len(opus_packet).to_bytes(2, "big") # payload length
|
||||
header[4:8] = sequence.to_bytes(4, "big") # sequence
|
||||
header[8:12] = timestamp.to_bytes(4, "big") # 时间戳
|
||||
header[12:16] = len(opus_packet).to_bytes(4, "big") # opus长度
|
||||
|
||||
# 发送包含头部的完整数据包
|
||||
complete_packet = bytes(header) + opus_packet
|
||||
await conn.websocket.send(complete_packet)
|
||||
|
||||
|
||||
# 播放音频
|
||||
async def sendAudio(conn, audios, frame_duration=60):
|
||||
"""
|
||||
@@ -42,9 +64,6 @@ async def sendAudio(conn, audios, frame_duration=60):
|
||||
"""
|
||||
if audios is None or len(audios) == 0:
|
||||
return
|
||||
# 检查是否需要处理头部(只有当websocket URL以"?from=mqtt"为结尾时才处理头部)
|
||||
request_path = conn.websocket.request.path
|
||||
need_header = request_path.endswith("?from=mqtt")
|
||||
|
||||
if isinstance(audios, bytes):
|
||||
if conn.client_abort:
|
||||
@@ -71,19 +90,19 @@ async def sendAudio(conn, audios, frame_duration=60):
|
||||
if delay > 0:
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
if need_header:
|
||||
# 为opus数据包添加16字节头部
|
||||
timestamp = int((flow_control["start_time"] + flow_control["packet_count"] * frame_duration / 1000) * 1000) % (2**32)
|
||||
header = bytearray(16)
|
||||
header[0] = 1 # type
|
||||
header[2:4] = len(audios).to_bytes(2, 'big') # payload length
|
||||
header[4:8] = flow_control["sequence"].to_bytes(4, 'big') # connection id/sequence
|
||||
header[8:12] = timestamp.to_bytes(4, 'big') # 时间戳
|
||||
header[12:16] = len(audios).to_bytes(4, 'big') # opus长度
|
||||
|
||||
# 发送包含头部的完整数据包
|
||||
complete_packet = bytes(header) + audios
|
||||
await conn.websocket.send(complete_packet)
|
||||
if conn.conn_from_mqtt_gateway:
|
||||
# 计算时间戳
|
||||
timestamp = int(
|
||||
(
|
||||
flow_control["start_time"]
|
||||
+ flow_control["packet_count"] * frame_duration / 1000
|
||||
)
|
||||
* 1000
|
||||
) % (2**32)
|
||||
# 调用通用函数发送带头部的数据包
|
||||
await _send_to_mqtt_gateway(
|
||||
conn, audios, timestamp, flow_control["sequence"]
|
||||
)
|
||||
else:
|
||||
# 直接发送opus数据包,不添加头部
|
||||
await conn.websocket.send(audios)
|
||||
@@ -97,30 +116,21 @@ async def sendAudio(conn, audios, frame_duration=60):
|
||||
start_time = time.perf_counter()
|
||||
play_position = 0
|
||||
|
||||
# 检查是否需要添加头部(只有当websocket URL以"?from=mqtt"为结尾时才添加头部)
|
||||
request_path = conn.websocket.request.path
|
||||
need_header = request_path.endswith("?from=mqtt")
|
||||
|
||||
# 执行预缓冲
|
||||
pre_buffer_frames = min(3, len(audios))
|
||||
for i in range(pre_buffer_frames):
|
||||
if need_header:
|
||||
# 为预缓冲包添加头部
|
||||
timestamp = int((start_time + i * frame_duration / 1000) * 1000) % (2**32)
|
||||
header = bytearray(16)
|
||||
header[0] = 1 # type
|
||||
header[2:4] = len(audios[i]).to_bytes(2, 'big') # payload length
|
||||
header[4:8] = i.to_bytes(4, 'big') # sequence
|
||||
header[8:12] = timestamp.to_bytes(4, 'big') # 时间戳
|
||||
header[12:16] = len(audios[i]).to_bytes(4, 'big') # opus长度
|
||||
|
||||
complete_packet = bytes(header) + audios[i]
|
||||
await conn.websocket.send(complete_packet)
|
||||
if conn.conn_from_mqtt_gateway:
|
||||
# 计算时间戳
|
||||
timestamp = int((start_time + i * frame_duration / 1000) * 1000) % (
|
||||
2**32
|
||||
)
|
||||
# 调用通用函数发送带头部的数据包
|
||||
await _send_to_mqtt_gateway(conn, audios[i], timestamp, i)
|
||||
else:
|
||||
# 直接发送预缓冲包,不添加头部
|
||||
await conn.websocket.send(audios[i])
|
||||
remaining_audios = audios[pre_buffer_frames:]
|
||||
|
||||
|
||||
# 播放剩余音频帧
|
||||
for i, opus_packet in enumerate(remaining_audios):
|
||||
if conn.client_abort:
|
||||
@@ -135,25 +145,19 @@ async def sendAudio(conn, audios, frame_duration=60):
|
||||
delay = expected_time - current_time
|
||||
if delay > 0:
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
if need_header:
|
||||
# 为opus数据包添加16字节头部 (timestamp at offset 8, length at offset 12)
|
||||
timestamp = int((start_time + play_position / 1000) * 1000) % (2**32) # 使用播放位置计算时间戳
|
||||
|
||||
if conn.conn_from_mqtt_gateway:
|
||||
# 计算时间戳和序列号
|
||||
timestamp = int((start_time + play_position / 1000) * 1000) % (
|
||||
2**32
|
||||
) # 使用播放位置计算时间戳
|
||||
sequence = pre_buffer_frames + i # 确保序列号连续
|
||||
header = bytearray(16)
|
||||
header[0] = 1 # type
|
||||
header[2:4] = len(opus_packet).to_bytes(2, 'big') # payload length
|
||||
header[4:8] = sequence.to_bytes(4, 'big') # sequence
|
||||
header[8:12] = timestamp.to_bytes(4, 'big') # 时间戳在第8-11字节
|
||||
header[12:16] = len(opus_packet).to_bytes(4, 'big') # opus长度在第12-15字节
|
||||
|
||||
# 发送包含头部的完整数据包
|
||||
complete_packet = bytes(header) + opus_packet
|
||||
await conn.websocket.send(complete_packet)
|
||||
# 调用通用函数发送带头部的数据包
|
||||
await _send_to_mqtt_gateway(conn, opus_packet, timestamp, sequence)
|
||||
else:
|
||||
# 直接发送opus数据包,不添加头部
|
||||
await conn.websocket.send(opus_packet)
|
||||
|
||||
|
||||
play_position += frame_duration
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user