diff --git a/xtts/voxtral/bridge.py b/xtts/voxtral/bridge.py index 13c514b..cb13048 100644 --- a/xtts/voxtral/bridge.py +++ b/xtts/voxtral/bridge.py @@ -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