Reputation: 48659
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:
PAN
and framePosition
.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):
START
event to signal that it has started playingSTOP
event.stop
and start
have no effect (but do not throw exceptions.)0
, even when the clip is audible (for the last time.)Any idea what could be causing this?
I don't think it's a threading issue (at least not in my code.) Only one thread is calling the public methods of my class (and they are all synchronized
anyway)
Might be related to this bug.
Upvotes: 2
Views: 1073
Reputation: 66721
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.)
Upvotes: 1