/*
* 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 javaforce.codec.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;
int i = 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) */
i = 0;
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 m = 0;
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;
}
}
}