fixed voice chat crashing when releasing V

This commit is contained in:
Lucca Pirovano
2026-07-16 17:36:33 -04:00
parent f806fa8efc
commit b30fc10648
+53 -1
View File
@@ -167,6 +167,8 @@ class LiveMicRelay:
self.input_device_index = input_device_index
self.active = False
# "idle" | "connecting" | "live" - read by the HUD to show status
self.state = "idle"
self._ssh_client = None
self._ssh_channel = None
self._sock = None
@@ -186,12 +188,30 @@ class LiveMicRelay:
if not HAS_SSH:
print("🎙️ Live mic needs paramiko: pip install paramiko")
return
# If a previous relay thread is still tearing itself down, wait for
# it to finish before reusing self._stream/_sock/_ssh_* for a new one.
if self._thread and self._thread.is_alive():
self._thread.join(timeout=2.0)
self.active = True
self.state = "connecting"
self._thread = threading.Thread(target=self._run, daemon=True)
self._thread.start()
def stop(self):
"""Signal the relay thread to stop and wait for it to exit.
Deliberately does NOT touch self._stream/_sock/_ssh_* here: this is
called from the main/render thread on key-up, and closing a
PortAudio stream from a different thread while the relay thread is
mid blocking-read() on it is what caused the ALSA segfault on
release. Only _run() (via _cleanup, in its own thread) closes them.
"""
self.active = False
self.state = "idle"
if self._thread and self._thread.is_alive() and threading.current_thread() is not self._thread:
self._thread.join(timeout=2.0)
def _cleanup(self):
if self._sock:
try: self._sock.close()
except: pass
@@ -237,6 +257,8 @@ class LiveMicRelay:
input_device_index=self.input_device_index,
frames_per_buffer=1024)
print("🎙️ Talk relay: live")
if self.active: # could've been stopped while we were still connecting
self.state = "live"
while self.active:
try:
@@ -247,7 +269,9 @@ class LiveMicRelay:
except Exception as e:
print(f"🎙️ Talk relay error: {e}")
finally:
self.stop()
self.active = False
self.state = "idle"
self._cleanup()
print("🎙️ Talk relay: stopped")
# ==========================================
@@ -685,6 +709,34 @@ class NaoTeleop:
text = self.font.render(f"{self.music.current_title[:45]}", True, (0, 255, 100))
self.screen.blit(text, (10, 10))
# Voice chat HUD (top-right) - only shown while V is held / connecting
relay_state = getattr(self.mic_relay, "state", "idle")
if relay_state == "connecting":
label, color = "VOICE: CONNECTING...", (255, 200, 0)
elif relay_state == "live":
label, color = "VOICE: LIVE - speak now", (0, 255, 100)
else:
label, color = None, None
if label:
text = self.font.render(label, True, color)
pad = 8
box_w, box_h = text.get_width() + pad * 2 + 18, text.get_height() + pad
box_x = self.display_width - box_w - 10
box_y = 10
s = pygame.Surface((box_w, box_h))
s.set_alpha(180)
s.fill((0, 0, 0))
self.screen.blit(s, (box_x, box_y))
# pulsing dot for "live" so it's easy to catch out of the corner of your eye
if relay_state == "live":
pulse = 0.5 + 0.5 * abs((pygame.time.get_ticks() % 1000) / 500.0 - 1.0)
dot_color = tuple(int(c * pulse) for c in color)
else:
dot_color = color
pygame.draw.circle(self.screen, dot_color, (box_x + pad + 5, box_y + box_h // 2), 5)
self.screen.blit(text, (box_x + pad + 18, box_y + pad // 2))
# Music search interface
if self.music_search_mode:
box_h = min(self.display_height - 80, 260)