Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
    
You're looking to have the audio 'normalized.' I found this thread and there's lots of good information in there. Hope it helps! – bobsbarricades Aug 14 '11 at 20:02
up vote 59 down vote accepted

Built-in Normalization Filters

Current ffmpeg has two filters that can be directly used for normalization – although they are already quite advanced, so they do not simply apply gain. Here they are:

  • 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.
  • 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.

If you want a “simple” RMS-based normalization to 0 dBFS, read on.


For the lazy ones:

I created a Python script to normalize media files, available on PyPi as well. You simply:

  • download ffmpeg (choose a static build)
  • put the ffmpeg executable in your $PATH by either adding it in, for example, /usr/local/bin, or adding its directory to $PATH
  • To install: Either run pip install ffmpeg-normalize or in the source code, run python setup.py install.
  • Once the script is installed, use ffmpeg-normalize

On Ubuntu / Debian:

sudo apt-get install ffmpeg python-pip sudo pip install ffmpeg-normalize

For example, to normalize the audio of every MP4 file to 0 dB, run this:

ffmpeg-normalize *.mp4 

It'd output WAV files with the normalized audio, using the prefix normalized-. You have to merge the audio back into the original files. See this answer on how to do it.


Using ffmpeg to normalize audio

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" -f null /dev/null

Replace /dev/null with NUL on Windows. 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 -strict experimental -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.

share|improve this answer
2  
I'm using your answer, thanks. Usually in Step 1 I only output 20min of audio (-t 00:20:00), in Step 2 I run normalize-audio with -n (--no-adjust) parameter (just output info) and look on the gain. If it's less then 1dB I just don't bother with audio level otherwise I run ffmpeg when transcoding with volume=5dB. I reckon 20min is pretty representative. – Art Shayderov Jul 16 '12 at 12:09
    
There is also tools/normalize.py in the ffmpeg source, but I've never really looked into it. Might be worth a try. – LordNeckbeard Nov 26 '14 at 23:14
    
If you direct the output of the volume detect step to a file it will process much faster. I'm piping the output to -f wav foo.wav and it only takes a couple seconds to process the file rather than minutes. Once the volume detect is complete I just delete foo.wav. – Jack Cox May 25 '15 at 9:36
    
that does not work if the max volume is above 0dB (possible with floating point PCM or lossy formats like Vorbis, AAC, MP3, etc) — it gets clipped to 0dB. – Sarge Borsch Jun 19 '15 at 5:55
    
this is possible to solve by inserting negative gain filter before volumedetect, for example like this: -af volume=-12dB,volumedetect — here, as long as the peak amplitude does not exceed +12dB, it'll be possible to calculate correct peak amplitude – Sarge Borsch Jun 19 '15 at 5:59

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
share|improve this answer

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
share|improve this answer

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .