/*
 * Short term port to Java.
 * Copyright (C) 1999  Christopher Edwards
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

package org.tritonus.lowlevel.gsm;



public class Short_term
{
	public void Gsm_Short_Term_Analysis_Filter(
		Gsm_State	S,
		short[]		LARc,	/* coded log area ratio [0..7]  IN  */
		short[]		s	/* signal [0..159]          IN/OUT  */
		)
		throws ArrayIndexOutOfBoundsException
	{
		short[] LARp = new short[8];

		int array_index0 = S.getJ();
		int array_index1 = array_index0;

		array_index1 ^= 1;
		S.setJ( (short) array_index1);

		if( array_index0 < 0 || array_index0 > 1 ||
		    array_index1 < 0 || array_index1 > 1 )
		{
			throw new ArrayIndexOutOfBoundsException
				("Gsm_Short_Term_Synthesis_Filter: Indexing LARpp "
				 +"incorrectly. Should be >= 0 and <= 1");
		}

		short[] LARpp_j       = S.getLARppIndexed( array_index0 );
		short[] LARpp_j_1     = S.getLARppIndexed( array_index1 );

		Decoding_of_the_coded_Log_Area_Ratios( LARc, LARpp_j );

		Coefficients_0_12(  LARpp_j_1, LARpp_j, LARp );
		LARp_to_rp( LARp );
		Short_term_analysis_filtering( S, LARp, 13, s, 0 );

		Coefficients_13_26( LARpp_j_1, LARpp_j, LARp);
		LARp_to_rp( LARp );
		Short_term_analysis_filtering( S, LARp, 14, s, 13);

		Coefficients_27_39( LARpp_j_1, LARpp_j, LARp);
		LARp_to_rp( LARp );
		Short_term_analysis_filtering( S, LARp, 13, s, 27);

		Coefficients_40_159( LARpp_j, LARp);
		LARp_to_rp( LARp );
		Short_term_analysis_filtering( S, LARp, 120, s, 40);
	}



	public void Gsm_Short_Term_Synthesis_Filter(
		Gsm_State S,
		short[] LARcr,        /* received log area ratios [0..7] IN  */
		short[] wt,           /* received d [0..159]             IN  */
		int[] s               /* signal   s [0..159]            OUT  */
		)
		throws ArrayIndexOutOfBoundsException
	{
		short[] LARp = new short[8];

		int array_index0 = S.getJ();
		int array_index1 = array_index0;

		array_index1 ^= 1;
		S.setJ( (short) array_index1);

		if( array_index0 < 0 || array_index0 > 1 || 
		    array_index1 < 0 || array_index1 > 1 )
		{
			throw new ArrayIndexOutOfBoundsException
				("Gsm_Short_Term_Synthesis_Filter: Indexing LARpp "
				 +"incorrectly. Should be >= 0 and <= 1");
		}

		short[] LARpp_j       = S.getLARppIndexed( array_index0 );
		short[] LARpp_j_1     = S.getLARppIndexed( array_index1 );

		Decoding_of_the_coded_Log_Area_Ratios( LARcr, LARpp_j );

		Coefficients_0_12( LARpp_j_1, LARpp_j, LARp );
		LARp_to_rp( LARp );
		Short_term_synthesis_filtering( S, LARp, 13, wt, s, 0 );

		Coefficients_13_26( LARpp_j_1, LARpp_j, LARp);
		LARp_to_rp( LARp );
		Short_term_synthesis_filtering( S, LARp, 14, wt, s, 13 );

		Coefficients_27_39( LARpp_j_1, LARpp_j, LARp);
		LARp_to_rp( LARp );
		Short_term_synthesis_filtering( S, LARp, 13, wt, s, 27 );

		Coefficients_40_159( LARpp_j, LARp );
		LARp_to_rp( LARp );
		Short_term_synthesis_filtering(S, LARp, 120, wt, s, 40);

		S.setLARppIndexed(array_index0, LARpp_j);
		S.setLARppIndexed(array_index1, LARpp_j_1);
	}



	public static void Decoding_of_the_coded_Log_Area_Ratios(
		short[]  LARc,         /* coded log area ratio [0..7]  IN      */
		short[]  LARpp)        /* out: decoded ..                      */
	{
		short   temp1 = 0;
		int 	index = 0;

		/*  This procedure requires for efficient implementation
		 *  two tables.
		 *
		 *  INVA[1..8] = integer( (32768 * 8) / real_A[1..8])
		 *  MIC[1..8]  = minimum value of the LARc[1..8]
		 */

		/*  Compute the LARpp[1..8]
		 */

		STEP(LARc, LARpp, index++, temp1, (short)0, (short) -32, (short) 13107);
		STEP(LARc, LARpp, index++, temp1, (short)0, (short) -32, (short) 13107);
		STEP(LARc, LARpp, index++, temp1, (short)2048, (short)-16, (short)13107);
		STEP(LARc, LARpp, index++, temp1, (short)-2560, (short)-16, (short)13107);

		STEP(LARc, LARpp, index++, temp1, (short) 94, (short) -8, (short) 19223);
		STEP(LARc, LARpp, index++, temp1, (short) -1792, (short)-8, (short)17476);
		STEP(LARc, LARpp, index++, temp1, (short) -341, (short)-4, (short)31454);
		STEP(LARc, LARpp, index++, temp1, (short) -1144, (short)-4, (short)29708);

		/* NOTE: the addition of *MIC is used to restore
		 *       the sign of *LARc.
		 */
	}



	public static void STEP(short[] LARc, short[] LARpp, int index, 
				short temp1, short B, short MIC, short INVA) 
	{
		temp1    = (short) (Add.GSM_ADD( LARc[index], MIC ) << 10);       
		temp1    = Add.GSM_SUB( temp1, (short) (B << 1) );            
		temp1    = Add.GSM_MULT_R( INVA, temp1 );           
		LARpp[index] = Add.GSM_ADD( temp1, temp1 );
	}



	/* 4.2.9 */
	/* Computation of the quantized reflection coefficients
	 */
    
	/* 4.2.9.1  Interpolation of the LARpp[1..8] to get the LARp[1..8]
	 */

	/*
	 *  Within each frame of 160 analyzed speech samples the short term
	 *  analysis and synthesis filters operate with four different sets of
	 *  coefficients, derived from the previous set of decoded LARs(LARpp(j-1))
	 *  and the actual set of decoded LARs (LARpp(j))
	 *
	 * (Initial value: LARpp(j-1)[1..8] = 0.)
	 */
	public static void Coefficients_0_12(
		short[] LARpp_j_1,
		short[] LARpp_j,
		short[] LARp)
	{
		for (int i = 0; i < 8; i++)
		{
			LARp[i] = Add.GSM_ADD(Add.SASR(LARpp_j_1[i], 2 ), 
					      Add.SASR(LARpp_j[i], 2 ));

			LARp[i] = Add.GSM_ADD(LARp[i], 
					      Add.SASR(LARpp_j_1[i], 1));
		}
	}



	public static void Coefficients_13_26(
		short[] LARpp_j_1,
		short[] LARpp_j,
		short[] LARp)
	{
		for (int i = 0; i < 8; i++)
		{
			LARp[i] = Add.GSM_ADD(Add.SASR(LARpp_j_1[i], 1), 
					      Add.SASR(LARpp_j[i], 1 ));
		}
	}



	public static void Coefficients_27_39(
		short[] LARpp_j_1,
		short[] LARpp_j,
		short[] LARp)
	{
		for (int i = 0; i < 8; i++)
		{
			LARp[i] = Add.GSM_ADD(Add.SASR(LARpp_j_1[i], 2 ), 
					      Add.SASR(LARpp_j[i], 2 ));

			LARp[i] = Add.GSM_ADD(LARp[i], 
					      Add.SASR(LARpp_j[i], 1 ));
		}
	}



	public static void Coefficients_40_159(
		short[] LARpp_j,
		short[] LARp)
	{
		for (int i = 0; i < 8; i++)
			LARp[i] = LARpp_j[i];
	}



	/* 4.2.9.2 */

	/*
	 *  The input of this method is the interpolated LARp[0..7] array.
	 *  The reflection coefficients, rp[i], are used in the analysis
	 *  filter and in the synthesis filter.
	 */
	public static void LARp_to_rp(
		short[] LARp)   /* [0..7] IN/OUT  */
	{
		short           temp;

		for (int i = 0; i < 8; i++)
		{
			if (LARp[i] < 0)
			{
				temp = (short) (LARp[i] == Gsm_Def.MIN_WORD ? 
						Gsm_Def.MAX_WORD : -(LARp[i]));

				LARp[i] = (short) (- ((temp < 11059) ? temp << 1
						      : ((temp < 20070) ? temp + 11059
							 :  Add.GSM_ADD( (short) (temp >> 2), 
									 (short) 26112 ))));
			}
			else
			{
				temp  = LARp[i];
				LARp[i] = (short) ((temp < 11059) ? temp << 1
						   : ((temp < 20070) ? temp + 11059
						      :  Add.GSM_ADD( (short) (temp >> 2), 
								      (short) 26112 )));
			}
		}
	}



	/*
	 *  This procedure computes the short term residual signal d[..] to be fed
	 *  to the RPE-LTP loop from the s[..] signal and from the local rp[..]
	 *  array (quantized reflection coefficients).  As the call of this
	 *  procedure can be done in many ways (see the interpolation of the LAR
	 *  coefficient), it is assumed that the computation begins with index
	 *  k_start (for arrays d[..] and s[..]) and stops with index k_end
	 *  (k_start and k_end are defined in 4.2.9.1).  This procedure also
	 *  needs to keep the array u[0..7] in memory for each call.
	 */
	private void Short_term_analysis_filtering(
		Gsm_State S,
		short[] rp,     /* [0..7]       IN      */
		int     k_n,    /*   k_end - k_start    */
		short[] s,      /* [0..n-1]     IN/OUT  */
		int s_index)
	{
		short[]         u = S.getU();
		short           di = 0, zzz = 0, ui = 0, sav = 0, rpi = 0;

		while (k_n != 0)
		{
			k_n--; 

			di = sav = s[s_index];

			for (int i = 0; i < 8; i++)
			{               /* YYY */
				ui    = u[i];
				rpi   = rp[i];
				u[i]  = sav;

				zzz   = Add.GSM_MULT_R(rpi, di);
				sav   = Add.GSM_ADD(   ui,  zzz);

				zzz   = Add.GSM_MULT_R(rpi, ui);
				di    = Add.GSM_ADD(   di,  zzz );
			}
			s[s_index++] = di;
		}
		S.setU(u);
	}



	public static void Short_term_synthesis_filtering(
		Gsm_State S,
		short[] rrp,    /* [0..7]       IN      */
		int     k,      /* k_end - k_start      */
		short[] wt,     /* [0..k-1]     IN      */
		int[]   sr,     /* [0..k-1]     OUT     */
		int 	wt_sr_index_start)
	{
		short[] v_temp = S.getV();
		short   sri = 0, tmp1 = 0, tmp2 = 0;

		int 	index = wt_sr_index_start;

		while (k != 0)
		{
			k--;
			sri = wt[index];
			for (int i = 7; i >= 0; i--)
			{

				/* sri = GSM_SUB( sri, gsm_mult_r( rrp[i], 
				 * 	 	  v_temp[i] ) );
				 */

				tmp1 = rrp[i];
				tmp2 = v_temp[i];
				tmp2 = (short) ( tmp1 == Gsm_Def.MIN_WORD && 
						 tmp2 == Gsm_Def.MIN_WORD
						 ? Gsm_Def.MAX_WORD
						 : 0x0FFFF & (( (int)tmp1 * (int)tmp2
								+ 16384) >> 15)) ;

				sri  = Add.GSM_SUB( sri, tmp2 );

				/* v[i+1] = GSM_ADD( v_temp[i], 
				 *		     gsm_mult_r( rrp[i], sri ) );
				 */

				tmp1  = (short) ( tmp1 == Gsm_Def.MIN_WORD && 
				                  sri == Gsm_Def.MIN_WORD
						  ? Gsm_Def.MAX_WORD
						  : 0x0FFFF & (( (int)tmp1 * (int)sri
								 + 16384) >> 15)) ;

				v_temp[i+1] = Add.GSM_ADD( v_temp[i], tmp1);
			}
			sr[index++] = v_temp[0] = sri;
		}
		S.setV(v_temp);
	}
}
