Sign in to follow this  
jeff8j

Java Microphone Latency 3s

Recommended Posts

I am new to doing anything with a microphone but 3 seconds latency is not what I expected I was kind of thinking half a second to a second on slow computers. Its probably me but I cant find anything to go off of to speed things up. This is the code I managed to cobble together from the oracle sites. Oh and another thing that makes me think i must be doing something wrong is I can here small clicks every second or so.

Can someone take a look at this and either tell me what im doing wrong to cause it slow or tell me that the problem is java or what. Thank you


float sampleRate = 44000;
int sampleSizeInBits = 8;
int channels = 1;
boolean signed = false;
boolean bigEndian = true;

TargetDataLine line;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); // format is an AudioFormat object
if (!AudioSystem.isLineSupported(info)) {
// Handle the error ...
return;
}

SourceDataLine sdl;

// Obtain and open the line.
try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);

int numBytesRead;
//byte[] data = new byte[line.getBufferSize() / 5];
byte[] data = new byte[line.getBufferSize()];

//open output stream
sdl = AudioSystem.getSourceDataLine( format );
sdl.open( format );

// Begin audio capture.
line.start();
// Begin audio out
sdl.start();

// Here, stopped is a global boolean set by another thread.
while (running) {
// Read the next chunk of data from the TargetDataLine.
numBytesRead = line.read(data, 0, data.length);
sdl.write( data, 0, numBytesRead );
}
sdl.drain();
sdl.stop();
sdl.close();
} catch (LineUnavailableException ex) {
// Handle the error ...
}


Share this post


Link to post
Share on other sites
I can't given any direct advice, as I've never used the raw Java sound API. However, I can see that t=you are calling two potentially blocking functions in sequence. If one of them blocks for a while, this can cause your program to slow down. You can put a buffer between them to handle read/write speed mismatches.

Both SourceDataLine and TargetDataLine have available() methods you can use before writing the data.

Here is quick sample I threw together. I cannot test it because I have no microphone or speakers on this machine:

import java.util.ArrayDeque;
import java.util.Queue;

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;
import javax.sound.sampled.TargetDataLine;

class Help {

private static class Buffer {

private final byte [] data;

private final int length;

private final long time = System.currentTimeMillis();

public Buffer(byte [] data, int length) {
this.data = data;
this.length = length;
}

}

public static void main(String[] args) throws Exception {

float sampleRate = 44000;
int sampleSizeInBits = 8;
int channels = 1;
boolean signed = false;
boolean bigEndian = true;

AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);

DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);

if (!AudioSystem.isLineSupported(info)) {
// Handle the error ...
System.out.println("Line is not supported");
return;
}

try {
// Obtain and open the line.
TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);

// open output stream
SourceDataLine sdl = AudioSystem.getSourceDataLine(format);
sdl.open(format);

// Begin audio capture.
line.start();
// Begin audio out
sdl.start();

Queue<Buffer> buffers = new ArrayDeque<Buffer>();

boolean ignoreAvailable = true;
boolean running = true;
while (running) {
if(ignoreAvailable || line.available() > 0) {
byte[] data = new byte[line.getBufferSize()];
int len = line.read(data, 0, data.length);
buffers.add(new Buffer(data, len));
System.out.println("Read : " + len + " bytes");
}

while(!buffers.isEmpty()) {
Buffer buffer = buffers.peek();
if(ignoreAvailable || sdl.available() >= buffer.length) {
sdl.write(buffer.data, 0, buffer.length);
long current = System.currentTimeMillis();
System.out.println("Wrote: " + buffer.length + " bytes, delay = " + (current - buffer.time));
buffers.remove();
} else {
break; // No data available, go back to reading.
}

}
}

sdl.drain();
sdl.stop();
sdl.close();
} catch (LineUnavailableException ex) {
throw ex;
}
}
}


You can enable or disable the blocking check by changing "ignoreAvailable". This can allow you to test if my code makes any difference at all.

I've included a rudimentary profiling mechanism to allow you to see the delay between when a sample is read and when a sample is written. The output might give you hints as to what is the problem.

Share this post


Link to post
Share on other sites
Thank you for making all that but running your code isnt really an improvement.

Heres the dealy that its showing
min = Wrote: 50000 bytes, delay = 874
max = Wrote: 50000 bytes, delay = 1124
my estimated avg = 980

But its still about 3 seconds before I hear back what was played into the microphone and I still hear very low clicking like the sound is done then starts back up again.

Maybe this is somthing to note when I say boolean ignoreAvailable = false; I never hear anything and I never see a wrote just a read.

Share this post


Link to post
Share on other sites
Well I think I shed some light on the problem. Its because im using linux I hate to say theres anything wrong with linux so im going to immediately jump to the conclusion that the java runtime sucks for linux and its suns/oracles fault but all secretly funded by microsoft.

I even tried running it in the OpenJDK runtime and it still does the same thing.

If anyone knows why this is or how to fix this any help would make my life so much easier.

Share this post


Link to post
Share on other sites
I just tried it on my home machine, the delay is 0 for me but the sound is maybe a second behind my speech. There is a feedback loop there, it starts to screech pretty quick for me.

Yeah I had to add change the check to the following to allow it to work with "ignoreAvailable" enabled:

if(buffers.isEmpty() || ignoreAvailable || line.available() > 0) {
byte[] data = new byte[line.getBufferSize()];
int len = line.read(data, 0, data.length);
buffers.add(new Buffer(data, len));
System.out.println("Read : " + len + " bytes");
}


To be honest I've never really messed around with audio at such a low level. I won't presume to guess why you're getting such excessive delays. I just had a hunch that it was the blocking nature of the calls, otherwise I wouldn't have guessed at all.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this