diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index b2cc9fe6..4a410399 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -454,7 +454,7 @@ VAD: threshold: 0.5 threshold_low: 0.3 model_dir: models/snakers4_silero-vad - min_silence_duration_ms: 200 # 如果说话停顿比较长,可以把这个值设置大一些 + min_silence_duration_ms: 200 # 如果说话停顿比较长,可以把这个值设置大一些,实际延迟为:min_silence_duration_ms // 32 * 32ms LLM: # 所有openai类型均可以修改超参,以AliLLM为例 diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index 4337a488..fe213558 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -117,8 +117,8 @@ class ConnectionHandler: self.client_have_voice = False self.last_activity_time = 0.0 # 统一的活动时间戳(毫秒) self.client_voice_stop = False - self.client_voice_window = deque(maxlen=5) self.last_is_voice = False + self.continuous_false_count = 0 # asr相关变量 # 因为实际部署时可能会用到公共的本地ASR,不能把变量暴露给公共ASR diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index e7b6a138..4eaf9ab1 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -33,7 +33,7 @@ async def handleAudioMessage(conn, audio): async def resume_vad_detection(conn): # 等待2秒后恢复VAD检测 - await asyncio.sleep(1) + await asyncio.sleep(2) conn.just_woken_up = False diff --git a/main/xiaozhi-server/core/providers/asr/base.py b/main/xiaozhi-server/core/providers/asr/base.py index 250a25f2..2562f589 100644 --- a/main/xiaozhi-server/core/providers/asr/base.py +++ b/main/xiaozhi-server/core/providers/asr/base.py @@ -68,7 +68,7 @@ class ASRProviderBase(ABC): conn.asr_audio.clear() conn.reset_vad_states() - if len(asr_audio_task) > 15: + if len(asr_audio_task) > 15 or conn.client_listen_mode == "manual": await self.handle_voice_stop(conn, asr_audio_task) # 处理语音停止 diff --git a/main/xiaozhi-server/core/providers/vad/silero.py b/main/xiaozhi-server/core/providers/vad/silero.py index b516d8fb..70cb5459 100644 --- a/main/xiaozhi-server/core/providers/vad/silero.py +++ b/main/xiaozhi-server/core/providers/vad/silero.py @@ -1,6 +1,6 @@ import time -import numpy as np import torch +import numpy as np import opuslib_next from config.logger import setup_logging from core.providers.vad.base import VADProviderBase @@ -26,15 +26,12 @@ class VADProvider(VADProviderBase): threshold_low = config.get("threshold_low", "0.2") min_silence_duration_ms = config.get("min_silence_duration_ms", "1000") + # 双阈值判断参数 self.vad_threshold = float(threshold) if threshold else 0.5 self.vad_threshold_low = float(threshold_low) if threshold_low else 0.2 - self.silence_threshold_ms = ( - int(min_silence_duration_ms) if min_silence_duration_ms else 1000 - ) - - # 至少要多少帧才算有语音 - self.frame_window_threshold = 3 + # 静默阈值,连续多少帧低于最低值才判断为静音 + self.stop_false_count = int(min_silence_duration_ms) // 32 if min_silence_duration_ms else 31 def is_vad(self, conn, opus_packet): try: @@ -59,24 +56,24 @@ class VADProvider(VADProviderBase): # 双阈值判断 if speech_prob >= self.vad_threshold: - is_voice = True + client_have_voice = True elif speech_prob <= self.vad_threshold_low: - is_voice = False + client_have_voice = False else: - is_voice = conn.last_is_voice + client_have_voice = conn.last_is_voice + + if not client_have_voice: + conn.continuous_false_count += 1 + else: + conn.continuous_false_count = 0 # 声音没低于最低值则延续前一个状态,判断为有声音 - conn.last_is_voice = is_voice + conn.last_is_voice = client_have_voice - # 更新滑动窗口 - conn.client_voice_window.append(is_voice) - client_have_voice = (conn.client_voice_window.count(True) >= self.frame_window_threshold) + # 如果之前有声音,且连续为静音帧,达到静默阈值要求帧数,则认为已经说完一句话 + if conn.client_have_voice and conn.continuous_false_count >= self.stop_false_count: + conn.client_voice_stop = True - # 如果之前有声音,但本次没有声音,且与上次有声音的时间差已经超过了静默阈值,则认为已经说完一句话 - if conn.client_have_voice and not client_have_voice: - stop_duration = time.time() * 1000 - conn.last_activity_time - if stop_duration >= self.silence_threshold_ms: - conn.client_voice_stop = True if client_have_voice: conn.client_have_voice = True conn.last_activity_time = time.time() * 1000