/*
* 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;
}
}
}