/*
* 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 amit bhayani
* @kulikov
*/
public class Decoder implements Codec {
private final static Format gsm = FormatFactory.createAudioFormat("gsm", 8000);
private final static Format linear = FormatFactory.createAudioFormat("linear", 8000, 16, 1);
private short k_temp,temp,temp1,temp2,temp3,mant,msr=0,nc,bc,mc,xmaxc,exp,nr,nrp=40,brp,drpp,sri,itest;
private int i,j,k,l,framesCount;
private short[] xmc=new short[13];
private short[] xmp=new short[13];
private short[] erp=new short[40];
private short[] drp=new short[160];
private short[] result=new short[160];
private short[] LARc=new short[9];
private short[] LARp=new short[9];
private short[] LARpp=new short[9];
private short[] rp=new short[9];
private short[] v=new short[9];
private short[] LARpprev={0,0,0,0,0,0,0,0,0};
private static final int BUFFER_SIZE = 320;
byte[] resdata;
public Decoder()
{
for(i=0;i<120;i++)
drp[i]=0;
}
public Frame process(Frame frame) {
byte[] data=frame.getData();
if(data.length==0)
{
//dummy frame received , sending blank data
Frame res = Memory.allocate(320);
//load data into frame
data=res.getData();
for (i = 0; i < 320; i++)
data[i]=0;
res.setOffset(0);
res.setLength(320);
res.setTimestamp(frame.getTimestamp());
res.setDuration(frame.getDuration());
res.setSequenceNumber(frame.getSequenceNumber());
res.setEOM(frame.isEOM());
res.setFormat(linear);
return res;
}
if(data.length%33!=0)
throw new IllegalArgumentException("invalid frame size expected 33,received " + data.length);
framesCount=data.length/33;
Frame res = Memory.allocate(320*framesCount);
resdata=res.getData();
for(l=0;l<(data.length/33);l++)
{
k_temp=(short)(l*33);
if(((data[k_temp]>>4) & 0xF) != 0xD)
throw new IllegalArgumentException("not gsm fr frame,expected 0xD received " + Integer.toHexString(data[k_temp]>>4) + " FRAME SIZE:" + data.length);
//lets load LARC array
//LARC[1] - 4 bits from byte 0 and 2 bits from byte 1
LARc[1]=(short)(((data[k_temp]<<2)&0x3C) | ((data[k_temp+1]>>6) & 0x3));
//LARC[2] - 6 bits from byte 1
LARc[2]=(short)(data[k_temp+1]&0x3F);
//LARC[3] - 5 bits from byte 2
LARc[3]=(short)((data[k_temp+2]>>3) & 0x1F);
//LARC[4] - 3 bits from byte 2 and 2 bits from byte 3
LARc[4]=(short)(((data[k_temp+2]<<2)&0x1C) | ((data[k_temp+3]>>6) & 0x3));
//LARC[5] -4 bits from byte 3
LARc[5]=(short)((data[k_temp+3]>>2) & 0xF);
//LARC[6] - 2 bits from byte 3 and 2 bits from byte 4
LARc[6]=(short)(((data[k_temp+3]<<2)&0xC) | ((data[k_temp+4]>>6) & 0x3));
//LARC[7] - 3 bits from byte 4
LARc[7]=(short)((data[k_temp+4]>>3) & 0x7);
//LARC[8] - 3 bits from byte 4
LARc[8]=(short)(data[k_temp+4] & 0x7);
LARDecoding();
k_temp+=5;
//lets handle 4 subframes
for(k=0;k<4;k++)
{
//taking 7 bits for nc
nc=(short)((data[k_temp]>>1)&0x7F);
//taking one bit from byte 1 and 1 bit from byte 2 for bc
bc=(short)(((data[k_temp++]<<1) & 0x2) | ((data[k_temp]>>7)&0x1));
//taking 2 bits of mc
mc=(short)((data[k_temp]>>5) & 0X3);
//taking 5 bits from byte 2 and 1 bit from byte 3 for xmaxc
xmaxc=(short)(((data[k_temp++]<<1) & 0x3E) | ((data[k_temp]>>7)&0x1));
//loading xmc array
xmc[0]=(short)((data[k_temp]>>4) & 0x7);
xmc[1]=(short)((data[k_temp]>>1) & 0x7);
xmc[2]=(short)(((data[k_temp++]<<2)& 0x4) | ((data[k_temp]>>6)&0x3));
xmc[3]=(short)((data[k_temp]>>3) & 0x7);
xmc[4]=(short)(data[k_temp++] & 0x7);
xmc[5]=(short)((data[k_temp]>>5) & 0x7);
xmc[6]=(short)((data[k_temp]>>2) & 0x7);
xmc[7]=(short)(((data[k_temp++]<<1)& 0x6) | ((data[k_temp]>>7)&0x1));
xmc[8]=(short)((data[k_temp]>>4) & 0x7);
xmc[9]=(short)((data[k_temp]>>1) & 0x7);
xmc[10]=(short)(((data[k_temp++]<<2)& 0x4) | ((data[k_temp]>>6)&0x3));
xmc[11]=(short)((data[k_temp]>>3) & 0x7);
xmc[12]=(short)(data[k_temp++] & 0x7);
computeExpAndMant();
ACPMInverseQuantization();
RPEPositioning();
longTermSynthesisFiltering(k);
}
//drp has data now
shortTermFiltering(result);
deemphasisFilter(result);
upscale(result);
downscale(result);
//switch LARpp arrays
for(i=1;i<9;i++)
{
LARpprev[i]=LARpp[i];
LARpp[i]=0;
}
k_temp=(short)(l*320);
//load data into frame
for (i = 0; i < 160; i++)
{
resdata[k_temp+i*2]=(byte)(result[i]& 0xFF);
resdata[k_temp+i*2 + 1]=(byte)((result[i]>>8) & 0xFF);
}
}
res.setOffset(0);
res.setLength(320*framesCount);
res.setTimestamp(frame.getTimestamp());
res.setDuration(frame.getDuration());
res.setSequenceNumber(frame.getSequenceNumber());
res.setEOM(frame.isEOM());
res.setFormat(linear);
return res;
}
public Format getSupportedInputFormat() {
return gsm;
}
public Format getSupportedOutputFormat() {
return linear;
}
//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.15 compute exponent and mantissa
private void computeExpAndMant()
{
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);
}
//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++)
erp[i]=0;
for(i=0;i<13;i++)
erp[mc+3*i]=xmp[i];
}
//5.3.2 Long term synthesis filtering
private void longTermSynthesisFiltering(int subframe)
{
nr=nc;
if(nc<40)
nr=nrp;
else if(nc>120)
nr=nrp;
nrp=nr;
brp=BasicFunctions.QLB[bc];
for(i=0;i<40;i++)
{
drpp=BasicFunctions.mult_r(brp,drp[i-nr+120]);
drp[i+120]=BasicFunctions.add(erp[i],drpp);
}
for(i=0;i<119;i++)
{
drp[i]=drp[i+40];
}
temp=(short)(subframe*40);
for(i=0;i<40;i++)
{
result[temp]=drp[i];
temp++;
}
}
//5.2.9 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.3.4 - short term synthesis filtering
shortTermSynthesisFiltering(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.3.4 - short term synthesis filtering
shortTermSynthesisFiltering(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.3.4 - short term synthesis filtering
shortTermSynthesisFiltering(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.3.4 - short term synthesis filtering
shortTermSynthesisFiltering(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]);
}
}
//5.3.4 short term synthesis filtering
private void shortTermSynthesisFiltering(short[] data,int startIndex,int endIndex)
{
for(j=startIndex;j<=endIndex;j++)
{
sri=data[j];
for(i=1;i<9;i++)
{
sri=BasicFunctions.sub(sri,BasicFunctions.mult_r(rp[9-i],v[8-i]));
v[9-i]=BasicFunctions.add(v[8-i],BasicFunctions.mult_r(rp[9-i],sri));
}
data[j]=sri;
v[0]=sri;
}
}
//5.3.5 deempasis filter
private void deemphasisFilter(short[] data)
{
for(i=0;i<160;i++)
{
temp=BasicFunctions.add(data[i],BasicFunctions.mult_r(msr,(short)28180));
msr=temp;
data[i]=msr;
}
}
//5.3.6 upscale
private void upscale(short[] data)
{
for(i=0;i<160;i++)
data[i]=BasicFunctions.add(data[i],data[i]);
}
//5.3.7 truncation
private void downscale(short[] data)
{
for(i=0;i<160;i++)
{
data[i]=(short)(data[i]>>3);
data[i]=(short)(data[i]<<3);
}
}
}