Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-23 06:25:41 -06:00
commit 80bef6f524
63 changed files with 5674 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
"""Barge-in: notice that the user started talking *while the pet is talking*
so playback can be cut short mid-sentence.
Deliberately dumber than the utterance VAD in mic.py. The mic hears the pet's
own voice coming back out of the speakers, so a single loud frame proves
nothing — this requires several consecutive frames well above the normal
speech threshold (BARGE_IN_RMS_THRESHOLD defaults to 4x VAD_RMS_THRESHOLD).
Takes the same injectable stream shape as mic.record_utterance, so tests feed
it fake frames instead of real audio hardware.
"""
from __future__ import annotations
import numpy as np
from .. import config
from .mic import AudioStream, rms
class BargeInDetector:
"""Poll-driven: call check() repeatedly while audio plays. Each call
consumes exactly one mic frame (80ms at the default frame length), which
is also what paces the playback loop's polling."""
def __init__(
self,
stream: AudioStream,
threshold: int = None,
required_frames: int = None,
frame_len: int = config.FRAME_LEN,
):
self._stream = stream
self._threshold = config.BARGE_IN_RMS_THRESHOLD if threshold is None else threshold
self._required = max(1, config.BARGE_IN_FRAMES if required_frames is None else required_frames)
self._frame_len = frame_len
self._loud_frames = 0
@property
def loud_frames(self) -> int:
return self._loud_frames
def reset(self) -> None:
self._loud_frames = 0
def check(self) -> bool:
"""True once the user has been loud for long enough to count as an
interruption. Never raises: a mic hiccup mid-playback should not kill
the reply, it should just mean "no barge-in this frame"."""
try:
chunk, _ = self._stream.read(self._frame_len)
except Exception:
return False
frame = np.asarray(chunk)
if frame.ndim > 1:
frame = frame[:, 0]
if frame.size == 0:
return False
if rms(frame) >= self._threshold:
self._loud_frames += 1
else:
self._loud_frames = 0 # a single thump/cough shouldn't count
return self._loud_frames >= self._required