fix: setup voice before recv_loop, drain responses, check ws state
Root cause: recv_loop started before open_voice completed, bridge connection died during UI transition. Now setup completes first. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
216f2fe6a0
commit
ec17c085b2
33
relay.py
33
relay.py
|
|
@ -100,14 +100,25 @@ class BridgeClient:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Bridge recv error: {e}")
|
log.error(f"Bridge recv error: {e}")
|
||||||
|
|
||||||
|
async def _send_and_wait(self, cmd, wait_secs=1):
|
||||||
|
"""Send command and consume the response."""
|
||||||
|
self.send_cmd(cmd)
|
||||||
|
await asyncio.sleep(wait_secs)
|
||||||
|
# Drain any pending responses
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
ftype, data = await asyncio.wait_for(self._recv_frame(), timeout=0.5)
|
||||||
|
if ftype == 1:
|
||||||
|
msg = json.loads(data.decode())
|
||||||
|
log.info(f"Bridge: {msg}")
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
break
|
||||||
|
|
||||||
async def setup_voice(self):
|
async def setup_voice(self):
|
||||||
"""Open voice chat, start capture, enable inject."""
|
"""Open voice chat, start capture, enable inject."""
|
||||||
self.send_cmd({"cmd": "open_voice"})
|
await self._send_and_wait({"cmd": "open_voice"}, wait_secs=3)
|
||||||
await asyncio.sleep(3)
|
await self._send_and_wait({"cmd": "start"}, wait_secs=1)
|
||||||
self.send_cmd({"cmd": "start"})
|
await self._send_and_wait({"cmd": "inject_on"}, wait_secs=0.5)
|
||||||
await asyncio.sleep(1)
|
|
||||||
self.send_cmd({"cmd": "inject_on"})
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
log.info("Voice bridge ready (inject mode)")
|
log.info("Voice bridge ready (inject mode)")
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
|
|
@ -144,15 +155,15 @@ class Relay:
|
||||||
self.opus_decoder = opuslib.Decoder(ESP_SAMPLE_RATE, 1)
|
self.opus_decoder = opuslib.Decoder(ESP_SAMPLE_RATE, 1)
|
||||||
self.opus_encoder = opuslib.Encoder(ESP_SAMPLE_RATE, 1, opuslib.APPLICATION_AUDIO)
|
self.opus_encoder = opuslib.Encoder(ESP_SAMPLE_RATE, 1, opuslib.APPLICATION_AUDIO)
|
||||||
|
|
||||||
# Connect to voice bridge
|
# Connect to voice bridge and setup voice chat first
|
||||||
self.bridge = BridgeClient(self.bridge_host, self.bridge_port)
|
self.bridge = BridgeClient(self.bridge_host, self.bridge_port)
|
||||||
await self.bridge.connect()
|
await self.bridge.connect()
|
||||||
|
await self.bridge.setup_voice()
|
||||||
|
|
||||||
|
# Now start receiving speaker audio
|
||||||
self.bridge.on_speaker_frame = self._on_speaker_frame
|
self.bridge.on_speaker_frame = self._on_speaker_frame
|
||||||
recv_task = asyncio.create_task(self.bridge.start_recv_loop())
|
recv_task = asyncio.create_task(self.bridge.start_recv_loop())
|
||||||
|
|
||||||
# Setup voice chat
|
|
||||||
await self.bridge.setup_voice()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async for message in websocket:
|
async for message in websocket:
|
||||||
if isinstance(message, str):
|
if isinstance(message, str):
|
||||||
|
|
@ -226,7 +237,7 @@ class Relay:
|
||||||
|
|
||||||
async def _on_speaker_frame(self, pcm_bytes):
|
async def _on_speaker_frame(self, pcm_bytes):
|
||||||
"""Receive speaker PCM from bridge, resample, encode Opus, send to ESP32."""
|
"""Receive speaker PCM from bridge, resample, encode Opus, send to ESP32."""
|
||||||
if not self.ws:
|
if not self.ws or self.ws.closed:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
samples = np.frombuffer(pcm_bytes, dtype=np.int16)
|
samples = np.frombuffer(pcm_bytes, dtype=np.int16)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue