/*
**
**	$Id: alaw.c,v 1.2 1995/10/07 02:52:10 chris Exp $
**	$Revision: 1.2 $
**
**	$Filename: developer/alaw.c $
**	$Author: chris $
**	$Date: 1995/10/07 02:52:10 $
**
**	Convert A-Law to 16-bit and 8-bit linear audio format
**
**	COPYRIGHT (C) 1992-1994 BY CHRISTIAN A. WEBER, ZUERICH, SWITZERLAND. ALL
**	RIGHTS RESERVED. NO PART OF THIS SOFTWARE MAY BE COPIED, REPRODUCED,  OR
**	TRANSMITTED IN ANY FORM OR BY ANY MEANS,  WITHOUT THE PRIOR WRITTEN PER-
**	MISSION OF THE AUTHOR. NO WARRANTY. USE AT YOUR OWN RISK.
**
*/

#include "os.h"

#define  _ALAW
#include "alaw.h"


static S16	*table_alaw_to_16bit;
static U8	*table_16bit_to_alaw;


static S16 ALaw2Sample( S16 code )
{
	S16 sample;

	/* Revert bit order */
	code = (code<<4 & 0xf0) | (code>>4 & 0x0f);
	code = (code<<2 & 0xcc) | (code>>2 & 0x33);
	code = (code<<1 & 0xaa) | (code>>1 & 0x55);

	/* Invert even bits: */
	code ^= 0x55;

	/* Positive <-> Negative: */
	if (code & 0x80)			/* positive A-Law range */
		code &= ~0x80;
	else						/* negative A-Law range */
		code = ~code;			/* Dasselbe wie -code-1; */

	/* Gradient equations (Steigungsgleichungen) */
	if (code >= 112)
		sample = 16384  + (code-112) * 1024;
	else if (code >= 96)
		sample = 8192   + (code-96)  * 512;
	else if (code >= 80)
		sample = 4096   + (code-80)  * 256;
	else if (code >= 64)
		sample = 2048   + (code-64)  * 128;
	else if (code >= 48)
		sample = 1024   + (code-48)  * 64;
	else if (code >= 32)
		sample = 512    + (code-32)  * 32;
	else if (code >= -32)
		sample = -512   + (code+32)  * 16;
	else if (code >= -48)
		sample = -1024  + (code+48)  * 32;
	else if (code >= -64)
		sample = -2048  + (code+64)  * 64;
	else if (code >= -80)
		sample = -4096  + (code+80)  * 128;
	else if (code >= -96)
		sample = -8192  + (code+96)  * 256;
	else if (code >= -112)
		sample = -16384 + (code+112) * 512;
	else
		sample = -32768 + (code+128) * 1024;

	return sample;
}


static U8 Sample2ALaw( S16 sample )
{
	S16 code;

	/* Gradient equations (Steigungsgleichungen) */
	if (sample >= 16384)
		code =  112 + (sample-16384) / 1024;
	else if (sample >= 8192)
		code =   96 + (sample-8192)  / 512;
	else if (sample >= 4096)
		code =   80 + (sample-4096)  / 256;
	else if (sample >= 2048)
		code =   64 + (sample-2048)  / 128;
	else if (sample >= 1024)
		code =   48 + (sample-1024)  / 64;
	else if (sample >= 512)
		code =   32 + (sample-512)   / 32;
	else if (sample >= -512)
		code =  -32 + (sample+512)   / 16;
	else if (sample >= -1024)
		code =  -48 + (sample+1024)  / 32;
	else if (sample >= -2048)
		code =  -64 + (sample+2048)  / 64;
	else if (sample >= -4096)
		code =  -80 + (sample+4096)  / 128;
	else if (sample >= -8192)
		code =  -96 + (sample+8192)  / 256;
	else if (sample >= -16384)
		code = -112 + (sample+16384) / 512;
	else
		code = -128 + (sample+32768) / 1024;

	/* Positive <-> Negative: */
	if(code < 0)			/* negative A-Law range */
		code = ~code;
	else					/* positive A-Law range */
		code |= 0x80;

	/* Invert even bits: */
	code ^= 0x55;

	/* Revert bit order */
	code = (code<<4 & 0xf0) | (code>>4 & 0x0f);
	code = (code<<2 & 0xcc) | (code>>2 & 0x33);
	code = (code<<1 & 0xaa) | (code>>1 & 0x55);

	return (U8)code;
}


/****** ALAW_Init ***********************************************************
*
*   NAME
*       ALAW_Init -- Initialize the ALAW module
*
*   SYNOPSIS
*       ALAW_Init()
*
*       VOID ALAW_Init( VOID );
*
*   FUNCTION
*       Call this once before any other ALAW function.
*
*   INPUTS
*
*   RESULTS
*
*   SEE ALSO
*       ALAW_Exit()
*
****************************************************************************/

VOID
ALAW_Init( VOID )
{
	ALAW_Exit();

	if (table_alaw_to_16bit = OS_Malloc( 256 * sizeof( S16 ) ))
	{
		S16 *ptr = table_alaw_to_16bit, i;

		for (i = 0; i < 256; ++i, ++ptr)
			*ptr = ALaw2Sample( i );
	}

	if (table_16bit_to_alaw = OS_Malloc( 65536 * sizeof( U8 ) ))
	{
		U8	*ptr = table_16bit_to_alaw;
		S32 i;

		for (i = 0; i < 65536L; ++i, ++ptr)
			*ptr = Sample2ALaw( (S16)i );
	}
}


/****** ALAW_Exit ***********************************************************
*
*   NAME
*       ALAW_Exit -- Clean up the ALAW module
*
*   SYNOPSIS
*       ALAW_Exit()
*
*       VOID ALAW_Exit( VOID );
*
*   FUNCTION
*       Call this once at the end.
*
*   INPUTS
*
*   RESULTS
*
*   SEE ALSO
*       ALAW_Exit()
*
****************************************************************************/

VOID
ALAW_Exit( VOID )
{
	if (table_alaw_to_16bit)
	{
		OS_Free( table_alaw_to_16bit );
		table_alaw_to_16bit = NULL;
	}

	if (table_16bit_to_alaw)
	{
		OS_Free( table_16bit_to_alaw );
		table_16bit_to_alaw = NULL;
	}
}


/****** ALAW_ALawTo16Bit ****************************************************
*
*   NAME
*       ALAW_ALawTo16Bit -- Convert A-Law audio to 16 bit linear format
*
*   SYNOPSIS
*       ALAW_ALawTo16Bit( from, to, size )
*
*       void ALAW_ALawTo16Bit( U8 *, S16 *, U32 );
*
*   FUNCTION
*       Convert A-Law audio to 16 bit linear S16s.
*
*   INPUTS
*
*   RESULTS
*
*   SEE ALSO
*       ALAW_16BitToALaw()
*
****************************************************************************/

VOID
ALAW_ALawTo16Bit( U8 *alaw, S16 *dest, U32 size )
{
	U8 *send = alaw + size;

	while (alaw < send)
	{
		*dest++ = table_alaw_to_16bit[*alaw++];
	}
}


/****** ALAW_16BitToALaw ****************************************************
*
*   NAME
*       ALAW_16BitToALaw -- Convert 16 bit linear format to A-Law audio
*
*   SYNOPSIS
*       ALAW_16BitToALaw( from, to, size )
*
*       void ALAW_16BitToALaw( S16 *, U8 *, U32 );
*
*   FUNCTION
*       Convert 16 bit linear signed to A-Law
*
*   INPUTS
*
*   RESULTS
*
*   SEE ALSO
*       ALAW_ALawTo16Bit()
*
****************************************************************************/

VOID
ALAW_16BitToALaw( S16 *from, U8 *alaw, U32 size )
{
	U8 *dend = alaw + size;

	while (alaw < dend)
	{
		*alaw++ = table_16bit_to_alaw[(U16)*from++];
	}
}


