I've been working on a java project that makes calls from an usb modem. The application works pefrectly on my computer, but when I tryed to run it on a lower-specs one, the audio stream of the person calling from the pc goes out perfectly and it's perfectly heared on the phone called. But the audio that should be heared by the pc user gets delayed (3 to 5 secs), with white noise, and makes literally impossible to make a conversation.
Some things to take in mind:
Any clue on what could be the problem or how can it be solved?
Class made to handle the audio after I start the new thread:(ingoing call audio)
public class SerialVoiceReader implements Runnable{
/** The running. */
private volatile boolean running = true;
/** The in. */
DataInputStream in;
/** The af. */
AudioFormat af;
/** The samples per frame. */
private int samplesPerFrame = 160;
/** The audio buffer size. */
private int audioBufferSize = samplesPerFrame * 2 ; //20ms delay
private String tel;
private String timestamp;
public SerialVoiceReader ( DataInputStream in, AudioFormat af){
this.in = in;
this.af = af;
}
public void run (){
try
{
Info infos = new Info(SourceDataLine.class, af);
SourceDataLine dataLine = (SourceDataLine) AudioSystem.getLine(infos);
dataLine.open(dataLine.getFormat(),audioBufferSize *2);
dataLine.start();
// set the volume up
if (dataLine.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl volume = (FloatControl) dataLine.getControl(FloatControl.Type.MASTER_GAIN);
volume.setValue(volume.getMaximum());
}
// get a field from GUI to set as part of the file name
tel = CallGUI.telField.getText();
timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar.getInstance().getTime());
// save the stream to a file to later set the header and make it .wav format
FileOutputStream fos = new FileOutputStream("Llamadas/" + timestamp + "-" + tel + "-OUT.raw");
// the audio buffer writing (this is the audio that goes out on the call)
while (running){
byte[] buffer = new byte[audioBufferSize];
int offset = 0;
int numRead = 0;
while (running && (offset < buffer.length && (numRead = this.in.read(buffer, offset, buffer.length - offset)) >= 0))
{
offset += numRead;
}
if(running && offset>=0){
dataLine.write(buffer, 0, offset);
fos.write(buffer);
}
}
dataLine.stop();
dataLine.drain();
dataLine.close();
fos.close();
}
catch ( Exception e )
{
}
}
Class made to handle the audio after I start the new thread:(outgoing call audio)
public class SerialVoiceWriter implements Runnable{
/** The running. */
private volatile boolean running = true;
/** The out. */
DataOutputStream out;
/** The af. */
AudioFormat af;
/** The samples per frame. */
private int samplesPerFrame = 160;
/** The audio buffer size. */
private int audioBufferSize = samplesPerFrame * 2; //20ms delay
private String tel;
private String timestamp;
public SerialVoiceWriter ( DataOutputStream out, AudioFormat af, Boolean playMessage)
{
this.out = out;
this.af = af;
}
public void run ()
{
try
{
Info infos = new Info(TargetDataLine.class, af);
TargetDataLine dataLine = (TargetDataLine) AudioSystem.getLine(infos);
dataLine.open(dataLine.getFormat(),audioBufferSize*2 );
dataLine.start();
tel = CallGUI.telField.getText();
timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar.getInstance().getTime());
FileOutputStream fis = new FileOutputStream("Llamadas/" + timestamp + "-" + tel + "-IN.raw");
while (running){
byte[] audioBuffer = new byte[audioBufferSize];
int offset = 0;
int numRead = 0;
while (running && (offset < audioBuffer.length && (numRead = dataLine.read(audioBuffer, offset, audioBuffer.length - offset)) > 0))
{
offset += numRead;
}
if(running && offset>=0){
this.out.write(audioBuffer);
fis.write(audioBuffer);
}
}
dataLine.flush();
dataLine.stop();
dataLine.close();
fis.close();
dataLine = null;
}
catch (Exception e )
{
}
}
Thank you in advice
The steps you need to take are:
OutputStream fos = new BufferedOutputStream( new FileOutputStream("Llamadas/" + timestamp + "-" + tel + "-OUT.raw"));
You'll get hugely improved performance. The way it is now every iteration of the loop waits for the buffer to finish being written to disk. Physical disks are slow and that adds up to alot of waiting. This will take a little more work: Instead of writing to dataLine AND THEN writing to fos sequentially, write to them in parallel. They each take a certain amount of time to write data to their respective destinations. If fos takes X microseconds and dataLine takes Y your current code takes X + Y microseconds. If you do it in parallel you could end up only waiting max(X, Y). `
ExecutorService es = Executors.newFixedThreadPool(2); Callable<Void>[] calls = new Callable[2]; //... your other code here... if (running && offset >= 0) { final int finalOffset = offset; Callable<Void> call1 = new Callable<Void>() { @Override public Void call() throws Exception { dataLine.write(buffer, 0, finalOffset); return null; } }; Callable<Void> call2 = new Callable<Void>() { @Override public Void call() throws Exception { fos.write(buffer); // or however you need to write. return null; } }; calls[0] = call1; calls[1] = call2; List<Callable<Void>> asList = Arrays.asList(calls); es.invokeAll(asList); // invokeAll will block until both callables have completed. }
`
I think that whatever is causing the close to 100% CPU is the culprit. But that doesn't really tell you anything specific too look at. First thing is that since the problem is with playback on the lower end PC, you may want to check if audio drivers on that device are up to date. After that, I'd look at optimizing the portion of code that is handling the receiving of audio back. Although the older PC is lower specced, I don't think it should have any problems with what you are trying to implement. I suggest running a performance profiler while you app is running to see what is taking long.
Update: You could try upping the audioBufferSize to see if it has any effect, 20ms seems low. The code provided is just for audio sent from the pc. What about the audio received from the phone?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.