/* * Rpe 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 Rpe { private short exp_in; /* IN */ private short mant_in; /* IN */ private short exp_out; /* OUT */ private short mant_out; /* OUT */ private int xMp_point = 0; private static final int ENCODE = 0; private static final int DECODE = 1; private short[] x = new short[40]; /* signal [0..39] OUT */ public void Gsm_RPE_Encoding( short[] e, /* -5..-1][0..39][40..44 IN/OUT */ short[] xmaxc, /* [0..3] Coded maximum amplitude OUT */ short[] Mc, /* [0..3] RPE grid selection OUT */ int xmaxc_Mc_index, /* Ref. point for xmaxc and Mc */ short[] xMc, /* [0..12] OUT */ int xMc_index /* Ref. for xmc, '+=13' */ ) { short[] xM = new short[13]; short[] xMp = new short[13]; Weighting_filter( e ); /* Sets up the private data member 'x[40]' */ RPE_grid_selection( xM, Mc, xmaxc_Mc_index ); /* Sets up xM[13] */ /* Sets up xmc (13 array locations starting from xMc_index), * xmaxc (one array location at xmaxc_Mc_index), * exp_in and mant_in */ APCM_quantization( xM, xMc, xMc_index, xmaxc, xmaxc_Mc_index ); /* Sets up xMp[13] */ APCM_inverse_quantization( xMc, xMp, xMc_index, ENCODE ); RPE_grid_positioning( Mc[xmaxc_Mc_index], xMp, e, ENCODE ); } /* * The coefficients of the weighting filter are stored in a table * (see table 4.4). The following scaling is used: * * H[0..10] = integer( real_H[ 0..10] * 8192 ); */ private void Weighting_filter( short[] e) /* signal [-5..0.39.44] IN */ { int L_result = 0; /* * (e[-5..-1] and e[40..44] are allocated by the caller, * are initially zero and are not written anywhere.) * * e -= 5; * * The above case is true with the C code, although java * does not have pointers so e[0..49] is all set for this * method. */ /* Compute the signal x[0..39] */ for (int k = 0; k <= 39; k++) { L_result = 8192 >> 1; /* Every one of these multiplications is done twice -- * but I don't see an elegant way to optimize this. * Do you? */ /* #define STEP( i, H ) (e[ k + i ] * H) */ L_result +=( e[ k + 0 ] * -134 ) + ( e[ k + 1 ] * -374 ) /* + STEP( 2, 0 ) no sense in adding zero */ + ( e[ k + 3 ] * 2054 ) + ( e[ k + 4 ] * 5741 ) + ( e[ k + 5 ] * 8192 ) + ( e[ k + 6 ] * 5741 ) + ( e[ k + 7 ] * 2054 ) /* + STEP( 8, 0 ) no sense in adding zero */ + ( e[ k + 9 ] * -374 ) + ( e[ k + 10 ] * -134 ); /* 2 adds vs. >>16 => 14, minus one shift to compensate for * those we lost when replacing L_MULT by '*'. */ L_result = Add.SASR( L_result, 13 ); x[k] = (short) ((L_result < Gsm_Def.MIN_WORD ? Gsm_Def.MIN_WORD : (L_result > Gsm_Def.MAX_WORD ? Gsm_Def.MAX_WORD : L_result ))); } } /* * The signal x[0..39] is used to select the RPE grid which is * represented by Mc. */ private void RPE_grid_selection( short[] xM, /* [0..12] OUT */ short[] Mc_out, /* OUT */ int Mc_index) { int L_result = 0; int EM = 0; /* xxx should be L_EM? */ short Mc = 0; int L_common_0_3; /* common part of 0 and 3 */ L_result = 0; L_result += STEP( 0, 1 ) + STEP( 0, 2 ) + STEP( 0, 3 ) + STEP( 0, 4 ) + STEP( 0, 5 ) + STEP( 0, 6 ) + STEP( 0, 7 ) + STEP( 0, 8 ) + STEP( 0, 9 ) + STEP( 0, 10) + STEP( 0, 11) + STEP( 0, 12); L_common_0_3 = L_result; /* i = 0 */ L_result += STEP( 0, 0 ); L_result <<= 1; /* implicit in L_MULT */ EM = L_result; /* i = 1 */ L_result = 0; L_result += STEP( 1, 0 ) + STEP( 1, 1 ) + STEP( 1, 2 ) + STEP( 1, 3 ) + STEP( 1, 4 ) + STEP( 1, 5 ) + STEP( 1, 6 ) + STEP( 1, 7 ) + STEP( 1, 8 ) + STEP( 1, 9 ) + STEP( 1, 10) + STEP( 1, 11) + STEP( 1, 12); L_result <<= 1; if (L_result > EM) { Mc = 1; EM = L_result; } /* i = 2 */ L_result = 0; L_result += STEP( 2, 0 ) + STEP( 2, 1 ) + STEP( 2, 2 ) + STEP( 2, 3 ) + STEP( 2, 4 ) + STEP( 2, 5 ) + STEP( 2, 6 ) + STEP( 2, 7 ) + STEP( 2, 8 ) + STEP( 2, 9 ) + STEP( 2, 10) + STEP( 2, 11) + STEP( 2, 12); L_result <<= 1; if (L_result > EM) { Mc = 2; EM = L_result; } /* i = 3 */ L_result = L_common_0_3; L_result += STEP( 3, 12 ); L_result <<= 1; if (L_result > EM) { Mc = 3; EM = L_result; } /* Down-sampling by a factor 3 to get the selected xM[0..12] * RPE sequence. */ for (int i = 0; i <= 12; i ++) { xM[i] = x[Mc + 3*i]; } Mc_out[Mc_index] = Mc; } private int STEP(int m, int i ) { int L_temp; L_temp = Add.SASR( x[m + 3 * i], 2 ); return ( L_temp * L_temp ); } private void APCM_quantization ( short[] xM, /* [0..12] IN */ short[] xMc, /* [0..12] OUT */ int xMc_index, short[] xmaxc_out, /* OUT */ int xmaxc_index) throws IllegalArgumentException { int itest = 0; short xmax = 0, xmaxc = 0, temp = 0, temp1 = 0, temp2 = 0; short exp = 0, mant = 0; /* Find the maximum absolute value xmax of xM[0..12]. */ for (int i = 0; i <= 12; i++) { temp = xM[i]; temp = Add.GSM_ABS(temp); if (temp > xmax) { xmax = temp; } } /* Qantizing and coding of xmax to get xmaxc. */ exp = 0; temp = Add.SASR( xmax, 9 ); itest = 0; for (int i = 0; i <= 5; i++) { if(temp <= 0) { itest |= 1; } else { itest |= 0; } temp = Add.SASR( temp, 1 ); if( ! (exp <= 5)) { throw new IllegalArgumentException ("APCM_quantization: exp = " +exp+" is out of range. Should be <= 5"); } if (itest == 0) { exp++; /* exp = add (exp, 1) */ } } if( ! (exp <= 6 && exp >= 0)) { throw new IllegalArgumentException ("APCM_quantization: exp = " +exp+" is out of range. Should be >= -4 and <= 6"); } temp = (short) (exp + 5); if( ! (temp <= 11 && temp >= 0)) { throw new IllegalArgumentException ("APCM_quantization: temp = " +temp+" is out of range. Should be >= 0 and <= 11"); } xmaxc = Add.GSM_ADD( Add.SASR(xmax, temp), (short) (exp << 3) ); /* Quantizing and coding of the xM[0..12] RPE sequence * to get the xMc[0..12] */ APCM_quantization_xmaxc_to_exp_mant( xmaxc, ENCODE ); exp = exp_in; mant = mant_in; /* This computation uses the fact that the decoded version of xmaxc * can be calculated by using the exponent and the mantissa part of * xmaxc (logarithmic table). * So, this method avoids any division and uses only a scaling * of the RPE samples by a function of the exponent. A direct * multiplication by the inverse of the mantissa (NRFAC[0..7] * found in table 4.5) gives the 3 bit coded version xMc[0..12] * of the RPE samples. */ /* Direct computation of xMc[0..12] using table 4.5 */ if( ! (exp <= 4096 && exp >= -4096)) { throw new IllegalArgumentException ("APCM_quantization: exp = " +exp+" is out of range. Should be >= -4096 and <= 4096"); } if( ! (mant >= 0 && mant <= 7 )) { throw new IllegalArgumentException ("APCM_quantization: mant = " +mant+" is out of range. Should be >= 0 and <= 7"); } temp1 = (short) (6 - exp); /* normalization by the exponent */ temp2 = Gsm_Def.gsm_NRFAC[ mant ]; /* inverse mantissa */ for (int i = 0; i <= 12; i++) { if( ! (temp1 >= 0 && temp1 < 16)) { throw new IllegalArgumentException ("APCM_quantization: temp = " +temp+" is out of range. Should be >= 0 and < 16"); } temp = (short) (xM[i] << temp1); temp = Add.GSM_MULT( temp, temp2 ); temp = Add.SASR(temp, 12); xMc[i + xMc_index] = (short) (temp + 4); /* see note below */ } /* NOTE: This equation is used to make all the xMc[i] positive. */ mant_in = mant; exp_in = exp; xmaxc_out[xmaxc_index] = xmaxc; } public void APCM_quantization_xmaxc_to_exp_mant( short xmaxc_elem, int METHOD_ID) throws IllegalArgumentException { short exp = 0, mant = 0; /* Compute exponent and mantissa of the decoded version of xmaxc */ if (xmaxc_elem > 15) { exp = (short) (Add.SASR(xmaxc_elem, 3) - 1); } mant = (short) (xmaxc_elem - (exp << 3)); if (mant == 0) { exp = (short) -4; mant = (short) 7; } else { while (mant <= 7) { mant = (short) (mant << 1 | 1); exp--; } mant -= (short) 8; } if( exp < -4 || exp > 6 ) { throw new IllegalArgumentException ("APCM_quantization_xmaxc_to_exp_mant: exp = " +exp+" is out of range. Should be >= -4 and <= 6"); } if( mant < 0 || mant > 7 ) { throw new IllegalArgumentException ("APCM_quantization_xmaxc_to_exp_mant: mant = " +mant+" is out of range. Should be >= 0 and <= 7"); } if ( METHOD_ID == ENCODE ) { exp_in = exp; mant_in = mant; } else { /* DECODE */ exp_out = exp; mant_out = mant; } } public void Gsm_RPE_Decoding_java( short xmaxc_elem, /* From Gsm_Decoder xmaxc short array */ short Mc_elem, /* From Gsm_Decoder Mc short array */ int xmc_start, /* Starting point for the three bit part of xmc */ short[] xmc, /* [0..12], 3 bits IN */ short[] erp /* [0..39] OUT */ ) { short xMp[] = new short[13]; /* exp_out and mant_out are modified in this method */ APCM_quantization_xmaxc_to_exp_mant(xmaxc_elem, DECODE); APCM_inverse_quantization(xmc, xMp, xmc_start, DECODE); RPE_grid_positioning(Mc_elem, xMp, erp, DECODE); } /* * This part is for decoding the RPE sequence of coded xMc[0..12] * samples to obtain the xMp[0..12] array. Table 4.6 is used to get * the mantissa of xmaxc (FAC[0..7]). */ public void APCM_inverse_quantization( short[] xmc, /* [0..12] IN */ short[] xMp, /* [0..12] OUT */ int xmc_start, int METHOD_ID) throws IllegalArgumentException { short temp, temp1, temp2, temp3; if ( METHOD_ID == ENCODE ) { temp1 = Gsm_Def.gsm_FAC[ mant_in ]; temp2 = Add.GSM_SUB( (short)6, exp_in ); } else { /* DECODE */ temp1 = Gsm_Def.gsm_FAC[ mant_out ]; temp2 = Add.GSM_SUB( (short)6, exp_out ); } temp3 = Add.gsm_asl( (short)1, Add.GSM_SUB( temp2, (short)1 )); xMp_point = 0; for (int i = 0; i < 13; i++) { /* restore sign */ temp = (short) ((xmc[xmc_start++] << 1) - 7); if( ! (temp <= 7 && temp >= -7) ) { /* 4 bit signed */ throw new IllegalArgumentException ("APCM_inverse_quantization: temp = " +temp+" is out of range. Should be >= -7 and <= 7"); } temp <<= 12; /* 16 bit signed */ temp = Add.GSM_MULT_R( temp1, temp ); temp = Add.GSM_ADD( temp, temp3 ); xMp[ xMp_point++ ] = Add.gsm_asr( temp, temp2 ); } } /* * This method computes the reconstructed long term residual signal * ep[0..39] for the LTP analysis filter. The inputs are the Mc * which is the grid position selection and the xMp[0..12] decoded * RPE samples which are upsampled by a factor of 3 by inserting zero * values. */ public static void RPE_grid_positioning( short Mc, /* grid position IN */ short[] xMp, /* [0..12] IN */ short[] ep, /* [0..39] OUT */ int METHOD_ID) throws IllegalArgumentException { int i = 13; int xMp_index = 0; int ep_index; if (METHOD_ID == ENCODE) { ep_index = 5; } else { /* Decode */ ep_index = 0; } if( ! (0 <= Mc && Mc <= 3) ) { throw new IllegalArgumentException ("RPE_grid_positioning: Mc = " +Mc+" is out of range. Should be >= 0 and <= 3"); } switch (Mc) { case 3: ep[ep_index++] = 0; do { ep[ep_index++] = 0; ep[ep_index++] = 0; ep[ep_index++] = xMp[xMp_index++]; --i; } while (i != 0); break; case 2: do { ep[ep_index++] = 0; ep[ep_index++] = 0; ep[ep_index++] = xMp[xMp_index++]; --i; } while (i != 0); break; case 1: do { ep[ep_index++] = 0; ep[ep_index++] = xMp[xMp_index++]; ep[ep_index++] = 0; --i; } while (i != 0); break; case 0: do { ep[ep_index++] = xMp[xMp_index++]; ep[ep_index++] = 0; ep[ep_index++] = 0; --i; } while (i != 0); break; } if (METHOD_ID == ENCODE) { ep[ep_index++] = 0; } } }