0
$\begingroup$

I am converting 8-bit LPCM audio to 16-bit in software and, in order to cover the full 0–65535 destination range with my 0–255 source range, I multiply the samples by 65535/255, which is exactly 257.

However I have studied some existing software and, so far, I have noticed that they actually multiply by 256 (or shift 8 bits to the left), thus mapping 0–255 to the range 0—65280. This can be seen in extremely widespread software such as Sox (macro SOX_SIGNED_TO_SAMPLE), the SDL library (function SDL_Convert_U8_to_U16LSB), the FFmpeg swresample library (macro CONV_FUNC)… I have yet to find a piece of audio processing code that multiplies by 257.

I know a bit shift used to be significantly faster than an integer multiplication, but that is hardly the case nowadays. I also know that the difference between the range 0–65280 and the range 0–65535 will be physically unnoticeable, so there is hardly anything at stake here. But is there another reason not to want to use the full 16-bit integer range?

$\endgroup$
2
  • $\begingroup$ This is kind of borderline for this site. It's more of a signal processing question than a sound design question. $\endgroup$
    – AJ Henderson
    Feb 28 at 2:19
  • $\begingroup$ One thing to worry about is that the old 8-bit soundfiles (both .wav and .aif) were not two's-complement, but were offset-binary. 0x00 was the most negative value, 0x80 was zero, and 0xFF was the most positive value. But all of the 16-bit files and larger were two's-complement. So before you multiply by 256, first you gotta subtract 128 from your 8-bit value. Then multiply by 256. $\endgroup$ Mar 8 at 15:57

2 Answers 2

0
$\begingroup$

Multiplying by 257 maps 255 to 65535, and 254 to 65278:

11111111 => 1111111111111111
11111110 => 1111111011111110

Between these 16-bit values there are $65535 - 65278 - 1 = 256$ values. But, this operation duplicates the 8-bit pattern:

00010100 => 0001010000010100

which means that quantization (for example, to go back to 8-bits after processing the signal) will be computationally expensive.

Whereas multiplying by 256 does the mapping like this:

11111111 => 1111111100000000 (65280)
11111110 => 1111111000000000 (65024)

In this case, there are $65280 - 65024 - 1 = 255$ values "in between" samples. The "missing" 255 values are actually those between 65280 and 65535. In this case, quantization is trivial: the MSB of the dropped bits indicates in which direction to quantize.

In addition, multiplying by 256 gives you a bit of "headroom" above the original maximum 8-bit value. My understanding is that, in many applications, this additional headroom can be beneficial, since it helps prevent clipping.

$\endgroup$
0
$\begingroup$

If the 8-bit values are signed, from -128 to +127, you cannot multiply with 257 as the result won't fit into a 16-bit signed integer. But 256 will work just fine, ending up with -32768 to +32512 range.

While you can map 0..255 as unsigned 8-bit value into unsigned 16-bit value fully covering 0..65535 range, audio isn't only positive values, but AC over a midpoint.

In 8-bit unsigned values, 128 is commonly used as the 0V midpoint and 32768 for the 16-bit unsigned midpoint.

Multiplying the 8-bit unsigned midpoint value of 128 by 257 does not result into midpoint value of 16-bit unsigned, but 32896. This is 128 larger than 32768, so doing this will add a DC bias offset to the audio signal. It means you either have to declare this custom value as your new zero bias point and hanle it yourself as no other program or hardware expects this, or remove the offset to be compatible, but you cannot remove it without clipping, so it may not be a good choise to multiply with 257 with the case of unsigned valued either.

Therefore, it is better to just multiply by 256, so it will work for both signed and unsigned audio.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.