samedi 20 décembre 2014

G711 decode audio and play it on Android

I'm trying to play audio that I receive by socket on my Android phone. My computer is receiving by UDP G711 PCMU audio, decompress it to Linear PCM 16 bits and send it to the phone.


I found a decode algorithm here but I'm note really good in audio so I don't really understand the code.


So when I'm playing audio on Android, I just heard noise and a little bit my voice.. I think it's my decode method but I'm not sur.


Anyone can help me ?


Thanks!


G711.java



import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class G711 {
static final int[] seg_end = { 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF,
0x3FFF, 0x7FFF };

static int search(int val, int[] table) {
for (int i = 0; i < table.length; i++)
if (val <= table[i])
return i;
return table.length;
}

/** Bias for linear code. */
public static final int BIAS = 0x84;
/** Sign bit for a A-law byte. */
private final static int SIGN_BIT = 0x80;
/** Quantization field mask. */
private final static int QUANT_MASK = 0xf;
/** Left shift for segment number. */
private final static int SEG_SHIFT = 4;
/** Segment field mask. */
private final static int SEG_MASK = 0x70;


/*public static int encode(byte[] data, int offset, int len, byte[] res) {
short[] shorts = new short[data.length / 2];
ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()
.get(shorts);
int j = 0;
for (int i = 0; i < shorts.length; i++) {
short sample = shorts[i];
int sign = sample & 0x8000;
if (sign != 0) {
sample = (short) -sample;
sign = 0x80;
}
if (sample > CCLIP)
sample = CCLIP;
sample += CBIAS;
int exp;
short temp = (short) (sample << 1);
for (exp = 7; exp > 0; exp--) {
if ((temp & 0x8000) != 0)
break;
temp = (short) (temp << 1);
}
temp = (short) (sample >> (exp + 3));
int mantis = temp & 0x000f;
byte ulawByte = (byte) (sign | (exp << 4) | mantis);
res[j++] = (byte) ~ulawByte;
}
return len / 2;
}*/

public static byte[] encode(byte[] data) {
short[] shorts = new short[data.length / 2];
ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()
.get(shorts);
ByteBuffer buffer = ByteBuffer.allocate(data.length / 2);
for (int i = 0; i < shorts.length; i++) {
buffer.put((byte) linear2ulaw(shorts[i]));
}
return buffer.array();
}

public static byte[] decode(byte[] data) {
ByteBuffer buffer = ByteBuffer.allocate(2 * data.length);
for (int i = 0; i < data.length; i++) {
//buffer.putShort((short) ulaw2linear(data[i]));
buffer.putShort(mulawdecode(data[i]));
}
return buffer.array();
}


private static short mulawdecode(byte mulaw)
{
//Flip all the bits
mulaw = (byte)~mulaw;

//Pull out the value of the sign bit
int sign = mulaw & 0x80;
//Pull out and shift over the value of the exponent
int exponent = (mulaw & 0x70) >> 4;
//Pull out the four bits of data
int data = mulaw & 0x0f;

//Add on the implicit fifth bit (we know
//the four data bits followed a one bit)
data |= 0x10;
/* Add a 1 to the end of the data by
* shifting over and adding one. Why?
* Mu-law is not a one-to-one function.
* There is a range of values that all
* map to the same mu-law byte.
* Adding a one to the end essentially adds a
* "half byte", which means that
* the decoding will return the value in the
* middle of that range. Otherwise, the mu-law
* decoding would always be
* less than the original data. */
data <<= 1;
data += 1;
/* Shift the five bits to where they need
* to be: left (exponent + 2) places
* Why (exponent + 2) ?
* 1 2 3 4 5 6 7 8 9 A B C D E F G
* . 7 6 5 4 3 2 1 0 . . . . . . . <-- starting bit (based on exponent)
* . . . . . . . . . . 1 x x x x 1 <-- our data
* We need to move the one under the value of the exponent,
* which means it must move (exponent + 2) times
*/
data <<= exponent + 2;
//Remember, we added to the original,
//so we need to subtract from the final
data -= BIAS;
//If the sign bit is 0, the number
//is positive. Otherwise, negative.
return (short)(sign == 0 ? data : -data);
}
/**
* Converts a linear PCM value to u-law
*
* In order to simplify the encoding process, the original linear magnitude
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
* (33 - 8191). The result can be seen in the following encoding table:
*
* Biased Linear Input Code Compressed Code ------------------------
* --------------- 00000001wxyza 000wxyz 0000001wxyzab 001wxyz 000001wxyzabc
* 010wxyz 00001wxyzabcd 011wxyz 0001wxyzabcde 100wxyz 001wxyzabcdef 101wxyz
* 01wxyzabcdefg 110wxyz 1wxyzabcdefgh 111wxyz
*
* Each biased linear code has a leading 1 which identifies the segment
* number. The value of the segment number is equal to 7 minus the number of
* leading 0's. The quantization interval is directly available as the four
* bits wxyz. The trailing bits (a - h) are ignored.
*
* Ordinarily the complement of the resulting code word is used for
* transmission, and so the code word is complemented before it is returned.
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
*/
private static int linear2ulaw(int pcm_val) // 2's complement (16-bit range)
{
int mask;
int seg;
// unsigned char uval;
int uval;

// Get the sign and the magnitude of the value.
if (pcm_val < 0) {
pcm_val = BIAS - pcm_val;
mask = 0x7F;
} else {
pcm_val += BIAS;
mask = 0xFF;
}
// Convert the scaled magnitude to segment number.
seg = search(pcm_val, seg_end);

// Combine the sign, segment, quantization bits; and complement the code
// word.

if (seg >= 8)
return (0x7F ^ mask); // out of range, return maximum value.
else {
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
return (uval ^ mask);
}
}

/**
* ConvertS a u-law value to 16-bit linear PCM.
*
* First, a biased linear code is derived from the code word. An unbiased
* output can then be obtained by subtracting 33 from the biased code.
*
* Note that this function expects to be passed the complement of the
* original code word. This is in keeping with ISDN conventions.
*/
// public static int ulaw2linear(unsigned char u_val)
public static int ulaw2linear(int u_val) {
int t;
// Complement to obtain normal u-law value.
u_val = ~u_val;
// Extract and bias the quantization bits. Then shift up by the segment
// number and subtract out the bias.
t = ((u_val & QUANT_MASK) << 3) + BIAS;
// t<<=((unsigned)u_val&SEG_MASK)>>SEG_SHIFT;
t <<= (u_val & SEG_MASK) >> SEG_SHIFT;

return ((u_val & SIGN_BIT) != 0) ? (BIAS - t) : (t - BIAS);
}
}


Packet Send to the phone



byte[] buf = new byte[m_bufferSize];
m_packet = new DatagramPacket(buf, m_bufferSize);
m_RTPsocket.receive(m_packet);
if (m_packet.getSocketAddress().toString().equals("/192.168.1.252:8000")) {
m_buffer = m_packet.getData();
// m_rtdpPacket = new RTPPacket(m_buffer, m_buffer.length);
// byte[] payload = new byte[m_rtpPacket.getPayloadSize()];
// m_rtpPacket.getPayload(payload);
byte[] audio = new byte[m_buffer.length * 2];
audio = G711.decode(m_buffer);
m_cellPacket = new DatagramPacket(audio, audio.length,
m_ipCell, m_cellPort);
m_UDPsocket.send(m_cellPacket);
}


Android player



import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;

public class AudioReceiverThread implements Runnable {
private final String TAG = "VoIPAndroidReceiverThread";
// the server information
private static final int PORT = 45680;

// the audio recording options
private static final int RECORDING_RATE = 8000;
private static final int CHANNEL = AudioFormat.CHANNEL_OUT_STEREO;
private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;

@Override
public void run() {
Log.d(TAG, "Start AudioReceiverThread");
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
try {
DatagramSocket soc = new DatagramSocket(null);
soc.setReuseAddress(true);
soc.bind(new InetSocketAddress(PORT));
int buffSize = AudioTrack.getMinBufferSize(RECORDING_RATE,
CHANNEL, FORMAT);
Log.i(TAG, "Buffer Size : " + buffSize);
byte[] bSockBuffer = new byte[buffSize];
AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,
RECORDING_RATE, CHANNEL, FORMAT,
buffSize, AudioTrack.MODE_STREAM);
track.setPlaybackRate(RECORDING_RATE);
track.play();
DatagramPacket pack;
pack = new DatagramPacket(bSockBuffer, bSockBuffer.length);
while (!Thread.interrupted()) {
pack.setLength(buffSize);
soc.receive(pack);
track.write(pack.getData(), 0, pack.getLength());
}
track.stop();
track.release();
soc.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

}

Aucun commentaire:

Enregistrer un commentaire