|
|
|
@@ -126,6 +126,11 @@ SILERO_VAD_ENABLED = os.getenv("SILERO_VAD_ENABLED", "true").lower() in ("1", "t
|
|
|
|
|
SILERO_VAD_THRESHOLD = float(os.getenv("SILERO_VAD_THRESHOLD", "0.5"))
|
|
|
|
|
SILERO_MIN_SPEECH_MS = int(os.getenv("SILERO_MIN_SPEECH_MS", "150"))
|
|
|
|
|
SILERO_PAD_MS = int(os.getenv("SILERO_PAD_MS", "200"))
|
|
|
|
|
# Speech-Endpoint gegen laute Umgebungsmusik: der RMS-Stille-Endpoint feuert bei
|
|
|
|
|
# durchgehender Musik NIE (Energie bleibt oben). Deshalb waehrend lauter Phasen
|
|
|
|
|
# periodisch (alle X ms) mit Silero pruefen, ob im letzten endpoint_ms-Fenster
|
|
|
|
|
# ueberhaupt noch Sprache ist — wenn nicht (nur Musik/Stille), Turn beenden.
|
|
|
|
|
STREAM_SPEECH_ENDPOINT_CHECK_MS = int(os.getenv("STREAM_SPEECH_ENDPOINT_CHECK_MS", "700"))
|
|
|
|
|
|
|
|
|
|
_vad_state = {"model": None, "get_ts": None, "failed": False}
|
|
|
|
|
|
|
|
|
@@ -281,6 +286,7 @@ class StreamSession:
|
|
|
|
|
last_growth_at: float = 0.0
|
|
|
|
|
last_transcribe_at: float = 0.0
|
|
|
|
|
last_voice_at: float = 0.0
|
|
|
|
|
last_speech_check_at: float = 0.0 # Drossel fuer den Silero-Speech-Endpoint
|
|
|
|
|
noise_floor: float = 0.0
|
|
|
|
|
closed: bool = False
|
|
|
|
|
endpoint_sent: bool = False
|
|
|
|
@@ -506,6 +512,26 @@ class SessionManager:
|
|
|
|
|
"audioRequestId": sess.audio_request_id,
|
|
|
|
|
"text": "",
|
|
|
|
|
})
|
|
|
|
|
# Speech-Endpoint gegen laute Umgebungsmusik: es ist gerade laut (rms
|
|
|
|
|
# ueber Schwelle) — aber ist es Sprache oder Musik? Der RMS-Endpoint
|
|
|
|
|
# unten wuerde bei Musik NIE feuern (last_voice_at bleibt frisch).
|
|
|
|
|
# Deshalb gedrosselt mit Silero das letzte endpoint_ms-Fenster pruefen:
|
|
|
|
|
# KEINE Sprache drin (nur Musik) → Turn ist zu Ende. Real gesprochene
|
|
|
|
|
# Turns haben Sprache im Fenster → laufen weiter.
|
|
|
|
|
if (self._buffer_ms(sess) >= sess.endpoint_ms
|
|
|
|
|
and (now - sess.last_speech_check_at) * 1000.0 >= STREAM_SPEECH_ENDPOINT_CHECK_MS):
|
|
|
|
|
sess.last_speech_check_at = now
|
|
|
|
|
try:
|
|
|
|
|
tail_bytes = int(sess.endpoint_ms / 1000.0 * sess.sample_rate) * 2
|
|
|
|
|
tail = pcm_s16le_to_float32(bytes(sess.pcm_buffer[-tail_bytes:]))
|
|
|
|
|
segs = _speech_segments(tail)
|
|
|
|
|
if segs is not None and len(segs) == 0:
|
|
|
|
|
logger.info("Stream %s: Speech-Endpoint (keine Sprache im letzten %dms — Musik/Stille) → finalize",
|
|
|
|
|
sess.request_id[:8], sess.endpoint_ms)
|
|
|
|
|
await self._finalize(sess, "endpoint")
|
|
|
|
|
return
|
|
|
|
|
except Exception:
|
|
|
|
|
logger.exception("Speech-Endpoint-Check fehlgeschlagen — ignoriert")
|
|
|
|
|
else:
|
|
|
|
|
self._update_noise_floor(sess, rms)
|
|
|
|
|
# No-Speech-Timeout: wurde die GANZE Zeit KEINE Stimme erkannt
|
|
|
|
|