TechEarl

How to Normalize Audio Volume From the Command Line

Normalize MP3 and audio volume from the command line: ffmpeg's loudnorm filter for EBU R128 loudness (one-pass and the accurate two-pass), rsgain for ReplayGain tags across a whole library, and why loudness beats peak normalization.

Ishan Karunaratne⏱️ 9 min readUpdated
Share thisCopied
Normalize MP3 and audio volume from the command line with ffmpeg loudnorm (EBU R128 LUFS) and rsgain ReplayGain tagging across a library.

The modern way to normalize audio volume from the command line is loudness normalization (EBU R128 / LUFS), not peak normalization. For a single file, ffmpeg's loudnorm filter does it in one command. For a whole music library, rsgain writes ReplayGain tags that players honor without ever touching the audio. The old mp3gain you may have read about is unmaintained; rsgain is its successor.

bash
# Normalize one file to -16 LUFS (the loudnorm one-pass)
ffmpeg -i in.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11 out.mp3

# Tag a whole library with ReplayGain (non-destructive, recursive)
rsgain easy ~/Music

The rest of this page is the detail: why loudness beats peak, the accurate two-pass loudnorm, what each value means, and which LUFS target to aim for.

Peak normalization is not what you want

Peak normalization scales a file so its loudest sample hits a ceiling (say 0 dBFS or -1 dB). The problem is that a quiet, even track and a punchy, compressed track can have the same peak and sound nothing alike in volume. A single loud transient pins the peak while the rest of the track stays quiet, so peak-normalized files still jump around in perceived loudness. That is exactly the "one song is twice as loud as the next" problem people are trying to fix.

Loudness normalization measures perceived loudness over the whole file (the EBU R128 / ITU BS.1770 standard, expressed in LUFS) and adjusts to a target. Two files normalized to the same LUFS sound equally loud to a human, which is the point. Streaming platforms all switched to loudness normalization years ago, so matching their model locally is the correct move.

ffmpeg loudnorm: one-pass

loudnorm is built into ffmpeg, so if you already have ffmpeg you have it. The one-pass form is a single command:

bash
ffmpeg -i in.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11 out.mp3

The three knobs:

  • I is the integrated (target) loudness in LUFS. -16 is a sensible default for general listening and podcasts.
  • TP is the maximum true peak in dBTP. -1.5 leaves headroom so the file does not clip after lossy re-encoding.
  • LRA is the target loudness range. The filter's own default is 7; 11 is a looser range that the loudnorm author uses in the canonical example, and it is fine to leave alone for general material.

One pass is fast and good enough for casual use. Its weakness is that it normalizes in a single sweep, so the integrated loudness it lands on can drift a little from the I you asked for, especially on dynamic material.

One gotcha worth knowing: to measure true peaks accurately, loudnorm upsamples internally to 192 kHz, and that sample rate carries through to the output unless you ask for the original back. Append -ar (for example -ar 44100 or -ar 48000) so you do not end up with an unexpectedly large 192 kHz file:

bash
ffmpeg -i in.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11 -ar 44100 out.mp3

ffmpeg loudnorm: two-pass (the accurate way)

For broadcast-grade accuracy, measure first, then apply the measured values. Pass one runs loudnorm in analysis mode and prints JSON instead of writing a file:

bash
ffmpeg -i in.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json -f null -

That prints input_i, input_tp, input_lra, input_thresh, and target_offset. Feed the four input_* numbers back into a second pass as the measured_* parameters, pass target_offset as offset, and set linear=true:

bash
ffmpeg -i in.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-19.3:measured_TP=-3.1:measured_LRA=6.4:measured_thresh=-29.8:offset=0.5:linear=true:print_format=summary out.mp3

Substitute the real measured values from pass one (the numbers above are an example). The offset is the small target_offset correction the analysis pass computes; passing it back is what the loudnorm author's canonical workflow does, and you can drop it if you do not need that last fraction of a dB. With the measurements in hand, ffmpeg applies a single linear gain that hits the target precisely instead of guessing. This is the form to use when you actually care about the output landing on -16 LUFS rather than near it.

If wiring the two passes by hand is tedious, the Python wrapper ffmpeg-normalize automates exactly this two-pass loudnorm flow over one file or a batch.

rsgain: normalize a whole library without re-encoding

For a music collection, you usually do not want to rewrite every file. ReplayGain is the better model: scan each track, compute the gain needed to hit a reference loudness, and store that as a tag in the file's metadata. The audio data is untouched; any ReplayGain-aware player (foobar2000, VLC, mpv, Rhythmbox, Kodi, and most others) reads the tag and adjusts playback volume on the fly. Non-destructive, reversible, and it preserves album dynamics.

rsgain is the modern, actively maintained tool for this. Its Easy Mode points at a directory and scans recursively with sensible defaults:

bash
# Recursively scan and tag every supported file under the directory
rsgain easy ~/Music

rsgain easy writes both track gain (each file normalized on its own) and album gain (a single gain per album so the relative loudness between tracks on a record is preserved). A player set to album mode keeps the quiet intro and the loud chorus in their intended relationship; track mode flattens every song to the same level. rsgain handles MP3, FLAC, Opus, Ogg, M4A/MP4, WAV, and more, so it is not MP3-only despite the search term.

mp3gain is dead; use rsgain

Plenty of older guides reach for mp3gain -r *.mp3. It still runs, but mp3gain is unmaintained, MP3-only, and built around the older ReplayGain 1.0 model. rsgain is its spiritual successor: ReplayGain 2.0 (which uses the same EBU R128 / LUFS loudness measurement as loudnorm), modern formats, and active development. If a tutorial tells you to install mp3gain, install rsgain instead and use rsgain easy.

Which LUFS target should I use?

Pick the target for where the audio will be heard:

TargetLUFSUse it for
General / podcasts-16Apple Podcasts' recommendation; a safe all-round default
Music streaming-14Spotify and YouTube normalize playback to about -14 LUFS
EBU R128 broadcast-23TV and radio delivery specs

For audio you are mastering once to play "everywhere," -14 LUFS with a true peak of about -1 dBTP satisfies Spotify and YouTube and sits inside Apple's range. For ReplayGain tagging with rsgain, the ReplayGain 2.0 reference is -18 LUFS, which rsgain easy uses by default; you do not normally override it.

One thing to keep clear: loudnorm rewrites the audio to hit a fixed level, while rsgain tags the file and the player does the adjusting. Use loudnorm when you need a single export at a known loudness (a podcast episode, a video's audio track); use rsgain when you want a library that plays evenly without altering the files. The two are not competitors, they solve different problems.

FAQ

See also

Sources

Authoritative references this article was fact-checked against.

TagsffmpegloudnormrsgainReplayGainLUFSEBU R128normalize mp3CLI

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Convert a PSD to PNG From the Command Line

Convert a PSD to PNG from the command line with ImageMagick. The one trick that matters: design.psd[0] selects the flattened composite, so you get one PNG instead of a folder full of separate layers.