# wavefile normalization for 16 bit - should i use 32768 or 32767 or should that depend...?

DSP, Plug-in and Host development discussion.
KVRAF
3947 posts since 8 Mar, 2004 from Berlin, Germany
i guess this is a quite common situation: let's say if have some audio data in floating point format that is supposed to be normalized to the range -1..+1 and i want to write that into a 16 bit wave file. before converting to int, i would need to multiply all my samples by 2^15 = 32768. but is that actually right or should i use 2^15-1 = 32767? ...because assuming that my floating point data is normalized to the range -1..+1, the 32768 factor may lead to clipping, if the maximum absolute sample happens to be negative. or: should my normalization routine actually take into account the question, whether the max-abs sample is positive or negative and use different amplification factors in each case (1/maxAbs, if max-abs sample is positive, (1-1./32768)/maxAbs, if max-abs sample is negative) and then i can safely use 32768 as factor before conversion to int? how do you guys deal with this?

edit: wait: it's the other way around: the positive limit is +32767 and the negative is -32768
Last edited by Music Engineer on Sat Jun 19, 2021 2:51 pm, edited 1 time in total.

KVRAF
12881 posts since 8 Mar, 2005 from Utrecht, Holland
Semi-recent discussion on the subject:
search.php?keywords=32768&t=552377&sf=msgonly
We are the KVR collective. Resistance is futile. You will be assimilated.
My MusicCalc is back online!!

KVRAF

Topic Starter

3947 posts since 8 Mar, 2004 from Berlin, Germany
BertKoor wrote:
Sat Jun 19, 2021 2:36 pm
Semi-recent discussion on the subject:
search.php?keywords=32768&t=552377&sf=msgonly
ah thanks! OK - seems like, i should just normalize to +-1 and use

Code: Select all

``(((int) ((x * 32767) + 32768.5f)) - 32768)``
for the conversion. yea - dithering would be nice, too. maybe later. first things first. edit: oh yes, i see: a whole other rabbit hole. so far, i've not yet paid much attention to anything but floating point representations

KVRer
7 posts since 23 Jul, 2017
It's a matter of taste really, I don't think there's a convention.

Make sure you don't hit +32768 though because you'l get a clipping/buzzing/humming sound on certain conversions.

As you indicated, the range of 16-bit wav isn't symmetrical around zero, because of the 2's complement it has range -32768 , +32767.

Fun fact, 8 bit WAV are unsigned, while 16 bit is signed.
I found this website to be usefull:
http://soundfile.sapp.org/doc/WaveFormat/
It contain all needed information to write a simple WAV file yourselve.

KVRAF
12881 posts since 8 Mar, 2005 from Utrecht, Holland
flisk wrote:
Sat Jun 26, 2021 1:10 am
As you indicated, the range of 16-bit wav isn't symmetrical around zero, because of the 2's complement it has range -32768 , +32767.
I'd personally use the asymetrical bit for the dithering noise.
Multiply with 32767, subtract dithering bit, then truncate (or round, whatever you find works best)
Dithering is more important than getting the symmetry just right. The latter is inaudible.
We are the KVR collective. Resistance is futile. You will be assimilated.
My MusicCalc is back online!!

KVRAF
6398 posts since 12 Feb, 2006 from Helsinki, Finland
BertKoor wrote:
Sat Jun 26, 2021 1:20 am
flisk wrote:
Sat Jun 26, 2021 1:10 am
As you indicated, the range of 16-bit wav isn't symmetrical around zero, because of the 2's complement it has range -32768 , +32767.
I'd personally use the asymetrical bit for the dithering noise.
Multiply with 32767, subtract dithering bit, then truncate (or round, whatever you find works best)
Dithering is more important than getting the symmetry just right. The latter is inaudible.
You should dither symmetrically around zero so there is no average DC offset. I've observed cases where some DACs can actually produce (slightly) audible clicks at levels that you would expect to be completely inaudible, probably because of some weirdness with their internal oversampling or noise shaping or who knows what.
Preferred pronouns would be "it/it" because according to this country, I'm a piece of human trash.

KVRAF

Topic Starter

3947 posts since 8 Mar, 2004 from Berlin, Germany
this discussion about dithering is very interesting and soon enough i will probably also need to implement dither myself. but here i'm just talking about the "raw" wavefile read/write functionality which dither should not be part of. what i'm mostly concerned with here is bit accurate roundtrip safety. i want to load a wavefile, convert it to 32 bit float, convert it back to (16 or 24 bit) integer, save it, and get the exact same wavefile back, without any noise introduced by the back and forth conversion. anything else would be unreasonable. if the original file is 24 bit, i guess converting to 32 bit float without potential roundoff error calls for a power-of-two multiplier, i.e. 2^23...the 24 bits would indeed nicely fit into the single precision float mantissa, so a safe roundtrip should be possible. actually, it's feasible to do an exhaustive test for all the 16 million something values and i will do that. i guess with 16 bit, it's probably safe to use either 2^15 or 2^15-1 as factor, because of the 8 extra bits...but i don't know....will test...

edit: ok, the results are in: the conversion between 16 bit integer and 32 bit float via

Code: Select all

``````i = (((int) ((x * 32767) + 32768.5f)) - 32768);
f = (((float)(x+32768)) - 32768.5f) / 32767.f;
``````
and for 24 bit via:

Code: Select all

``````i = (((int) ((x * 8388607) + 8388608.5f)) - 8388608);
f = (((float)(x+8388608)) - 8388608.5f) / 8388607.f;
``````
did indeed give a lossless int -> float -> int roundtrip. i also tried replacing the division by multiplication with the reciprocal and that also worked in both cases. also of interest might be, how the boundary values of the int -> float conversion are mapped:

Code: Select all

``````f = WF::int16ToFloat32(-32768);    // maps to -1.00004578
f = WF::int16ToFloat32(+32767);    // maps to +0.999984741

f = WF::int24ToFloat32(-8388608);  // maps to -1.00000012
f = WF::int24ToFloat32(+8388607);  // maps to  1.00000000
``````
the negative values slightly exceed the nominal +-1 range, but i guess that's ok

I found this website to be useful:
http://soundfile.sapp.org/doc/WaveFormat/
It contain all needed information to write a simple WAV file yourselve.
thanks! yes. that infrastructural stuff, i have already implemented

KVRAF
7095 posts since 17 Feb, 2005
In case you attempt bit manipulation, remember that floats have 2 zeros, and integers have 1.

KVRAF

Topic Starter

3947 posts since 8 Mar, 2004 from Berlin, Germany
camsr wrote:
Wed Jun 30, 2021 7:53 am
In case you attempt bit manipulation, remember that floats have 2 zeros, and integers have 1.
bit manipulation? why would i attempt to do that in this context? or are you talking about the noise generation for dithering?