gpt4 book ai didi

java - 如何使用 SourceDataLine 在 java 中毫不延迟地流式传输声音

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:06:26 25 4
gpt4 key购买 nike

我想在 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++;
}
}
}

尝试一边听声音一边移动 slider 。是否可能,或者我是否必须创建内存缓冲区并将它们包装在 Clip 实例中?

最佳答案

修复方法是在 open(AudioFormat,int) 方法中指定缓冲区大小。实时音频可以接受 10ms-100ms 的延迟。非常低的延迟(例如)不适用于所有计算机系统,100 毫秒或更长的延迟可能会让您的用户感到厌烦。一个好的权衡是,例如50 毫秒。对于您的音频格式,8 位,44100Hz 的单声道,一个好的缓冲区大小是 2200 字节,几乎是 50 毫秒。

另请注意,不同的操作系统在 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);
}

关于java - 如何使用 SourceDataLine 在 java 中毫不延迟地流式传输声音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6986572/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com