""" builtin_mic.py — backward-compat shim. The G1 on-board microphone implementation now lives in [Voice/audio_io.py](Voice/audio_io.py) where it can be paired with the matching BuiltinSpeaker via `AudioIO.from_profile("builtin", ...)` — the same structure Sanad uses. This module exists so existing imports (`from Voice.builtin_mic import BuiltinMic`) keep working for the non-Gemini voice paths and for `API/audio_api.py`. It subclasses the canonical `BuiltinMic` and adds `read_seconds()`, which is used by `API/audio_api.record()`. Do not add new code here — extend Voice/audio_io.py instead. """ from __future__ import annotations import os import sys import time # When run directly (`python3 Voice/builtin_mic.py`) the parent # directory isn't on sys.path, so the `Voice.audio_io` import below # would fail. Add it before the import resolves. _PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if _PROJECT_DIR not in sys.path: sys.path.insert(0, _PROJECT_DIR) from Voice.audio_io import BuiltinMic as _BaseBuiltinMic class BuiltinMic(_BaseBuiltinMic): """G1 on-board mic + `read_seconds()` convenience.""" def read_seconds(self, seconds: float) -> bytes: """Capture `seconds` of audio and return as bytes.""" num_bytes = int(seconds * self.sample_rate * 2) out = bytearray() chunk_bytes = 1024 while len(out) < num_bytes: out.extend(self.read_chunk(min(chunk_bytes, num_bytes - len(out)))) return bytes(out) # Standalone test — capture 3 s and print energy stats if __name__ == "__main__": import array print("BuiltinMic standalone test — capturing 3 s from G1...") mic = BuiltinMic() mic.start() time.sleep(0.3) raw = mic.read_seconds(3.0) mic.stop() samples = array.array("h", raw) if not samples: print(" FAIL — got zero samples") else: mn = min(samples); mx = max(samples) mean_abs = sum(abs(s) for s in samples) / len(samples) print(f" samples={len(samples)} min={mn} max={mx} mean|s|={mean_abs:.0f}") if mean_abs > 30: print(" OK — mic is capturing audio") else: print(" WARN — signal very low")