I am making a voice chat/messenger program and I got voice to work with one person in the chat but when i add a second to it the voices are laggy and cut up. I think the problem is in the Client Audio Receive class. If you don't think it is I will link the rest in a pastebin.
package client;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class ClientAudioRec implements Runnable {
private ObjectInputStream i2;
private Socket s;
private AudioFormat af;
public ClientAudioRec(Socket s2, AudioFormat audioformat) {
s = s2;
af = audioformat;
}
public void run() {
try {
i2 = new ObjectInputStream(s.getInputStream());
} catch (IOException e2) {
e2.printStackTrace();
}
SourceDataLine inSpeaker = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
try {
inSpeaker = (SourceDataLine)AudioSystem.getLine(info);
inSpeaker.open(af);
} catch (LineUnavailableException e1) {
e1.printStackTrace();
}
int bytesRead = 0;
byte[] inSound = new byte[100];
inSpeaker.start();
while(true)
{
try{
bytesRead = i2.read(inSound, 0, inSound.length);
} catch (Exception e){
e.printStackTrace();
}
if(bytesRead >= 0)
{
inSpeaker.write(inSound, 0, bytesRead);
}
}
}
}
I'd be suspicious of your Server Side Voice code: byte[] soundData = new byte[1];
. A one byte buffer? Can you make the CPU work any harder? Oh, and you do that in your Client Side Audio Input code too.
What is the data rate your sending voice at? Cell phones use 20ms frames. These are completely sampled (20ms), then transmitted to the base station (20 ms), and possibly sent to another cell phone (20ms), and then finally played through the speaker for a delay of at least 60ms. No unnatural delay is heard. The cell phone data rate is 8kbps, so each frame is 160 bits, or 20 bytes. I'd increase your buffer size to at least 20 bytes (perhaps as high as 50) and see if you get any improvement.
The socket's "Quality of Service" setting can affect the performance. For VoIP, you want a low latency connection. I'm not certain how to set that for Java Sockets; I'll have to do some reading. TCP_NODELAY
is another option to set (if possible), to prevent delayed acknowledgements slowing down subsequent packets. This happens with many small packets being sent. Sending larger packets will mitigate this, which is another reason to increase your buffer to more than 1 byte!
Edit
Instead of sending many tiny buffers, you should accumulate data into larger fixed size frames (such as 20ms of data), and only send complete frames. To accumulate data into a frame buffer, you use the #read(byte[] buffer, int offset, int length)
method. For example:
byte[] buffer = new byte[100];
int offset = 0;
while(true) {
// Read as many bytes as possible, up to remaining space in buffer
int bytes_read = source.read(buffer, offset, buffer.length - offset);
if (bytes_read >= 0) {
// Accumulate number of bytes that has been read.
offset += bytes_read;
if (offset == buffer.length) {
// Buffer is full, send it.
sink.write(buffer, 0, buffer.length);
// Clear buffer for next frame
offset = 0;
}
} else {
break; // End of stream
}
}
If 30 bytes are read, they are read into the buffer at offset=0
, and offset
is incremented to 30. If 60 more bytes are read in the next pass, they are read into the buffer starting at offset=30
, and offset
is incremented to 90. If 50 bytes become available after that, only 10 bytes will be read (buffer.length-offset
) filling the buffer. The buffer is then sent, and offset
is reset to zero. The remaining 40 bytes (or perhaps more, since data keeps arriving) will be read on the next call.
Note: you should use a similar loop around sink.write()
, in case the entire buffer cannot be written to the socket in a single call.