Skip to content

Build1 publisher3 min readPublished

A 2.4-second silence gap buys five words of audio description

In a dev.to walkthrough, FFmpeg's silencedetect finds the gaps, a word budget prices each one at 2.5 words a second, and any narration that renders longer than its gap is thrown out and generated again shorter.

The Engineer · Build desk

Illustration accompanying A 2.4-second silence gap buys five words of audio description

What happened

  • A dev.to walkthrough builds audio description in seven Python steps, from an FFmpeg silence map through a TTS render that is rejected if it overruns, to a second audio rendition in the HLS ladder.
  • The first step calls FFmpeg's silencedetect with noise=-30dB and d=1.2, and the parser reads stderr, because that is where the filter writes its silence_start and silence_end lines.
  • The budget step trims 0.15 seconds from each end of a gap, multiplies what is left by 2.5 words per second and truncates, so the gap sets the sentence length before anything is generated.
  • On the sample video the detector found 47 gaps and 31 cleared the four-word floor, with the remaining sixteen logged against WCAG 1.2.7 at Level AAA, where the only fix is pausing the video.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint The detection floor and the word floor are tuned in different files and disagree. Pushing silencedetect's d below 1.9 seconds only adds gaps the budget gate will drop, so the extra sensitivity just makes the log longer.
  • decision For the third of gaps too short to narrate, the choice is between pausing playback for extended description at Level AAA and shipping partial coverage. The budget step forces that choice before a script is written.
  • cost Borrowing someone else's words-per-second constant moves the failure to the most expensive step in the chain, because every overrun is only caught after you have paid to synthesise the audio.
  • capability The hard check turns whether a description fits into a pass/fail duration comparison. A build can run that gate unattended on every rebuild, which editorial judgement about description quality cannot.

word_budget() is three operations. Subtract 0.30 seconds of padding from the measured gap, multiply by 2.5 words per second, truncate to an integer [12]. Run it on the second gap in the post's own silencedetect output, which lasts 2.4 seconds, and (2.4 - 0.30) x 2.5 gives 5.25, so int() takes it to five words [8][1]. The 3.648-second gap gets eight [2].

"A great 9-second description is a defect if the gap is 2.4 seconds," the post says [3]. At 2.5 words per second, nine seconds of narration is about 22 words, four and a half times what that gap will accept [5].

The two length thresholds in the pipeline are set independently and they do not line up. silencedetect runs with d=1.2, on the reasoning that a gap under about a second cannot hold a useful sentence anyway [7][10]. usable_gaps then drops any gap whose budget comes out under four words, and clearing four words takes a gap of at least 1.9 seconds [13][3]. So everything between 1.2 and 1.9 seconds is detected and then discarded [4]. The post wants those logged instead of silently dropped, because they are the moments where WCAG's extended audio description criterion 1.2.7, Level AAA, applies and the only fix is pausing the video [15].

The walkthrough tells you to check 2.5 for yourself: measure your own voice by rendering 100 known-length sentences and dividing, because voices and languages differ substantially [16]. Assume too high a rate and the budget overfills. A five-word line from a voice that actually speaks 2.1 words a second renders in 2.38 seconds against a usable window of 2.10, an overrun of roughly a quarter of a second [8]. Nothing upstream of the renderer catches that. Step five does: reject if the rendered audio is longer than the gap, then retry shorter [19].

silencedetect writes its silence_start and silence_end lines to stderr, not stdout, which is why find_gaps scans proc.stderr [6]. A parser reading stdout returns no gaps. The 0.15 seconds trimmed from each end exists because narration that begins the instant dialogue stops sounds wrong and clips on some decoders; that padding costs 0.75 words out of every budget [11][7].

The claim that the overrun check is "the step everyone skips" is the author's, offered without a survey of implementations [2]. The 47-gap count is one video [14]. For those numbers to transfer, your content needs similar dialogue density, your noise threshold has to be strict enough for its music bed, and your TTS voice has to run near 2.5 words a second [9][16]. The last two steps leave the original track alone: step six overlays narration on the original audio, and step seven ships the result as a second audio rendition in the HLS ladder [18].

What to watch

  • Whether later sections of the walkthrough publish a retry cap for step five, and what the pipeline emits when every shorter attempt still overruns its gap.
  • Measured words-per-second figures for common TTS voices and for non-English languages, which would replace the 2.5 default the post admits is an assumption.
  • Whether the pipeline grows an extended-description mode that pauses playback for the sub-1.9-second gaps it currently only logs.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories