Explorar o código

Fix: Follow-Up wartet auf TTS-Abspielen bevor Aufnahme startet

Der Client startete die Follow-Up Aufnahme sofort nach Empfang
des FollowUpRequest — waehrend die TTS-Ausgabe noch lief.
Das Mikrofon nahm die eigene TTS-Ausgabe auf → "Keine Sprache".

Jetzt sendet der Server die TTS-Audio-Dauer (audio_duration) im
FollowUpRequest mit. Der Client wartet diese Zeit + 0.5s Buffer
ab bevor er die Aufnahme startet.

- FollowUpRequest: neues Feld audio_duration (float, Sekunden)
- IntentDispatcher: duration_seconds aus tts_completed durchreichen
- Client: asyncio.sleep(audio_duration + 0.5) vor Follow-Up Start

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
patrick hai 2 meses
pai
achega
e582d8c7fc

+ 13 - 11
trixy_core/client.py

@@ -862,19 +862,21 @@ class ClientApplication(IApplication):
         pdebug(f"Follow-Up-Request empfangen: {request.question[:50]}...")
 
         # Warten bis TTS-Audio fertig abgespielt ist.
-        # Der FollowUpRequest kommt oft gleichzeitig mit dem Audio —
+        # Der FollowUpRequest kommt gleichzeitig mit dem Audio —
         # wir duerfen nicht aufnehmen waehrend Trixy noch spricht,
         # sonst nimmt das Mikrofon die eigene TTS-Ausgabe auf.
-        audio_player = getattr(self, "_audio_player", None)
-        if audio_player and hasattr(audio_player, "is_playing"):
-            import asyncio
-            wait_count = 0
-            while audio_player.is_playing and wait_count < 60:
-                await asyncio.sleep(0.5)
-                wait_count += 1
-            if wait_count > 0:
-                # Kurze Pause nach TTS damit Echo abklingt
-                await asyncio.sleep(0.3)
+        audio_duration = getattr(request, "audio_duration", 0) or 0
+        if not audio_duration:
+            # Fallback: aus audio_data Laenge schaetzen
+            audio_data = getattr(request, "audio_data", "")
+            if audio_data:
+                audio_duration = len(audio_data) / 2 / 16000  # hex → bytes → seconds @16kHz
+            else:
+                audio_duration = 5.0  # Konservativer Default
+
+        if audio_duration > 0:
+            pinfo(f"Follow-Up: Warte {audio_duration:.1f}s bis TTS abgespielt ist")
+            await asyncio.sleep(audio_duration + 0.5)  # +0.5s Buffer fuer Echo
 
         # WakewordService in Follow-Up Modus versetzen
         # (startet neue Aufnahme ohne Wakeword)

+ 1 - 0
trixy_core/network/cmd/wakeword.py

@@ -98,6 +98,7 @@ class FollowUpRequest(CommandMessage):
     audio_data: str = ""              # Hex-encodiertes TTS-Audio (optional)
     follow_up_number: int = 0         # Nummer der Rückfrage
     timeout_seconds: float = 60.0     # Timeout für Antwort
+    audio_duration: float = 0.0       # TTS-Audio-Dauer in Sekunden (fuer Wait)
 
 
 @dataclass

+ 6 - 1
trixy_core/nlp/intent_dispatcher.py

@@ -408,6 +408,7 @@ class IntentDispatcherService(IService):
         audio_data_hex = event_data.get("audio_data", "")
         request_id = event_data.get("request_id", "")
         session_id = event_data.get("session_id", "")
+        duration_seconds = event_data.get("duration_seconds", 0)
 
         # Audio an Satellite senden (falls satellite_id vorhanden)
         if satellite_id and audio_data_hex:
@@ -441,6 +442,7 @@ class IntentDispatcherService(IService):
             await self._send_follow_up_request(
                 followup_info["satellite_id"],
                 followup_info["session_id"],
+                audio_duration=duration_seconds,
             )
         else:
             # Kein Follow-up → ConversationEnd senden
@@ -468,7 +470,9 @@ class IntentDispatcherService(IService):
         except Exception as e:
             perror(f"Fehler beim Senden von ConversationEnd: {e}")
 
-    async def _send_follow_up_request(self, satellite_id: str, session_id: str = "") -> None:
+    async def _send_follow_up_request(
+        self, satellite_id: str, session_id: str = "", audio_duration: float = 0,
+    ) -> None:
         """Sendet FollowUpRequest an den Satellite → Client wechselt in Hoer-Modus."""
         satellites = getattr(self._application, "satellites", None)
         if satellites is None:
@@ -485,6 +489,7 @@ class IntentDispatcherService(IService):
                 session_id=session_id,
                 question="",  # Keine explizite Rueckfrage — Konversation geht einfach weiter
                 timeout_seconds=60.0,
+                audio_duration=audio_duration,
             )
             await satellite.send_command(cmd)
             pdebug(f"FollowUpRequest gesendet an {satellite_id}")