Skip to main content

Normalize audio via ffmpeg

Project description


Build Status PyPI version

A utility for batch-normalizing audio using ffmpeg.

This program normalizes media files to a certain loudness level using the EBU R128 loudness normalization procedure. It can also perform RMS-based normalization (where the mean is lifted or attenuated), or peak normalization to a certain target level.

Batch processing of several input files is possible, including video files.




pip3 install ffmpeg-normalize

Or download this repository, then run pip install ..


ffmpeg-normalize [-h] [-o OUTPUT [OUTPUT ...]] [-of OUTPUT_FOLDER] [-f]
                    [-d] [-v] [-n] [-pr] [--version] [-nt {ebu,rms,peak}]
                    [-t TARGET_LEVEL] [-p] [-lrt LOUDNESS_RANGE_TARGET]
                    [-tp TRUE_PEAK] [--offset OFFSET] [--dual-mono]
                    [-c:a AUDIO_CODEC] [-b:a AUDIO_BITRATE]
                    [-ar SAMPLE_RATE] [-koa] [-prf PRE_FILTER]
                    [-pof POST_FILTER] [-vn] [-c:v VIDEO_CODEC] [-sn]
                    [-mn] [-cn] [-e EXTRA_OUTPUT_OPTIONS]
                    [-ofmt OUTPUT_FORMAT] [-ext EXTENSION]
                    input [input ...]

For more information, run ffmpeg-normalize -h, or read on.


Please read this section for a high level introduction.

What does the program do?

The program takes one or more input files and, by default, writes them to a folder called normalized, using an .mkv container. All audio streams will be normalized so that they have the same (perceived) volume.

How do I specify the input?

Just give the program one or more input files as arguments. It works with most media files.

How do I specify the output?

You can specify one output file name for each input file with the -o option. In this case, the container format (e.g. .wav) will be inferred from the file name extension that you've given.

If you don't specify the output file name for an input file, the container format will be MKV, and the output will be written to normalized/<input>.mkv.

Using the -ext option, you can supply a different output extension common to all output files, e.g. -ext m4a.

What will get normalized?

By default, all streams from the input file will be written to the output file. For example, if your input is a video with two language tracks and a subtitle track, both audio tracks will be normalized independently. The video and subtitle tracks will be copied over to the output file.

How will the normalization be done?

The normalization will be performed with the loudnorm filter from FFmpeg, which was originally written by Kyle Swanson. It will bring the audio to a specified target level. This ensures that multiple files normalized with this filter will have the same perceived loudness.

What codec is chosen?

The default audio encoding method is uncompressed PCM (pcm_s16le) to avoid introducing compression artifacts. This will result in a much higher bitrate than you might want, for example if your input files are MP3s.

Some containers (like MP4) also cannot handle PCM audio. If you want to use such containers and/or keep the file size down, use -c:a and specify an audio codec (e.g., -c:a aac for ffmpeg's built-in AAC encoder).


Normalize two WAV files and write them to the specified output files with uncompressed PCM WAV as audio codec:

ffmpeg-normalize file1.wav file2.wav -o file1-normalized.wav -o file2-normalized.wav

Normalize a number of videos in the current folder and write them to a folder called normalized, converting all audio streams to AAC with 192 kBit/s.

ffmpeg-normalize *.mkv -c:a aac -b:a 192k

For Windows, the above would be written as a loop:

for %%f in ("*.mkv") do ffmpeg-normalize "%%f" -c:a aac -b:a 192k

Normalize an MP3 file and write an MP3 file (you have to explicitly specify the encoder):

ffmpeg-normalize input.mp3 -c:a libmp3lame -b:a 320k -o output.mp3

Normalize many files, keeping PCM audio, but choosing a different container:

ffmpeg-normalize *.wav -c:a pcm_s16le -ext aif

Instead of EBU R128, one might just want to use simple peak normalization to 0 dB:

ffmpeg-normalize test.wav --normalization-type peak --target-level 0 --output normalized.wav
ffmpeg-normalize test.wav -nt peak -t 0 -o normalized.wav

You can (if you really need to!) also overwrite your input file. Warning, this will destroy data:

ffmpeg-normalize input.mp4 -o input.mp4 -f

If you need some fancy extra options, such as setting vbr for the libfdk_aac encoder, pass them to the -e/--extra-options argument:

ffmpeg-normalize input.m4a -c:a libfdk_aac -e='-vbr 3' -o output.m4a

Further examples? Please submit a PR so I can collect them.

Detailed Options

File Input/Output

  • input: Input media file(s)

  • -o OUTPUT [OUTPUT ...], --output OUTPUT [OUTPUT ...]: Output file names.

    Will be applied per input file.

    If no output file name is specified for an input file, the output files will be written to the default output folder with the name <input>.<ext>, where <ext> is the output extension (see -ext option).

  • -of OUTPUT_FOLDER, --output-folder OUTPUT_FOLDER: Output folder (default: normalized)

    This folder will be used for input files that have no explicit output name specified.


  • -f, --force: Force overwrite existing files

  • -d, --debug: Print debugging output

  • -v, --verbose: Print verbose output

  • -n, --dry-run: Do not run normalization, only print what would be done

  • -pr, --progress: Show progress bar for files and streams

  • --version: Print version and exit


  • -nt {ebu,rms,peak}, --normalization-type {ebu,rms,peak}: Normalization type (default: ebu).

    EBU normalization performs two passes and normalizes according to EBU R128.

    RMS-based normalization brings the input file to the specified RMS level.

    Peak normalization brings the signal to the specified peak level.

  • -t TARGET_LEVEL, --target-level TARGET_LEVEL: Normalization target level in dB/LUFS (default: -23).

    For EBU normalization, it corresponds to Integrated Loudness Target in LUFS. The range is -70.0 - -5.0.

    Otherwise, the range is -99 to 0.

  • -p, --print-stats: Print first pass loudness statistics formatted as JSON to stdout.

EBU R128 Normalization

  • -lrt LOUDNESS_RANGE_TARGET, --loudness-range-target LOUDNESS_RANGE_TARGET: EBU Loudness Range Target in LUFS (default: 7.0).

    Range is 1.0 - 20.0.

  • -tp TRUE_PEAK, --true-peak TRUE_PEAK: EBU Maximum True Peak in dBTP (default: -2.0).

    Range is -9.0 - +0.0.

  • --offset OFFSET: EBU Offset Gain (default: 0.0).

    The gain is applied before the true-peak limiter. Range is -99.0 - +99.0.

  • --dual-mono: Treat mono input files as "dual-mono".

    If a mono file is intended for playback on a stereo system, its EBU R128 measurement will be perceptually incorrect. If set, this option will compensate for this effect. Multi-channel input files are not affected by this option.

Audio Encoding

  • -c:a AUDIO_CODEC, --audio-codec AUDIO_CODEC: Audio codec to use for output files.

    See ffmpeg -encoders for a list.

    Will use PCM audio with input stream bit depth by default.

  • -b:a AUDIO_BITRATE, --audio-bitrate AUDIO_BITRATE: Audio bitrate in bits/s, or with K suffix.

    If not specified, will use codec default.

  • -ar SAMPLE_RATE, --sample-rate SAMPLE_RATE: Audio sample rate to use for output files in Hz.

    Will use input sample rate by default.

  • -koa, --keep-original-audio: Copy original, non-normalized audio streams to output file

  • -prf PRE_FILTER, --pre-filter PRE_FILTER: Add an audio filter chain before applying normalization.

    Multiple filters can be specified by comma-separating them.

    For EBU, the filter will be applied during the second pass.

  • -pof POST_FILTER, --post-filter POST_FILTER: Add an audio filter chain after applying normalization.

    Multiple filters can be specified by comma-separating them.

    For EBU, the filter will be applied during the second pass.

Other Encoding Options

  • -vn, --video-disable: Do not write video streams to output

  • -c:v VIDEO_CODEC, --video-codec VIDEO_CODEC: Video codec to use for output files (default: 'copy').

    See ffmpeg -encoders for a list.

    Will attempt to copy video codec by default.

  • -sn, --subtitle-disable: Do not write subtitle streams to output

  • -mn, --metadata-disable: Do not write metadata to output

  • -cn, --chapters-disable: Do not write chapters to output

Output Format

  • -e EXTRA_OUTPUT_OPTIONS, --extra-output-options EXTRA_OUTPUT_OPTIONS: Extra output options list.

    A list of extra ffmpeg command line arguments.

    You can either use a JSON-formatted list (i.e., a list of comma-separated, quoted elements within square brackets), or a simple string of space-separated arguments.

    If JSON is used, you need to wrap the whole argument in quotes to prevent shell expansion and to preserve literal quotes inside the string. If a simple string is used, you need to specify the argument with -e=.

    Examples: -e '[ "-vbr", "3" ]' or -e="-vbr 3"

  • -ofmt OUTPUT_FORMAT, --output-format OUTPUT_FORMAT: Media format to use for output file(s).

    See ffmpeg -formats for a list.

    If not specified, the format will be inferred by ffmpeg from the output file name. If the output file name is not explicitly specified, the extension will govern the format (see '--extension' option).

  • -ext EXTENSION, --extension EXTENSION: Output file extension to use for output files that were not explicitly specified. (Default: mkv)

Environment Variables

The program additionally respects environment variables:


    Sets the path to the temporary directory in which files are stored before being moved to the final output directory. Note: You need to use full paths.


    Sets the full path to an ffmpeg executable other than the system default.


The program doesn't work because the "loudnorm" filter can't be found

Make sure you run ffmpeg v3.1 or higher and that loudnorm is part of the output when you run ffmpeg -filters. Many distributions package outdated ffmpeg 2.x versions, or (even worse), Libav's ffmpeg disguising as a real ffmpeg from the FFmpeg project.

Some ffmpeg builds also do not have the loudnorm filter enabled.

You can always download a static build from their website and use that.

If you have to use an outdated ffmpeg version, you can only use rms or peak as normalization types, but I can't promise that the program will work correctly.

Should I use this to normalize my music collection?

When you run ffmpeg-normalize and re-encode files with MP3 or AAC, you will inevitably introduce generation loss. Therefore, I do not recommend running this on your precious music collection, unless you have a backup of the originals or accept potential quality reduction. If you just want to normalize the subjective volume of the files without changing the actual content, consider using MP3Gain and aacgain.

Why are my output files MKV?

I chose MKV as a default output container since it handles almost every possible combination of audio, video, and subtitle codecs. If you know which audio/video codec you want, and which container is supported, use the output options to specify the encoder and output file name manually.

The conversion does not work and I get a cryptic ffmpeg error!

One possible reason is that the input file contains some streams that cannot be mapped to the output file. Examples:

  • You are trying to normalize a movie file, writing to a .wav or .mp3 file. WAV/MP3 files only support audio, not video. Disable video and subtitles with -vn and -sn, or choose a container that supports video (e.g. .mkv).

  • You are trying to normalize a file, writing to an .mp4 container. This program defaults to PCM audio, but MP4 does not support PCM audio. Make sure that your audio codec is set to something MP4 containers support (e.g. `-c:a aac).

The default output container is .mkv as it will support most input stream types. If you want a different output container, make sure that it supports your input file's video, audio, and subtitle streams (if any).

Also, if there is some other broken metadata, you can try to disable copying over of metadata with -mn.

What are the different normalization algorithms?

  • EBU R128 is an EBU standard that is commonly used in the broadcasting world. The normalization is performed using a psychoacoustic model that targets a subjective loudness level measured in LUFS (Loudness Unit Full Scale). R128 is subjectively more accurate than any peak or RMS-based normalization. More info on R128 can be found in the official document and the loudnorm filter description by its original author.

  • Peak Normalization analyzes the peak signal level in dBFS and increases the volume of the input signal such that the maximum in the output is 0 dB (or any other chosen threshold). Since spikes in the signal can cause high volume peaks, peak normalization might still result in files that are subjectively quieter than other, non-peak-normalized files.

  • RMS-based Normalization analyzes the RMS power of the signal and changes the volume such that a new RMS target is reached. Otherwise it works similar to peak normalization.

Couldn't I just run loudnorm with ffmpeg?

You absolutely can. However, you can get better accuracy and linear normalization with two passes of the filter. Since ffmpeg does not allow you to automatically run these two passes, you have to do it yourself and parse the output values from the first run. If this program is too over-engineered for you, you could also use an approach such as featured in this Ruby script that performs the two loudnorm passes.

After updating, this program does not work as expected anymore!

You are probably using a 0.x version of this program. There are significant changes to the command line arguments and inner workings of this program, so please adapt your scripts to the new one. Those changes were necessary to address a few issues that kept piling up; leaving the program as-is would have made it hard to extend it. You can continue using the old version (find it under Releases on GitHub or request the specific version from PyPi), but it will not be supported anymore.

Can I buy you a beer / coffee / random drink?

If you found this program useful and feel like giving back, feel free to send a donation via PayPal.


The MIT License (MIT)

Copyright (c) 2015-2018 Werner Robitza

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.



All notable changes to this project will be documented in this file. Dates are displayed in UTC.

Generated by auto-changelog.


19 November 2019

  • use minimal dependency for tqdm bcb83ab
  • Bump version to 1.15.5. d6f6683
  • remove specific python version requirement 505cbff


19 November 2019


15 October 2019

  • Bump version to 1.15.3. dc012cd
  • do not print stream warning when there is only one stream f91063a
  • remove previous dist versions before release be661c7


12 July 2019

  • warn when duration cannot be read, fixes #105 #105
  • update README a8348e1
  • Bump version to 1.15.2. 8dca501


17 June 2019

  • add output to unit test failures 93c5f2c
  • Bump version to 1.15.1. 3ea3f83
  • fix input label for audio stream 7ab58f2


17 June 2019

  • add pre-and post-filter hooks, fixes #67 #67
  • warn when file is too short, fixes #87 #87
  • Bump version to 1.15.0. 326b0ba
  • update release method to twine fbd5213
  • document audiostream class 5fe6b3d


14 June 2019

  • fix -cn description #99
  • handle progress output from ffmpeg, fixes #10 #10
  • add nicer headers for options in README 18477a7
  • Bump version to 1.14.1. 9ad984b


24 April 2019

  • add option to keep original audio, fixes #83 #83
  • move to 6773380
  • add release script 7f92904
  • add small developer guide on releasing a090628


22 February 2019

  • cap measured input loudness, fixes #92 #92
  • bump version 4675a94


10 January 2019

  • clarify extra argument options, move to main entry point bb94f74
  • Update issue templates dfc3265
  • improve readme 8013626


28 October 2018

  • copy metadata from individual streams, fixes #86 #86
  • bump version be39298
  • add python version for pyenv c2cbd1b


9 July 2018

  • update README, fixes #79 and addresses #80 #79
  • bump version ceec297


12 June 2018


5 May 2018


5 May 2018


25 April 2018

  • Stderror decoding ignoring utf8 encoding errors #69
  • bump version ceb0a6c


24 April 2018

  • do not require main module in, fixes #68 #68
  • bump version feacd24


15 April 2018


11 April 2018

  • add option to disable chapters, fixes #65, also fix issue with metadata #65
  • bump version 4b1b988
  • fix unit test 99dd387


10 April 2018

  • Set default loudness target to -23, fixes #48 #48
  • bump version 698e8b7


4 April 2018

  • Stdout and stderror decoding ignoring utf8 encoding errors #64
  • bump version 96c422a


22 March 2018

  • add errors for impossible format combinations, fixes #60 #60
  • fix ordering of output maps, fixes #63 #63
  • improve documentation 5e1d566
  • bump version 88cfb56


6 March 2018

  • add option to print first pass statistics 30ad9a8


4 March 2018

  • restrict parsing to valid JSON part only, fixes #61 #61
  • bump version 3a59fb6
  • add an example for MP3 encoding a6c3ece
  • update paypal link 27ac79d


8 February 2018

  • do not check for file existence, fixes #57 #57
  • do not print escape sequences on Windows 3d0fecf
  • bump version 563c0fb
  • add github issue template 310b0ea


1 February 2018

  • do not check for ffmpeg upon module import f09cbe6
  • bump version 3b6cde4


1 February 2018

  • fix issue with wrong adjustment parameters, fixes #54 #54
  • rename function test e615bcc
  • bump version dec40b3


30 January 2018

  • allow setting FFMPEG_PATH and document TMP 4bc8859


26 January 2018

  • handle edge case for short input clips 3e0d32f


26 January 2018

  • bump version 811ab59
  • do not try to remove nonexisting file in case of error in command 5674589


26 January 2018


25 January 2018

  • fix bug with target level for peak/RMS 70b537b
  • update documentation formatting 6b31270
  • update history 36b7c6f


24 January 2018


23 January 2018

  • WIP: v1.0 rewrite fcdc506
  • new feature detection, add documentation, contributors guide etc. 37645b9
  • add version info and test case for dry run 20b6d50


9 October 2017

  • use shutil.move instead of os.rename 035e4df


17 September 2017

  • allow setting threshold to 0 5b63056


14 September 2017

  • expand tilde and environment variables, fixes #36 #37
  • Merge pull request #37 from Mathijsz/fix-which-path-expansion #36
  • expand tilde and environment variables, fixes #36 #36
  • update README and indentation d9b359c
  • update README w.r.t. loudnorm filter fa07e6e
  • update a35b7c8


2 August 2017


31 July 2017

  • allow overwriting input file, fixes #22 #22
  • better handle cmd arguments 801ee34
  • version bump 0d9b5bd
  • Update fa32325


4 April 2017

  • define file encode for python3, fixes #24 #24
  • use Markdown instead of RST for README/HISTORY 9728ae3
  • minor documentation improvements b793489
  • bump version and README 76d6138


13 February 2017

  • Fix for #13 #21
  • Add extended normalisation options #15
  • add option for no prefix, fixes #20 #20
  • handle spaces in path, fixes #12 #12
  • update README, fixes #11 #11
  • code cleanup, add option to set format and audio codec f08d680
  • bump to v0.2.0 2354c0b
  • update documentation 78a202f


15 December 2015

  • Add #8
  • Fixed ffmpeg v2.6.3 compatibility and docopt config #6
  • Installation update to ffmpeg #5
  • Rename project #4
  • Docopt, Setuptools, avconv compatibility #2
  • check for Windows .exe, fixes #10 #10
  • check path and fix #9 #9
  • various improvements, fixes #1 #1
  • Removed Windows carraige returns from 35565da
  • functional, communicate with avconv/ffmpeg about overwriting 5660d92
  • initial commit ec4730f

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for ffmpeg-normalize, version 1.15.5
Filename, size File type Python version Upload date Hashes
Filename, size ffmpeg_normalize-1.15.5-py2.py3-none-any.whl (30.6 kB) File type Wheel Python version py2.py3 Upload date Hashes View hashes
Filename, size ffmpeg-normalize-1.15.5.tar.gz (2.0 MB) File type Source Python version None Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page