Подтвердить что ты не робот

Звук 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

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

4b9b3361

Ответ 1

Это выглядит как ошибка в реализации Sun Synthesizer.

Я не исследовал это глубоко, но я обнаружил, что проблема, по-видимому, в Jitter Corrector, которая обертывает 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();
    }

Ответ 2

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

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

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

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