Skip to content

Surface an all-silent capture instead of transcribing it #88

Description

@jamditis

A capture that is silent for its whole duration is currently transcribed like any other. It should be surfaced as a condition, because the two things that produce it are both real and both invisible from the loop's output.

The two sources

A hardware mute. The Anker PowerConf has a capacitive mute that gates the stream with no Linux-level signal — flagged as an operational cost in #36. Nothing in the capture path can tell a muted mic from a quiet room.

A dead or wrong device. A device that opens successfully and delivers frames of nothing, which is the shape a disconnected or misrouted input takes when the stream itself is healthy.

Both present identically: capture_request runs, returns frames, transcription produces nothing or hallucinated filler, and the loop reports no error. The operator sees an assistant that stopped responding, with no indication of which layer to look at.

This is the same diagnostic gap 394a805 closed for narrowband capture, and the reasoning there applies unchanged: the failure is invisible at every stage before the transcript, so the mic is the last thing suspected. That warning cost an afternoon to earn. This is the adjacent case, where the audio is not degraded but absent.

Why it is worth its own check

Energy endpointing cannot distinguish these, and adding the case to endpointing would be the wrong place. pipeline.py:686-690 counts frames below _SILENCE_RMS to find the trailing edge of speech. An all-silent capture is not a short utterance, it is the absence of one, and treating it as an endpoint just yields an empty transcript faster.

It also sits on the other side of #35. That bug is over-capture — sustained noise resets the quiet counter so a capture never ends and runs to the 8-second cap. This is under-capture, where every frame is quiet. Same blind spot in the same signal, opposite directions, and one condition check covers this half.

Shape

Cheap, and hardware-free in the same way capture_quality.py already is:

  • After a capture completes, if every frame is below _SILENCE_RMS, do not transcribe it.
  • Report it as a distinct condition rather than an empty result — the message should name what to check (mute button, device selection) rather than only stating that nothing was heard.
  • The threshold already exists and is already the pipeline's notion of "not speech," so no new tuning constant.

Worth being deliberate about one edge: a genuine wake with no follow-up speech is already handled by _NO_SPEECH_ONSET_FRAMES, so this should apply to the captured-request path and not double-report the abandoned-wake case.

Testable without a microphone

An array of zeros or low-amplitude noise is enough to exercise it, matching the hardware-free-core split the project already draws and test_capture_quality.py's existing approach.

Split out of #36 rather than folded in, since that issue is a mic comparison and this is pipeline reliability that pays off regardless of which mic wins.

wake-20260730T0805-f4f8fd

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew capability or featurereliabilityCorrectness, error handling, and unattended robustness

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions