2016-08-14 2 views
12

Je développe un music programming language, et j'utilise la JVM (via Clojure) pour jouer des partitions écrites dans cette langue. Jusqu'à présent, nous utilisons simplement le MidiSynthesizer javax.sound.midi pour jouer les scores. Comme Clojure a un temps de démarrage lent et que nous voulons être capable de jouer un score depuis la ligne de commande et de l'entendre immédiatement, nous avons choisi de structurer l'interpréteur de partition comme un processus de serveur d'arrière-plan et de communiquer avec lui en utilisant un client de ligne de commande plus léger écrit en Java. Tout cela fonctionne très bien pour la plupart, cependant, il y a un problème étrange que nous voyons où si vous démarrez le serveur, puis fermez votre ordinateur portable * et laissez-le hiberner, puis l'ouvrir à nouveau et avoir le serveur joue un score, l'audio ne se produit pas immédiatement mais est retardé de plusieurs secondes. En exécutant le serveur avec la journalisation de débogage, je peux réellement voir que les événements on/off de note de MIDI se produisent immédiatement (et temporisés correctement), mais l'acoustique est retardée.Java L'audio MIDI est retardé après la sortie de l'ordinateur portable

* Ceci peut être spécifique ou non à la plate-forme. Je vois le problème sur mon Macbook Pro 2014 exécutant OS X 10.9.5 Mavericks.

Pour aider affinez, je mets ensemble cet exemple simple (en utilisant Java, pas Clojure) qui illustre le problème:

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

J'ai été me gratter la tête sur ce pendant un certain temps maintenant . Pourquoi l'audio est-il retardé, et y a-t-il quelque chose que nous puissions faire à ce sujet?

Répondre

4

Cela ressemble à un bug dans l'implémentation de Synthesizer de Sun.

Je n'ai pas étudié cela en profondeur, mais j'ai trouvé que le problème est apparemment dans Jitter Corrector qui enveloppe AudioInputStream. Le thread Jitter Corrector se base sur System.nanoTime(). Cependant nanoTime peut sauter quand un ordinateur se réveille du mode veille ou hibernation.

La solution consiste à désactiver Jitter Corrector. Vous pouvez le faire en ouvrant Synthétiseur cette façon:

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

J'ai essayé ceci et peut vérifier que cela fonctionne comme une solution de contournement! Impressionnant! –

+0

Malheureusement, l'utilisation de ce work-around a le mauvais effet secondaire de rendre le calendrier des événements un peu moins précis. Ce n'est pas terrible, mais c'est évident dans le cas de l'application sur laquelle je travaille. Je remarque une nette différence entre la correction de la gigue étant sur et hors. –

+1

Puisqu'il s'agit de la seule réponse que j'ai reçue, et qu'elle explique au moins le problème et une façon de le résoudre, je le considérerai comme la réponse acceptée. J'ai fait un fichier [un bug] (http://bugs.java.com/bugdatabase/view_bug.do?bug_id = JDK-8164300) avec Java, donc les doigts croisés qu'il pourrait être fixé à un moment donné. –

0

En plus de @ solution de apangin, j'ai trouvé deux autres solutions de contournement:

  • Avant chaque lecture, fermer et réouvrir le même synthétiseur exemple.

  • Utilisez une nouvelle occurrence de synthétiseur pour chaque lecture.

Aucune de ces sont idéales car il faut quelques secondes pour ouvrir une instance de synthétiseur (même si elle est un existant qui était auparavant ouvert), mais ces solutions de contournement peut être suffisant pour certains cas d'utilisation.