我正在开发music programming language,并使用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
我已经在这一段时间里摸不着头脑了。为什么音频会延迟,我们可以做些什么呢?
答案 0 :(得分:4)
这看起来像Sun的Synthesizer实现中的一个错误。
我没有深入研究过这个问题,但我发现这个问题显然存在于包裹AudioInputStream
的抖动校正器中。抖动校正器线程依赖于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();
}
答案 1 :(得分:0)
除了@ apangin的解决方案之外,我还找到了另外两种解决方法:
在每次播放之前,请关闭并重新打开相同的Synthesizer实例。
每次播放都使用新的Synthesizer实例。
这些都不是理想的,因为打开合成器实例需要几秒钟(即使它是以前打开的现有实例),但这些解决方法可能足以满足某些用例。