mp3rgain vs mp3gain, aacgain, rsgain & ffmpeg
Nearly every tool here is a ReplayGain tool — they all measure loudness the same way in principle. What actually separates them is where the correction ends up: in a tag the player reads, in the bitstream itself, or in re-encoded samples. This page starts there, then places each tool — including mp3rgain — on the resulting map.
One measurement, three destinations
- Write ReplayGain tags. Measure loudness, store the result as metadata,
and let the player apply it. Nothing about the audio changes. rsgain, loudgain, foobar2000
and
ffmpeg -af replaygainall do this. - Re-encode the audio. Decode, apply gain to the samples, encode again.
Works everywhere, at the cost of a generation of lossy quality on every pass.
ffmpeg loudnormis the usual example. - Rewrite the gain field inside the bitstream. MP3 and AAC frames carry a
global_gainvalue that scales decoder output. Change it and the volume changes permanently, for every player, without decoding a single sample. mp3gain, aacgain, foobar2000 and mp3rgain do this.
1 and 3 are not rival camps. mp3gain, aacgain and mp3rgain do both: they
run a ReplayGain analysis, bake the result into the bitstream, and write standard
REPLAYGAIN_TRACK_GAIN / REPLAYGAIN_ALBUM_GAIN tags describing the
residual, so a ReplayGain-aware player and a dumb one arrive at the same loudness.
foobar2000 covers both routes too. "Tagger versus gain tool" is the wrong axis — the real
question is whether a tool can touch the bitstream at all, and whether it can put it back.
Where those tags land, on MP3
"Writes ReplayGain tags" hides a detail that decides whether your player ever sees them. MP3 has two competing metadata containers, and the classic lineage picked the one modern players do not read. The gap is wider than it sounds — APEv2 ReplayGain on MP3 is not merely unpopular, it is close to unreadable:
- ffmpeg does not read APEv2 on MP3 at all. A well-formed APEv2 tag —
correct
APETAGEXheader and footer, read back happily by mp3rgain itself — yields no tags whatsoever fromffprobe -show_entries format_tags. The same file written as ID3v2 shows every value. That is mpv, VLC, Kodi and everything else built on libavformat. - Rockbox handles APE tags only for WavPack and Musepack, not for MP3.
- foobar2000 writes ReplayGain to ID3v2 and expects it there.
So the two tag families mp3rgain writes have genuinely different audiences, and since 3.2.0 each one goes where its readers are:
| Tag | Container | Read by |
|---|---|---|
REPLAYGAIN_* | ID3v2 TXXX | Players. Also where foobar2000, rsgain and loudgain put theirs |
MP3GAIN_UNDO, MP3GAIN_MINMAX | APEv2 | Nothing but the mp3gain lineage — which is exactly why -u still rolls back a library mp3gain processed |
Two flags override the split when you want everything in one place:
mp3rgain -r *.mp3 # default: ReplayGain in ID3v2, undo in APEv2
mp3rgain -r -s a *.mp3 # everything in APEv2 — byte-for-byte mp3gain
mp3rgain -r -s i *.mp3 # everything in ID3v2
Reading is forgiving in both directions: -s c merges both containers and
-u finds the undo tag in either, so files tagged by mp3gain, or by an older
mp3rgain, still inspect and roll back correctly. Re-running the default over an APEv2-tagged
file moves the ReplayGain values to ID3v2 and clears the APEv2 copies, so the two can never
drift apart. MP3GAIN_ALBUM_MINMAX stays APEv2-only in every mode.
None of this affects AAC/M4A, which always uses MP4 freeform atoms that foobar2000 reads, and none of it affects the bitstream rewrite itself: the volume change is in the frames either way, so a ReplayGain-blind player is right regardless of which container won.
Upgrading from 3.1.x or earlier? Those versions put everything in APEv2.
Nothing breaks — your files keep playing at the corrected volume, because that correction is
baked into the frames, not the tags. Re-run mp3rgain to move the ReplayGain values where
players can see them, or pass -s a to keep the old layout exactly.
Which destination do you want? If every device you use honours ReplayGain tags, stop at approach 1 — it is reversible by definition and stores track and album values side by side. If some of them do not — car stereos, older hardware players, DJ gear, cheap DAPs, many phone apps — the tags are silently ignored and approach 3 is the only lossless answer.
Where each tool sits
| Tool | Status | ReplayGain | Gain applied by | Formats | Undo | Platform |
|---|---|---|---|---|---|---|
| mp3rgain | Active | RG1 default, --rg2 / --r128 opt-in — writes tags | Bitstream gain field, or tags only with --tags-only | MP3, AAC/M4A, raw ADTS .aac | Yes | macOS, Linux, Windows (CLI + GUI) |
| mp3gain | Unmaintained (~2015) | RG1 only — writes tags | Bitstream gain field | MP3 | Yes | Windows, Linux (aging builds) |
| aacgain | Unmaintained (~2009) | RG1 only — writes tags | Bitstream gain field | MP3, AAC/M4A | Yes | Rarely builds on modern 64-bit systems |
| foobar2000 | Active | RG2 / R128 — the reference implementation | Tags, or bitstream gain field | Tags for most formats; bitstream for MP3 and AAC in MP4/MKA | Tags yes — baked-in gain no | Windows GUI only |
| rsgain | Active | RG2 / R128 — writes tags | Player, from the tags | Many | N/A — tags only | Cross-platform CLI |
| loudgain | Superseded by rsgain | RG2 / R128 — writes tags | Player, from the tags | Many | N/A — tags only | Linux, macOS |
| ffmpeg loudnorm | Active | No — R128 normalizer, not a tagger | Re-encoded samples | Everything | No — lossy | Cross-platform CLI |
Two rows deserve a footnote. foobar2000 is not just another entry here: the
Hydrogenaudio community around it is where the ReplayGain 2.0 specification was worked out, and
its scanner is the practical reference other implementations get checked against. If you want
textbook-correct ReplayGain and you are on Windows, that is the answer, and mp3rgain's
--rg2 mode exists to agree with it rather than to compete with it. And the three
bitstream tools at the top are also taggers — the classic mp3gain lineage has always
written ReplayGain tags next to the gain it bakes in.
Why the classic tools ran out
What went unmaintained was never ReplayGain itself — foobar2000 and rsgain carry the standard forward in better shape than ever. What lapsed was the specific lineage that pairs a ReplayGain analysis with a lossless bitstream rewrite on the command line. mp3gain's upstream development stopped around 2015 and aacgain's around 2009. aacgain is the harder loss: it is the tool people reach for when a library contains M4A files, and it has become difficult to build on current 64-bit toolchains. That left two practical options for lossless AAC gain: run an ancient binary, or sit at a Windows desktop with foobar2000 — which does the job well, but only from the GUI and without an undo path.
mp3rgain exists to cover the case neither one does: one memory-safe Rust binary for both MP3 and AAC, scriptable and headless, with undo on both — which is what makes it usable in a container or a cron job where a mistake needs to be reversible. If you are at a Windows desktop, foobar2000 remains an excellent answer.
Reference levels
A recurring source of confusion when comparing tools is that they normalise to different targets, so identical inputs legitimately produce different numbers:
| Standard | Reference | Measured by | mp3rgain flag |
|---|---|---|---|
| ReplayGain 1.0 | 89 dB | mp3gain, aacgain, mp3rgain (default) | default |
| ReplayGain 2.0 | −18 LUFS | foobar2000 (reference), rsgain, loudgain | --rg2 |
| EBU R128 | −23 LUFS | ffmpeg ebur128, broadcast tooling | --r128 |
mp3rgain defaults to ReplayGain 1.0 so re-scanning an mp3gain-processed library reproduces
the same values instead of shifting everything. --rg2 and --r128 switch
to ITU-R BS.1770 gated measurement when you would rather match the modern taggers — the target
being foobar2000's numbers, not a separate dialect of them. In those modes mp3rgain also writes
a REPLAYGAIN_ALGORITHM tag, so a library scanned in mixed modes stays legible.
Peak values on AAC
The other place identical inputs legitimately produce different numbers. On an AAC file, mp3rgain can report a noticeably higher peak than rsgain or foobar2000 while the gain values agree to 0.01 dB. Neither tool is wrong; they are not measuring the same signal.
Lossy AAC decodes to samples that can exceed full scale, and some encoders leave isolated bursts that decode well above 1.0. Decoders disagree about what to do with them. symphonia (which mp3rgain uses) and ffmpeg's native decoder pass them through; Apple's AudioToolbox decoder hard-limits at exactly 1.0. mp3rgain reports the peak of the decoded signal as it is, so a tool measuring a clamped decode necessarily reports less.
One track of a ffmpeg -c:a aac -b:a 256k encode, measured every way:
| Measurement | Value | dBFS |
|---|---|---|
| mp3rgain sample peak | 1.790314 | +5.06 |
| ffmpeg's decoder, same frame | 1.790310 | +5.06 |
mp3rgain true peak (--true-peak) | 2.287768 | +7.19 |
ffmpeg ebur128=peak=true | (7.3 print) | +7.30 |
| ideal band-limited interpolation | 2.310848 | +7.27 |
| the same, after clamping to [−1, 1] | 1.321509 | +2.42 |
rsgain -t | 1.307024 | +2.33 |
mp3rgain lands 0.09 dB under the exact unclamped answer and rsgain 0.10 dB under the exact clamped one — the residual a 49-tap 4× oversampling filter leaves in both cases. The two tools agree with their own inputs to the same precision.
mp3rgain does not clamp because the peak should describe the file rather than whichever decoder the listener happens to use, and float playback paths (CoreAudio, WASAPI shared mode, PipeWire, most software players) really do reach the DAC with those samples. Over-reporting costs a little unnecessary attenuation; under-reporting clips.
In practice it rarely changes anything: applying each track's own gain to the unclamped peak
on the album this was reported against gives a worst case of 0.84, so no player attenuates
further either way. It matters only on a quiet recording carrying the same kind of burst, where
peak-based clipping prevention would cap the gain differently. If you want the bursts gone at
source, they come from the encoder — re-encoding with Apple's
(ffmpeg -c:a aac_at) dropped that track from 1.790314 to 1.276827. The full
investigation is in
issue #350.
Migrating from mp3gain or aacgain
The CLI flags, the TSV output layout, and the APEv2 mp3gain_undo tag are all
mp3gain-compatible. For most setups migration is a single substitution:
sed -i 's/\bmp3gain\b/mp3rgain/g' your_script.sh
Existing undo tags written by mp3gain remain valid, so a library that mp3gain already
processed can be rolled back with mp3rgain -u. One default is intentionally
inverted: mp3gain applies from stored tags unless you pass -s r, while mp3rgain
re-analyses unless you pass -s R (v3.4+), which applies from stored tags and
rescans only files whose tags are missing. The full flag-equivalence table,
Dockerfile substitution patterns and the intentional behavioural differences are documented in
the migration guide, and
the bit-level verification against the original mp3gain is in the
compatibility report.
When not to use mp3rgain
An honest boundary is more useful than a feature list:
- Your whole playback chain honours ReplayGain tags. Use rsgain. Tags are non-destructive and carry track and album values simultaneously.
- You want the most standards-faithful ReplayGain, and you are on Windows. Use foobar2000. It is the closest thing ReplayGain 2.0 has to a reference implementation: a full BS.1770 scanner, correct tags across far more formats than mp3rgain handles, and an "apply to file content" pass that bakes gain into MP3 and AAC bitstreams when you need that. Reach for mp3rgain instead when you need a command line, a non-Windows host, or an undo path.
- You need FLAC, Opus or Vorbis. Those formats have no equivalent in-bitstream gain field. Tag-based tools are the answer.
- You are producing a delivery master. Broadcast and streaming delivery usually wants a measured, documented loudness pass in a DAW or with
ffmpeg loudnorm, not a gain-field rewrite. - You want playback to switch between per-track and per-album loudness. mp3rgain writes both tag sets, but the gain baked into the bitstream is a single choice made at apply time; only a tag-driven player can flip between the two afterwards.