/*---------------------------------------------------------------------------*\

    FILE....: ALAW.C
    TYPE....: C Functions
    AUTHOR..: David Rowe
    DATE....: 16/10/97

    A-law compression functions.


         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1999-2007 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License as published by the Free Software Foundation; either
         version 2.1 of the License, or (at your option) any later version.

         This library is distributed in the hope that it will be useful,
         but WITHOUT ANY WARRANTY; without even the implied warranty of
         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
         Lesser General Public License for more details.

         You should have received a copy of the GNU Lesser General Public
         License along with this library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "alawmulaw.h"
#include <assert.h>
#include <stdint.h>

/* A-law constants */

#define	LSIGN	0x8000		/* linear sign bit 15 = 1 if negative	   */
#define	ASIGN	0x80		/* A-law sign bit 7 = 1 if negative	   */
#define	AMAX	0x0fff		/* max A-law value in linear form	   */
#define	AMAG	0x7f		/* A-law magnitude bits 		   */
#define	BIAS	33		/* converts seg end points to powers of 2  */
#define	LINEAR	0xf		/* masks of linear portion of magnitude	   */

static short alaw_to_linear_lut[256];
static char linear_to_alaw_lut[65536];

static void init_alaw_to_linear_lut(void);
static void init_linear_to_alaw_lut(void);

/*--------------------------------------------------------------------------*\

	FUNCTION.: init_alaw_luts
	AUTHORS..: Mark Mickan
	DATE.....: 29 Jan 2007

	Call the necessary lookup table initialisation functions.  This
	function should be called in initialisation code, before either
	alaw_encode or alaw_decode are called.

\*--------------------------------------------------------------------------*/

void init_alaw_luts(void)
{
    init_alaw_to_linear_lut();
    init_linear_to_alaw_lut();
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: init_alaw_to_linear_lut
	AUTHORS..: Mark Mickan
	DATE.....: 29 Jan 2007

	Calculate the linear equivalent of all possible A-law samples.
	Heavily based on David Rowe's original alaw_decode function.
	The linear samples are in Q15 format.

	Reference: TI databook
		   "Theory, Alogorithms, and Implementations Vol 1", p171

\*--------------------------------------------------------------------------*/

static void init_alaw_to_linear_lut(void)
{
    unsigned long  acc,p,s,q;
    int    i;

    for(i=0; i<256; i++) {
	acc = i ^ 0x55;	/* even bit inversion */

	/* extract sign */

	if (acc & ASIGN)
	    p = LSIGN;
	else
	    p = 0;
	acc &= AMAG;

	/* extract q and s */

	q = acc & 0xf;
	s = acc >> 4;

	/* form linear sample */

	if (s != 0) {
	    acc = 2*q+BIAS;
	    acc <<= (s-1);
	}
	else
	    acc = 2*q+1;

	acc <<= ALAW_SCALE;
	if (p)
	    alaw_to_linear_lut[i] = -(short)acc;
	else
	    alaw_to_linear_lut[i] = (short)acc;
    }
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: init_linear_to_alaw_lut
	AUTHOR...: Mark Mickan
	DATE.....: 29 Jan 2007

	Calculate the A-law equivalent of all possible linear samples
	and store them in a lookup table.  The linear samples are
       	assumed to be in Q15 format.  Heavily based on David Rowe's
	original alaw_encode function.

	Reference: TI databook
		   "Theory, Alogorithms, and Implementations Vol 1", p171

	A-law format: psssqqqq

		       p is the sign
		       s is the segment
		       q if the quantisation bin number

\*--------------------------------------------------------------------------*/

static void init_linear_to_alaw_lut(void)
{
    unsigned short y,mag;
    unsigned short p,s,q,b;
    unsigned long  acc;
    int    i;

    for(i=-32768; i<32768; i++) {
	acc = (i >> ALAW_SCALE) & 0xffff;

	/* separate sign */

	if (acc & LSIGN) {
	    p = ASIGN;
	    mag = -(short)acc;
	}
	else {
	    p = 0;
	    mag = (short)acc;
	}

	/* encode magnitude */

	if (mag > AMAX)
	    y = p + AMAG;
	else {

	    /* determine left most bit and therefore segment */

	    b = 0;
	    acc = mag >> 1;
	    while(acc) {
		b++;
		acc >>= 1;
	    }

	    if (b > 4) {
		s = b - 4;

		q = mag >> (b-4);
		q &= LINEAR;
	    }
	    else {
		s = 0;
		q = mag >> 1;
	    }

	    y = p + (s<<4) + q;
	}

	linear_to_alaw_lut[i+32768] = y^0x55;		/* even bit inversion */
    }
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: alaw_encode
	AUTHOR...: David Rowe
	DATE.....: 14/10/97

	Encodes a vector of linear samples to A-law equivalents.  The linear
	samples are assumed to be in Q15 format.

\*--------------------------------------------------------------------------*/

void alaw_encode(char alaw[], const short linear[], unsigned short sz)
/*  char   alaw[];	A-law encoded samples 	                */
/*  word   linear[];	13 bit magnitude samples in LSB		*/
/*  unsigned short sz;	number of sample in buffers		*/
{
    int    i;

    for(i=0; i<sz; ++i) {
	alaw[i] = linear_to_alaw_lut[linear[i]+32768];
    }
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: alaw_decode
	AUTHOR...: David Rowe
	DATE.....: 14/10/97
	AUTHOR...: Mark Mickan (modified to use lookup table)
	DATE.....: 29 Jan 2007

	Decodes a vector of A-law samples to linear equivalents using
	the lookup table calculated in init_alaw_to_linear_lut.  The
	linear samples are in Q15 format.

\*--------------------------------------------------------------------------*/

void alaw_decode(short linear[], const unsigned char alaw[], unsigned short sz)
/*  word   linear[];	13 bit magnitude samples in LSB		*/
/*  char   char[];	A-law encoded samples 	                */
/*  unsigned short sz;	number of sample in buffers		*/
{
    int i;

    for (i=0; i<sz; ++i) {
	linear[i] = alaw_to_linear_lut[alaw[i]];
    }
}

