/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.restcomm.media.codec.gsm; import org.restcomm.media.spi.dsp.Codec; import org.restcomm.media.spi.format.Format; import org.restcomm.media.spi.format.FormatFactory; import org.restcomm.media.spi.memory.Frame; import org.restcomm.media.spi.memory.Memory; /** * * @author oifa yulian * */ public class Encoder implements Codec { private final static Format gsm = FormatFactory.createAudioFormat("gsm", 8000); private final static Format linear = FormatFactory.createAudioFormat("linear", 8000, 16, 1); private short z1=0,mp=0,k_temp,temp,temp1,temp2,temp3,s1,msp,lsp,smax,dmax,scalauto,di,sav,scal,nc,bc,R,S,bp,mc,xmax,exp,itest,xmaxc,mant; private short[] r=new short[9]; private short[] rp=new short[9]; private short[] LAR=new short[9]; private short[] LARc=new short[9]; private short[] LARp=new short[9]; private short[] LARpprev={0,0,0,0,0,0,0,0,0}; private short[] LARpp=new short[9]; private short[] ACF=new short[9]; private short[] K=new short[9]; private short[] P=new short[9]; private short[] u={0,0,0,0,0,0,0,0}; private int L_z2=0,L_s2,L_temp,L_max,L_result,L_power; private int i,j,k,em; private int[] L_ACF=new int[9]; private short[] signal = new short[160]; private short[] dp=new short[120]; private short[] dpp=new short[40]; private short[] e=new short[40]; private short[] ep=new short[40]; private short[] x=new short[40]; private short[] xm=new short[13]; private short[] xmc=new short[13]; private short[] xmp=new short[13]; private short[] wt=new short[50]; public Encoder() { for(i=0;i<120;i++) dp[i]=0; } public Frame process(Frame frame) { // encode into short values byte[] data = frame.getData(); // int k = 0; for (i = 0; i < 160; i++) { signal[i] = ((short) ((data[i*2 + 1] << 8) | (data[i*2] & 0xFF))); } Frame res = Memory.allocate(33); data = res.getData(); //preprocessing downscale(signal); offsetCompensation(signal); preemphasis(signal); //lpc analysis autocorrelation(signal); computeReflection(); transformToLAR(); quantizationAndCoding(); //short term analysis LARDecoding(); shortTermFiltering(signal); //switch LARpp arrays for(i=1;i<9;i++) { LARpprev[i]=LARpp[i]; LARpp[i]=0; } //write 5 LARC bytes //0XD0 + 4 bits of LARC[1] data[0] = (byte) ( 0xD0 | ((LARc[1] >> 2) & 0xF)); //2 bits of LARC[1] + 6 bits of LARC[2] data[1] = (byte) ((LARc[1] << 6) | (LARc[2] & 0x3F)); //5 bits of LARC[3] + 3 bits of LARC[4] data[2] = (byte) ((LARc[3] << 3) | ((LARc[4] >> 2) & 0x7)); //2 bits of LARC[4] + 4 bits of LARC[5] + 2 bits of LARC[6] data[3] = (byte) ((LARc[4] << 6) | ((LARc[5] & 0xF) << 2) | ((LARc[6] >> 2) & 0x3)); //2 bits of LARC[6] + 3 bits of LARC[7] + 3 bits of LARC[8] data[4] = (byte) ((LARc[6] << 6) | ((LARc[7] & 0x7) << 3) | (LARc[8] & 0x7)); k_temp=5; for(k=0;k<4;k++) { //long term calculateLTPParams(signal,k); longTermAnalysis(signal,k); //rpe encoding weightingFilter(); gridSelection(); ACPMQuantization(); ACPMInverseQuantization(); RPEPositioning(); //updating dp updatedp(); //write frame 7 bytes //7 bits of NC + one bits of BC data[k_temp++] = (byte) ((nc << 1) | ((bc >> 1) & 0x1)); //1 bit of BC + 2 bits of MC + 5 bits of XMAXC data[k_temp++] = (byte) ((bc << 7) | ((mc & 0x3) << 5) | ((xmaxc >> 1) & 0x1F)); //1 bit of XMAXC + 3 bits of XMC[0] + 3 bits of XMC[1] + 1 bit of XMC[2] data[k_temp++] = (byte) ((xmaxc << 7) | ((xmc[0] & 0x7) << 4) | ((xmc[1] & 0x7) << 1) | ((xmc[2] >> 2) & 0x1)); //2 bits of XMC[2] + 3 bits of XMC[3] + 3 bits of XMC[4] data[k_temp++] = (byte) ((xmc[2] << 6) | ((xmc[3] & 0x7) << 3) | (xmc[4] & 0x7)); //3 bits of XMC[5] + 3 bits of XMC[6] + 2 bits of XMC[7] data[k_temp++] = (byte) ((xmc[5] << 5) | ((xmc[6] & 0x7) << 2) | ((xmc[7] >> 1) & 0x3)); //1 bit of XMC[7] + 3 bits of XMC[8] + 3 bits of XMC[9] + 1 bit of XMC[10] data[k_temp++] = (byte) ((xmc[7] << 7) | ((xmc[8] & 0x7) << 4) | ((xmc[9] & 0x7) << 1) | ((xmc[10] >> 2) & 0x1)); //2 bits of XMC[10]+ 3 bits of XMC[11] + 3 bits of XMC[12] data[k_temp++] = (byte) ((xmc[10] << 6) | ((xmc[11] & 0x7) << 3) | (xmc[12] & 0x7)); } //encoder.encode(signal, res.getData()); res.setOffset(0); res.setLength(33); res.setTimestamp(frame.getTimestamp()); res.setDuration(frame.getDuration()); res.setSequenceNumber(frame.getSequenceNumber()); res.setEOM(frame.isEOM()); res.setFormat(gsm); return res; } public Format getSupportedInputFormat() { return linear; } public Format getSupportedOutputFormat() { return gsm; } //5.2.1 downscale private void downscale(short[] data) { for(i=0;i<160;i++) data[i]=(short)((data[i]>>3)<<2); } //5.2.2 offset compensation private void offsetCompensation(short[] data) { for(i=0;i<160;i++) { s1=BasicFunctions.sub(data[i],z1); z1=data[i]; L_s2=s1; L_s2<<=15; msp=(short)(L_z2 >> 15); lsp=(short)BasicFunctions.L_sub(L_z2,msp<<15); L_s2=BasicFunctions.L_add(L_s2,BasicFunctions.mult_r(lsp,(short)32735)); L_z2=BasicFunctions.L_add(BasicFunctions.L_mult(msp,(short)32735)>>1,L_s2); data[i]=(short)(BasicFunctions.L_add(L_z2,16384)>>15); } } //5.2.3 offset compensation private void preemphasis(short[] data) { for(i=0;i<160;i++) { temp=data[i]; data[i]=BasicFunctions.add(data[i],BasicFunctions.mult_r(mp,(short)-28180)); mp=temp; } } //5.2.4 auto correlation private void autocorrelation(short[] data) { smax=0; for(i=0;i<160;i++) { temp=BasicFunctions.abs(data[i]); if(temp>smax) smax=temp; } if(smax==0) scalauto=0; else scalauto=BasicFunctions.sub((short)4,BasicFunctions.norm(smax<<16)); if(scalauto>0) { temp=(short)(16384>>(scalauto-1)); for(int i=0;i<160;i++) data[i]=BasicFunctions.mult_r(data[i],temp); } for(i=0;i<9;i++) { L_ACF[i]=0; for(j=i;j<160;j++) L_ACF[i]=BasicFunctions.L_add(L_ACF[i],BasicFunctions.L_mult(data[j],data[j-i])); } if(scalauto>0) { for(i=0;i<160;i++) data[i]=(short)(data[i]<<scalauto); } } //5.2.5 reflection coeficients private void computeReflection() { if(L_ACF[0]==0) { for(i=1;i<9;i++) r[i]=0; return; } temp=BasicFunctions.norm(L_ACF[0]); for(i=0;i<9;i++) ACF[i]=(short)((L_ACF[i]<<temp)>>16); for(i=1;i<8;i++) K[9-i]=ACF[i]; for(i=0;i<9;i++) P[i]=ACF[i]; for(i=1;i<9;i++) { if(P[0]<BasicFunctions.abs(P[1])) { for(j=i;j<9;j++) r[j]=0; return; } r[i]=BasicFunctions.div(BasicFunctions.abs(P[1]),P[0]); if(P[1]>0) r[i]=BasicFunctions.sub((short)0,r[i]); if(i==8) return; P[0]=BasicFunctions.add(P[0],BasicFunctions.mult_r(P[1],r[i])); for(j=1;j<=8-i;j++) { P[j]=BasicFunctions.add(P[j+1],BasicFunctions.mult_r(K[9-j],r[i])); K[9-j]=BasicFunctions.add(K[9-j],BasicFunctions.mult_r(K[j+1],r[i])); } } } //5.2.6 transformation of reflection coeficients to Log.-Area Ratios private void transformToLAR() { for(i=1;i<9;i++) { temp=BasicFunctions.abs(r[i]); if(temp<22118) temp=(short)(temp>>1); else if(temp<31130) temp=BasicFunctions.sub(temp,(short)11059); else temp=(short)(BasicFunctions.sub(temp,(short)26112)<<2); LAR[i]=temp; if(r[i]<0) LAR[i]=BasicFunctions.sub((short)0,LAR[i]); } } //5.2.7 Quantization and coding of the LAR private void quantizationAndCoding() { for(i=1;i<9;i++) { temp=BasicFunctions.mult(BasicFunctions.A[i-1],LAR[i]); temp=BasicFunctions.add(temp,BasicFunctions.B[i-1]); temp=BasicFunctions.add(temp,(short)256); LARc[i]=(short)(temp>>9); if(LARc[i]>BasicFunctions.MAC[i-1]) LARc[i]=BasicFunctions.MAC[i-1]; else if(LARc[i]<BasicFunctions.MIC[i-1]) LARc[i]=BasicFunctions.MIC[i-1]; LARc[i]=BasicFunctions.sub(LARc[i],BasicFunctions.MIC[i-1]); } } //5.2.8 Decoding of coded LAR private void LARDecoding() { for(i=1;i<9;i++) { temp=(short)(BasicFunctions.add(LARc[i],BasicFunctions.MIC[i-1])<<10); temp2=(short)(BasicFunctions.B[i-1]<<1); temp=BasicFunctions.sub(temp,temp2); temp=BasicFunctions.mult_r(BasicFunctions.INVA[i-1],temp); LARpp[i]=BasicFunctions.add(temp,temp); } } //5.2.9-10 Short term filtering private void shortTermFiltering(short[] data) { //************************************* //from 0 to 12 //************************************* //5.2.9.1 - Interpolation of LARpp to get LARp for(i=1;i<9;i++) { LARp[i]=BasicFunctions.add((short)(LARpprev[i]>>2),(short)(LARpp[i]>>2)); LARp[i]=BasicFunctions.add(LARp[i],(short)(LARpprev[i]>>1)); } //5.2.9.2 - Computation of rp from interpolated LARp computeRP(); //5.2.10 - short term analysis filtering shortTermAnalysis(data,0,12); //************************************* //from 13 to 26 //************************************* //5.2.9.1 - Interpolation of LARpp to get LARp for(i=1;i<9;i++) LARp[i]=BasicFunctions.add((short)(LARpprev[i]>>1),(short)(LARpp[i]>>1)); //5.2.9.2 - Computation of rp from interpolated LARp computeRP(); //5.2.10 - short term analysis filtering shortTermAnalysis(data,13,26); //************************************* //from 27 to 39 //************************************* //5.2.9.1 - Interpolation of LARpp to get LARp for(i=1;i<9;i++) { LARp[i]=BasicFunctions.add((short)(LARpprev[i]>>2),(short)(LARpp[i]>>2)); LARp[i]=BasicFunctions.add(LARp[i],(short)(LARpp[i]>>1)); } //5.2.9.2 - Computation of rp from interpolated LARp computeRP(); //5.2.10 - short term analysis filtering shortTermAnalysis(data,27,39); //************************************* //from 40 to 159 //************************************* //5.2.9.1 - Interpolation of LARpp to get LARp for(i=1;i<9;i++) LARp[i]=LARpp[i]; //5.2.9.2 - Computation of rp from interpolated LARp computeRP(); //5.2.10 - short term analysis filtering shortTermAnalysis(data,40,159); } private void computeRP() { for(i=1;i<9;i++) { temp=BasicFunctions.abs(LARp[i]); if(temp<11059) temp=(short)(temp<<1); else if(temp<20070) temp=BasicFunctions.add(temp,(short)11059); else temp=BasicFunctions.add((short)(temp>>2),(short)26112); rp[i]=temp; if(LARp[i]<0) rp[i]=BasicFunctions.sub((short)0,rp[i]); } } private void shortTermAnalysis(short[] data,int startIndex,int endIndex) { for(j=startIndex;j<=endIndex;j++) { di=data[j]; sav=di; for(i=1;i<9;i++) { temp=BasicFunctions.add(u[i-1],BasicFunctions.mult_r(rp[i],di)); di=BasicFunctions.add(di,BasicFunctions.mult_r(rp[i],u[i-1])); u[i-1]=sav; sav=temp; } data[j]=di; } } //5.2.11 Calculation of LTP Parameters private void calculateLTPParams(short[] data,int step) { //search for optimum scaling temp2=(short)(40*(step+1)); dmax=0; for(i=40*step;i<temp2;i++) { temp=BasicFunctions.abs(data[i]); if(temp>dmax) dmax=temp; } temp=0; if(dmax==0) scal=0; else temp=BasicFunctions.norm(dmax<<16); if(temp>6) scal=0; else scal=BasicFunctions.sub((short)6,temp); //initializing working array temp2=(short)(40*step); for(i=0;i<40;i++) { wt[i]=(short)(data[temp2]>>scal); temp2+=1; } //searching for maximum cross-correlation L_max=0; nc=40; for(j=40;j<=120;j++) { L_result=0; for(i=0;i<40;i++) { L_temp=BasicFunctions.L_mult(wt[i],dp[i-j+120]); L_result=BasicFunctions.L_add(L_temp,L_result); } if(L_result>L_max) { nc=(short)j; L_max=L_result; } } //Rescaling L_max L_max=L_max>>BasicFunctions.sub((short)6,scal); //initializing working array for(i=0;i<40;i++) wt[i]=(short)(dp[i-nc+120]>>3); //compute a power L_power=0; for(i=0;i<40;i++) { L_temp=BasicFunctions.L_mult(wt[i],wt[i]); L_power=BasicFunctions.L_add(L_temp,L_power); } //normalization of L_max and L_power if(L_max<=0) { bc=0; return; } if(L_max>=L_power) { bc=3; return; } temp=BasicFunctions.norm(L_power); R=(short)((L_max<<temp)>>16); S=(short)((L_power<<temp)>>16); //coding of ltp gain for(bc=0;bc<3;bc++) if(R<=BasicFunctions.mult(S,BasicFunctions.DLB[bc])) return; } //5.2.12 Long Term analysis filtering private void longTermAnalysis(short[] data,int step) { bp=BasicFunctions.QLB[bc]; temp2=(short)(40*step); for(i=0;i<40;i++) { dpp[i]=BasicFunctions.mult_r(bp,dp[i-nc+120]); e[i]=BasicFunctions.sub(data[temp2],dpp[i]); temp2+=1; } } //5.2.13 Weighting filter private void weightingFilter() { for(i=0;i<5;i++) wt[i]=0; for(;i<45;i++) wt[i]=e[i-5]; for(;i<50;i++) wt[i]=0; for(i=0;i<40;i++) { L_result=8192; for(j=0;j<11;j++) { L_temp=BasicFunctions.L_mult(wt[i+j],BasicFunctions.H[j]); L_result=BasicFunctions.L_add(L_result,L_temp); } L_result=BasicFunctions.L_add(L_result,L_result); L_result=BasicFunctions.L_add(L_result,L_result); x[i]=(short)(L_result>>16); } } //5.2.14 Grid selection private void gridSelection() { em=0; mc=0; for(j=0;j<4;j++) { L_result=0; for(i=0;i<13;i++) { temp=(short)(x[j+(3*i)]>>2); L_temp=BasicFunctions.L_mult(temp,temp); L_result=BasicFunctions.L_add(L_temp,L_result); } if(L_result>em) { mc=(short)j; em=L_result; } } for(i=0;i<13;i++) xm[i]=x[mc+3*i]; } //5.2.15 ACPM Quantization of selected RPE sequence private void ACPMQuantization() { //find maximum of xm xmax=0; for(i=0;i<13;i++) { temp=BasicFunctions.abs(xm[i]); if(temp>xmax) xmax=temp; } //quantization and coding of xmax to get xmaxc exp=0; temp=(short)(xmax>>9); itest=0; for(i=0;i<6;i++) { if(temp<=0) itest=1; temp=(short)(temp>>1); if(itest==0) exp=BasicFunctions.add(exp,(short)1); } temp=BasicFunctions.add(exp,(short)5); xmaxc=BasicFunctions.add((short)(xmax>>temp),(short)(exp<<3)); //compute exponent and mantissa exp=0; if(xmaxc>15) exp=BasicFunctions.sub((short)(xmaxc>>3),(short)1); mant=BasicFunctions.sub(xmaxc,(short)(exp<<3)); //normalize mantissa if(mant==0) { exp=-4; mant=15; } else { itest=0; for(i=0;i<3;i++) { if(mant>7) itest=1; if(itest==0) { mant=BasicFunctions.add((short)(mant<<1),(short)1); exp=BasicFunctions.sub(exp,(short)1); } } } mant=BasicFunctions.sub(mant,(short)8); //direct computation of xmc temp1=BasicFunctions.sub((short)6,exp); temp2=BasicFunctions.NRFAC[mant]; for(i=0;i<13;i++) { temp=(short)(xm[i]<<temp1); temp=BasicFunctions.mult(temp,temp2); xmc[i]=BasicFunctions.add((short)(temp>>12),(short)4); } } //5.2.16 ACPM Inverse Quantization private void ACPMInverseQuantization() { temp1=BasicFunctions.FAC[mant]; temp2=BasicFunctions.sub((short)6,exp); temp3=(short)(1<<BasicFunctions.sub(temp2,(short)1)); for(i=0;i<13;i++) { temp=BasicFunctions.sub((short)(xmc[i]<<1),(short)7); temp=(short)(temp<<12); temp=BasicFunctions.mult_r(temp1,temp); temp=BasicFunctions.add(temp,temp3); xmp[i]=(short)(temp>>temp2); } } //5.2.17 RPE grip positioning private void RPEPositioning() { for(i=0;i<40;i++) ep[i]=0; for(i=0;i<13;i++) ep[mc+3*i]=xmp[i]; } //5.2.18 Update short term residual signal private void updatedp() { for(i=0;i<80;i++) dp[i]=dp[i+40]; temp=0; for(;i<120;i++,temp++) dp[i]=BasicFunctions.add(ep[temp],dpp[temp]); } }