Java MIDI аудио задерживается после выхода ноутбука из спящего режима

Я занимаюсь разработкой языка программирования музыки и использую JVM (через Clojure) для воспроизведения музыкальных партитур, написанных на этом языке. Пока что мы просто используем javax.sound.midi MidiSynthesizer для воспроизведения результатов.

Поскольку у Clojure медленное время запуска, и мы хотим иметь возможность проигрывать партитуры из командной строки и немедленно их прослушивать, мы решили структурировать интерпретатор партитур как фоновый серверный процесс и взаимодействовать с ним, используя более легкий клиент командной строки, написанный на Java.

Все это прекрасно работает по большей части, однако есть странная проблема, которую мы видим, когда вы запускаете сервер, затем закрываете свой ноутбук * и даете ему спящий режим, затем снова открываете его и заставляете сервер играть счет, звук не происходит сразу, но задерживается на несколько секунд. Запустив сервер с ведением журнала отладки, я действительно вижу, что события включения / выключения MIDI-нот происходят немедленно (и синхронизируются правильно), но звук задерживается.

* Это может быть или не быть конкретной платформой. Я вижу проблему на моем Macbook Pro 2014 под управлением OS X 10.9.5 Mavericks.

Чтобы сузить его, я собрал этот простой пример (с использованием Java, а не Clojure), который демонстрирует проблему:

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

Я почесал голову над этим какое-то время. Почему звук задерживается, и можем ли мы что-то с этим сделать?

2 ответа

Решение

Это похоже на ошибку в реализации Sun синтезатора.

Я не исследовал это глубоко, но я обнаружил, что проблема, по-видимому, в Jitter Corrector, который оборачивает AudioInputStream, Поток Jitter Corrector полагается на System.nanoTime(), тем не мение nanoTime может подскочить, когда компьютер выходит из режима ожидания или спящего режима.

Обходной путь должен отключить Jitter Corrector. Вы можете сделать это, открыв 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();
    }

В дополнение к решению @apangin я нашел два других обходных пути:

  • Перед каждым воспроизведением закрывайте и снова открывайте один и тот же экземпляр синтезатора.

  • Используйте новый экземпляр синтезатора для каждого воспроизведения.

Ни один из них не идеален, потому что требуется несколько секунд, чтобы открыть экземпляр синтезатора (даже если это существующий, который был ранее открыт), но эти обходные пути могут быть достаточными для некоторых случаев использования.

Другие вопросы по тегам