How lossless MP3 and AAC volume normalization works
Updated · mp3rgain v3.16.2
Every MP3 and AAC frame carries a small number, global_gain, that tells the
decoder how loud to play that frame. mp3gain, aacgain, and mp3rgain change volume by editing
that number and nothing else. The compressed audio is not decoded and encoded again, so no
quality is lost, the new volume plays on every device, and writing the old number back
undoes it exactly. The price is that volume moves in steps of 1.5 dB.
Three ways to make files the same loudness
| Method | What changes | Quality | Works on players that ignore tags | Tools |
|---|---|---|---|---|
| ReplayGain tags | A tag says how much to turn the file up or down; the player does it | Untouched | No | rsgain, loudgain, foobar2000 |
| Re-encoding | The audio is decoded, scaled, and compressed again | A lossy generation is added | Yes | ffmpeg loudnorm, most audio editors |
| Gain field rewrite | The global_gain number in each frame | Untouched | Yes | mp3rgain, mp3gain, aacgain, foobar2000 |
mp3rgain does the third and the first together: it rewrites the gain field and writes standard ReplayGain tags describing what is left over, so a player that reads tags and one that does not end up at the same loudness. Each tool compared.
The gain field
MP3 and AAC do not store samples. They store a compact description of the sound's
frequency content, plus scale information the decoder uses to turn that description back
into samples. Part of the scale information is global_gain, an 8-bit value
(0 to 255) held in every MP3 granule and every AAC channel element.
The decoder scales its output in proportion to 2global_gain/4. Raising the value by one therefore multiplies the output by the fourth root of two, about 1.19, which is 1.505 dB. That is where the step size comes from: it is a property of the formats, not a choice made by the tool.
Steps (-g) | Change | Amplitude |
|---|---|---|
+1 | +1.5 dB | × 1.19 |
+2 | +3.0 dB | × 1.41 |
+4 | +6.0 dB | × 2.00 |
-2 | −3.0 dB | × 0.71 |
-4 | −6.0 dB | × 0.50 |
Why this is lossless
Applying gain to an MP3 does not decode any audio. mp3rgain finds each frame, reads its
side information, and changes the global_gain value of each granule and channel.
AAC needs more parsing, because finding every global_gain means walking the
bitstream element by element, but the principle is the same. The bits that describe the
sound itself are copied through unchanged, so there is no second round of lossy compression
and nothing to add up over repeated changes.
You can check this. After a gain change and an undo, a file is byte-identical to the original:
shasum -a 256 song.mp3
mp3rgain -g 2 song.mp3 # +3.0 dB
mp3rgain -u song.mp3 # undo
shasum -a 256 song.mp3 # same hash as before
For MP3, the frames mp3rgain writes are also byte-identical to the ones mp3gain writes for the same change; the compatibility report shows the comparison.
Measuring: ReplayGain
Before changing anything, mp3rgain has to know how loud a file is. That is the one step that decodes audio, and it only reads. Three measurements are available:
| Mode | Flag | Reference | Matches |
|---|---|---|---|
| ReplayGain 1.0 | default | 89 dB | mp3gain, aacgain |
| ReplayGain 2.0 | --rg2 | −18 LUFS | foobar2000, rsgain, loudgain |
| EBU R128 | --r128 | −23 LUFS | ffmpeg ebur128, broadcast tools |
Track gain (-r) measures and corrects each file on its own.
Album gain (-a) measures an album as a whole and applies one
correction to all of its tracks, which keeps their relative levels. The measured correction
is then rounded to whole steps, so the audio lands within about 0.75 dB of the target.
Tags that describe what is left
After applying gain, mp3rgain writes the standard
REPLAYGAIN_TRACK_GAIN and REPLAYGAIN_ALBUM_GAIN tags with the
residual: the small difference between the step it applied and the exact value it
measured. This is mp3gain's convention. A player that honours ReplayGain applies the
residual and reaches the exact target; a player that ignores tags is already within 0.75 dB
of it. Neither applies the correction twice.
If you would rather not change the audio at all, --tags-only writes the full
values as tags and leaves every frame as it was.
Undo
Each change is recorded in an undo tag: mp3gain's own MP3GAIN_UNDO on MP3,
and an mp3rgain_undo atom on AAC. mp3rgain -u reads it and writes
every frame's original value back. Undo is cumulative, so several changes in a row are
reversed in one go, and it works on files that mp3gain adjusted. Its limits:
- A frame whose value would pass 0 or 255 is clamped there and cannot be restored exactly. mp3rgain warns when this happens.
- An MP3 that already had an ID3v2 tag keeps it after undo, rewritten as ID3v2.4, so the audio is restored exactly but the file is not byte-identical. A file without one is (since v3.16.2).
-s swrites no tags, including the undo tag, so that change cannot be undone.
Clipping
Turning a file up can push its peaks past full scale. mp3rgain measures the peak during
analysis and warns when the gain it is about to apply would clip. -k limits the
gain so that it does not; the desktop app has this on by default.
Safe writes
Every change is written to a temporary file, flushed to disk, and renamed over the
original, so an interrupted or failed write leaves the original file as it was. A read-only
file is refused, not replaced. -n previews a run without writing, and
-p keeps each file's modification time.
What this method cannot do
- Hit an exact loudness in the audio. Steps are 1.5 dB. For an exact
value, write tags (
--tags-only) or re-encode with ffmpegloudnorm. - HE-AAC.
global_gainreaches only the AAC core, not the SBR high band, so mp3rgain skips HE-AAC files. - FLAC, Ogg Vorbis, Opus, WAV, ALAC. mp3rgain does not handle them; a tag-based tool such as rsgain is the right choice.
- Change dynamics. This is gain only: no compression and no limiter. Quiet passages stay quiet relative to loud ones.