
Codecs · 21 August 2026 · 7 min
Why does my master clip after MP3 or AAC? Re-encoding eats true-peak headroom
Your master clips after MP3 or AAC because a lossy encoder doesn’t copy your waveform, it rebuilds it. AAC, MP3, Opus and Ogg Vorbis keep a reduced description of the sound and reconstruct it on playback, and the rebuilt peaks can land higher than the ones you limited, even above 0 dBFS. A master whose sample peaks sit at −0.1 dBFS is the classic victim. Keep true peak at or below −1.0 dBTP on the file you hand off (−2.0 if it’s louder than −14 LUFS, as Spotify asks), then encode a test and measure the result.
This one catches careful people. Your limiter is set to −0.1, the meter never goes red, the WAV has zero clipped samples. You upload it, and the streamed version has a crunch on the loudest snare hits that you can’t find anywhere in the session. You didn’t imagine it. The file the listener hears isn’t the file you bounced.
What’s the difference between sample peak and true peak?
A sample-peak meter reports the biggest number stored in the file. But a digital file is a set of dots, and what comes out of a converter is the smooth line through them. That line can swing higher between two dots than either dot is. Those are inter-sample peaks.
The cleanest example is a bit of arithmetic. Take a 12 kHz sine at a 48 kHz sample rate. That’s exactly four samples per cycle. If the samples happen to land at 45°, 135°, 225° and 315° of the wave, every single one sits at 0.707 of the real crest. A sample-peak meter reads 3 dB low, and the wave between the samples is 3 dB hotter than anything in the file.
A true-peak meter estimates that line by oversampling the signal, following the method in ITU-R BS.1770 (the same standard that defines LUFS). Its unit is dBTP. That’s why delivery specs talk in dBTP, not dBFS.
Why does converting to MP3 or AAC make peaks louder?
Two things happen inside a lossy encoder, and both can push peaks up:
- Rounding. The encoder cuts the audio into short blocks, turns each block into frequency components and stores them with less precision than the original. On playback the components are added back together, plus a small error. That error goes up and down at random, and wherever it adds to a peak, the peak gets taller.
- Missing top end. Lower bitrates throw away the highest frequencies. Take the sharp corners off a heavily limited waveform and it rings, overshooting where the corners used to be. That’s the same physics as the Gibbs phenomenon.
Apple says it plainly in its Apple Digital Masters guide: checking the encoded file matters, because levels that don’t show overs in PCM can still clip once encoded. The same guide recommends leaving at least 1 dB of headroom.
What happens to a −0.1 dBFS master after five different encodes?
To put numbers on it we built a 30-second synthetic beat (kick, snare, hats, saturated bass, detuned chords; no real songs) and pushed it 6 dB into a plain sample-peak limiter set to −0.1 dBFS, the way plenty of loud masters get made. Then we encoded it with FFmpeg’s built-in AAC encoder, LAME MP3 and libopus, decoded each one to 32-bit float so overs above 0 dBFS are kept, and measured true peak. We ran it three times: as limited, trimmed so the WAV sat at −1.0 dBTP, and trimmed to −2.0 dBTP.
| Decoded true peak | As limited | Trimmed to −1.0 dBTP | Trimmed to −2.0 dBTP |
|---|---|---|---|
| Source WAV | +1.2 dBTP | −1.0 dBTP | −2.0 dBTP |
| AAC 256 kbps | +2.6 | −0.4 | −1.3 |
| AAC 128 kbps | +2.6 | +0.3 | −1.4 |
| MP3 320 kbps | +1.2 | −1.2 | −2.0 |
| MP3 128 kbps | +1.7 | −1.2 | −2.0 |
| Opus 128 kbps | +1.2 | −1.0 | −2.0 |
Integrated loudness was −7.6 LUFS as limited, −9.8 and −10.8 LUFS trimmed. Synthetic material and one encoder of each kind, so trust the shape more than the decimals. What we take from it:
- The “−0.1 dBFS” master was already over before any codec touched it. The sample-peak limiter held every sample at −0.1 dBFS, and the true peak was still +1.2 dBTP. Every encode of it decoded with samples above full scale.
- −1.0 dBTP was enough for MP3 and Opus, not for AAC. MP3 and Opus stayed at or under −1.0. FFmpeg’s AAC rose 0.6 dB at 256 kbps and 1.3 dB at 128 kbps, which put the 128k file over 0 dBFS.
- −2.0 dBTP cleared everything. The worst encode came out at −1.3 dBTP.
We also ran a less-limited version of the same beat. It didn’t fare better at 128 kbps AAC: trimmed to −1.0 dBTP, it decoded at +1.9. So the encoder and bitrate decide a lot, and −1 dBTP is a floor, not a guarantee. (One method note: FFmpeg’s AAC encoder threw a spurious spike in its first few milliseconds when audio started on sample one, so each test file has half a second of silence at both ends.)
The BeatGoHard A/B above is the real-world version of the same lesson: zero clipped samples, and the true peak still had to come down from −0.3 to −1.0 dBTP before it was safe to ship.
How much true-peak headroom do I need for streaming?
The published numbers line up:
- Spotify asks for true peak below −1 dBTP, and below −2 dBTP if the master is louder than −14 LUFS integrated, because louder tracks are more susceptible to extra distortion when encoded.
- Apple’s mastering guide recommends at least 1 dB of headroom.
So: −1.0 dBTP is the minimum. Go to −2.0 if the master is loud, or if you know it’s heading somewhere that encodes at low bitrates. If one master goes to every store, the strictest ceiling wins. And if you do any processing after the limiter (normalising, dithering, sample-rate conversion), measure after the last step you control, not on the DAW meter.
Does a higher bitrate fix it?
It helps, but it won’t rescue a master that’s already over. In our test, AAC at 256 kbps rose 0.6 dB from a −1.0 dBTP start where 128 kbps rose 1.3 dB. But from the as-limited master, both bitrates decoded at +2.6 dBTP. Bitrate is the platform’s choice anyway. Headroom is yours.
Why does re-encoding an already encoded file make it worse?
Because every generation starts from the last one’s overshoot. We took the −1.0 dBTP master, encoded it to AAC at 256 kbps (think: the MP4 your video editor exports), then encoded that to Opus at 128 kbps (think: what a platform makes of your upload). Straight from the WAV, Opus came out at −1.0 dBTP. Via the AAC file it came out at −0.7, and the AAC file itself was −0.4.
That 0.3 dB isn’t dramatic on its own, but it stacks with everything else. The practical rules: send stores WAV or FLAC, never an MP3 you made; give your video editor the WAV, not a streaming rip; and export video audio at the highest bitrate your editor offers.
How do I test my own master before upload?
You don’t have to guess what an encoder will do. Encode it yourself and measure. This script runs a master through five encodes with FFmpeg and prints the true peak of each decoded file:
#!/usr/bin/env python3
"""codec_overshoot.py: how much does a lossy encode push your peaks up?
python3 codec_overshoot.py master.wav # as delivered
python3 codec_overshoot.py master.wav -1.0 # first trim so true peak sits at -1.0 dBTP
Needs ffmpeg (with libmp3lame and libopus). Encodes to AAC, MP3 and Opus, decodes to
32-bit float (so overs above 0 dBFS are kept, not clipped) and measures each file with
ffmpeg's ebur128 filter (true peak is BS.1770 style, oversampled)."""
import re, subprocess, sys, tempfile, os
def measure(path):
err = subprocess.run(["ffmpeg", "-hide_banner", "-nostats", "-i", path, "-af",
"ebur128=peak=true+sample", "-f", "null", "-"],
capture_output=True, text=True).stderr
tp = float(re.findall(r"True peak:\s+Peak:\s+(-?[\d.]+|-inf)", err)[-1])
sp = float(re.findall(r"Sample peak:\s+Peak:\s+(-?[\d.]+|-inf)", err)[-1])
lufs = float(re.findall(r"I:\s+(-?[\d.]+) LUFS", err)[-1])
return lufs, sp, tp
def run(*args):
subprocess.run(["ffmpeg", "-v", "error", "-y", *args], check=True)
src = sys.argv[1]
tmp = tempfile.mkdtemp()
if len(sys.argv) > 2: # trim to a true-peak ceiling first
ceiling = float(sys.argv[2])
gain = ceiling - measure(src)[2]
trimmed = os.path.join(tmp, "trimmed.wav")
run("-i", src, "-af", f"volume={gain:.2f}dB", "-c:a", "pcm_f32le", trimmed)
src = trimmed
codecs = [("AAC 256k", "m4a", ["-c:a", "aac", "-b:a", "256k"]),
("AAC 128k", "m4a", ["-c:a", "aac", "-b:a", "128k"]),
("MP3 320k", "mp3", ["-c:a", "libmp3lame", "-b:a", "320k"]),
("MP3 128k", "mp3", ["-c:a", "libmp3lame", "-b:a", "128k"]),
("Opus 128k", "opus", ["-c:a", "libopus", "-b:a", "128k"])]
I, sp, tp = measure(src)
print(f"{'file':10s} {'LUFS':>6s} {'sample pk':>10s} {'true pk':>8s} {'rise':>6s}")
print(f"{'source':10s} {I:6.1f} {sp:+9.2f} {tp:+7.2f}")
for name, ext, opts in codecs:
enc = os.path.join(tmp, f"enc.{ext}"); dec = os.path.join(tmp, "dec.wav")
run("-i", src, *opts, enc)
run("-i", enc, "-ar", "48000", "-c:a", "pcm_f32le", dec)
_, sp2, tp2 = measure(dec)
flag = " <- over 0 dBFS" if sp2 > 0 else ""
print(f"{name:10s} {'':6s} {sp2:+9.2f} {tp2:+7.2f} {tp2 - tp:+6.2f}{flag}")
Our run on the synthetic master, trimmed to −1.0 dBTP:
$ python3 codec_overshoot.py loud_master.wav -1.0
file LUFS sample pk true pk rise
source -9.8 -2.30 -1.00
AAC 256k -0.70 -0.40 +0.60
AAC 128k +0.10 +0.30 +1.30 <- over 0 dBFS
MP3 320k -1.50 -1.20 -0.20
MP3 128k -1.20 -1.20 -0.20
Opus 128k -1.30 -1.00 +0.00
If anything prints over 0 dBFS, or comes out much above −1.0, trim the master and run it again. On a Mac, Apple’s own afclip tool (described in the same Apple guide) checks a file for clipping, encoded files included.
If you’d rather not run scripts at all, CoProducer’s analyzer reads true peak, integrated LUFS and clipped samples on your bounce locally. It’s a quick way to catch the −0.1 dBFS master before an encoder does.
Do slowed, sped-up or retuned versions need a new check?
Yes, every time. Changing speed or pitch resamples the audio, which rebuilds the waveform between samples just like an encoder does, so the true peak moves even when the sample peak looks the same. A screwed render, a sped-up TikTok version and a 432 Hz retune are each a new master. Measure them as one. We’ve covered it for screwed tempos and 432 Hz retunes.
- Why does my song clip after converting to MP3 or AAC?
- Lossy encoders rebuild your waveform from a reduced description of it, and the rebuilt peaks can land higher than the originals, even above 0 dBFS. A master limited to −0.1 dBFS sample peak has no room for that. Leave true-peak headroom and measure the encoded file.
- Does MP3 need more headroom than AAC?
- It depends on the encoder and bitrate more than the format name. In our synthetic test, FFmpeg’s AAC encoder overshot the most (a −1.0 dBTP master rose 1.3 dB at 128 kbps), while MP3 and Opus barely moved. Your distributor uses its own encoders, so measure an encode rather than trusting folklore.
- Is −1 dBTP enough headroom for streaming?
- It’s the usual floor. Spotify asks for true peak below −1 dBTP, or below −2 dBTP if the master is louder than −14 LUFS, and Apple recommends at least 1 dB of headroom. In our test, −1 dBTP was enough for MP3 and Opus but not for FFmpeg’s AAC (−0.4 dBTP at 256 kbps, +0.3 at 128 kbps); −2 dBTP kept every encode at or below −1.3 dBTP.
- Do slowed, sped-up or retuned versions need a new true-peak check?
- Yes. Changing speed or pitch resamples the audio, which rebuilds the waveform and moves the peaks between samples. Re-measure every screwed, sped-up or retuned render before it ships, even if the original master passed.