/* * 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 org.tritonus.lowlevel.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; /* 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 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)); } }