DEV Community

Cover image for A waveform is not a silence detector: building browser audio edits
dev truth
dev truth

Posted on

A waveform is not a silence detector: building browser audio edits

A waveform is a useful overview of a recording. It is a poor source of truth for deciding which samples to delete. A display can normalize a quiet file until it looks loud, or compress several seconds into one bar. Feeding those bars into a detector would make presentation choices affect the edit.

This is an implementation case study from my project, AudioCut. An AI agent drafted this article from the current decoding, analysis, interval-removal and export code. The numerical example below also appears in an existing unit test; the limitations are part of the implementation, not benchmark claims.

1. Keep analysis data separate from display data

AudioCut analyzes decoded audio in 20 ms windows. Each window gets an RMS value: square the samples, average them, then take the square root. The implementation accumulates the squared samples across all channels before taking that average.

The interface has a different job. It reduces the analysis to 180 display bars, takes the maximum window level within each bar, normalizes the bars against their visual peak, and applies a small minimum height so the waveform remains visible.

Those transformations are useful for a chart. They discard information that the detector needs. Normalization removes the absolute level relationship, and aggregation reduces time resolution. The detector therefore consumes the original window-level array, not the displayed waveform.

This also makes a useful architectural boundary: changing the chart's bar count should not change the removal intervals.

2. Define quietness and duration independently

The detector converts the selected dB threshold to linear amplitude with 10 ** (thresholdDb / 20). At -40 dB, that threshold is 0.01. A window qualifies when its RMS is at or below that value.

Duration is a separate rule. The minimum number of consecutive windows is ceil(minimumSilence / windowSeconds), with a lower bound of one. A 0.5-second minimum therefore requires 25 consecutive 20 ms windows. A 0.2-second quiet run should not qualify just because it is very quiet.

The implementation tracks the start of each quiet run and commits it when a louder window arrives. It also flushes the pending run at the end of the array. Without that final step, trailing silence would disappear from the detector's results.

There is a stereo tradeoff here. Combining squared samples avoids cancellation between opposite-polarity channels, which can happen if channels are summed first. But a silent channel still lowers the combined RMS relative to measuring the active channel alone. A detector that must protect speech present in any channel may need a different aggregation rule, such as the maximum of per-channel RMS values. That would be a design change, not the current behavior.

3. Padding changes the deletion interval

A detected quiet run is not yet the range that should be removed. AudioCut moves its start forward by the padding and its end backward by the same amount. Both endpoints are clamped to the media duration. The remaining deletion must span at least one analysis window.

An existing regression fixture makes this concrete:

  • 50 windows at amplitude 0.001, followed by 10 windows at 0.5.
  • Window duration: 0.02 seconds; total duration: 1.2 seconds.
  • Threshold: -40 dB; minimum quiet duration: 0.5 seconds.
  • Padding: 0.1 seconds at each end.
  • Expected deletion: 0.1 to 0.9 seconds, or 0.8 seconds removed.

Notice the order: the minimum-duration check applies to the quiet run before padding. Padding then decides how much of that run survives around the join. These settings answer different questions and should not be collapsed into one vaguely named sensitivity slider.

4. Keep a removal plan before copying samples

The detector returns ordered time ranges. The processing stage derives the complementary ranges to keep, converts their boundaries to sample frames, and copies each channel into a new audio buffer in sequence.

That separation makes the math easier to inspect than deleting samples during detection. The original decoded buffer remains available, and the output can be encoded independently. In this implementation, the result is a newly encoded MP3.

There are practical limits. Detection has window-level resolution; rounding an endpoint to a sample frame does not recover information that the analysis never measured. The copy path also concatenates the retained ranges without a crossfade. Padding can preserve some breathing room, but it does not guarantee an inaudible join. Listening around cuts remains necessary.

5. Budget for decoded audio, not the compressed file

A small compressed input can expand into a large PCM buffer. Before allocating that buffer, the code estimates duration × sampleRate × channels × 4 bytes for Float32 samples.

For example, ten minutes of stereo audio at 48 kHz requires 230,400,000 bytes, about 220 MiB, for one decoded buffer alone. That is an arithmetic example, not a supported-duration promise. Decoder chunks, the edited buffer and encoding also consume memory. A check on the first buffer is a guardrail, not a measurement of peak memory use.

Likewise, recognizing an MP4 container does not establish that its audio track can be decoded. The input path checks the available audio tracks and the selected track's decoding support before processing it. Video input produces audio output; this pipeline does not edit the picture.

A small regression checklist

The existing audio-math test file passes five tests, including the padding fixture and rejection of a short quiet run. Those tests establish specific cases, not complete audio quality or browser compatibility.

Useful additional cases for an audio-editing pipeline include:

  • Quiet runs at the beginning and end of a recording.
  • Padding that consumes the entire candidate interval.
  • Speech in only one stereo channel.
  • A silent file and a file with no qualifying gaps.
  • Changing waveform display resolution without changing detected intervals.
  • Listening to the exported file around every join, alongside numerical duration checks.

For a concrete interface to the controls discussed here, AudioCut's Silence Remover exposes threshold, minimum duration and padding, then creates a separate MP3 for preview and download. Start with a short recording you have permission to edit.

The broader lesson is to keep three things distinct: measurements of the source, the plan for changing it, and the picture used to explain that plan. Each layer can then be checked against its own requirements.

Top comments (0)