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

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

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 10:51 pm, edited 1 time in total.
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

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. Image
My MusicCalc is served over https!!

Post

BertKoor wrote: Sat Jun 19, 2021 10: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
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

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.

Post

flisk wrote: Sat Jun 26, 2021 9: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. Image
My MusicCalc is served over https!!

Post

BertKoor wrote: Sat Jun 26, 2021 9:20 am
flisk wrote: Sat Jun 26, 2021 9: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.

Post

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
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

In case you attempt bit manipulation, remember that floats have 2 zeros, and integers have 1.

Post

camsr wrote: Wed Jun 30, 2021 3:53 pm 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?
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post Reply

Return to “DSP and Plugin Development”