Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java MIDI audio is delayed after laptop comes out of hibernation

I'm developing a music programming language, and using the JVM (via Clojure) to play musical scores written in this language. So far, we are just using the javax.sound.midi MidiSynthesizer to play the scores.

Because Clojure has a slow startup time and we want to be able to play a score from the command-line and hear it immediately, we've chosen to structure the score interpreter as a background server process, and communicate with it using a more lightweight command-line client written in Java.

All of this is working great for the most part, however, there is a strange issue that we're seeing where if you start the server, then close your laptop* and let it hibernate, then open it again and have the server play a score, the audio doesn't happen immediately but is delayed for several seconds. Running the server with debug logging, I can actually see that the MIDI note on/off events are happening immediately (and timed correctly), but the audio is delayed.

*This may or may not be platform-specific. I'm seeing the issue on my 2014 Macbook Pro running OS X 10.9.5 Mavericks.

To help narrow it down, I put together this simple example (using Java, not Clojure) that demonstrates the problem:

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

I've been scratching my head over this for a while now. Why is the audio delayed, and is there anything we can do about it?

like image 267
Dave Yarwood Avatar asked Aug 14 '16 16:08

Dave Yarwood


1 Answers

This looks like a bug in Sun's implementation of Synthesizer.

I did not investigate this deeply, but I've found that the problem is apparently in Jitter Corrector that wraps AudioInputStream. Jitter Corrector thread relies on System.nanoTime(). However nanoTime may jump when a computer wakes up from standby or hibernate mode.

The work-around is to disable Jitter Corrector. You can do so by opening Synthesizer this way:

    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();
    }
like image 51
apangin Avatar answered Nov 08 '22 10:11

apangin