2016-08-14 21 views
12

Zajmuję się tworzeniem music programming language i używanie JVM (przez Clojure) do odtwarzania wyników muzycznych napisanych w tym języku. Do tej pory używamy tylko javax.sound.midi MidiSynthesizer do grania partytur.Dźwięk Java MIDI jest opóźniony po wyjściu laptopa ze stanu hibernacji.

Ponieważ Clojure ma powolny czas uruchamiania i chcemy móc odtwarzać partytury z wiersza poleceń i natychmiast je usłyszeć, zdecydowaliśmy się zbudować interpreter partytury jako proces serwera w tle i komunikować się z nim używając bardziej lekkiego klienta wiersza poleceń napisanego w Javie.

Wszystko to działa świetnie w większości przypadków, jednak istnieje dziwny problem, który widzimy, gdy uruchomisz serwer, a następnie zamknij laptopa * i pozwól hibernacji, a następnie otwórz go ponownie i serwer odtwarza partyturę, dźwięk nie następuje natychmiast, ale jest opóźniony o kilka sekund. Uruchamiając serwer z logowaniem debugowania, widzę, że zdarzenia włączania/wyłączania nut MIDI następują natychmiastowo (i poprawnie), ale dźwięk jest opóźniony.

* To może ale nie musi być zależne od platformy. Widzę problem na moim MacBooku Pro 2014 z systemem OS X 10.9.5 Mavericks.

Aby pomóc sprowadzić, ułożyła Ten prosty przykład (przy użyciu języka Java, nie Clojure), który demonstruje problem:

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

Byłem zarysowania głowę nad tym przez chwilę teraz . Dlaczego dźwięk jest opóźniony i czy możemy coś z tym zrobić?

Odpowiedz

4

Wygląda to na błąd w implementacji syntezatora firmy Sun.

Nie zbadałem tego głęboko, ale odkryłem, że problem jest najwyraźniej w Jitter Corrector, który owija AudioInputStream. Wątek Jitter Corrector opiera się na System.nanoTime(). Jednak nanoTime może skakać, gdy komputer budzi się z trybu gotowości lub hibernacji.

Obejście problemu polega na wyłączeniu funkcji Jitter Corrector. Można to zrobić poprzez otwarcie Syntezator ten sposób:

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(); 
    } 
+0

Próbowałem tego i mogę sprawdzić, czy działa jako obejście problemu! Niesamowite! –

+0

Niestety, używanie tego obejścia ma nieprzyjemny efekt uboczny polegający na zmniejszeniu dokładności czasu zdarzeń. To nie jest straszne, ale jest to oczywiste w przypadku aplikacji, nad którą pracuję. Dostrzegam wyraźną różnicę między korektą jittera w porównaniu do off. –

+1

Ponieważ jest to jedyna odpowiedź, jaką otrzymałem, a ona przynajmniej wyjaśnia problem i sposób rozwiązania, uznaję to za zaakceptowaną odpowiedź. Zrobiłem plik [błąd] (http://bugs.java.com/bugdatabase/view_bug.do?bug_id = JDK-8164300) z Javą, więc kciuki, że może to być naprawione w pewnym momencie. –

0

Poza użytkownika @ apangin rozwiązania, znalazłem dwie inne obejścia:

  • Przed każdym odtwarzania, zamknąć i ponownie otworzyć ten sam syntezator instancja.

  • Użyj nowej instancji Syntezatora dla każdego odtwarzania.

Żadna z nich nie są idealne, ponieważ trwa kilka sekund, aby otworzyć wystąpienie syntezator (nawet jeśli jest to istniejący że był wcześniej otwarty), ale te obejścia może być wystarczające dla niektórych zastosowań.