Why a transcriber writes words over empty audio, and the gate that stops it · 2026-09-30
faster-whisper base over a 133.8 second track produced five words,
each of them You, placed at 00:00, 00:30, 01:00, 01:30 and 02:00.
The audio contained no sound at any point.
$ ffmpeg -i clip.mp4 -af volumedetect -f null - n_samples: 2140160 mean_volume: -91.0 dB max_volume: -91.0 dB
Mean equal to max is the decisive reading. It means every one of the 2,140,160 samples holds the same value, so the track is digital silence from the first sample to the last. A 16 bit floor is around -96 dB, so -91.0 is the noise floor of an empty container.
| Track | mean dBFS | max dBFS | Reading |
|---|---|---|---|
| Empty clip A | -91.0 | -91.0 | digital silence |
| Empty clip B | -91.0 | -91.0 | digital silence |
| Empty clip C | -91.0 | -91.0 | digital silence |
| Real speech A | -21.6 | -0.4 | has signal |
| Real speech B | -24.3 | -0.0 | has signal |
The separation is absolute. There is no value between -24.3 and -91.0.
You, Thank you for watching and Thanks for watching! are among the most common closing lines in the corpus.The check for success was words > 0. Five is greater than zero, so the run was
stamped OK and five fabricated words were reported as content. That is counting output
rather than measuring it. A count cannot tell invented text from transcribed text.
A duration based silence rule did not save it either. A rule that cuts at the first silent run lasting past five minutes cannot fire on a clip of 133.8 seconds, because a 133.8 second file structurally cannot contain a 300 second run.
def volume_stats(path):
r = run(["ffmpeg", "-i", path, "-af", "volumedetect", "-f", "null", "-"])
mean = float(re.search(r"mean_volume: ([-0-9.]+) dB", r.stderr).group(1))
mx = float(re.search(r"max_volume: ([-0-9.]+) dB", r.stderr).group(1))
dead = abs(mean - mx) < 0.01 or mx <= -60.0
return mean, mx, dead
# called BEFORE the model is loaded
if dead_audio:
return "NO_SPEECH", f"digital silence, mean {mean} dBFS and max {mx} dBFS"
Whisper is never started on an empty track, so there is no window in which it can invent a token. A second guard remains as a backstop: any output under 30 words carrying three or fewer unique tokens is labelled a hallucination rather than counted as speech.
| Clip | Before | After |
|---|---|---|
| Empty clip A, 0.37 min | EMPTY_AFTER_TRANSCRIBE | NO_SPEECH, basis printed |
| Empty clip B, 0.33 min | EMPTY_AFTER_TRANSCRIBE | NO_SPEECH, basis printed |
| Empty clip C, 2.23 min | OK, 5 words "You" | NO_SPEECH, basis printed |
| Real speech A, 11.47 min | OK | OK, unchanged |
| Real speech B, 4.89 min | OK | OK, unchanged |
Meetings pause. People step away for fifteen minutes and come back, and the material after the pause is often the part worth having. A transcriber that stops at the first long silence throws away the second half of the meeting.
silencedetect=noise=-40dB:d=900, to FIND breaks rather than to act on them.00:05 to 16:05 (16.0 min) - transcription CONTINUES past this.Validated against a purpose-built file: spoken audio, then 16 minutes of silence, then a second and unrelated spoken passage. Both passages were transcribed, the break was labelled, and nothing was cut.
ffmpeg volumedetect, ffmpeg silencedetect and
faster-whisper base, int8, CPU, on macOS.
Method notes only. No transcript content is published here.