How to Prevent Audio Pops at Cut Boundaries in Video Editing
Audio pops occur at cut boundaries when PCM samples jump abruptly between non-zero values without zero-crossing, and you eliminate them by applying short 30ms fade-ins and fade-outs to every video segment using FFmpeg's afade filter.
When concatenating video segments during post-processing, abrupt discontinuities in the audio waveform create sharp edges that the human ear perceives as clicks or pops. The browser-use/video-use repository solves this problem by implementing automated micro-fades during the extraction phase, ensuring smooth audio transitions without perceptible volume drops.
What Causes Audio Pops at Cut Boundaries?
Audio pops arise from waveform discontinuities at the exact frame where one clip ends and another begins. Pulse-code modulation (PCM) samples at cut points are rarely at zero-crossing; instead, the waveform jumps instantaneously from one amplitude value to another. This sharp transition contains high-frequency energy that manifests as an audible click.
The severity depends on the amplitude difference between the final sample of the first clip and the initial sample of the second clip. Without processing, these artifacts persist through final encoding and delivery.
The 30ms Fade Strategy in video-use
The repository prevents audio pops at cut boundaries by baking 30-millisecond fades into every extracted segment. This duration is long enough to smooth discontinuities yet short enough to remain imperceptible as a deliberate fade effect.
According to the source code in helpers/render.py (lines 149-190), the implementation constructs an FFmpeg audio filter that applies fade-in at the start and fade-out at the end of each segment.
Calculating Fade Parameters
The fade logic handles edge cases where segments might be shorter than the fade duration:
# helpers/render.py – fade calculation (Rule 3)
fade_out_start = max(0.0, duration - 0.03)
af = f"afade=t=in:st=0:d=0.03,afade=t=out:st={fade_out_start:.3f}:d=0.03"
The max(0.0, duration - 0.03) calculation ensures the fade-out begins 30ms before the clip ends, but never starts before time zero, preventing filter errors on extremely short segments.
Applying Filters During Segment Extraction
The extract_segment function (lines 190-207 in helpers/render.py) passes the constructed filter to FFmpeg via the -af option:
cmd = [
"ffmpeg", "-y",
"-ss", f"{seg_start:.3f}",
"-i", str(source),
"-t", f"{duration:.3f}",
"-vf", vf,
"-af", af, # ← 30ms fades applied here
"-c:v", "libx264", "-preset", preset, "-crf", crf,
"-pix_fmt", "yuv420p", "-r", "24",
"-c:a", "aac", "-b:a", "192k", "-ar", "48000",
"-movflags", "+faststart",
str(out_path),
]
Because each segment ends with a fade-out to silence and the subsequent segment begins with a fade-in from silence, the concatenated result contains continuous waveform transitions.
Implementation Examples
Raw FFmpeg Command
To apply this technique manually without the Python wrapper:
ffmpeg -i input.mp4 -ss 12.345 -t 3.210 \
-af "afade=t=in:st=0:d=0.03,afade=t=out:st=3.177:d=0.03" \
-c:v copy -c:a aac -b:a 192k output_clip.mp4
Note that st=3.177 equals 3.210 - 0.033, accounting for the 30ms fade duration.
Python Integration
Using the repository's extract_segment function automatically applies the fade filters:
from pathlib import Path
from helpers.render import extract_segment
source = Path("interview_footage.mp4")
start_time = 45.5
duration = 8.0
extract_segment(
source=source,
seg_start=start_time,
duration=duration,
grade_filter="", # no color grading
out_path=Path("segment_with_fades.mp4")
)
This processes the clip with fades baked into the output, ready for seamless concatenation.
Handling Edge Cases
The implementation guards against three specific failure modes:
- Segments shorter than 30ms: The
max()function clamps the fade-out start to zero, preventing negative timestamps in the FFmpeg filtergraph. - Inconsistent segment lengths: Every segment processed through
extract_segmentreceives identical fade treatment, ensuring uniform audio levels across the timeline. - Codec compatibility: Because
afadeoperates on decoded PCM samples before re-encoding, the technique works with AAC, MP3, FLAC, or any FFmpeg-supported audio codec.
Summary
- Audio pops at cut boundaries result from abrupt PCM sample jumps when waveforms do not cross zero between segments.
- The
video-userepository eliminates these artifacts by applying 30ms fade-ins and fade-outs to every extracted segment. - The
helpers/render.pymodule automatically calculates fade parameters to prevent exceeding segment durations. - FFmpeg's
afadefilter processes transitions during extraction, baking smooth audio directly into output files.
Frequently Asked Questions
Why 30 milliseconds specifically for audio fades?
This duration balances artifact elimination with perceptual transparency. Thirty milliseconds is long enough to smooth the discontinuity that causes audio pops at cut boundaries, yet short enough that listeners perceive the audio as cutting cleanly rather than fading audibly.
Can this method cause gaps or overlaps when concatenating segments?
No. Each segment ends with a fade-out to digital silence (zero amplitude) and begins with a fade-in from silence. When concatenated, the transition moves from zero to zero, creating a continuous waveform without gaps or overlaps between clips.
What happens if a video segment is shorter than 30ms?
The code prevents filter errors by calculating fade_out_start = max(0.0, duration - 0.03). For segments shorter than 30ms, this clamps the fade-out start time to zero, causing FFmpeg to shorten the fade proportionally rather than throwing an error or requesting negative timestamps.
Does this approach work with lossless audio codecs like FLAC?
Yes. The afade filter operates on raw PCM samples before the final encode stage, making it codec-agnostic. Whether encoding to AAC at 192kbps or FLAC for archive purposes, the fade processing occurs at the sample level, ensuring pops are eliminated regardless of the output format.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →