/* * Lpc 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 Lpc { private int L_ACF[] = new int[9]; public void Gsm_LPC_Analysis( short[] so, /* 0..159 signals IN/OUT */ short[] LARc) /* 0..7 LARc's OUT */ { Autocorrelation(so); Reflection_coefficients(LARc); Transformation_to_Log_Area_Ratios(LARc); Quantization_and_coding(LARc); } private void Autocorrelation(short[] so) /* [0..159] IN/OUT */ throws IllegalArgumentException { int i = 0, sp_index = 0; short temp = 0, smax = 0, scalauto = 0; int L_temp = 0; int L_temp2 = 0; /* Dynamic scaling of the array s[0..159] */ /* Search for the maximum. */ for (int k = 0; k <= 159; k++) { temp = Add.GSM_ADD(so[k], (short) 0); if (temp > smax) { smax = temp; } } /* Computation of the scaling factor. */ if (smax == 0) { scalauto = 0; } else { if (!(smax > 0)) { throw new IllegalArgumentException("Autocorrelation: smax = " + smax + " should be > 0."); } scalauto = (short) (4 - Add.gsm_norm((int) (smax << 16))); /* sub(4,..) */ } /* Scaling of the array s[0...159] */ if (scalauto > 0) { if (!(scalauto <= 4)) { throw new IllegalArgumentException("Autocorrelation: scalauto = " + scalauto + " should be <= 4."); } switch (scalauto) { case 1: for (int k = 0; k <= 159; k++) { so[k] = Add.GSM_MULT_R(so[k], (short) 16384); } break; case 2: for (int k = 0; k <= 159; k++) { so[k] = Add.GSM_MULT_R(so[k], (short) (16384 >> 1)); } break; case 3: for (int k = 0; k <= 159; k++) { so[k] = Add.GSM_MULT_R(so[k], (short) (16384 >> 2)); } break; case 4: for (int k = 0; k <= 159; k++) { so[k] = Add.GSM_MULT_R(so[k], (short) (16384 >> 3)); } break; } } /* Compute the L_ACF[..]. */ short[] sp = so; short sl = sp[sp_index]; // Zero out L_ACF int[] temp_arr = {0, 0, 0, 0, 0, 0, 0, 0, 0}; System.arraycopy(temp_arr, 0, L_ACF, 0, L_ACF.length); L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); sl = sp[++sp_index]; L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); L_ACF[1] += (int) (sl * sp[(sp_index - 1)]); sl = sp[++sp_index]; L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); L_ACF[1] += (int) (sl * sp[(sp_index - 1)]); L_ACF[2] += (int) (sl * sp[(sp_index - 2)]); sl = sp[++sp_index]; L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); L_ACF[1] += (int) (sl * sp[(sp_index - 1)]); L_ACF[2] += (int) (sl * sp[(sp_index - 2)]); L_ACF[3] += (int) (sl * sp[(sp_index - 3)]); sl = sp[++sp_index]; L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); L_ACF[1] += (int) (sl * sp[(sp_index - 1)]); L_ACF[2] += (int) (sl * sp[(sp_index - 2)]); L_ACF[3] += (int) (sl * sp[(sp_index - 3)]); L_ACF[4] += (int) (sl * sp[(sp_index - 4)]); sl = sp[++sp_index]; L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); L_ACF[1] += (int) (sl * sp[(sp_index - 1)]); L_ACF[2] += (int) (sl * sp[(sp_index - 2)]); L_ACF[3] += (int) (sl * sp[(sp_index - 3)]); L_ACF[4] += (int) (sl * sp[(sp_index - 4)]); L_ACF[5] += (int) (sl * sp[(sp_index - 5)]); sl = sp[++sp_index]; L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); L_ACF[1] += (int) (sl * sp[(sp_index - 1)]); L_ACF[2] += (int) (sl * sp[(sp_index - 2)]); L_ACF[3] += (int) (sl * sp[(sp_index - 3)]); L_ACF[4] += (int) (sl * sp[(sp_index - 4)]); L_ACF[5] += (int) (sl * sp[(sp_index - 5)]); L_ACF[6] += (int) (sl * sp[(sp_index - 6)]); sl = sp[++sp_index]; L_ACF[0] += (int) (sl * sp[(sp_index - 0)]); L_ACF[1] += (int) (sl * sp[(sp_index - 1)]); L_ACF[2] += (int) (sl * sp[(sp_index - 2)]); L_ACF[3] += (int) (sl * sp[(sp_index - 3)]); L_ACF[4] += (int) (sl * sp[(sp_index - 4)]); L_ACF[5] += (int) (sl * sp[(sp_index - 5)]); L_ACF[6] += (int) (sl * sp[(sp_index - 6)]); L_ACF[7] += (int) (sl * sp[(sp_index - 7)]); sl = sp[++sp_index]; for (i = sp_index; i < 160; ++i) { sl = sp[i]; L_ACF[0] += (int) (sl * sp[(i - 0)]); L_ACF[1] += (int) (sl * sp[(i - 1)]); L_ACF[2] += (int) (sl * sp[(i - 2)]); L_ACF[3] += (int) (sl * sp[(i - 3)]); L_ACF[4] += (int) (sl * sp[(i - 4)]); L_ACF[5] += (int) (sl * sp[(i - 5)]); L_ACF[6] += (int) (sl * sp[(i - 6)]); L_ACF[7] += (int) (sl * sp[(i - 7)]); L_ACF[8] += (int) (sl * sp[(i - 8)]); } for (int k = 0; k < 9; k++) { L_ACF[k] <<= 1; } /* Rescaling of the array s[0..159] */ if (scalauto > 0) { if (!(scalauto <= 4)) { throw new IllegalArgumentException("Autocorrelation: scalauto = " + scalauto + " should be <= 4."); } for (int k = 0; k < 160; k++) { so[k] <<= scalauto; } } } private void Reflection_coefficients(short[] r /* 0...7 OUT */) throws IllegalArgumentException { short temp = 0; int ltmp = 0; int r_index = 0; short[] ACF = new short[9]; /* 0..8 */ short[] P = new short[9]; /* 0..8 */ short[] K = new short[9]; /* 2..8 */ /* Schur recursion with 16 bits arithmetic. */ if (L_ACF[0] == 0) { /* everything is the same. */ for (int i = 0; i < 8; i++) { r[i] = 0; } return; } if (L_ACF[0] == 0) { throw new IllegalArgumentException("Reflection_coefficients: L_ACF[0] = " + L_ACF[0] + " should not = 0."); } temp = Add.gsm_norm(L_ACF[0]); if (!(temp >= 0 && temp < 32)) { throw new IllegalArgumentException("Reflection_coefficients: temp = " + temp + " should be >= 0 and < 32."); } /* ? overflow ? */ for (int i = 0; i <= 8; i++) { ACF[i] = Add.SASR(L_ACF[i] << temp, 16); } /* Initialize array P[..] and K[..] for the recursion. */ System.arraycopy(ACF, 0, K, 0, 7); System.arraycopy(ACF, 0, P, 0, 8); /* Compute reflection coefficients */ for (int n = 1; n <= 8; n++, r_index++) { temp = P[1]; temp = Add.GSM_ABS(temp); if (P[0] < temp) { for (int i = n; i < 8; i++) { r[i] = 0; } return; } r[r_index] = Add.gsm_div(temp, P[0]); if (!(r[r_index] >= 0)) { throw new IllegalArgumentException("Reflection_coefficients: r[" + r_index + "] = " + r[r_index] + " should be >= 0"); } if (P[1] > 0) { /* r[n] = sub(0, r[n]) */ r[r_index] = (short) (-(r[r_index])); } if (r[r_index] == Gsm_Def.MIN_WORD) { throw new IllegalArgumentException("Reflection_coefficients: r[" + r_index + "] = " + r[r_index] + " should not be " + Gsm_Def.MIN_WORD); } if (n == 8) { return; } /* Schur recursion */ temp = Add.GSM_MULT_R(P[1], r[r_index]); P[0] = Add.GSM_ADD(P[0], temp); for (int m = 1; m <= 8 - n; m++) { temp = Add.GSM_MULT_R(K[m], r[r_index]); P[m] = Add.GSM_ADD(P[m + 1], temp); temp = Add.GSM_MULT_R(P[m + 1], r[r_index]); K[m] = Add.GSM_ADD(K[m], temp); } } } /* 4.2.6 */ /* * The following scaling for r[..] and LAR[..] has been used: * * r[..] = integer( real_r[..]*32768. ); -1 <= real_r < 1. * LAR[..] = integer( real_LAR[..] * 16384 ); * with -1.625 <= real_LAR <= 1.625 */ private void Transformation_to_Log_Area_Ratios(short[] r /* 0..7 IN/OUT */) throws IllegalArgumentException { short temp; /* Computation of the LAR[0..7] from the r[0..7] */ for (int i = 0; i < 8; i++) { temp = r[i]; temp = Add.GSM_ABS(temp); if (!(temp >= 0)) { throw new IllegalArgumentException("Transformation_to_Log_Area_Ratios: temp = " + temp + " should be >= 0 "); } if (temp < 22118) { temp >>= 1; } else if (temp < 31130) { if (!(temp >= 11059)) { throw new IllegalArgumentException("Transformation_to_Log_Area_Ratios: temp = " + temp + " should be >= 11059 "); } temp -= 11059; } else { if (!(temp >= 26112)) { throw new IllegalArgumentException("Transformation_to_Log_Area_Ratios: temp = " + temp + " should be >= 26112 "); } temp -= 26112; temp <<= 2; } r[i] = (short) (r[i] < 0 ? -temp : temp); if (r[i] == Gsm_Def.MIN_WORD) { throw new IllegalArgumentException("Transformation_to_Log_Area_Ratios: r[" + i + "] = " + r[i] + " should not be = " + Gsm_Def.MIN_WORD); } } } /* 4.2.7 */ /* This procedure needs four tables; the following equations * give the optimum scaling for the constants: * * A[0..7] = integer( real_A[0..7] * 1024 ) * B[0..7] = integer( real_B[0..7] * 512 ) * MAC[0..7] = maximum of the LARc[0..7] * MIC[0..7] = minimum of the LARc[0..7] */ private void Quantization_and_coding(short[] LAR /* [0..7] IN/OUT */) { int index = 0; STEP2(20480, 0, 31, -32, LAR, index++); STEP2(20480, 0, 31, -32, LAR, index++); STEP2(20480, 2048, 15, -16, LAR, index++); STEP2(20480, -2560, 15, -16, LAR, index++); STEP2(13964, 94, 7, -8, LAR, index++); STEP2(15360, -1792, 7, -8, LAR, index++); STEP2(8534, -341, 3, -4, LAR, index++); STEP2(9036, -1144, 3, -4, LAR, index++); } private void STEP2(int A, int B, int MAC, int MIC, short[] LAR, int index) { short temp = 0; temp = Add.GSM_MULT((short) A, LAR[index]); temp = Add.GSM_ADD(temp, (short) B); temp = Add.GSM_ADD(temp, (short) 256); temp = Add.SASR(temp, 9); LAR[index] = (short) (temp > MAC ? MAC - MIC : (temp < MIC ? 0 : temp - MIC)); } }