tags:

views:

77

answers:

1

I am seeing some strange behaviour with Clip instances in Java.

The purpose of the class I am working on is to keep count of the number of Clip instances containing the same sound sample (indexed by URI.) When the application requests to play a clip and there are already three or more clips from the same source already playing, the following steps are performed:

  • Sort the currently-playing clips by a weighted sum of PAN and framePosition.
  • Select the clip with the highest value as the one to be stopped and re-started.
  • Re-start the clip (the following method):

void restart(Clip clip, float gain, float pan) {
    clip.stop();
    clip.flush();
    pan = Math.max(-1f, Math.min(pan, 1f));
    ((FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN))
                        .setValue(gain);
    ((FloatControl) clip.getControl(FloatControl.Type.PAN))
                        .setValue(pan);
    clip.setFramePosition(0);
    clip.start();
}

Strange behaviour occurs if this method is called many times in quick succession (e.g. 20 times within 1ms):

Any idea what could be causing this?

I don't think it's a threading issue. Only one thread is calling the public methods of my class (and they are all synchronized anyway)

+1  A: 

The calls to DataLine.start and DataLine.stop are already synchronized on the DataLine's mixer inside AbstractDataLine.

I strongly suspect that somewehere down the call stack (below implStart()/implStop() of whatever DataLine incarnation you got, very likely inside the native nStart/nstop) at least one asynchronous call is made, thus resulting in the race condition you observe.

It would be impossible for you to work around this kind of problem using synchronized or any other Java construct without more intimate knowledge of the native implementation that is called.

A viable, immediate workaround could be to close the old clip and open a new instance instead of rewinding the old one. Not optimal, but it may well do the trick pending a deeper investigation.

In order to be able to perform the aforementioned deeper investigation, one would have to know what platform you are on, as well as a confirmation of the actual (implementation) class names of your Clip and Mixer instances.

UPDATE

In parallel, please use introspection to set com.sun.media.sound.Printer.trace = true (or provide your own implementation of com.sun.media.sound.Printer in the CLASSPATH.)

Essentially DirectClip.open() spawns a thread which accesses several volatile variables (of particular interest being doIO) in a non-threadsafe manner, which could potentially result in the main playback loop hanging.

You can confirm (or infirm) this (in conjunction with the Printer traces) by forcing a thread dump at the time of the apparent hang, and inspecting the playback thread state/stack trace (or use a debugger.)

If doIO etc. access turns out not to be the problem then continuing to dig at the native implementations is still the thing to do; if doIO etc. access does turn out to be the problem then again, there is no easy fix (you can try to use introspection to grab DirectClip.thread and signal it periodically in case it stalls because of doIO -- again, to be confirmed.)

vladr
The mixer is a `com.sun.media.sound.DirectAudioDevice` and the clip is a `com.sun.media.sound.DirectAudioDevice.DirectClip`.
finnw
...and the platform is...? :)
vladr
@Vlad Romascanul, Originally on Windows XP. Reproduced on Vista (but it happens less frequently on Vista than on XP.) Failed to reproduce on Ubuntu, but that was running on a netbook with only one processor core (which may mask the problem if it is a threading issue in a library.)
finnw
OK, thx. Try to do the stuff in the post *UPDATE*, and see what comes out of it -- with a bit of luck "that is it"...
vladr
Still unsolved, but +1 for the suggestions and workaround
finnw