13

我正在开发一种音乐编程语言,并使用 JVM(通过 Clojure)来播放用这种语言编写的乐谱。到目前为止,我们只是使用 javax.sound.midi MidiSynthesizer 来播放乐谱。

因为 Clojure 的启动时间很慢,并且我们希望能够从命令行播放乐谱并立即听到,所以我们选择将乐谱解释器构建为后台服务器进程,并使用更轻量级的方式与其通信用 Java 编写的命令行客户端。

所有这一切在大多数情况下都很好用,但是,我们看到一个奇怪的问题,如果您启动服务器,然后关闭您的笔记本电脑*并让它休眠,然后再次打开它并让服务器播放得分,音频不会立即发生,而是延迟几秒钟。使用调试日志运行服务器,我实际上可以看到 MIDI 音符开/关事件立即发生(并且定时正确),但音频被延迟。

*这可能是也可能不是特定于平台的。我在运行 OS X 10.9.5 Mavericks 的 2014 Macbook Pro 上看到了这个问题。

为了帮助缩小范围,我整理了一个简单的示例(使用 Java,而不是 Clojure)来演示该问题:

https://github.com/daveyarwood/java-midi-delayed-audio-example

一段时间以来,我一直在为此挠头。为什么音频会延迟,我们能做些什么呢?

4

2 回答 2

4

这看起来像是 Sun 的 Synthesizer 实现中的一个错误。

我没有对此进行深入调查,但我发现问题显然出在 Jitter Corrector 中AudioInputStream。Jitter Corrector 线程依赖于System.nanoTime(). 但是nanoTime,当计算机从待机或休眠模式唤醒时可能会跳转。

解决方法是禁用抖动校正器。您可以通过以下方式打开 Synthesizer:

    synth = MidiSystem.getSynthesizer();

    if (synth instanceof com.sun.media.sound.SoftSynthesizer) {
        Map<String, Object> params = Collections.singletonMap("jitter correction", false);
        ((com.sun.media.sound.SoftSynthesizer) synth).open(null, params);
    } else {
       synth.open();
    }
于 2016-08-15T02:09:58.720 回答
0

除了@apangin 的解决方案,我还发现了另外两个解决方法:

  • 在每次播放之前,关闭并重新打开同一个 Synthesizer 实例。

  • 每次播放都使用一个新的 Synthesizer 实例。

这些都不是理想的,因为打开合成器实例需要几秒钟(即使它是以前打开的现有实例),但这些变通方法可能足以满足某些用例。

于 2017-02-19T17:16:44.120 回答