/*==============================================================================================
 *							FILE "ADPCM.C"
 *
 *		Created	   :    03 / 11 / 97
 *		Author	   :    Nicolas Meyer
 *		Description:    Code source of private functions declared in "Adpcm.h", which manage
 *		                decompression in pentium code. 
 *
 *		Copyright (C) 1997 by Ubi Sound Studio Entertainment
 *		All rights Reserved
 *==============================================================================================*/

/*
 * INCLUDED HEADERS
 */
#include "ApmType.h"
#define M_OPTIMISE_ADPCM_

/*
 * GLOBAL VARIABLE
 */
static long glIndexTable[16] = {
			-1, -1, -1, -1, 2, 4, 6, 8,
			-1, -1, -1, -1, 2, 4, 6, 8
};
/*
 * Step conversion table 1:4
 */
static long glStepSizeTable[89] = {
			7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
			19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
			50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
			130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
			337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
			876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
			2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
			5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
			15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};

/*********************************************************************************************************
void AdpcmDecoderMono( unsigned long nLen, char** ppchDataIn, long** pplDataOut, LPADPCMSTATEMONO pState )

Parameters
	In : - unsigned long    nLen         Number of data
	     - char**           ppnDataIn    Address of first raw uncompressed data in ADPCM 4 bits

	Out: - char**              ppnDataIn     Address of first raw uncompressed data in ADPCM 4 bits
	     - long**              pplDataOut    Pointer on the address of out data
	     - LPADPCMSTATEMONO    pState        Address of stereo state descriptor

Returned value:
	     - NONE

Description:
	Procedure which decode APM data into PCM, the compression ratio is 1:4. Data in are stored in
	a signed 4 bits format (2 concatenated data are equivalent to one byte)

*/
void AdpcmDecoderMono( unsigned long nLen, char** ppchDataIn, long** pplDataOut, LPADPCMSTATEMONO pState )
{
    signed char* inp;         /* Input buffer pointer */
    long*        outp;        /* output buffer pointer */
    int          sign;        /* Current adpcm sign bit */
    int          delta;       /* Current adpcm output value */
    int          step;        /* Stepsize */
    int          valpred;     /* Predicted value */
    int          vpdiff;      /* Current change to valpred */
    int          index;       /* Current step change index */
    int          inputbuffer; /* place to keep next 4-bit value */
    ApmBool      bufferstep;  /* toggle between inputbuffer/input */

/*
 * Protection
 */
	if( ! nLen )
		return;
    outp = *pplDataOut;
    inp  = (signed char *)( *ppchDataIn );

    valpred     = pState->lValPrev;
    index       = pState->lIndex;
    bufferstep  = pState->bHighByte;
    inputbuffer = pState->lLastValue;
    step        = glStepSizeTable[index];

// Boucle **do while** trés optimisée, lorsqu'on effectue la décompression d'un seul sample
	do {
		nLen--;	
/*
 * Step 1 - get the delta value
 */
		if( bufferstep ) {
			delta      = inputbuffer & 0xf;
			bufferstep = FALSE;
		}
		else {
#ifdef M_OPTIMISE_ADPCM_
/*
 * Case we point a 4 byte border
 */
			if( (unsigned long)inp % 4 )
				inputbuffer >>= 8;
			else
				inputbuffer   = *( (long*)inp );
			inp++;
#else
			inputbuffer	=	*inp++;
#endif
			delta      = ( inputbuffer >> 4 ) & 0xf;
			bufferstep = TRUE;
		}
/*
 * Step 2 - Find new index value (for later)
 */
		index += glIndexTable[delta];
		if( index < 0 )
			index = 0;
		else {
			if( index > 88 )
				index = 88;
		}
/*
 * Step 3 - Separate sign and magnitude
 */
		sign   = delta & 8;
		delta &= 7;

/*
 * Step 4 - Compute difference and new predicted value
 *
 * Computes 'vpdiff = ( 2*delta + 1 ) * step/8 = ( delta + 0.5 ) * step/4', but see comment
 * in adpcm_coder.
 */
		vpdiff = step >> 3;
		if( delta & 4 )	// Un "ET" prend plus de temp qu'un compare
			vpdiff  += step;
		if( delta & 2 )
			vpdiff  += step >> 1;
		if( delta & 1 )
			vpdiff  += step >> 2;

		if( sign )
		  valpred   -= vpdiff;
		else
		  valpred   += vpdiff;
/*
 * Step 5 - clamp output value
 */
		if( valpred > 32767 )
			valpred = 32767;
		else
			if( valpred < -32768 )
				valpred = -32768;
/*
 * Step 6 - Update step value
 */
		step = glStepSizeTable[ index ];
/*
 * Step 7 - Output value
 */
		*outp++ = valpred;
    }
	while( nLen > 0 );
	*pplDataOut        = outp;
    pState->lValPrev   = valpred;
    pState->lIndex     = index;
	pState->bHighByte  = (ApmBool)bufferstep;
    pState->lLastValue = inputbuffer;
    *ppchDataIn        = (char*)inp;
}

/*********************************************************************************************************
void AdpcmDecoderStereo( unsigned long nLen, char** ppchDataIn, long** pplDataOut, LPADPCMSTATESTEREO pState )

Parameters
	In : - unsigned long         nLen          Number of sample to decompress
	     - char**                ppchDataIn    Address of first raw uncompressed data
	     - LPADPCMSTATESTEREO    pState        Address of stereo state descriptor

	Out: - char**                ppnDataIn     Address of first raw uncompressed data
	     - long**                pplDataOut    Pointer on the address of out data
	     - LPADPCMSTATESTEREO    pState        Address of stereo state descriptor

Returned value:
	     - NONE

Description:
	Procedure which decode APM data into PCM, the compression ratio is 1:4. Data in are stored in
	a signed 4 bits format (2 concatenated data are equivalent to one byte)

*/
void AdpcmDecoderStereo( unsigned long nLen, char** ppchDataIn, long** pplDataOut, LPADPCMSTATESTEREO pState )
{
    signed char* inp;                       /* Input buffer pointer */
    long*        outp;                      /* output buffer pointer */
    int          signR,signL;               /* Current adpcm sign bit */
    int          deltaR,deltaL;             /* Current adpcm output value */
    int          stepR,stepL;               /* Stepsize */
    int          valpredR,valpredL;         /* Predicted value */
    int          vpdiffR,vpdiffL;           /* Current change to valpred */
    int          indexR,indexL;             /* Current step change index */
    int          inputbufferR,inputbufferL; /* place to keep next 4-bit value */
    ApmBool      bufferstep;                /* toggle between inputbuffer/input */

	if( !nLen )
		return;	// Protection
    outp         = *pplDataOut;
    inp          = (signed char *)( *ppchDataIn );

    valpredR	 = pState->lValPrevR;
    indexR		 = pState->lIndexR;
    stepR        = glStepSizeTable[indexR];
    valpredL     = pState->lValPrevL;
    indexL       = pState->lIndexL;
    inputbufferR = pState->lLastValueR;
    inputbufferL = pState->lLastValueL;
    bufferstep	 = pState->bHighByte;
    stepL		 = glStepSizeTable[indexL];

    
// Boucle **do while** trés optimisée, lorsqu'on effectue la décompression d'un seul sample
	do {

		nLen--;
/*
 * Step 1 - get the delta value
 */
		if( bufferstep ) {
			deltaR     = inputbufferR & 0xf;
			deltaL     = inputbufferL & 0xf;
			bufferstep = FALSE;
		}
		else {
#ifdef M_OPTIMISE_ADPCM_
			if( (unsigned long)inp % 4 ) {
				inputbufferL >>= 16;
				inputbufferR >>= 16;
			}
			else {
				inputbufferL   = *( (long*)inp );
				inputbufferR   = inputbufferL >> 8;
			}
			inp += 2;
#else
			inputbufferL	=	*inp++;
			inputbufferR	=	*inp++;
#endif
			deltaL     = ( inputbufferL >> 4 ) & 0xf;
			deltaR     = ( inputbufferR >> 4 ) & 0xf;
			bufferstep = TRUE;
		}
/*
 * Step 2 - Find new index value (for later)
 */
		indexR += glIndexTable[deltaR];
		if( indexR < 0 )
			indexR = 0;
		if( indexR > 88 )
			indexR = 88;

		indexL += glIndexTable[deltaL];
		if( indexL < 0 )
			indexL = 0;
		if( indexL > 88 )
			indexL = 88;
/*
 * Step 3 - Separate sign and magnitude
 */
		signR   = deltaR & 8;
		deltaR &= 7;
		signL   = deltaL & 8;
		deltaL &= 7;

/*
 * Step 4 - Compute difference and new predicted value
 *
 * Computes 'vpdiff = (delta+0.5)*step/4', but see comment
 * in adpcm_coder.
 */
		vpdiffR = stepR >> 3;
		if( deltaR & 4 )
			vpdiffR += stepR;
		if( deltaR & 2 )
			vpdiffR += stepR >> 1;
		if( deltaR & 1 )
			vpdiffR += stepR >> 2;
		vpdiffL = stepL >> 3;
		if( deltaL & 4 )
			vpdiffL += stepL;
		if( deltaL & 2 )
			vpdiffL += stepL >> 1;
		if( deltaL & 1 )
			vpdiffL += stepL >> 2;

		if( signR )
			valpredR -= vpdiffR;
		else
			valpredR += vpdiffR;
		
		if( signL )
			valpredL -= vpdiffL;
		else
			valpredL += vpdiffL;
/*
 * Step 5 - clamp output value
 */
		if( valpredR > 32767 )
			valpredR = 32767;
		else
			if( valpredR < -32768 )
				valpredR = -32768;
		if( valpredL > 32767 )
			valpredL = 32767;
		else
			if( valpredL < -32768 )
				valpredL = -32768;
/*
 * Step 6 - Update step value
 */
		stepR = glStepSizeTable[indexR];
		stepL = glStepSizeTable[indexL];
/*
 * Step 7 - Output value
 */
		*outp++ = valpredL;
		*outp++ = valpredR;
    }
	while( nLen > 0 );
    *pplDataOut         = outp;
	pState->bHighByte   = (ApmBool)bufferstep;
    pState->lValPrevR   = valpredR;
    pState->lIndexR     = indexR;
    pState->lLastValueR = inputbufferR;
    pState->lValPrevL   = valpredL;
    pState->lIndexL     = indexL;
    pState->lLastValueL = inputbufferL;
    *ppchDataIn         = (char*)inp;
}

/*********************************************************************************************************
void AdpcmStoreClampDatas( long* plClampBegin, long* plClampEnd, short** ppshDataOut )

Paramètres
	In : - long*      plClampBegin    Begining address of a 32 bits buffer
	     - long*      plClampEnd      Ending address of a 32 bits buffer
	     - short**    ppshDataOut     Address of the first byte PCM data

	Out: - short**    ppshDataOut     Address of the first byte PCM data

Returned value:
	     - NONE

Description:
	Procedure which fill a buffer with PCM data. But execute a clamp operation before

*/
void AdpcmStoreClampDatas( long* plClampBegin, long* plClampEnd, short** ppshDataOut )
{
	long*  plTempCurrent;
	short* pshDatas;

	pshDatas = *ppshDataOut;
	for( plTempCurrent = plClampBegin; plTempCurrent < plClampEnd; plTempCurrent ++, pshDatas++ ) {
		if( ( *plTempCurrent < 32767 ) &&
			( *plTempCurrent > - 32768 ) ) {
			*pshDatas = (short)( *plTempCurrent );
		}
		else {
			if( *plTempCurrent >= 32767 )
				*pshDatas = 32767;
			else
				*pshDatas = - 32768;
		}
	}
	*ppshDataOut = pshDatas;
}
