I've been trying to implement a way to play audio in my Java application using java's sound API. I've got a few problems here and there, but the most annoying one right now is that closing a channel clip.close() when there is less than one second remaining of audio lags my application for 2 seconds.
I dug around and figured out that, for some reason, when the line is trying to terminate its thread with the command thread.join(2000), the thread is still alive and it causes it to wait for that whole 2 second to end.
The thread just so happens to stay alive only if there is less than 1 second of audio left. At any other time, when I try to stop the audio, no problems occur.
Here's the code that's being called:
clip.close();
Which goes to
/**
* This should also stop the line. The closed line should not be running or active.
* After we close the line, we reset the format and buffer size to the defaults.
*/
@Override
public final void close() {
//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
synchronized (mixer) {
if (isOpen()) {
// stop
stop();
// set the open state to false and send events
setOpen(false);
// close resources for this line
implClose();
//... more code
}
}
}
implClose then goes to
@Override
void implClose() {
// dispose of thread
Thread oldThread = thread;
thread = null;
doIO = false;
if (oldThread != null) {
// wake up the thread if it's in wait()
synchronized(lock) {
lock.notifyAll();
}
// wait for the thread to terminate itself,
// but max. 2 seconds. Must not be synchronized!
try {
oldThread.join(2000); //This is where the problem occurs. The thread is still alive and the application is forced to wait 2 seconds.
} catch (InterruptedException ie) {}
}
}
Here is the join method
public final synchronized void join(final long millis)
throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
}
} else if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
I've been trying to figure it out for the last 4 hours, but with an internet connection that is down 80% of the time and inadequate java knowledge, I haven't been able to yet.
Thank you for your time!
Edit:
Here is how the clip is instantiated and closed:
I first get a clip from a list of lines offered by the mixer:
//This is done with the main thread
for (Line.Info lineInfo : linesInfo) {
if (lineInfo.getLineClass().isAssignableFrom(Clip.class)) {
try {
line = AudioSystem.getLine(lineInfo);
clip = (Clip) line;
audioPlayback.setClip(clip);//This is a custom class that holds the clip
}
catch(LineUnavailableException e) {
System.out.println(e);
}
}
}
This code is ran from the custom class when I press on a button to start the clip:
//Main thread is used here too
try {
//Code to get format, data and data length
clip.open(format, data, 0, length);
}
catch (LineUnavailableException e) {
System.out.println(e);
}
And finally, to close the clip:
//Main thread executes this as well
clip.close();
Everything is done using the initial thread. I even removed all other thread instantiations from my code to make sure no other threads could cause the issue, but to no avail.
The red thread hangs for two seconds when stop is pressed(when there is less than a second of audio left).

The following is is intended to be a more minimal case that demonstrates the problem. I am able to recreate the problem with this code.
The resource sineticking3secs.wav, was made using Audacity. Format: 16-bit, 44100 fps, stereo, little-endian. It was made by appending 500 millisecond length sine tones using the Generate tool, at 220, 330, 220, 330, 440, 660 Hz. The segments make it easier to tell how close to the end we are when we close the clip. Can be downloaded from http://adonax.com/audio/sineticking3secs.wav
For both Java 11 and Java 18 running on Windows 11, the elapsed time listed on the console when the Close Clip button is pressed confirms that there is a 2-second pause when the button calls the
clip.close()method, but only when theCliphas less than one second's worth of data left to play.I tried looking at bug reports on Clip, but I can't figure out how to find the documentation on the details. There are a couple that mention the timing of closing in the title, for example https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8207506. How does one navigate to the details? [EDIT: I tried a search on the bug ID and found this: https://bugs.openjdk.org/browse/JDK-8207210, so it isn't the same bug. I will try posting a bug report later in the day if I get a chance.]
I experimented with using a
SourceDataLineinstead of aClip. There was no problem closing theSourceDataLine.I wrote a library called AudioCue-maven that has a Clip "improvement". AudioCue uses
SourceDataLinefor output and causes no pauses when closing. It's available via maven.org, (groupid is com.adonax, artifactID is audiocue).If you decide to try it, the code for instantiating is as follows:
Closing will close the associated
SourceDataLinebut won't remove the AudioCue's internal PCM array from memory. You'll have to set the variable to null for that. But if you simply reopen the AudioCue after closing it, it will play from the point where it was closed, so one has to reposition it back to the startclip.setFramePosition(0).