FFmpeg Loudnorm Dual-Pass Loudness Normalization

This guide provides a straightforward tutorial on how to perform dual-pass loudness normalization using the loudnorm filter in FFmpeg. You will learn how to run the first pass to analyze your audio’s baseline measurements and how to apply those parameters in the second pass to achieve precise, broadcast-compliant audio levels according to the EBU R128 standard.

Why Dual-Pass?

While FFmpeg’s loudnorm filter can run in a single-pass mode, it relies on dynamic estimation which can lead to compression artifacts or inaccurate target levels. A dual-pass approach first analyzes the entire audio file to gather precise metadata, then uses those exact measurements in the second pass to apply linear normalization. This results in much higher audio quality and perfect compliance with your target parameters.


Step 1: The First Pass (Analysis)

In the first pass, you run the loudnorm filter with the print_format=json option enabled. This analyzes the file and outputs the measurements to your terminal without writing a new audio file.

Run the following command:

ffmpeg -i input.wav -af loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json -f null -

Parameter Breakdown: * I=-16: Sets the target integrated loudness to -16 LUFS (common for podcasts/stereo web audio). * TP=-1.5: Sets the target true peak to -1.5 dBTP. * LRA=11: Sets the target loudness range to 11 LU. * -f null -: Tells FFmpeg to discard the output video/audio stream since we only need the text readout.

Analyzing the Output

At the end of the command line output, you will see a JSON block that looks like this:

{
    "input_i" : "-22.50",
    "input_lra" : "14.20",
    "input_tp" : "-3.10",
    "input_thresh" : "-32.80",
    "output_i" : "-16.10",
    "output_lra" : "10.10",
    "output_tp" : "-1.50",
    "output_thresh" : "-26.20",
    "normalization_type" : "dynamic",
    "target_offset" : "0.10"
}

Write down or copy the values for input_i, input_lra, input_tp, input_thresh, and target_offset.


Step 2: The Second Pass (Normalization)

Now, feed the measured values from the JSON output back into the loudnorm filter. This allows FFmpeg to perform precise linear normalization.

Using the values from the JSON example above, run the second pass command:

ffmpeg -i input.wav -af loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-22.50:measured_LRA=14.20:measured_TP=-3.10:measured_thresh=-32.80:offset=0.10 -ar 48000 output.wav

Key Additions in the Second Pass: * measured_I: Set to the input_i value (-22.50). * measured_LRA: Set to the input_lra value (14.20). * measured_TP: Set to the input_tp value (-3.10). * measured_thresh: Set to the input_thresh value (-32.80). * offset: Set to the target_offset value (0.10). * -ar 48000: Explicitly sets the output sample rate to 48 kHz (or your preferred sample rate). The loudnorm filter internally upsamples to 192 kHz for calculation, so defining the output sample rate prevents unexpected sample rate changes in your final file.