How to stream audio in Java without delay using SourceDataLine

I want to generate sounds based on user actions in Java. Even if I set the buffer size in SourceDataLine to the smallest possible value (1 frame), I still have a delay of about 1 second.

Since a piece of code is worth a thousand words (or was it a photograph?), Here is the code:

import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; import javax.swing.JFrame; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class SoundTest { private static int sliderValue = 500; public static void main(String[] args) throws Exception { final JFrame frame = new JFrame(); final JSlider slider = new JSlider(500, 1000); frame.add(slider); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { sliderValue = slider.getValue(); } }); frame.pack(); frame.setVisible(true); final AudioFormat audioFormat = new AudioFormat(44100, 8, 1, true, true); final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, 1); final SourceDataLine soundLine = (SourceDataLine) AudioSystem.getLine(info); soundLine.open(audioFormat); soundLine.start(); byte counter = 0; final byte[] buffer = new byte[1]; byte sign = 1; while (frame.isVisible()) { if (counter > audioFormat.getFrameRate() / sliderValue) { sign = (byte) -sign; counter = 0; } buffer[0] = (byte) (sign * 30); soundLine.write(buffer, 0, 1); counter++; } } } 

Try moving the slider while listening to the sound. Is this possible, or do I need to create buffers in memory and transfer them to Clip instances?

+7
source share
1 answer

The fix is ​​to specify the size of the buffer in the open(AudioFormat,int) method open(AudioFormat,int) . A delay of 10 ms-100 ms will be acceptable for real-time audio. Very low latencies, for example, will not work on all computer systems, and 100 ms or more is likely to annoy your users. A good compromise is, for example, 50ms. For your audio format, 8 bits, mono at 44100 Hz, a good buffer size is 2200 bytes, which is almost 50 ms.

Also note that different OSs have different sound capabilities in Java. On Windows and Linux, you can work with fairly small buffer sizes, but OS X uses an older implementation with significantly greater latency.

In addition, writing bytes of data bytes in SourceDataLine is very inefficient (the buffer size is set in the open() method, and not in write() ), as a rule, I always write one full buffer size to SourceDataLine.

After setting up SourceDataLine, use this code:

 final int bufferSize = 2200; // in Bytes soundLine.open(audioFormat, bufferSize); soundLine.start(); byte counter = 0; final byte[] buffer = new byte[bufferSize]; byte sign = 1; while (frame.isVisible()) { int threshold = audioFormat.getFrameRate() / sliderValue; for (int i = 0; i < bufferSize; i++) { if (counter > threshold) { sign = (byte) -sign; counter = 0; } buffer[i] = (byte) (sign * 30); counter++; } // the next call is blocking until the entire buffer is // sent to the SourceDataLine soundLine.write(buffer, 0, bufferSize); } 
+14
source

All Articles