
Mastering · 21 August 2026 · 6 min
Can mastering fix a bad mix? When a mastering pass is enough and when to reopen the session
Mastering can fix problems that affect the whole song the same way: overall loudness, the peak ceiling, and broad tone. It can’t fix problems that live inside the mix, like a vocal that clipped, a bad edit, a buried snare or a widener that cancels in mono. If the mix is honest, a quick mastering pass, even an automatic one, is a real time saver. If it isn’t, reopen the session first.
There’s a reason this question keeps coming up. One-click mastering is cheap and fast, and the mix is the part that takes forever. It’s tempting to bounce, drop the file on a mastering tool and hope it smooths everything out. Sometimes that works fine. Sometimes it makes a bad mix louder, which mostly makes its problems easier to hear.
What can a mastering pass actually fix?
Think of mastering as processing that treats the stereo file as one thing. It works on everything at once, so it’s good at problems that are everywhere at once:
- Overall loudness. A mix sitting at −19 LUFS can be brought up toward a delivery level with gain and a limiter.
- The ceiling. A true-peak limiter keeps the loudest moments under −1 dBTP so encoders and converters don’t clip them.
- Broad tone. A mix that’s a little dull or a touch heavy in the low mids can take a gentle EQ tilt.
- Consistency. Several songs that need to sit together can be lined up in loudness and tone.
Notice what those have in common: if you turn the whole song up 2 dB, every instrument goes up 2 dB. That’s fine when the balance inside the mix is already right.
What can’t mastering fix?
Anything that happens to one part and not the others. A mastering EQ that tames a harsh vocal also dulls the cymbals sitting at the same frequencies. A limiter that catches a clipped snare also squashes the kick. The usual suspects:
- Clipping that happened before the bounce. If a channel, a bus or a fixed-point bounce clipped, the tops of the wave are gone. Turning the file down afterwards doesn’t bring them back.
- Balance. A vocal that’s too quiet in the chorus, a bass that disappears on one note. Those are fader and automation moves.
- Edits and timing. Clicks at comp joins, a late double, a breath cut in half.
- Mono cancellation. A widener or a flipped polarity on one side can make a part vanish when the song plays in mono, on a phone speaker or a club system. The phase correlation essay covers how to hear it.
- DC offset. A constant offset from a faulty plugin or a bad recording chain eats headroom. You can high-pass it out at mastering, but the plugin that caused it is still in the session waiting to do it again.
How do you tell which problem you have?
Ears first, but numbers help, especially for the problems you can’t hear on your monitors. Here’s a small triage script. It reads a bounce and sorts what it finds into two piles: things a mastering pass can handle, and things that send you back to the session. Loudness and true peak come from FFmpeg’s ebur128 filter, which follows ITU-R BS.1770. The rest is a few lines of NumPy.
#!/usr/bin/env python3
"""mix_triage.py: should this mix go through a mastering pass, or back into the session?
Usage: python3 mix_triage.py MIX.wav [MIX2.wav ...]
Reads each file and sorts what it finds into two piles:
MASTER CAN FIX whole-file gain and ceiling: integrated loudness, true peak
OPEN THE SESSION problems baked into the mix: flat-topped (clipped) runs, a big DC offset,
left/right that cancel in mono (more than 4.5 dB lost when folded to mono;
identical channels lose 0 dB, unrelated ones about 3 dB)
Loudness and true peak come from FFmpeg ebur128 (ITU-R BS.1770). The clip check counts runs of 4+
consecutive samples within 0.01 dB of the file's own peak, which catches audio that was clipped
upstream and turned down afterwards. Your files are only read. Needs: Python 3, NumPy, FFmpeg."""
import re, subprocess, sys
import numpy as np
def meter(path):
err = subprocess.run(["ffmpeg", "-hide_banner", "-nostats", "-i", path, "-af", "ebur128=peak=true",
"-f", "null", "-"], capture_output=True, text=True).stderr
return float(re.findall(r"I:\s+(-?[\d.]+) LUFS", err)[-1]), float(re.findall(r"Peak:\s+(-?[\d.]+) dBFS", err)[-1])
def samples(path):
raw = subprocess.run(["ffmpeg", "-v", "error", "-i", path, "-f", "f32le", "-ac", "2", "-"], capture_output=True, check=True).stdout
return np.frombuffer(raw, np.float32).reshape(-1, 2).astype(np.float64)
def clip_runs(ch, min_run=4):
top = np.abs(ch).max() * 10 ** (-0.01 / 20)
hot = np.abs(ch) >= top
edges = np.diff(np.concatenate([[0], hot.astype(int), [0]]))
starts, ends = np.where(edges == 1)[0], np.where(edges == -1)[0]
return int(np.sum(ends - starts >= min_run))
def triage(path, target=-14.0):
I, TP = meter(path); x = samples(path)
runs = sum(clip_runs(x[:, c]) for c in range(2))
dc = 20 * np.log10(np.abs(x.mean(0)).max() + 1e-12)
mono = 10 * np.log10(np.mean(((x[:, 0] + x[:, 1]) / 2) ** 2) / np.mean((x ** 2).mean(1)) + 1e-12)
fix, session = [], []
gain = min(target - I, -1.0 - TP) # loudest it can go by gain alone without breaking -1 dBTP
if target - I <= -1.0 - TP:
fix.append(f"{I:.1f} LUFS, true peak {TP:+.1f} dBTP: {target - I:+.1f} dB of plain gain reaches {target:.0f} LUFS"
f" with true peak at {TP + target - I:+.1f} dBTP")
elif gain >= 0:
fix.append(f"{I:.1f} LUFS, true peak {TP:+.1f} dBTP: gain can add {gain:.1f} dB before -1 dBTP;"
f" {target:.0f} LUFS needs a true-peak limiter")
else:
fix.append(f"{I:.1f} LUFS, true peak {TP:+.1f} dBTP: needs {-gain:.1f} dB less, or a true-peak limiter")
if runs >= 3: session.append(f"{runs} flat-topped runs: something clipped before the bounce. Find it and fix it there")
if dc > -40: session.append(f"DC offset {dc:.0f} dBFS: find the track or plugin causing it")
if mono < -4.5: session.append(f"mono fold-down loses {-mono:.1f} dB: parts cancel in mono. Check wideners and polarity")
print(f"== {path}")
for s in fix: print(" MASTER CAN FIX " + s)
for s in session: print(" OPEN THE SESSION " + s)
print(" verdict: " + ("open the session first" if session else "honest mix, a mastering pass is fine") + "\n")
if __name__ == "__main__":
for p in sys.argv[1:]: triage(p)
We ran it on three synthetic files, made from a generated beat and a speech-like test voice, not a real song. The first is an honest mix that’s simply quiet. The second is the same mix where a 6-second chorus flat-topped in a fixed-point bounce and then the whole file was turned down 3 dB. The third adds a pad whose left and right channels are opposite polarity, the kind of thing a careless stereo widener does.
$ python3 mix_triage.py mix_quiet.wav mix_clipped.wav mix_wide.wav
== mix_quiet.wav
MASTER CAN FIX -19.0 LUFS, true peak -3.3 dBTP: gain can add 2.3 dB before -1 dBTP; -14 LUFS needs a true-peak limiter
verdict: honest mix, a mastering pass is fine
== mix_clipped.wav
MASTER CAN FIX -13.8 LUFS, true peak -0.1 dBTP: needs 0.9 dB less, or a true-peak limiter
OPEN THE SESSION 547 flat-topped runs: something clipped before the bounce. Find it and fix it there
verdict: open the session first
== mix_wide.wav
MASTER CAN FIX -19.0 LUFS, true peak -6.7 dBTP: +5.0 dB of plain gain reaches -14 LUFS with true peak at -1.7 dBTP
OPEN THE SESSION mono fold-down loses 4.9 dB: parts cancel in mono. Check wideners and polarity
verdict: open the session first
Reading those, still on synthetic audio:
- The quiet mix is a mastering job. Plain gain gets it 2.3 dB louder before true peak hits −1 dBTP. Going further toward −14 LUFS needs a true-peak limiter, which is exactly what a mastering pass does.
- The clipped mix is louder than the quiet one and its peak reads below full scale, so a quick look says it’s fine. It has 547 flat-topped runs. No limiter can put those tops back.
- The wide mix measures well: −19 LUFS, lots of peak headroom. But it loses 4.9 dB when folded to mono, where an ordinary stereo mix loses well under 3 dB. A mastering pass would make it louder and leave the pad vanishing on phones.
Treat the thresholds as starting points, not rules. Some genres clip on purpose, and some wide ambient music is meant to be wide. The script only tells you where to look.
Why does a clipped mix look fine on a peak meter?
Because a peak meter only tells you the highest value in the file, not how it got there. If a bounce clips at full scale and someone later turns the file down 3 dB, the peak reads −3 dBFS and looks like a sensible, careful mix. The flat tops are still there, just 3 dB lower. That’s why the script looks for runs of several identical samples at the file’s own maximum, instead of checking for 0 dBFS.
You can see it by eye, too. Zoom into the loudest moment of the waveform. A healthy wave is rounded at its peaks. A clipped one has a flat lid on it, often for several samples in a row.
When is a one-click mastering pass the right call?
More often than purists admit. If the triage comes back clean and the mix sounds right on two or three systems, an automatic pass that brings it to a sensible level with a legal peak is a perfectly good master for a lot of releases: demos, beat tapes, socials, a single you need out by Friday. The reason to use a local tool for that is speed and privacy. Your unreleased song stays on your machine, and you can rerun it as often as you like.
It’s the wrong call when you’re hoping it will fix something you already suspect is wrong. If you’re reaching for the mastering tool because the vocal feels buried, the vocal fader is the faster fix. The essay on when not to reach for the limiter makes the same point from the other side, and splitting stems can help you find which part is causing trouble when you only have the bounce.
What should you check after the master?
A master is a new render, so check it like one:
- Measure the file you’ll upload. Integrated LUFS and true peak. Under −1 dBTP for most services; Spotify’s own guidance asks for under −2 dBTP when a master is louder than −14 LUFS.
- Compare at matched loudness. Turn the master down to the mix’s level and switch between them. If the master only sounds better when it’s louder, it isn’t better.
- Listen in mono and on a small speaker. That’s where widener problems and lost bass show up.
- Keep the mix file. If you need a different master later, start from the mix, not from the master.
The short version: master the mix you have when it’s honest, and reopen the session when it isn’t. Ten minutes of fixing a clipped bus beats an hour of trying to hide it.
- Can mastering fix a bad mix?
- Only the parts of it that affect the whole file the same way: overall loudness, the peak ceiling, and broad tone like a mix that’s a little dark. Anything inside one part of the mix, like a clipped vocal, a buried snare, a bad edit or a widener that cancels in mono, has to be fixed in the session.
- Will an automatic mastering tool replace a mastering engineer?
- It replaces waiting on simple jobs: turning an honest mix up to a sensible level with a legal peak. It doesn’t replace judgment on a mix with real problems, and it can’t tell you that the problem is in the mix. That’s still your call, or an engineer’s.
- How can I tell if my mix clipped before I bounced it?
- Look for flat tops: runs of several samples in a row sitting at the file’s highest level. A peak meter won’t show it if the file was turned down afterwards, because the peak then reads below 0 dBFS. A clip detector, or zooming in on the loudest part of the waveform, will.
- Should I check the file again after mastering?
- Yes. Measure the exact file you’ll upload: integrated loudness, true peak, and a quick mono listen. Any processing, including a one-click master, is a new render with new peaks.