I have a Sound class that contains a method that, when called, plays a sound using a Clip object (in this case, clip
).
public static void play() {
clip.stop(); // The purpose of the first three lines
clip.flush(); // is to restart the Clip object so it
clip.setFramePosition(0); // can be played multiple times.
clip.start();
}
The instantiation of the Clip object occurs in a separate static method which is called prior to this method, which is why the above method can be declared static.
Another class that implements KeyListener
contains the following code:
public void keyPressed(KeyEvent e) {
Sound.play(); // Sound is the class that implements the previous method.
}
Therefore, my code should be playing the sound associated with clip
everytime a key is pressed. However, if I press a key quickly and repeatedly, the sound will sometimes not play. This is especially noticeable after a while (It seems as though the problem gets worse after each key press).
Why does this happen, and how can I circumvent this problem?
I have had the same kind of problems in the past, and something which worked for me is adding a line listener for whenever a line has finished, and closing it.
The code underneath is a stripped down version of what I use:
music = AudioSystem.getClip();
AudioInputStream ais = AudioSystem.getAudioInputStream(Sound.class.getResource("/sounds" + filename));
music.open(ais);
music.addLineListener(new LineListener(){
public void update(LineEvent e){
if(e.getType() == LineEvent.Type.STOP){
e.getLine().close();
}
}
});
music.start();
When you create the clip, just add in the line listener. When you reset the clip using your play function, it should play properly. I hope this works for you!
I came across this post via a reference in a similar question.
Yes, the number of Clips playing at once can contribute to the lag, but I don't know how to predict how much of an effect it will be, as this depends on how the JVM interacts with the OS.
You might be able to aid the situation by making the buffer size of the Clip smaller. SourceDataLine and Clip both seem to only allow interactions with incoming requests at buffer boundaries (I'm not sure this is a 100% accurate statement). Specifying the Clip's buffer size is a bit circuitous, as it requires obtaining the data as a PCM array. But if you wish to give it a try, the API is here.
A good way to reduce the number of lines is to use a sound library like TinySound. Many of the programmers at Java-gaming.org have used this library with success.
Another alternative that I'm promoting is AudioCue, a class I recently wrote for concurrent Clip
playback. The license is BCD, source is provided, so feel free to examine, tinker with, and make use of the code. AudioCue
doesn't reduce the number of output lines (yet). But if you are doing the common thing of managing multiple copies of the same cue, it could help in this regard as all concurrent instances are mixed down to a single output. Basic principle: the file is loaded into an array and played back via cursors that iterate over the array and merge their output into a SourceDataLine
. This setup also allows implementation of smooth, real-time volume, panning and pitch fading.