/*
* Encoder is the base class for all GSM encoding.
* 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;
import java.io.*;
public class Encoder
{
/* Every Encoder has a state through completion */
private Gsm_State g_s = new Gsm_State();
private Long_term lg_term_Obj = new Long_term();
private Lpc lpc_Obj = new Lpc();
private Rpe rpe_Obj = new Rpe();
private Short_term sh_term_Obj = new Short_term();
/* [0..7] LAR coefficients OUT */
private short LARc[] = new short[8];
/* [0..3] LTP lag OUT */
private short Nc[] = new short[4];
/* [0..3] coded LTP gain OUT */
private short Mc[] = new short[4];
/* [0..3] RPE grid selection OUT */
private short bc[] = new short[4];
/* [0..3] Coded maximum amplitude OUT */
private short xmaxc[] = new short[4];
/* [13*4] normalized RPE samples OUT */
private short xmc[] = new short[13*4];
/* Reads 160 bytes */
private int[] input_signal = new int[160]; /* [0..159] OUT */
/* Writes 33 bytes */
private byte[] frame = new byte[Gsm_Def.FRAME_SIZE];
/**
* Encoder class constructor.
*/
public Encoder()
{
}
/**
* Remove the header info from the stream and verifies the file type.
* As defined by the NeXT/Sun audio file format U-law (.au).
* For more info see the README file. <br><br>
* Note: Most of this info is not needed to reproduce the sound file
* after encoding. All that is needed is the magic number and the sampling rate
* to reproduce the sound file during decoding.
*
* @param in Strip the header from a Sun/Next formated sound stream.
*/
public static void stripAUHeader(InputStream in)
throws Exception {
DataInputStream input = new DataInputStream(in );
/* Just read these bits from the stream and do nothing with them */
int magic = input.readInt(); /* magic number SND_MAGIC ((int)0x2e736e64),
* which equals ".snd".)
*/
input.readInt(); /* offset or pointer to the data */
input.readInt(); /* number of bytes of data */
int dataFormat = input.readInt(); /* the data format code */
int sampleRate = input.readInt(); /* the sampling rate = ~8000 samples/sec. */
input.readInt(); /* the number of channels */
input.readChar(); /* optional text information - 4 chars */
input.readChar();
input.readChar();
input.readChar();
if( magic != 0x2E736E64) // ".snd" in ASCII
{
// throw new GsmException("AuFile wrong Magic Number");
}
else if( dataFormat != 1 ) // 8-Bit mu-Law
{
// throw new GsmException("AuFile not 8-bit Mu-Law");
}
else if( sampleRate != 8000) // 8kHz
{
// throw new GsmException("AuFile not 8kHz");
}
}
/**
* Encode the specified file.
* <br>This method calls the <code>stripAUHeader</code> method for you.<br>
* stripAUHeader will verify file type.
*
* @param input_file The name of the file to encode.
* @param output_file The name of the GSM encoded file.
*/
public void encode(String input_file, String output_file)
throws Exception {
File arg1 = new File(input_file);
if(!arg1.exists() || !arg1.isFile() || !arg1.canRead())
{
throw new IOException("File : " +
input_file + "\ndoes not exist or cannot be read.");
}
FileInputStream from = null;
FileOutputStream to = null;
try
{
from = new FileInputStream(input_file);
to = new FileOutputStream(output_file);
// Remove the header. It gets mangled by the encoding.
stripAUHeader(from);
// Read bytes till EOF.
while(ulaw_input(from) > 0)
{
//System.out.println("Entering Native method.");
gsm_encode();
// Need to do some error check here. Update ulaw_output.
ulaw_output(to); // Write bytes.
}
}
catch (Exception e)
{
throw new Exception("Encoder: " + e.getMessage());
}
finally
{
if (from != null)
{
try
{
from.close();
}
catch (IOException e)
{
throw new IOException("Encoder: " + e.getMessage());
}
}
if (to != null)
{
try
{
to.close();
}
catch (IOException e)
{
throw new IOException("Encoder: " + e.getMessage());
}
}
}
}
/**
* Encode the specified InputStream.
*
* @param input The stream to encode.
* @param output_file The name of the GSM encoded file.
*/
public void encode(InputStream input, String output_file)
throws IOException
{
FileOutputStream to = null;
try
{
to = new FileOutputStream(output_file);
// Read bytes till EOF.
while(ulaw_input(input) > 0)
{
gsm_encode();
// Need to do some error check here. Update ulaw_output.
ulaw_output(to); // Write bytes.
}
}
catch (IOException e)
{
throw new IOException( "Encoder: " + e.getMessage());
}
finally
{
if (to != null)
{
try
{
to.close();
}
catch (IOException e)
{
throw new IOException("Encoder: " + e.getMessage());
}
}
}
}
/** Encodes a block of data.
*
* @param asBuffer an 160-element array with the data to encode
* int PCM 16 bit format.
*
* @param abFrame the encoded GSM frame (33 bytes).
*/
public void encode(short[] asBuffer, byte[] abFrame)
{
for (int i = 0; i < 160; i++)
{
input_signal[i] = asBuffer[i];
}
gsm_encode();
System.arraycopy(frame, 0, abFrame, 0, frame.length);
}
/**
* Read 160 bytes from a U-law stream and set up the input_signal array.
*/
private int ulaw_input(InputStream in)
throws IOException
{
int c = 0;
int i = 0;
for (i = 0; i < input_signal.length && ((c = in.read()) != -1); i++)
{
if (c < 0)
{
throw new IOException("Encoder ulaw_input: Corrupt InputStream.");
}
else
{
input_signal[i] = u2s[c];
}
}
return (i);
}
private void gsm_encode()
{
int index = 0;
Gsm_Coder_java();
frame[index++] = (byte) (((0xD) << 4) /* 1 */
| ((LARc[0] >> 2) & 0xF));
frame[index++] = (byte) (((LARc[0] & 0x3) << 6) /* 2 */
| (LARc[1] & 0x3F));
frame[index++] = (byte) (((LARc[2] & 0x1F) << 3) /* 3 */
| ((LARc[3] >> 2) & 0x7));
frame[index++] = (byte) (((LARc[3] & 0x3) << 6) /* 4 */
| ((LARc[4] & 0xF) << 2)
| ((LARc[5] >> 2) & 0x3));
frame[index++] = (byte) (((LARc[5] & 0x3) << 6) /* 5 */
| ((LARc[6] & 0x7) << 3)
| (LARc[7] & 0x7));
frame[index++] = (byte) (((Nc[0] & 0x7F) << 1) /* 6 */
| ((bc[0] >> 1) & 0x1));
frame[index++] = (byte) (((bc[0] & 0x1) << 7) /* 7 */
| ((Mc[0] & 0x3) << 5)
| ((xmaxc[0] >> 1) & 0x1F));
frame[index++] = (byte) (((xmaxc[0] & 0x1) << 7) /* 8 */
| ((xmc[0] & 0x7) << 4)
| ((xmc[1] & 0x7) << 1)
| ((xmc[2] >> 2) & 0x1));
frame[index++] = (byte) (((xmc[2] & 0x3) << 6) /* 9 */
| ((xmc[3] & 0x7) << 3)
| (xmc[4] & 0x7));
frame[index++] = (byte) (((xmc[5] & 0x7) << 5) /* 10 */
| ((xmc[6] & 0x7) << 2)
| ((xmc[7] >> 1) & 0x3));
frame[index++] = (byte) (((xmc[7] & 0x1) << 7) /* 11 */
| ((xmc[8] & 0x7) << 4)
| ((xmc[9] & 0x7) << 1)
| ((xmc[10] >> 2) & 0x1));
frame[index++] = (byte) ((((xmc[10] & 0x3) << 6) /* 12 */
| ((xmc[11] & 0x7) << 3)
| (xmc[12] & 0x7)));
frame[index++] = (byte) (((Nc[1] & 0x7F) << 1) /* 13 */
| ((bc[1] >> 1) & 0x1));
frame[index++] = (byte) (((bc[1] & 0x1) << 7) /* 14 */
| ((Mc[1] & 0x3) << 5)
| ((xmaxc[1] >> 1) & 0x1F));
frame[index++] = (byte) (((xmaxc[1] & 0x1) << 7) /* 15 */
| ((xmc[13] & 0x7) << 4)
| ((xmc[14] & 0x7) << 1)
| ((xmc[15] >> 2) & 0x1));
frame[index++] = (byte) (((xmc[15] & 0x3) << 6)
| ((xmc[16] & 0x7) << 3)
| (xmc[17] & 0x7));
frame[index++] = (byte) (((xmc[18] & 0x7) << 5)
| ((xmc[19] & 0x7) << 2)
| ((xmc[20] >> 1) & 0x3));
frame[index++] = (byte) (((xmc[20] & 0x1) << 7)
| ((xmc[21] & 0x7) << 4)
| ((xmc[22] & 0x7) << 1)
| ((xmc[23] >> 2) & 0x1));
frame[index++] = (byte) (((xmc[23] & 0x3) << 6)
| ((xmc[24] & 0x7) << 3)
| (xmc[25] & 0x7));
frame[index++] = (byte) (((Nc[2] & 0x7F) << 1) /* 20 */
| ((bc[2] >> 1) & 0x1));
frame[index++] = (byte) (((bc[2] & 0x1) << 7)
| ((Mc[2] & 0x3) << 5)
| ((xmaxc[2] >> 1) & 0x1F));
frame[index++] = (byte) (((xmaxc[2] & 0x1) << 7)
| ((xmc[26] & 0x7) << 4)
| ((xmc[27] & 0x7) << 1)
| ((xmc[28] >> 2) & 0x1));
frame[index++] = (byte) (((xmc[28] & 0x3) << 6)
| ((xmc[29] & 0x7) << 3)
| (xmc[30] & 0x7));
frame[index++] = (byte) (((xmc[31] & 0x7) << 5)
| ((xmc[32] & 0x7) << 2)
| ((xmc[33] >> 1) & 0x3));
frame[index++] = (byte) (((xmc[33] & 0x1) << 7)
| ((xmc[34] & 0x7) << 4)
| ((xmc[35] & 0x7) << 1)
| ((xmc[36] >> 2) & 0x1));
frame[index++] = (byte) (((xmc[36] & 0x3) << 6)
| ((xmc[37] & 0x7) << 3)
| (xmc[38] & 0x7));
frame[index++] = (byte) (((Nc[3] & 0x7F) << 1)
| ((bc[3] >> 1) & 0x1));
frame[index++] = (byte) (((bc[3] & 0x1) << 7)
| ((Mc[3] & 0x3) << 5)
| ((xmaxc[3] >> 1) & 0x1F));
frame[index++] = (byte) (((xmaxc[3] & 0x1) << 7)
| ((xmc[39] & 0x7) << 4)
| ((xmc[40] & 0x7) << 1)
| ((xmc[41] >> 2) & 0x1));
frame[index++] = (byte) (((xmc[41] & 0x3) << 6) /* 30 */
| ((xmc[42] & 0x7) << 3)
| (xmc[43] & 0x7));
frame[index++] = (byte) (((xmc[44] & 0x7) << 5) /* 31 */
| ((xmc[45] & 0x7) << 2)
| ((xmc[46] >> 1) & 0x3));
frame[index++] = (byte) (((xmc[46] & 0x1) << 7) /* 32 */
| ((xmc[47] & 0x7) << 4)
| ((xmc[48] & 0x7) << 1)
| ((xmc[49] >> 2) & 0x1));
frame[index++] = (byte) (((xmc[49] & 0x3) << 6) /* 33 */
| ((xmc[50] & 0x7) << 3)
| (xmc[51] & 0x7));
}
private void Gsm_Coder_java()
{
int xmc_point = 0;
int Nc_bc_index = 0;
int xmaxc_Mc_index = 0;
int dp_dpp_point_dp0 = 120;
//short[] ep = new short[40];
short[] e = new short[50];
short[] so = new short[160];
Gsm_Preprocess(so);
lpc_Obj.Gsm_LPC_Analysis(so, LARc);
sh_term_Obj.Gsm_Short_Term_Analysis_Filter(g_s, LARc, so);
short[] dp = g_s.getDp0();
short[] dpp = dp;
for( int k = 0; k <= 3; k++, xmc_point += 13 )
{
lg_term_Obj.Gsm_Long_Term_Predictor(
so, /* d [0..39] IN */
k*40, /* so entry point */
e, /* e+5 [0..39] OUT */
dp, /* Referance to Gsm_State dp0 */
dpp, /* Referance to Gsm_State dp0 */
dp_dpp_point_dp0, /* Where to start the dp0 ref */
Nc, /* [0..3] coded LTP gain OUT */
bc, /* [0..3] RPE grid selection OUT */
Nc_bc_index++ /* The current referance point for Nc & bc */
);
rpe_Obj.Gsm_RPE_Encoding
(e, /* e + 5 ][0..39][ IN/OUT */
xmaxc, /* [0..3] Coded maximum amplitude OUT */
Mc, /* [0..3] coded LTP gain OUT */
xmaxc_Mc_index++, /* The current referance point */
xmc, /* [13*4] normalized RPE samples OUT */
xmc_point /* The current referance point for xmc */ );
for( int i = 0; i <= 39; i++ )
{
dp[ i + dp_dpp_point_dp0 ] = Add.GSM_ADD( e[5 + i],
dpp[i + dp_dpp_point_dp0] );
}
g_s.setDp0(dp);
dp_dpp_point_dp0 += 40;
}
for( int i = 0; i < 120; i++ )
{
g_s.setDp0Indexed(i, g_s.getDp0Indexed( (160 + i) ));
}
}
private void Gsm_Preprocess(short[] so) /* [0..159] IN/OUT */
throws IllegalArgumentException
{
int index = 0, so_index = 0;
short z1 = g_s.getZ1();
int L_z2 = g_s.getL_z2();
int mp = g_s.getMp();
short s1 = 0, msp = 0, lsp = 0, SO = 0;
int L_s2 = 0, L_temp = 0;
int k = 160;
while (k != 0)
{
k--;
/* 4.2.1 Downscaling of the input signal
*/
SO = (short)(Add.SASR((short)input_signal[index++], (short)3) << 2);
if ( ! (SO >= -0x4000) )
{ /* downscaled by */
throw new IllegalArgumentException
("Gsm_Preprocess: SO = "
+SO+" is out of range. Sould be >= -0x4000 ");
}
if ( ! (SO <= 0x3FFC) )
{ /* previous routine. */
throw new IllegalArgumentException
("Gsm_Preprocess: SO = "
+SO+" is out of range. Sould be <= 0x3FFC ");
}
/* 4.2.2 Offset compensation
*
* This part implements a high-pass filter and requires extended
* arithmetic precision for the recursive part of this filter.
* The input of this procedure is the array so[0...159] and the
* output the array sof[ 0...159 ].
*/
/* Compute the non-recursive part
*/
s1 = (short) (SO - z1); /* s1 = gsm_sub( *so, z1 ); */
z1 = SO;
if(s1 == Gsm_Def.MIN_WORD)
{
throw new IllegalArgumentException
("Gsm_Preprocess: s1 = "
+s1+" is out of range. ");
}
/* Compute the recursive part
*/
L_s2 = s1;
L_s2 <<= 15;
/* Execution of a 31 bv 16 bits multiplication
*/
msp = Add.SASR( L_z2, 15 );
/* gsm_L_sub(L_z2,(msp<<15)); */
lsp = (short) (L_z2 - ((int) (msp << 15)));
L_s2 += Add.GSM_MULT_R( lsp, (short) 32735 );
L_temp = (int)msp * 32735; /* GSM_L_MULT(msp,32735) >> 1;*/
L_z2 = Add.GSM_L_ADD( L_temp, L_s2 );
/* Compute sof[k] with rounding
*/
L_temp = Add.GSM_L_ADD( L_z2, 16384 );
/* 4.2.3 Preemphasis
*/
msp = Add.GSM_MULT_R( (short) mp, (short) -28180 );
mp = Add.SASR( L_temp, 15 );
so[so_index++] = Add.GSM_ADD( (short) mp, msp );
}
g_s.setZ1(z1);
g_s.setL_z2(L_z2);
g_s.setMp(mp);
}
/* Write the encoded bytes to the stream. */
private void ulaw_output(FileOutputStream out)
throws IOException
{
int i = 0;
for (i = 0; i < frame.length; i++)
{
out.write(frame[i]);
}
}
/**
* Used for debugging.
*
* @param state The Gsm_State object to be viewed.
*/
private void dump_Gsm_State(Gsm_State state)
{
state.dump_Gsm_State();
}
/*
* This is the encoding matrix.
*
* Java does not have an unsigned short, ie. 16 bits, so
* I will use the upper 16 bits of the integer. This wastes
* a little memory although I do not think it will cause a
* problem.
*/
private static final int u2s[] =
{
33280, 34308, 35336, 36364, 37393, 38421, 39449, 40477,
41505, 42534, 43562, 44590, 45618, 46647, 47675, 48703,
49474, 49988, 50503, 51017, 51531, 52045, 52559, 53073,
53587, 54101, 54616, 55130, 55644, 56158, 56672, 57186,
57572, 57829, 58086, 58343, 58600, 58857, 59114, 59371,
59628, 59885, 60142, 60399, 60656, 60913, 61171, 61428,
61620, 61749, 61877, 62006, 62134, 62263, 62392, 62520,
62649, 62777, 62906, 63034, 63163, 63291, 63420, 63548,
63645, 63709, 63773, 63838, 63902, 63966, 64030, 64095,
64159, 64223, 64287, 64352, 64416, 64480, 64544, 64609,
64657, 64689, 64721, 64753, 64785, 64818, 64850, 64882,
64914, 64946, 64978, 65010, 65042, 65075, 65107, 65139,
65163, 65179, 65195, 65211, 65227, 65243, 65259, 65275,
65291, 65308, 65324, 65340, 65356, 65372, 65388, 65404,
65416, 65424, 65432, 65440, 65448, 65456, 65464, 65472,
65480, 65488, 65496, 65504, 65512, 65520, 65528, 0,
32256, 31228, 30200, 29172, 28143, 27115, 26087, 25059,
24031, 23002, 21974, 20946, 19918, 18889, 17861, 16833,
16062, 15548, 15033, 14519, 14005, 13491, 12977, 12463,
11949, 11435, 10920, 10406, 9892, 9378, 8864, 8350,
7964, 7707, 7450, 7193, 6936, 6679, 6422, 6165,
5908, 5651, 5394, 5137, 4880, 4623, 4365, 4108,
3916, 3787, 3659, 3530, 3402, 3273, 3144, 3016,
2887, 2759, 2630, 2502, 2373, 2245, 2116, 1988,
1891, 1827, 1763, 1698, 1634, 1570, 1506, 1441,
1377, 1313, 1249, 1184, 1120, 1056, 992, 927,
879, 847, 815, 783, 751, 718, 686, 654,
622, 590, 558, 526, 494, 461, 429, 397,
373, 357, 341, 325, 309, 293, 277, 261,
245, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0
};
}