Switching the mic off interrupts speech (distinct tone); background jobs keep running
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LzKpYaEsuWDevuNnvSjcFW
This commit is contained in:
@@ -91,6 +91,9 @@ class Assistant:
|
||||
self.conn = None
|
||||
self.jobs: set[asyncio.Task] = set()
|
||||
self.greeted = False
|
||||
self.current_response = ""
|
||||
self.response_active = False
|
||||
self.cancelled_responses: set[str] = set()
|
||||
|
||||
# ---------- alati ----------
|
||||
async def tool_wikipedia(self, args: dict) -> str:
|
||||
@@ -155,6 +158,20 @@ class Assistant:
|
||||
ok = False
|
||||
await self.notify_model(("GOTOVO. " if ok else "GREŠKA. ") + result + " Reci to djetetu kratko, na bosanskom.")
|
||||
|
||||
async def interrupt_speech(self) -> None:
|
||||
"""Prekini govor koji je u toku (pozadinski poslovi nastavljaju)."""
|
||||
speaking = self.speaker.is_playing(0.0) or self.response_active
|
||||
if self.response_active and self.conn:
|
||||
self.cancelled_responses.add(self.current_response)
|
||||
try:
|
||||
await self.conn.send({"type": "response.cancel"})
|
||||
except Exception: # noqa: BLE001
|
||||
log.exception("response.cancel nije uspio")
|
||||
if speaking:
|
||||
await self.speaker.clear()
|
||||
log.info("govor prekinut (mikrofon ugašen)")
|
||||
await self.speaker.write(audio.tone_off())
|
||||
|
||||
async def notify_model(self, text: str) -> None:
|
||||
if not self.conn:
|
||||
return
|
||||
@@ -193,19 +210,20 @@ class Assistant:
|
||||
present = False
|
||||
while True:
|
||||
now = asyncio.get_event_loop().time()
|
||||
if now - last_check > 1.5:
|
||||
if now - last_check > 0.7:
|
||||
present = await audio.source_present(self.cfg.mic_device)
|
||||
last_check = now
|
||||
if not present:
|
||||
if self.mic.running:
|
||||
await self.mic.stop()
|
||||
log.info("mikrofon isključen (prekidač), ne slušam")
|
||||
await asyncio.sleep(0.5)
|
||||
await self.interrupt_speech()
|
||||
await asyncio.sleep(0.3)
|
||||
continue
|
||||
if not self.mic.running:
|
||||
await self.mic.start()
|
||||
log.info("mikrofon uključen, slušam")
|
||||
await self.speaker.write(audio.tone())
|
||||
await self.speaker.write(audio.tone_on())
|
||||
chunk = await self.mic.read()
|
||||
if not chunk:
|
||||
await self.mic.stop()
|
||||
@@ -220,7 +238,14 @@ class Assistant:
|
||||
async for event in self.conn:
|
||||
t = event.type
|
||||
if t == "response.output_audio.delta":
|
||||
if getattr(event, "response_id", "") in self.cancelled_responses:
|
||||
continue
|
||||
await self.speaker.write(base64.b64decode(event.delta))
|
||||
elif t == "response.created":
|
||||
self.current_response = event.response.id or ""
|
||||
self.response_active = True
|
||||
elif t == "response.done":
|
||||
self.response_active = False
|
||||
elif t == "response.output_audio_transcript.delta":
|
||||
transcript += event.delta
|
||||
elif t == "response.output_audio_transcript.done":
|
||||
|
||||
@@ -36,6 +36,16 @@ def tone(freq: float = 880.0, ms: int = 150, volume: float = 0.3) -> bytes:
|
||||
return samples.tobytes()
|
||||
|
||||
|
||||
def tone_on() -> bytes:
|
||||
"""Mikrofon uključen: dva tona naviše."""
|
||||
return tone(660, 120) + tone(990, 160)
|
||||
|
||||
|
||||
def tone_off() -> bytes:
|
||||
"""Mikrofon isključen / prekid govora: dva tona naniže."""
|
||||
return tone(990, 120) + tone(440, 220)
|
||||
|
||||
|
||||
async def source_present(name: str) -> bool:
|
||||
"""Da li PipeWire trenutno ima izvor tog imena (USB mikrofon nestane kad se isključi prekidač)."""
|
||||
if not name:
|
||||
|
||||
Reference in New Issue
Block a user