笔记本电脑退出休眠后 Java MIDI 音频延迟

Posted

技术标签:

【中文标题】笔记本电脑退出休眠后 Java MIDI 音频延迟【英文标题】:Java MIDI audio is delayed after laptop comes out of hibernation 【发布时间】:2016-08-14 16:15:23 【问题描述】:

我正在开发一个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

我已经为此苦苦思索了一段时间。为什么音频会延迟,我们可以做些什么?

【问题讨论】:

【参考方案1】:

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

我没有对此进行深入调查,但我发现问题显然出在包含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();
    

【讨论】:

我试过了,可以验证它是否可以作为一种解决方法!太棒了! 不幸的是,使用这种变通方法确实会产生令人讨厌的副作用,即使事件的时间不太准确。这并不可怕,但在我正在处理的应用程序的情况下很明显。我注意到抖动校正开启与关闭之间存在明显差异。 由于这是我得到的唯一答案,而且它至少解释了问题和解决问题的一种方法,我会认为它是公认的答案。我确实使用 Java 归档了a bug,所以祈祷它可能会在某个时候修复。 知道如何在 Java 11 上禁用抖动校正吗?【参考方案2】:

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

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

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

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

【讨论】:

以上是关于笔记本电脑退出休眠后 Java MIDI 音频延迟的主要内容,如果未能解决你的问题,请参考以下文章

java midi 延迟

ubuntu 笔记本电脑合上后就自动休眠了,怎么改成合上只关闭屏幕?

笔记本安装Ubuntu休眠后无法唤醒解决方法

C# - 使用外部 Midi 控制器更改音量

基本软件合成器的延迟随着时间的推移而增长

如何在 Java 中播放(MIDI)序列中的音频剪辑?