DSP Trick: Fast Floating-Point to mu-Law Conversion

From: Jim Thomas
Subject: Fast floting point to mu-law conversion
Date: 6 May 1999
THIS WORK IS PLACED IN THE PUBLIC DOMAIN

Name: Fast floating point to mu-law conversion

Category: Algorithmic

Application: When you need to convert audio data to mu-law and don't have a lot of time to mess around.

Advantages: Very Fast (I implemented this in a seven cycle loop on a Sharc processor).

Introduction: This trick is based upon the happy coincidence that mu-law compressed data is very much like a poor man's floating point format. Mu-law is a companding method fully descried in the ITU specification G.711. I believe this algorithm to be fully compliant.

The Trick: The algorithm is performed in a few steps. The steps are described below. Although I have implemented and tested this approach in assembly on a Sharc, I am not at liberty to post the code (I do not own it). This approach will work when the data is already in floating-point format as per IEEE 754. Other floating point formats (i.e. Texas Instruments) may work as well, but will require adaptation. The input data is expected to range from -1.0 to +1.0. I present a C algorithm, which will not be fast, but should get the basic idea across.

#define MAX   (8031.0/8192.0)
#define BIAS  (33.0/8192.0)
#define MAGIC (0x16f)
float x;
long  mu;
long  *x_as_long;
/* limit the sample between +/- max and take absolute value */
x = input_sample;
if ((x < -MAX) || (x > MAX))
  x = MAX;
else if (x < 0)
  x = -x;
/* add bias */
x += BIAS;
/* Extract the segment and quantization bits from the exponent and the
mantissa.  Since we have limited the range of the signal already, the
exponent will be well restricted. The pointer "x_as_long" is used to
avoid unwanted type conversion.  In assembly, this is easy. */
x_as_long = (long*)&x;
mu = (*x_as_long >> 19) & 0xff;
/* Unfortunately, mu needs a slight (but magical) adjustment */
mu = MAGIC - mu;
/* All that remains is to splice in the sign bit */
if (input_samle >= 0)
  mu |= 0x80;