Java Sound Resources: FAQ: Performance Issues

This page presents Questions and Answers related to the Java Sound API.

Performance issues

1. General
1.1. Isn't Java too slow for sound applications?
1.2. Is it possible to apply an FFT transform in real time under Java?
1.3. Is there a high-resolution clock in Java?
1.4. How do I use sun.misc.Perf?
1.5. What resolution can I expect from System.currentTimeMillis()?
1.6. What precision can I expect from Thread.sleep()?
2. Audio
2.1. What latency can I expect with Java Sound?
2.2. How do I reduce latency? How can I record sound and simultaneously play it back without delay?
2.3. I want to provide successive multi track recording: I record new channels while playing back all previously recorded channels. However, I get synchronization problems, there are different delays
2.4. How can I improve the performance of SourceDataLine.write()?
3. MIDI
3.1. Why is MidiSystem.getSequencer() (and related methods) slow when the application is run in a Java Web Start environment?

1. General

1.1. Isn't Java too slow for sound applications?
1.2. Is it possible to apply an FFT transform in real time under Java?
1.3. Is there a high-resolution clock in Java?
1.4. How do I use sun.misc.Perf?
1.5. What resolution can I expect from System.currentTimeMillis()?
1.6. What precision can I expect from Thread.sleep()?
1.1.

Isn't Java too slow for sound applications?

Let's collect some ethernal wisdom:

  • The earth is flat.

  • and, not to forget: Java is slow.

As several applications prove (see links section), Java is sufficient to build audio editors, multitrack recording systems and MIDI processing software. Try it out!

If your still dont believe it, here some pointers to "scientific" work:

For some tips on how to measure Java Sound performance see Florian's mail. (Matthias)

1.2.

Is it possible to apply an FFT transform in real time under Java?

At least for JIT compilers, Java is very fast (under certain conditions it reaches or tops C++ performance!) - and I think even on no-JIT compilers, FFT analysis is no problem in real time, of course depending on available processor power and resources. (Florian)

1.3.

Is there a high-resolution clock in Java?

There are several possibilities:

  • With the JDK 1.5.0, use the method java.lang.System.nanoTime(). The specification of this method does not quarantee a certain precision. However, the implementation in the Sun JDK always uses the best precision available on the system: microsecond resolution on x86 hordware, nanosecond resolution on SPARC hardware.

  • The method MidiDevice.getMicrosecondPosition() can be used as a clock. You have to open a MidiDevice other than a Sequencer. Note that for Sequencer, this method returns the position inside the Sequence, which is something completely different. While this, too, is somewhat of a hack, it is much more portable than to use sun.misc.Perf.

  • Starting with version 1.4.2, the Sun JDK contains an undocumented class sun.misc.Perf. It allows you to access the performance timer of the CPU. This is known to work well on all plattforms supported by Sun. However, note that using such undocumented classes is not portable. Therefore, in general it's not a good idea.

(Matthias)

1.4.

How do I use sun.misc.Perf?

First of all, you shouldn't use it. sun.misc.Perf is not an official API. This means that the interface may change without notice, or the class may disappear completely. And Java implementation other than those based on the Sun code base are not likely to provide this class at all.

But OK, there might be reasons to use it. So here is the sketch:

					// may throw SecurityException
					sun.misc.Perf perf = sun.misc.Perf.getPerf();
					long ticksPerSecond = perf.highResFrequency();
					long currTick = perf.highResCounter();
					long milliSeconds = (currTick * 1000) / ticksPerSecond;

For more details, including javadoc, see Florian's mail. (Matthias)

1.5.

What resolution can I expect from System.currentTimeMillis()?

The following table is based on measurements by YIP Chi Lap [Beta] (raw data):

operating systemtimer resolution
Linux (2.2, x86)1 ms
Mac OS X1 ms
Windows 200010 ms
Windows 9860 ms
Solaris (2.8, i386)1 ms
Solaris (2.7, sun4u)1 ms

(Matthias)

1.6.

What precision can I expect from Thread.sleep()?

The fundamental problem with short sleeps is that a call to sleep finishes the current scheduling time slice. Only after all other threads/process finished, the call can return.

For the Sun JDK, Thread.sleep(1) is reported to be quite precise on Windows. For Linux, it depends on the timer interrupt of the kernel. If the kernel is compiled with HZ=1000 (the default on alpha), the precision is reported to be good. For HZ=100 (the default on x86) it typically sleeps for 20 ms.

Using Thread.sleep(millis, nanos) doesn't improve the results. In the Sun JDK, the nanosecond value is just rounded to the nearest millisecond. (Matthias)

2. Audio

2.1. What latency can I expect with Java Sound?
2.2. How do I reduce latency? How can I record sound and simultaneously play it back without delay?
2.3. I want to provide successive multi track recording: I record new channels while playing back all previously recorded channels. However, I get synchronization problems, there are different delays
2.4. How can I improve the performance of SourceDataLine.write()?
2.1.

What latency can I expect with Java Sound?

. (Matthias)

2.2.

How do I reduce latency? How can I record sound and simultaneously play it back without delay?

You'll never be able to completely remove latency. Digital audio on computers is processed in "chunks", i.e. a small number of samples. When recording, you have to wait until one chunk is filled, then it gets passed to your application. So there is a minimum delay of the time of a chunk latency for recording. For playback the same "problem": the soundcard gets chunks from the application, so when one chunk is passed, the soundcard starts playing with the first sample of the chunk and it takes the time of the chunk that the last samples has been played. So, for playback you'll get the same delay. Added to that, the software (i.e. the different hardware layers copying chunks, etc.) and the hardware (hardware latency) add to the total delay.

There are approaches like DirectSound, ASIO or Alsa that all try to minimize the software part's latency and to allow very small chunks. When used efficiently on high-quality soundcards, an audio loop is possible with a barely noticeable delay (e.g. 20ms).

Java Sound (and therefore also JMF) actually adds one layer to the software part, so latency is slightly increased compared to native audio applications. You control the chunk size with the buffer size argument when constructing a SourceDataLine or TargetDataLine in Java Sound. I don't know about JMF's concepts. I also experienced some problems with Java Sound recording when using small buffers (like 1024 samples at 44.1 kHz). (Florian)

2.3.

I want to provide successive multi track recording: I record new channels while playing back all previously recorded channels. However, I get synchronization problems, there are different delays

The delay, of when the data arrives regarded to when you started recording, should be quite fixed. However, recording and playback may have different delays. The delays depend on the soundcard, the operating system (including the soundcard drivers) and the implementation of Java Sound you use (and also a bit on what JDK you use).

Also, the Java Sound API does not provide means for querying the device's latency. This would be quite difficult anyway for the Java Sound subsystem. However, normal soundcards have very little hardware delay (even the cheap ones), so once they receive the command to start recording, they really start it in a matter of some milliseconds - typically 20 - 30ms.

Using the soundcard via Java Sound, there are many layers until the data arrives in your program, so some delay is added. However, I haven't noticed a relevant delay when recording. I haven't tried to do your approach of measuring the delay, though.

So what you should is, is to call the start() method both on TargetDataLine and SourceDataLine "at once" with the minimum overhead possible. Notice that the buffers of the recording line arrive when they have been finished recording (i.e. when the amount of time has been elapsed), while playback lines need a buffer prior that they are played. This may not be obvious at first. So when you do record and playback at once, the first received block from the recording line corresponds in time to the first played buffer (which has been submitted right with "start").

For playback of the 2 files, you should not use 2 playback lines, but mix them from your program. Like that, you can at least control the exact sample-delay of the 2 playback lines and adjust it (probably only possible by ear).

These are handmade solutions. The "real" solution is to make use of Mixer.synchronize(). This is the proposed solution to your problem from the Java Sound API. Like that you can synchronize the recording line and the playback line - or the 2 playback lines. However, it seems that it isn't implemented in current versions of Java Sound (although I haven't tested it). (Florian)

2.4.

How can I improve the performance of SourceDataLine.write()?

SourceDataLine.write() is very efficient: it just copies the passed data to a buffer. Being useful, it can't do less. If your program depends on the execution time of SourceDataLine.write(), it's typically a misconception of how it should be used. Audio data is handled in blocks of at least a few hundred bytes, often several kBytes. If you call SourceDataLine.write() with single samples or frames of 1, 2 or 4 bytes, you're indeed likely to run into a performance issue.

And a quote from Florian:

Note that there is no general contract about how slow/fast write() runs. Device I/O is unpredictable and different from platform to platform, and often even dependent on driver implementation. SourceDataLine.write() should always be executed in a thread that is independent from user interaction. Just as you do for e.g. network i/o.

(Matthias)

3. MIDI

3.1. Why is MidiSystem.getSequencer() (and related methods) slow when the application is run in a Java Web Start environment?
3.1.

Why is MidiSystem.getSequencer() (and related methods) slow when the application is run in a Java Web Start environment?

Calling these methods triggers a lazy download of .jar files. This is a bug not specific to Java Sound. See also Topic: lazy download with ImageIO?, bug #5047548and Q: 2.2 (Matthias)