Add binaural recording + tune spatial tracking

binaural_recorder.py: Records left/right ear streams as stereo WAV
in rolling 5-minute segments. Training data for spatial audio models.
Enabled via BINAURAL_RECORD=1 env var.

spatial.py: Tune smoothing — alpha 0.3→0.4 (snappier response),
idle return speed 0.05→0.03 (gentler drift), timeout 2s→1.5s.

headmic.py: Wire binaural recorder into audio loop, add /recording
endpoint for stats, feed both ear streams (not just best beam).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex
2026-04-12 20:53:05 -05:00
parent afc8694c1a
commit 36aeb19280
3 changed files with 157 additions and 4 deletions

View File

@@ -170,6 +170,9 @@ sound_ring_buffer = None # collections.deque, filled by listener_loop
# Speaker recognizer globals
speaker_recognizer = None
enrollment_buffer = None # list of frame bytes, set during enrollment
# Binaural recorder
binaural_recorder = None
enrollment_name = None
# Audio stream
@@ -265,6 +268,13 @@ def listener_loop():
state.active_side = side
# Feed binaural recorder (both ears)
if binaural_recorder:
binaural_recorder.feed(
dual_stream.left.get_frame(),
dual_stream.right.get_frame() if dual_stream.right else None
)
# Feed sound classifier ring buffer
if sound_ring_buffer is not None:
sound_ring_buffer.append(frame_data)
@@ -454,7 +464,7 @@ app = FastAPI(title="HeadMic", description="Vixy's Ears 🦊👂 (Dual XVF3800)"
@app.on_event("startup")
async def startup():
global sound_classifier, sound_ring_buffer, speaker_recognizer, dual_stream, LEDS_AVAILABLE, spatial_tracker
global sound_classifier, sound_ring_buffer, speaker_recognizer, dual_stream, LEDS_AVAILABLE, spatial_tracker, binaural_recorder
state.running = True
@@ -530,6 +540,15 @@ async def startup():
logger.info("Spatial tracking started (%d Hz, %.0fmm baseline, pushing gaze to %s)",
DOA_POLL_HZ, array_sep, EYE_SERVICE_URL)
# --- Binaural recording ---
if os.environ.get("BINAURAL_RECORD", "").lower() in ("1", "true", "yes"):
from binaural_recorder import BinauralRecorder
rec_dir = os.environ.get("BINAURAL_DIR", os.path.expanduser("~/headmic/recordings"))
binaural_recorder = BinauralRecorder(output_dir=rec_dir)
binaural_recorder.start()
else:
logger.info("Binaural recording disabled (set BINAURAL_RECORD=1 to enable)")
# --- Main listener ---
thread = threading.Thread(target=listener_loop, daemon=True)
thread.start()
@@ -540,6 +559,8 @@ async def startup():
async def shutdown():
state.running = False
leds_off()
if binaural_recorder:
binaural_recorder.stop()
if dual_stream:
dual_stream.stop()
@@ -606,6 +627,16 @@ async def doa():
}
# --- Binaural recording ---
@app.get("/recording")
async def recording():
"""Binaural recording status."""
if not binaural_recorder:
return {"recording": False, "enabled": False}
return binaural_recorder.stats
# --- Device info ---
@app.get("/devices")