Audio streaming via TCP socket on Android
Sooo...I just solved this only hours after I desperatly put bounty on it, but thats worth it.
I decided to start over. For the design thing with threads etc. I took some help from this awesome project, it helped me a lot. Now I use only one thread. It seems like the main point was the casting stuff, but I am not too sure, it also may have been the multithreading. I don't know what kind of bytes the byte[] constructor of AudioTracker expects, but certainly no float bytes. So I knew I need to use the short[] constructor. What I did was
-put the bytes in a byte[]
-take 4 of them and cast them to a float in a loop
-take each float and cast them to shorts
Since I already did that before, I am not too sure what the problem was. But now it works. I hope this can help someone who wents trough the same pain as me. Big thanks to all of you who participated and commented.
Edit: I just thought about the changes and figured that me using CHANNEL_CONFIGURATION_STEREO instead of MONO earlier has contributed a lot to the stuttering. So you might want to try that one first if you encounter this problem. Still for me it was only a part of the solution, changing just that didn't help.
static final int frequency = 44100;
static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
boolean isPlaying;
int playBufSize;
Socket socket;
AudioTrack audioTrack;
playBufSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency, channelConfiguration, audioEncoding, playBufSize, AudioTrack.MODE_STREAM);
new Thread() {
byte[] buffer = new byte[4096];
public void run() {
try {
socket = new Socket(ip, port);
}
catch (Exception e) {
e.printStackTrace();
}
audioTrack.play();
isPlaying = true;
while (isPlaying) {
int readSize = 0;
try { readSize = socket.getInputStream().read(buffer); }
catch (Exception e) {
e.printStackTrace();
}
short[] sbuffer = new short[1024];
for(int i = 0; i < buffer.length; i++)
{
int asInt = 0;
asInt = ((buffer[i] & 0xFF) << 0)
| ((buffer[i+1] & 0xFF) << 8)
| ((buffer[i+2] & 0xFF) << 16)
| ((buffer[i+3] & 0xFF) << 24);
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
int k=0;
try{k = i/4;}catch(Exception e){}
sbuffer[k] = (short)(asFloat * Short.MAX_VALUE);
i=i+3;
}
audioTrack.write(sbuffer, 0, sbuffer.length);
}
audioTrack.stop();
try { socket.close(); }
catch (Exception e) { e.printStackTrace(); }
}
}.start();
Get rid of all, all, the available()
tests. Just let your code block in the following read()
statement(s). You don't have anything better to do anyway, and you're just burning potentially valuable CPU cycles by even trying to avoid the block.
EDIT To be specific:
try {
socket.connect(address, timeout);
} catch (IOException e2) {
e2.printStackTrace();
}
Poor practice to catch this exception and allow the following code to continue as though it hadn't happened. The exception should be allowed to propagate to the caller.
try {
is = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
Ditto.
try {
if(dis.available()>0)Log.d("PCMSocket", "receiving");
Remove. You're receiving anyway.
music = new byte[4];
while (dis.available() > 0)
Pointless. Remove. The following reads will block.
{
music[i]=0;
Pointless. Remove.
music[i] = dis.readByte();
if(i==3)
{
int asInt = 0;
asInt = ((music[0] & 0xFF) << 0)
| ((music[1] & 0xFF) << 8)
| ((music[2] & 0xFF) << 16)
| ((music[3] & 0xFF) << 24);
This is all pointless. Replace it all with short asInt = dis.readInt();
.
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
Given that the original conversion to short
was via floatValue * Short.MAX_VALUE
, this conversion should be asFloat = (float)asInt/Short.MAX_VALUE
.
if(i==4)
If i
was 3 before it will be 4 now, so this test is also pointless.
music = new byte[4];
You don't need to reallocate music
. Remove.
} catch (IOException e) {
e.printStackTrace();
}
See above. Pointless. The exception should be allowed to propagate to the caller.
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
All this should be in a finally
block.
}
};
while(true){
while(!doStop)
You don't need both these loops.
try{
writeSamples(fmusic);
}catch(Exception e)
{
e.printStackTrace();
}
See above. Pointless. The exception should in this case terminate the loop, as any IOException
writing to a socket is fatal to the connection.
if( buffer.length < samples.length )
buffer = new short[samples.length];
Why isn't buffer
already the right size? Alternatively, what if buffer.length > samples.length
?