Java Microphone Latency 3s

Started by
6 comments, last by user0 13 years, 4 months ago
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 objectif (!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 ...}


Advertisement
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.
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.
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.
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.
Thank you for helping. What operating system are you using?
Windows 7. When I wrote the code earlier, the "soundless" machine was running on Linux.
Yea I think its a linux thing I have dual boot with ubuntu and windows 7 and windows 7 works finr well has like a 1 second delay thats ok in my case but ubuntu has the clicking and about 3 second delay.

This topic is closed to new replies.

Advertisement