mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-26 09:03:54 +08:00
update:修复iotbug
This commit is contained in:
@@ -30,15 +30,25 @@ class ASRProviderBase(ABC):
|
||||
@staticmethod
|
||||
def decode_opus(opus_data: List[bytes]) -> bytes:
|
||||
"""将Opus音频数据解码为PCM数据"""
|
||||
try:
|
||||
decoder = opuslib_next.Decoder(16000, 1) # 16kHz, 单声道
|
||||
pcm_data = []
|
||||
buffer_size = 960 # 每次处理960个采样点
|
||||
|
||||
decoder = opuslib_next.Decoder(16000, 1) # 16kHz, 单声道
|
||||
pcm_data = []
|
||||
for opus_packet in opus_data:
|
||||
try:
|
||||
# 使用较小的缓冲区大小进行处理
|
||||
pcm_frame = decoder.decode(opus_packet, buffer_size)
|
||||
if pcm_frame:
|
||||
pcm_data.append(pcm_frame)
|
||||
except opuslib_next.OpusError as e:
|
||||
logger.bind(tag=TAG).warning(f"Opus解码错误,跳过当前数据包: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"音频处理错误: {e}", exc_info=True)
|
||||
continue
|
||||
|
||||
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
|
||||
return pcm_data
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"音频解码过程发生错误: {e}", exc_info=True)
|
||||
return []
|
||||
|
||||
@@ -9,10 +9,14 @@ import uuid
|
||||
from core.providers.asr.base import ASRProviderBase
|
||||
from funasr import AutoModel
|
||||
from funasr.utils.postprocess_utils import rich_transcription_postprocess
|
||||
import shutil
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
MAX_RETRIES = 2
|
||||
RETRY_DELAY = 1 # 重试延迟(秒)
|
||||
|
||||
|
||||
# 捕获标准输出
|
||||
class CaptureOutput:
|
||||
@@ -68,46 +72,69 @@ class ASRProvider(ASRProviderBase):
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""语音转文本主处理逻辑"""
|
||||
file_path = None
|
||||
try:
|
||||
# 合并所有opus数据包
|
||||
if self.audio_format == "pcm":
|
||||
pcm_data = opus_data
|
||||
else:
|
||||
pcm_data = self.decode_opus(opus_data)
|
||||
retry_count = 0
|
||||
|
||||
combined_pcm_data = b"".join(pcm_data)
|
||||
while retry_count < MAX_RETRIES:
|
||||
try:
|
||||
# 合并所有opus数据包
|
||||
if self.audio_format == "pcm":
|
||||
pcm_data = opus_data
|
||||
else:
|
||||
pcm_data = self.decode_opus(opus_data)
|
||||
|
||||
# 判断是否保存为WAV文件
|
||||
if self.delete_audio_file:
|
||||
pass
|
||||
else:
|
||||
file_path = self.save_audio_to_file(pcm_data, session_id)
|
||||
combined_pcm_data = b"".join(pcm_data)
|
||||
|
||||
# 语音识别
|
||||
start_time = time.time()
|
||||
result = self.model.generate(
|
||||
input=combined_pcm_data,
|
||||
cache={},
|
||||
language="auto",
|
||||
use_itn=True,
|
||||
batch_size_s=60,
|
||||
)
|
||||
text = rich_transcription_postprocess(result[0]["text"])
|
||||
logger.bind(tag=TAG).debug(
|
||||
f"语音识别耗时: {time.time() - start_time:.3f}s | 结果: {text}"
|
||||
)
|
||||
# 检查磁盘空间
|
||||
if not self.delete_audio_file:
|
||||
free_space = shutil.disk_usage(self.output_dir).free
|
||||
if free_space < len(combined_pcm_data) * 2: # 预留2倍空间
|
||||
raise OSError("磁盘空间不足")
|
||||
|
||||
return text, file_path
|
||||
# 判断是否保存为WAV文件
|
||||
if self.delete_audio_file:
|
||||
pass
|
||||
else:
|
||||
file_path = self.save_audio_to_file(pcm_data, session_id)
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"语音识别失败: {e}", exc_info=True)
|
||||
return "", file_path
|
||||
# 语音识别
|
||||
start_time = time.time()
|
||||
result = self.model.generate(
|
||||
input=combined_pcm_data,
|
||||
cache={},
|
||||
language="auto",
|
||||
use_itn=True,
|
||||
batch_size_s=60,
|
||||
)
|
||||
text = rich_transcription_postprocess(result[0]["text"])
|
||||
logger.bind(tag=TAG).debug(
|
||||
f"语音识别耗时: {time.time() - start_time:.3f}s | 结果: {text}"
|
||||
)
|
||||
|
||||
# finally:
|
||||
# # 文件清理逻辑
|
||||
# if self.delete_audio_file and file_path and os.path.exists(file_path):
|
||||
# try:
|
||||
# os.remove(file_path)
|
||||
# logger.bind(tag=TAG).debug(f"已删除临时音频文件: {file_path}")
|
||||
# except Exception as e:
|
||||
# logger.bind(tag=TAG).error(f"文件删除失败: {file_path} | 错误: {e}")
|
||||
return text, file_path
|
||||
|
||||
except OSError as e:
|
||||
retry_count += 1
|
||||
if retry_count >= MAX_RETRIES:
|
||||
logger.bind(tag=TAG).error(
|
||||
f"语音识别失败(已重试{retry_count}次): {e}", exc_info=True
|
||||
)
|
||||
return "", file_path
|
||||
logger.bind(tag=TAG).warning(
|
||||
f"语音识别失败,正在重试({retry_count}/{MAX_RETRIES}): {e}"
|
||||
)
|
||||
time.sleep(RETRY_DELAY)
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"语音识别失败: {e}", exc_info=True)
|
||||
return "", file_path
|
||||
|
||||
finally:
|
||||
# 文件清理逻辑
|
||||
if self.delete_audio_file and file_path and os.path.exists(file_path):
|
||||
try:
|
||||
os.remove(file_path)
|
||||
logger.bind(tag=TAG).debug(f"已删除临时音频文件: {file_path}")
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(
|
||||
f"文件删除失败: {file_path} | 错误: {e}"
|
||||
)
|
||||
|
||||
@@ -72,6 +72,10 @@ class IntentProvider(IntentProviderBase):
|
||||
'返回: {"function_call": {"name": "get_time"}}\n'
|
||||
"```\n"
|
||||
"```\n"
|
||||
"用户: 当前电池电量是多少?\n"
|
||||
'返回: {"function_call": {"name": "get_battery_level", "arguments": {"response_success": "当前电池电量为{value}%", "response_failure": "无法获取Battery的当前电量百分比"}}}\n'
|
||||
"```\n"
|
||||
"```\n"
|
||||
"用户: 我想结束对话\n"
|
||||
'返回: {"function_call": {"name": "handle_exit_intent", "arguments": {"say_goodbye": "goodbye"}}}\n'
|
||||
"```\n"
|
||||
@@ -224,7 +228,8 @@ class IntentProvider(IntentProviderBase):
|
||||
if function_name == "continue_chat":
|
||||
# 保留非工具相关的消息
|
||||
clean_history = [
|
||||
msg for msg in conn.dialogue.dialogue
|
||||
msg
|
||||
for msg in conn.dialogue.dialogue
|
||||
if msg.role not in ["tool", "function"]
|
||||
]
|
||||
conn.dialogue.dialogue = clean_history
|
||||
|
||||
Reference in New Issue
Block a user