From be9719e61feab28612499680be9efd7411c5254f Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sat, 26 Sep 2026 08:56:08 +0200 Subject: Let a button press cut the current reply (barge-in) Every press bumps a turn counter and kills the running aplay; queued audio from an older turn is dropped, and reply() now runs on its own thread and returns once its turn is stale, so the button loop is never blocked. A cut-off exchange is left out of history so user and assistant messages still alternate. Co-Authored-By: Claude Opus 5.5 --- assistant.py | 99 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 34 deletions(-) (limited to 'assistant.py') diff --git a/assistant.py b/assistant.py index 635fb81..7724b73 100644 --- a/assistant.py +++ b/assistant.py @@ -260,35 +260,51 @@ def ask(messages, calls, tools=None): yield delta["content"] +turn = 0 # bumped by every button press: older replies and audio are stale +player = None # the aplay speaking now, killed by a new press + + def speaker(q): + global player from kokoro_onnx import Kokoro k = Kokoro(CFG["kokoro"]["model"], CFG["kokoro"]["voices"]) # Each voice is a weighted blend of Kokoro style vectors. voices = {l: sum(w * k.get_voice_style(n) for n, w in v["blend"].items()) for l, v in CFG["voice"].items()} - play = None + play, playing = None, None # aplay and the turn it speaks for while True: - item = q.get() - if item is None: # end of reply - if play: - play.stdin.close() - play.wait() - play = None + t, item = q.get() + if t != turn: # from a reply the user cut off continue - if isinstance(item, float): # seconds of silence - if play: - play.stdin.write(bytes(int(sr * item) * 2)) - continue - text, lang = item - audio, sr = k.create(text, voice=voices[lang], lang=KLANG[lang]) - if play is None: - play = subprocess.Popen(["aplay", "-q", "-r", str(sr), "-f", "S16_LE", "-c", "1"], - stdin=subprocess.PIPE) - # A new stream wakes the suspended sink and the first ~0.2 s is - # lost: lead with silence so it eats that, not the first word. - play.stdin.write(bytes(int(sr * 0.3) * 2)) - # The pipe buffer lets synthesis of the next sentence overlap playback. - play.stdin.write((audio.clip(-1, 1) * 32767).astype("