繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM