Reputation: 531
The play
method below is from a class which, upon instantiation, reads a .wav file into a byte array called data
, and stores the sound format in an AudioFormat
object called format
.
I have a program that calls play
from a java.util.Timer
. When I go into the folder with all the relevant .class files and I run the program using the command java MainClass
, everything works as expected. However, when I put all the .class files in an executable .jar and run the program using the command java -jar MyProgram.jar
, sounds played using the play
method are cut off after something like 50 to 150 ms.
public void play() throws LineUnavailableException {
final Clip clip = (Clip)AudioSystem.getLine(new DataLine.Info(Clip.class, format));
clip.open(format, data, 0, data.length);
new Thread() {
public void run() {
clip.start();
try {
Thread.sleep(300); // all sounds are less than 300 ms long
} catch (InterruptedException ex) { /* i know, i know... */ }
clip.close();
}
}.start();
}
A few comments:
I've tried increasing the sleep time in the play
method up to 1000 ms, with no change in behavior.
Timing the Thread.sleep
using System.nanoTime
confirms that the thread is sleeping exactly as long as expected.
Since the sound file to be played is pre-loaded into memory, I don't think the act of extracting the sound resource from the .jar can be causing the problem.
I've tried running the program from both inside and outside the jar using the memory pool size options -Xms2m
and -Xmx64m
(separately), with no change in behavior.
I'm running OpenJDK Java 6 on Ubuntu 11.04. Any idea what's going on?
Upvotes: 3
Views: 670
Reputation: 205885
Amplifying on @Anrew's and @Phil's answers, there's an outline of obtaining a Clip
and playing it asynchronously below. There's a complete example here.
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(...);
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
...
// Play the sound in a separate thread.
private void playSound() {
Runnable soundPlayer = new Runnable() {
@Override
public void run() {
try {
clip.setMicrosecondPosition(0);
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
};
new Thread(soundPlayer).start();
}
Upvotes: 4
Reputation: 168845
It is likely the loading of the byte[]
that is the problem.
Upvotes: 3
Reputation: 7910
I don't know if this is directly related to the problem, but it is somewhat self-defeating to instantiate a new Clip every time you play that Clip. The point of a Clip is to be able to start it without having to load it first. As you are currently set up, your Clip won't even start playing until it has been fully loaded. (SourceDataLines start playing more quickly than Clips when you include the instantiation steps, as they don't wait for all the data to load before commencing.)
So, as a first step to solving this problem, I would suggest instantiating the various Clips prior to the play call. I believe if you do this right, and the clip start is in its own thread, it can be allowed to run its course with no need to mess with Thread.sleep. You just have to reposition the clip back to its starting frame prior to the next start. (Can be done in the play call just prior to the start.
Once your usage of Clip has become more conventional, it might be easier to figure out if there is something else going on.
I am also unclear why the source .wav files are being loaded into byte arrays as an intermediate step. Might as well load them directly into Clips. They will take up pretty much the same amount of space either way, given the preponderance of PCM data, and be ready to go when you call them.
Disabling error messages is also self-defeating. Java could be trying to give you a perfectly good diagnostic, maybe not in this particular sleep call (I know, I know) but if you do this often, who knows. :)
Upvotes: 4