A fast PCM WAV normalizer

For many tasks involving audio (e.g. compressing WAV files to MP3), it's nice to have a utility which can normalize audio files. The purpose of this is to make all audio files sound equally loud. I used to use CoolEdit (from Syntrillium) to do this task, but since this audio editor is capable of doing much more than just normalizing waves, it's a bit slow for this task. Besides, it cannot be easily called from a batch file.

So I looked for a better alternative, but found none. That was when I decided to write my own normalizer. It's a small Win32 console mode application called normalize. It can handle 8-bit and 16-bit PCM WAV files of up to 4 GBs (larger WAV files are not possible anyway), and it is fast. Hell, faster than your disk I/O anyway.

How it works
It works like this: if you do not specify an amplification factor (either just as a factor or in decibels), it will first search the whole file for the negative and positive peaks. Then it will divide either 32767 or 127 (depending on whether the file is 16-bit or 8-bit) by the highest amplitude found (negative or positive, whichever is greater), and the result is the amplification factor. A substitution table is built in memory that contains the new (amplified) sample value for each possible input value. Thus the time-consuming task of doing floating-point multiplication has to be done only once (and at most 65536 multiplications).
When these steps have been completed, normalize will pass over the file again, reading blocks of 64 KB to memory, amplifying them (by substituting sample values from the table), and writing them back to the file (of course overwriting the original data).

Fighting spurious peaks
Sometimes WAV files have peak samples that occur so rarely or in non-critical passages that it can be afforded to clip them without introducing audible artifacts. If these are not clipped, they prevent normalizing the whole file to "normal" levels (as by default normalize does not clip any sample at all). To help combat this problem, Lapo Luchini contributed some code which internally produces a full statistic of the sample values and only considers peak values that have a given percentile in the statistic. It's called "smartpeak" (-s option); start with a percentile of 99.99% and decrease if necessary.

On an AMD Athlon 1.1 GHz with an IBM 30 GB/7200 RPM/UDMA100 hard disk, a 1.3 GB WAV file can be normalized in about 3 minutes.

Improving Speed
About the biggest speed gain can be achieved when using two hard disks instead of one. On one disk you put the source file, and let normalize write the output to a file on the other disk. Thus both disks can be working at the same time (one reading, the other writing), and the effective speed you get will be about the physical write speed of your disk. If you use only one disk, it will have to seek every now and then to read new data, and then again to write the normalized data back.


If you're looking for normalize-0.3x-beta: forget it, it's gone. I've figured out that the world doesn't need
MMX/SSE/SIMD support for peak sample searching because everybody has got fast processors
but very few people have got high-speed SCSI/RAID systems. So... since I don't have time to merge
these two, I decided to get rid of the beta since it doesn't really make sense. Believe me.

Get normalize-0.253 (Win32 CLI)
(Older versions: normalize-0.252 normalize-0.25 normalize-0.241 normalize-0.24 normalize-0.23
normalize-0.23 normalize-0.22 normalize-0.21)

normalize v0.253 (c) 2000-2004 Manuel Kasper <mk@neon1.net>.
All rights reserved.
smartpeak code by Lapo Luchini <lapo@lapo.it>.
Visit http://neon1.net/ for updates. Usage: normalize [flags] input-file -l <ratio> don't find peaks but multiply each sample by <ratio> -a <level> don't find peaks; amplify by <level> (given in dB) -m <percent> normalize to <percent> (default 100) -s <percent> smartpeak: count as a peak only a signal that has the given percentile (50%-100%) -x <level> abort if gain increase is smaller than <level> (in dB) -p prompt before starting normalization -b <size> specify I/O buffer size (in KB; 16..16384; default 64) -o <file> write output to <file> (instead of overwriting original) -q quiet (no screen output) -d don't abort batch if user skips normalization of one file -h display this help error levels: 0 = no error, 1 = I/O error, 2 = parameter error, 3 = no amplification required, 4 = out of memory, 5 = user abort - wildcards are allowed in 'input-file' (e.g. normalize *.wav) - 'input-file' needs to be a PCM WAV file.

Get the source code

I have finally decided to release the source code to normalize due to numerous requests.
I hereby place it under the GNU General Public License.
You can download the source code from here:
Get normalize-0.253 source code

Note that there are no comments in the source - I have not written it with the intent to release it.
I will not answer any questions concerning the source code, so please don't ask. Thank you.

But I want a GUI!
Bruce Heller made a very nice GUI for those of you who don't like fiddling around with command line arguments. Check out his web site at http://www.bheller.com/. Thanks, Bruce!

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
normalize is © 2000-2004 Manuel Kasper. All rights reserved.

Big thanks go to Georges Wanderstok; he invested much time to do some thorough tests on normalize and sent me many good suggestions. Some of them have already been incorporated in v0.21, others will be integrated in the next versions. Thank you, Georges!
Thanks also to Terje Mathisen for providing me with some ideas to improve the speed of normalize... Thanks to him normalize now has MMX and SSE support for the min/max sample scanner.
I also want to thank Lapo Luchini for contributing the smartpeak search code.



Read this (if you care)

Version history

0.253 - the absolute value of the amplification factor is now used when comparing with the -x option (thanks to Dave Case for reporting this)

0.252 - the WAV reader now skips all unknown subchunks

0.25 - incorporated some code contributed by Lapo Luchini to help fight "spurious peaks"

0.241 - added '-d' flag (don't abort) - see usage above

0.24 - Jack Vande Bunte suggested to add wildcard support. Here it is! Now you can call normalize like this: 'normalize *.wav' and it will normalize all files with a .wav extension in the current directory.

0.232 - I seriously messed up something in the compile of the v0.231 which went to the website (see the red box above). This is fixed now. No changes to the code were made in this release.

0.231 - normalizing a file consisting only of null samples (digital silence) resulted in the output being all -32768 samples (or maybe even a crash). Thanks again to Georges Wanderstok for pointing this out.
(Note: this is not fixed yet in 0.3-beta)

0.23 - the -o option was defunct for 16-bit WAV files - all it did was to write silence to the output file. Fixed this one now (forgot to move two lines of code while I was changing the parameter parser); thanks to Cyber who brought this to my attention.

- samples are now clipped correctly when "over-normalizing" - instead of producing overflows and very strange results, they're just clipped to the highest/lowest possible sample value (no speed penalty since normalize is table-driven)

0.21 - added new options: -p, -x, -m, -o; changed default buffer size to 64 KB (faster on most systems)

0.1.2 - minor cosmetic changes; added a timer which tells you how long it took; added the '-q' switch which disables all screen output (useful in batch scripts) - first version to be released

0.1.1 - some small changes to handle the strange 'fact' header that some audio recorders insert (never released)

0.1 - made it table-driven: speed is now MUCH better (never released)

0.0.1 - first version. It worked. :) (never released)

© 2000-2004 by Manuel Kasper <mk@neon1.net>. All rights reserved.