SX Microcontroller DSP Math Method

ulaw to 16bit linear

From Scott Dattalo

http://www-svr.eng.cam.ac.uk/comp.speech/Section2/Q2.7.html

http://www-s.ti.com/sc/psheets/spra267/spra267.pdf

shows:

> >            linear                            compressed
> > 12 11 10 9  8  7  6  5  4  3  2  1  0 | 6 5 4  3  2  1  0
> > ----------------------------------------------------------
> > 0   0 0  0  0  0  0  1  Q3 Q2 Q1 Q0 x   0 0 0 Q3 Q2 Q1 Q0
> > 0   0 0  0  0  0  1  Q3 Q2 Q1 Q0  x x   0 0 1 Q3 Q2 Q1 Q0
> > 0   0 0  0  0  1  Q3 Q2 Q1 Q0  x  x x   0 1 0 Q3 Q2 Q1 Q0
> > 0   0 0  0  1  Q3 Q2 Q1 Q0  x  x  x x   0 1 1 Q3 Q2 Q1 Q0
> > 0   0 0  1  Q3 Q2 Q1 Q0  x  x  x  x x   1 0 0 Q3 Q2 Q1 Q0
> > 0   0 1  Q3 Q2 Q1 Q0  x  x  x  x  x x   1 0 1 Q3 Q2 Q1 Q0
> > 0   1 Q3 Q2 Q1 Q0  x  x  x  x  x  x x   1 1 0 Q3 Q2 Q1 Q0
> > 1  Q3 Q2 Q1 Q0  x  x  x  x  x  x  x x   1 1 1 Q3 Q2 Q1 Q0

Now, A law has alternate bit inversion, and this only seems to have 7 bits compressed. Just remember that in the compressed for each bit is equal to 3dB up to 0dBmO.

Another search yielded a C solution:

http://www-svr.eng.cam.ac.uk/comp.speech/Section2/Q2.7.html

The portion of interest:

/*
** This routine converts from ulaw to 16 bit linear.
**
** Craig Reese: IDA/Supercomputing Research Center
** 29 September 1989
**
** References:
** 1) CCITT Recommendation G.711  (very difficult to follow)
** 2) MIL-STD-188-113,"Interoperability and Performance Standards
**     for Analog-to_Digital Conversion Techniques,"
**     17 February 1987
**
** Input: 8 bit ulaw sample
** Output: signed 16 bit linear sample
*/

int
ulaw2linear(ulawbyte)
unsigned char ulawbyte;
{
  static int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764};
  int sign, exponent, mantissa, sample;

  ulawbyte = ~ulawbyte;
  sign = (ulawbyte & 0x80);
  exponent = (ulawbyte >> 4) & 0x07;
  mantissa = ulawbyte & 0x0F;
  sample = exp_lut[exponent] + (mantissa << (exponent + 3));
  if (sign != 0) sample = -sample;

  return(sample);
}

This is ulaw and not A-law.

...

The array exp_lut[8] can be expressed:

exp_lut[i] = 132 *( (2^i) - 1)

You could use a look up table (like in the C code) or you could calculate it. But since there are only 8 cases, I think a table approach is fastest

; ulawbyte contains the u-law encoded byte we wish to convert to it's
; linear form.
;

ulaw2linear:

	mov	W, <>ulawbyte
	and	W, #$07	; Get the exponent
	add	PC, W

	jmp	ulaw_exp0
	jmp	ulaw_exp1
	jmp	ulaw_exp2
	jmp	ulaw_exp3
	jmp	ulaw_exp4
	jmp	ulaw_exp5
	jmp	ulaw_exp6

ulaw_exp7

	mov	W, <<ulawbyte
	and	W, #$1e
	mov	ulaw_linhi, W
	add	ulaw_linhi, W

	mov	W, #LOW(127*132)
	mov	ulaw_linlo, W
	mov	W, #HIGH(127*132)
	add	ulaw_linhi, W

	jmp	ulaw_sign

ulaw_exp6

	mov	W, <<ulawbyte
	and	W, #$1e
	mov	ulaw_linhi, W

	mov	W, #LOW(63*132)
	mov	ulaw_linlo, W
	mov	W, #HIGH(63*132)
	add	ulaw_linhi, W

	jmp	ulaw_sign

ulaw_exp5

	mov	W, ulawbyte
	and	W, #$0f
	mov	ulaw_linhi, W

	mov	W, #LOW(31*132)
	mov	ulaw_linlo, W
	mov	W, #HIGH(31*132)
	add	ulaw_linhi, W

	jmp	ulaw_sign

ulaw_exp4

	mov	W, >>ulawbyte
	and	W, #$07
	mov	ulaw_linhi, W

	mov	W, >>known_zero
	mov	ulaw_linlo, W

	mov	W, #LOW(15*132)
	add	ulaw_linlo, W
	mov	W, #HIGH(15*132)
	snb	C
	inc	ulaw_linhi
	add	ulaw_linhi, W

	jmp	ulaw_sign

ulaw_exp3

	mov	W, >>ulawbyte
	and	W, #$07
	mov	ulaw_linhi, W

	mov	W, >>known_zero
	mov	ulaw_linlo, W

	rr	ulaw_linhi
	rr	ulaw_linlo


	mov	W, #LOW(7*132)
	add	ulaw_linlo, W
	mov	W, #HIGH(7*132)
	snb	C
	inc	ulaw_linhi
	add	ulaw_linhi, W

	jmp	ulaw_sign

ulaw_exp2
	mov	W, <>ulawbyte
	and	W, #$f0
	mov	ulaw_linlo, W
	add	ulaw_linlo, W
	mov	W, <<known_zero
	mov	ulaw_linhi, W

	mov	W, #LOW(3*132)
	add	ulaw_linlo, W
	mov	W, #HIGH(3*132)
	snb	C
	inc	ulaw_linhi
	add	ulaw_linhi, W

	jmp	ulaw_sign

ulaw_exp0
	mov	W, <>ulawbyte
	mov	ulaw_linlo, W
	mov	W, >>ulaw_linlo
	and	W, #$78

	clr	ulaw_linhi

	jmp	ulaw_sign

ulaw_exp1
	mov	W, <>ulawbyte
	and	W, #$f0
	mov	ulaw_linlo, W
	clr	ulaw_linhi

	mov	W, #LOW(1*132)
	add	ulaw_linlo, W
	mov	W, #HIGH(1*132)
	snb	C
	inc	ulaw_linhi
	add	ulaw_linhi, W


ulaw_sign
	sb	ulawbyte.7
	ret

	not	ulaw_linhi
	not	ulaw_linlo
	inc	ulaw_linhi
	incsz	ulaw_linlo
	dec	ulaw_linhi

	ret

Now, this is untested and unoptimized, but it's close to being right and as fast as possible. If you want a shorter routine that's somewhat slower, then a slight modification of my last post will work. It takes advantage of the fact that:

  132 = 4 * 33

and

  (2^n - 1) * X = (X << n) - X

If ulawbyte is formatted:

sxyzabcd
 s - sign
 xyz  - exponent
 abcd - mantissa

Then this routine will convert it to the linear form

	mov	W, <<ulawbyte	;w = xyzabcd?  carry = s
	and	W, #$1e	;w = 000abcd0
	addlw 0x21          ;w = 001abcd1  carry=0
	mov	Hack, W
	mov	W, #$21	;w = 001abcd1  carry=0
	add	W, Hack

	mov	ulaw_linlo, W
	clr	ulaw_linhi

	snb	ulawbyte.4
	rl	ulaw_linlo	;01abcd10

	sb	ulawbyte.5
	jmp	L1

                         ;If bit 4 and bit 5 are set then
	rl	ulaw_linlo	; 1abcd100
	rl	ulaw_linlo	; abcd1000
	rl	ulaw_linhi	; 00000001

	sb	ulawbyte.6
	jmp	L2

	swap	ulaw_linhi
	swap	ulaw_linlo	;shift left four positions
	mov	W, ulaw_linlo
	and	W, #$0f
	or	ulaw_linhi, W
;;;	xor	ulaw_linlo, W	;probably unnecessary to clear lsbs
                           ;since they are "don't cares"...

L2:
 ;; If the sign bit is set, then we need to compute
 ;;   33 - ulaw_linhi:ulaw_linhi
 ;; otherwise we need
 ;;   ulaw_linhi:ulaw_linhi - 33


	mov	W, #-33
	add	ulaw_linlo, W
	sb	C
	dec	ulaw_linhi

	sb	ulawbyte.7
	jmp	L4


	not	ulaw_linhi
	not	ulaw_linlo
	inc	ulaw_linhi
	incsz	ulaw_linlo
	dec	ulaw_linhi

L4
	rl	lin_lo	;Final shift
	rl	lin_hi

	ret