202

I want the loudest peak sound in a movie clip to be as loud as the codec allows, then have every other sound amplified accordingly.

What's a practical example in order to accomplish this using ffmpeg?

Sathyajith Bhat
  • 61,504
  • 38
  • 179
  • 264
Jon Skarpeteig
  • 2,141
  • 3
  • 13
  • 8
  • 1
    You're looking to have the audio 'normalized.' I found [this thread](http://forum.doom9.org/showthread.php?t=108179) and there's lots of good information in there. Hope it helps! – bobsbarricades Aug 14 '11 at 20:02

6 Answers6

323

Option 1: Built-in Normalization Filters

Current ffmpeg has three filters that can be directly used for normalization – although they are already quite advanced, so they do not simply apply gain to reach a peak level. If you want a “simple” RMS-based or peak normalization to 0 dBFS (or any other target), go to the last section of this answer.

The basic usage for a filter is:


ffmpeg -i input -filter:a filter output

Here are the three more advanced filters:

  • loudnorm: loudness normalization according to EBU R128. You can set an integrated loudness target, a loudness range target, or maximum true peak. This is recommended for publishing audio and video and it is used by broadcasters all over the world. You should use this filter in a two-pass mode, so I recommend my wrapper script ffmpeg-normalizefor it (for more info see below).

  • dynaudnorm: “intelligent” loudness normalization without clipping, which applies normalization dynamically over windowed portions of the file. This may change the characteristics of the sound, so it should be applied with caution. Example for medium-level compression:

    ffmpeg -i input.wav -filter:a "dynaudnorm=p=0.9:s=5" output.wav
    
  • speechnorm: specifically made for speech normalization. See the examples here.

Also, the volume filter can be used to perform simple volume adjustments. See the Audio Volume Manipulation wiki entry for more.


Option 2: Use the ffmpeg-normalize tool

Since you should use the loudnorm filter with two passes, and that might be a bit complicated to wrap in a script, I created a Python program to normalize media files, available on PyPi as well. You simply:

  • download ffmpeg (choose a static build, version 3.1 or higher)
  • put the ffmpeg executable in your $PATH by either adding it in, for example, /usr/local/bin, or adding its directory to $PATH
  • Run pip install ffmpeg-normalize
  • Use ffmpeg-normalize

For example:

ffmpeg-normalize input.mp4 -o output.mp4 -c:a aac -b:a 192k

Or, to simply batch-normalize a number of audio files and write them as uncompressed WAV to an output folder:

ffmpeg-normalize *.m4a -of /path/to/outputFolder -ext wav

The tool supports EBU R128 (default), RMS and peak. Have a look at ffmpeg-normalize -h for more options and check the README for some examples.

Also, it supports re-encoding with other encoders (e.g., AAC or MP3), or automatic merging of the audio back into the video.


Option 3: Manually normalizing audio with ffmpeg

In ffmpeg you can use the volume filter to change the volume of a track. Make sure you download a recent version of the program.

This guide is for peak normalization, meaning that it will make the loudest part in the file sit at 0 dB instead of something lower. There is also RMS-based normalization which tries to make the average loudness the same across multiple files. To do that, do not try to push the maximum volume to 0 dB, but the mean volume to the dB level of choice (e.g. -26 dB).

Find out the gain to apply

First you need to analyze the audio stream for the maximum volume to see if normalizing would even pay off:

ffmpeg -i video.avi -af "volumedetect" -vn -sn -dn -f null /dev/null

Replace /dev/null with NUL on Windows.
The -vn, -sn, and -dn arguments instruct ffmpeg to ignore non-audio streams during this analysis. This drastically speeds up the analysis.

This will output something like the following:

[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] mean_volume: -16.0 dB
[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] max_volume: -5.0 dB
[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] histogram_0db: 87861

As you can see, our maximum volume is -5.0 dB, so we can apply 5 dB gain. If you get a value of 0 dB, then you don't need to normalize the audio.

Apply the volume filter:

Now we apply the volume filter to an audio file. Note that applying the filter means we will have to re-encode the audio stream. What codec you want for audio depends on the original format, of course. Here are some examples:

  • Plain audio file: Just encode the file with whatever encoder you need:

      ffmpeg -i input.wav -af "volume=5dB" output.mp3
    

    Your options are very broad, of course.

  • AVI format: Usually there's MP3 audio with video that comes in an AVI container:

      ffmpeg -i video.avi -af "volume=5dB" -c:v copy -c:a libmp3lame -q:a 2 output.avi
    

    Here we chose quality level 2. Values range from 0–9 and lower means better. Check the MP3 VBR guide for more info on setting the quality. You can also set a fixed bitrate with -b:a 192k, for example.

  • MP4 format: With an MP4 container, you will typically find AAC audio. We can use ffmpeg's build-in AAC encoder.

      ffmpeg -i video.mp4 -af "volume=5dB" -c:v copy -c:a aac -b:a 192k output.mp4
    

    Here you can also use other AAC encoders. Some of them support VBR, too. See this answer and the AAC encoding guide for some tips.

In the above examples, the video stream will be copied over using -c:v copy. If there are subtitles in your input file, or multiple video streams, use the option -map 0 before the output filename.

slhck
  • 223,558
  • 70
  • 607
  • 592
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackexchange.com/rooms/57096/discussion-on-answer-by-slhck-how-can-i-normalize-audio-using-ffmpeg). – Journeyman Geek Apr 14 '17 at 03:18
  • 27
    This is the gift that keeps on giving. 6 years later, and it's still being updated and maintained. Well done! – Jon Skarpeteig Aug 07 '17 at 21:04
  • Does option 3 avoid clipping if I set the new volume so max_volume is zero? i.e. using the opposite value initial given by max_volume – rraallvv Oct 26 '17 at 00:38
  • @rraallvv Yes, it should. That's also what the `ffmpeg-normalize` tool does, when you specify a level of 0 dB and peak-normalization. – slhck Oct 27 '17 at 07:43
  • 12
    To use the `loudnorm` (or other) filter: `ffmpeg -i input.wav -filter:a loudnorm output.wav` – Joschua Oct 21 '19 at 11:20
  • @Joschua Note that it's recommended to run that filter with a two-pass approach, where you get its output stats from the first run and apply them in the second run. That's why I wrote `ffmpeg-ǹormalize` to simplify that step. There's also a helper script that does that in Ruby: https://gist.github.com/kylophone/84ba07f6205895e65c9634a956bf6d54 – slhck Oct 21 '19 at 12:32
  • Is there a way to get the `-af volumedetect` output in a different format? Akin to the `print_format=json` option of `loudnorm` (`-af "loudnorm=I=-6:LRA=10:tp=-1:print_format=json"`)? – BallpointBen Apr 12 '20 at 02:39
  • @BallpointBen You can use `ffmpeg-normalize` and run: `ffmpeg-normalize -n --print-stats test/test.wav`. `-n` enables a dry-run, so no output will be written. – slhck Apr 12 '20 at 09:30
  • I found the default output of to be too quiet, plus it gave me a warning about a 192kHz sample rate? So I use `ffmpeg-normalize --sample-rate 44100 -t -14 [filename] -o [out_filename]` to fix both issues. Numbers of higher magnitudes than `-14` are more quiet. – Qwertie Jan 11 '21 at 18:48
  • Just wanted to say, this may be the best answer I've ever seen on any Stack Exchange, and I started using Stack Overflow in 2010 :-) – Freedom_Ben Feb 03 '21 at 16:08
  • To clarify, if I wanted RMS-based normalisation using your example, I would apply a gain of 16.0 dB? – Hashim Aziz Apr 14 '21 at 00:46
  • 1
    @HashimAziz Correct! You offset the mean rather than the peak. Beware of possible clipping though. – slhck Apr 15 '21 at 08:56
  • 1
    Note that loudnorm [requires more than 3s of audio](https://github.com/slhck/ffmpeg-normalize/issues/87) per the EBU definition of integrated loudness. I'm working with variable-length short dialogue and have to work around this. – zhark Aug 30 '21 at 16:21
  • You should probably include some command line examples for Option 1 like you do with the others. Would help future travelers to this question. Thanks for a well written answer. – ScottN Dec 09 '22 at 15:44
  • @ScottN Sure, added a few examples, although I'd rather link to the ones in the documentation to keep them as updated as possible. – slhck Dec 10 '22 at 18:54
7

I can not comment on the best message so that is my ugly bash based on it to do that

ffmpeg -i sound.mp3 -af volumedetect -f null -y nul &> original.txt
grep "max_volume" original.txt > original1.tmp
sed -i 's|: -|=|' original1.tmp
if [ $? = 0 ]
 then
 sed -i 's| |\r\n|' original.tmp
 sed -i 's| |\r\n|' original.tmp
 sed -i 's| |\r\n|' original.tmp
 sed -i 's| |\r\n|' original.tmp
 grep "max_volume" original1.tmp > original2.tmp
 sed -i 's|max_volume=||' original2.tmp
 yourscriptvar=$(cat "./original2.tmp")dB
 rm result.mp3
 ffmpeg -i sound.mp3 -af "volume=$yourscriptvar" result.mp3
 ffmpeg -i result.mp3 -af volumedetect -f null -y nul &> result.txt
fi
7

Here's a script to normalize sound levels of .m4a files. Watch out if the sound levels are too quiet to start with. The final sound can be better if you use something like Audacity in that case.

#!/bin/bash

# Purpose: Use ffmpeg to normalize .m4a audio files to bring them up to max volume, if they at first have negative db volume. Doesn't process them if not. Keeps bitrate same as source files.
# Parameters: $1 should be the name of the directory containing input .m4a files.
#   $2 should be the output directory.

INPUTDIR=$1
OUTPUTDIR=$2

<<"COMMENT"

# For ffmpeg arguments http://superuser.com/questions/323119/how-can-i-normalize-audio-using-ffmpeg
# and
# https://kdecherf.com/blog/2012/01/14/ffmpeg-converting-m4a-files-to-mp3-with-the-same-bitrate/
ffmpeg -i test.m4a -af "volumedetect" -f null /dev/null

ffmpeg -i test.m4a -af "volumedetect" -f null /dev/null 2>&1 | grep max_volume
# output: max_volume: -10.3 dB

ffmpeg -i test.m4a -af "volumedetect" -f null /dev/null 2>&1 | grep 'max_volume\|Duration'
# Output:
#  Duration: 00:00:02.14, start: 0.000000, bitrate: 176 kb/s
# [Parsed_volumedetect_0 @ 0x7f8531e011a0] max_volume: -10.3 dB

ffmpeg -i test.m4a -af "volumedetect" -f null /dev/null 2>&1 | grep max_volume | awk -F': ' '{print $2}' | cut -d' ' -f1
# Output: -10.3

ffmpeg -i test.m4a 2>&1 | grep Audio
# output: Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 170 kb/s (default)

ffmpeg -i test.m4a 2>&1 | grep Audio | awk -F', ' '{print $5}' | cut -d' ' -f1
# output: 170

# This works, but I get a much smaller output file. The sound levels do appear normalized.
ffmpeg -i test.m4a -af "volume=10.3dB" -c:v copy -c:a aac -strict experimental output.m4a

# Operates quietly.
ffmpeg -i test.m4a -af "volume=10.3dB" -c:v copy -c:a aac -strict experimental -b:a 192k output.m4a -loglevel quiet

COMMENT

# $1 (first param) should be the name of a .m4a input file, with .m4a extension
# $2 should be name of output file, with extension
function normalizeAudioFile {
    INPUTFILE=$1
    OUTPUTFILE=$2

    DBLEVEL=`ffmpeg -i ${INPUTFILE} -af "volumedetect" -f null /dev/null 2>&1 | grep max_volume | awk -F': ' '{print $2}' | cut -d' ' -f1`

    # We're only going to increase db level if max volume has negative db level.
    # Bash doesn't do floating comparison directly
    COMPRESULT=`echo ${DBLEVEL}'<'0 | bc -l`
    if [ ${COMPRESULT} -eq 1 ]; then
        DBLEVEL=`echo "-(${DBLEVEL})" | bc -l`
        BITRATE=`ffmpeg -i ${INPUTFILE} 2>&1 | grep Audio | awk -F', ' '{print $5}' | cut -d' ' -f1`

        # echo $DBLEVEL
        # echo $BITRATE

        ffmpeg -i ${INPUTFILE} -af "volume=${DBLEVEL}dB" -c:v copy -c:a aac -strict experimental -b:a ${BITRATE}k ${OUTPUTFILE} -loglevel quiet

    else
        echo "Already at max db level:" $DBLEVEL "just copying exact file"
        cp ${INPUTFILE} ${OUTPUTFILE}
    fi
}

for inputFilePath in ${INPUTDIR}/*; do
    inputFile=$(basename $inputFilePath)
    echo "Processing input file: " $inputFile
    outputFilePath=${OUTPUTDIR}/$inputFile
    normalizeAudioFile ${inputFilePath} ${outputFilePath}
done
Chris Prince
  • 171
  • 1
  • 2
3

I'd like to offer my own ffmpeg-based solution NormaWave for people who are new to ffmpeg and seeking alternatives to heavy and inaccurate software (Adobe Audition in my case). After setting up only 7 parameters, you have to click a single button, select the files and wait. It is as simple as possible. The script will read stats and apply up to 9 passes to some of the files to obtain your desired parameters (integrated loudness, loudness ratio, and maximum true peak).

You'll need Excel 2016 or later. It has the user-friendly interface. There are built-in instructions, links to the latest editions of ffmpeg and FLAC, and entry fields' descriptions with bits of theoretical information.

  1. Select the audio filter: volume or loudnorm.

  2. Select your input and output files' extensions (WAV, FLAC, MP3, or WEBM).

  3. Type in your parameters (IL, LRA, and TP) and the sample rate. The built-in check in every field won't let you input ffmpeg-unacceptable values. Or click "Delete/Backspace" for default values.

  4. In the beginning and in the end your files will be renamed back and forth to prevent ffmpeg failure.

  5. When job is done, you can analyze *.bat files and *.txt reports (created in the working folder) to decide what you should do with the final results.

No matter what filter you choose, the first pass is always the reading stats with loudnorm. There may be up to 9 passes, depending on your desired output parameters. Decreasing LRA is a very difficult task.

Redhot
  • 31
  • 2
  • 1
    Welcome to SuperUser! How is your answer better than the ones already here? This page is used a little like a wiki, the to answer is always updated to be correct. – DarkDiamond Jun 06 '22 at 07:34
  • Sorry, if I misplaced my answer. Should I have posted it as a comment to somebody's answer? I just suggested a solution for people who don't want to dive deep into ffmpeg's specs and get their normalized files as simple as possible. – Redhot Jun 06 '22 at 07:42
  • 1
    No, your post is an answer and a quite well written one. It just isn’t clear how it is better/easier to use. For someone who just googled some keywords and came across this page it probably is not clear where the difference between your approach and the others lies. For me who comes to SuperUser via Google, and then needs to try all answers in order to find the one that works best for me, it would be helpful to have an introductory sentence or something that makes exactly that more clear. – DarkDiamond Jun 06 '22 at 07:58
  • 1
    You are absolutely right. Made a correction. Let me know if it's still unclear what I'm trying to offer. – Redhot Jun 06 '22 at 09:48
  • The new answer is way better than the old one. The only thing I think is missing now is the fact that the script is yours, as a little mark because I’m the community guidelines it says that you mustn’t advertise your own software without making it clear. – DarkDiamond Jun 06 '22 at 10:21
1

I use command line (adjust it by yours demands)

mkdir NORMALIZED; for mkv in *.mkv ; do wav=`basename "$mkv" .mkv`.wav; echo "${wav}"; ffmpeg -i "${mkv}" -acodec copy "${wav}" ; normalize-audio "${wav}"; ffmpeg -i "${mkv}"  -i "${wav}" -codec copy NORMALIZED/"${mkv}"; done;

Or batch

[ ! -d work ] && echo "work directory does not ezists" && exit 1

for fn in *.mkv
do
  map=$(ffmpeg -i "$fn" |& grep Stream | grep pcm_s16le) # pcm_dvd ac3 mp3

  if [ "$map" != "" ]
  then
    echo "$fn"
    fb=${fn%.mkv} #; shopt -s extglob; fb=${fb%%+([[:space:]])}
    amap=${map:12:3}
    ffmpeg -y -i "$fn" -map $amap -acodec pcm_s16le work/"$fb".wav 2>/dev/null
    wavegain -y work/"$fb".wav
    ffmpeg -y -i "$fn" -i work/"$fb".wav -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 work/"$fb".mkv 2>/dev/null
    rm -f work/"$fb".wav
    echo
  fi
done

read -p "BAIGTA"
user1855805
  • 147
  • 1
  • 2
  • 8
-2

ffmpeg -i image.jpg -i "input.mp3" -acodec copy tmp.avi

mencoder -ovc copy -oac copy tmp.avi -of rawaudio -af volnorm=1 -oac mp3lame -lameopts cbr:preset=192 -srate 48000 -o "output.mp3"

rm -f tmp.avi

  • 4
    Comparing this with the other answers here, I hope it's clear that your post lacks contextual and explanatory information that would make it useful. What is "mencoder" and what role does it play in answering the question? – music2myear Mar 21 '19 at 20:50
  • 3
    Could you please [edit] your answer to give an explanation of why this code answers the question? Code-only answers are [discouraged](https://meta.stackexchange.com/questions/148272), because they don't teach the solution. – DavidPostill Mar 21 '19 at 21:04