将SourceDataLine与Java

时间:2018-05-04 21:10:12

标签: java multithreading javasound

我正在使用Java创建声音可视化工具,但我无法找到同步播放声音和显示声波的正确方法。

注意:下面的片段只是草图,但我实际上都试过了

尝试过的方法:

  1. 使用SourceDataLine.write()阻塞直到数据行缓冲区中有空间的事实。这导致缓冲区总是接近满,并且在刷新整个缓冲区后实际播放每个样本。这意味着半秒延迟。

    try (SourceDataLine sdl = AudioSystem.getSourceDataLine(someFormat)) {
                sdl.open(someFormat);
                sdl.start();
    
        while(true) {
            byte[] samples = getSamplesSomehow();
            sdl.write(samples, 0, samples.length); // <-- blocks
            displaySamples(samples);
        }
    }
    
  2. 在整个缓冲区刷新后将数据排队以绘制和出列(帧位置增加缓冲区大小或更多)。这只是行不通,我不知道为什么。

    Queue<Data> queue = new LinkedList<Data>();
    try (SourceDataLine sdl = AudioSystem.getSourceDataLine(someFormat)) {
                sdl.open(someFormat);
                sdl.start();
    
        while(true) {
            int position = sdl.getFramePosition();
            byte[] samples = getSamplesSomehow();
            Data data = new Data(position, samples); // <-- java bean
    
            // display
            if (queue.size > 0 && queue.peek().getPosition() < position - sdl.getBufferSize()) {
                byte[] samplesToDisplay = queue.remove().getSamples();
                displaySamples(samplesToDisplay);
            }
    
            // push new data
            queue.add(data);
            sdl.write(samples, 0, samples.length);
        }
    }
    
  3. 等待缓冲区几乎为空。这很好用但引入了忙碌的等待,CPU使用率增加了5倍。

    try (SourceDataLine sdl = AudioSystem.getSourceDataLine(someFormat)) {
                sdl.open(someFormat);
                sdl.start();
    
        while(true) {
            byte[] samples = getSamplesSomehow();
            while(sdl.getBufferSize() - sdl.available() >= data.length); // <-- busy waiting
            sdl.write(samples, 0, samples.length); // <-- doesn't get a chance to block
            displaySamples(samples);
        }
    }
    
  4. 这个问题的常见方法是什么?

1 个答案:

答案 0 :(得分:0)

在方法#1上,在输出SDL之前将数据发送到可视化工具

可视化器和音频播放应该各自都在自己的线程中。因此,使用线程安全松耦合进行数据切换。也许给你的Visualizer一个ConcurrentLinkedQueue,以FIFO的方式使用,让你的声音线程调用它。

半秒延迟表示SDL缓冲区相当大。您应该在SDL open方法中指定较小的缓冲区大小。我经常使用8K给出或取2倍。在44100 fps 16位立体声(每帧四个字节),应该只有0.05秒的延迟。如果小缓冲区大小导致丢失,请尝试加倍。例如,类AudioCue使用默认缓冲区大小仅为1K用于Clip等效播放,但混合工具(混合多个剪辑等效项)默认设置为更安全的8K。

根据获取样本的方式,如果输入的缓冲区大小与SDL缓冲区大小不同,则可能需要进行一些装配/反汇编。但这应该很容易在声音线程的循环中处理。