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.