[英]how to stream sound in java without delay using SourceDataLine
我想根據用戶在Java中的操作生成聲音。 即使我將SourceDataLine中的緩沖區大小設置為最小可能值(1幀),我仍然有大約1秒的延遲。
因為代碼片段價值千言萬語(或者它是圖片?),這里是代碼:
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++;
}
}
}
嘗試在聆聽聲音時移動滑塊。 是否可能,或者我是否必須創建內存緩沖區並將它們包裝在Clip實例中?
修復是在open(AudioFormat,int)
方法中指定緩沖區大小。 對於實時音頻,延遲10ms-100ms是可以接受的。 非常低的延遲會在所有計算機系統上無法正常工作,100毫秒或更長時間可能會讓您的用戶煩惱。 一個很好的權衡是,例如50ms。 對於音頻格式,8位,單聲道44100Hz,良好的緩沖區大小為2200字節,差不多50ms。
另請注意,不同的操作系統在Java中具有不同的音頻功能。 在Windows和Linux上,您可以使用非常小的緩沖區大小,但OS X使用具有明顯更大延遲的舊實現。
此外,將數據逐字節寫入SourceDataLine的效率非常低(緩沖區大小在open()
方法中設置,而不是在write()
),因為根據經驗,我總是將一個完整的緩沖區大小寫入SourceDataLine 。
設置SourceDataLine后,使用以下代碼:
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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.