mknight
mknight

Reputation: 23

How do I efficiently close a large number of clips as soon as they have finished playing?

I have a game where sound effects are played using this method:

 AudioInputStream inputStream = AudioSystem.getAudioInputStream(
          TheGame.class.getResource(url));
          Clip clip = AudioSystem.getClip();


           clip.open(inputStream);
           clip.start(); 

However, not closing the clips obviously builds up memory, and the fixes I've tried cause the game to freeze up occasionally. I tried :

  1. creating a new LineListener for every single clip that closes the clip when it stops. freezes up game OFTEN.
  2. Having one LineListener that listens to every clip that closes them when they stop. freezes often as well.
  3. Same as #2, but dividing the clips among multiple LineListeners, I've tried 4, 6, and 10, and all freeze up the game at least once.
  4. Never closing the clips, eventually the game will stop playing sounds and freeze up
  5. setting the clips to null as soon as they're opened, same result as #4

I'm not sure why closing clips seems to take so much time that it freezes up the entire game, is there a more time-efficient way?

Here is the code for the LineListener's update method I'm using:

    @Override
public void update(LineEvent event) {
     if (event.getType() == LineEvent.Type.STOP){
               Clip c = (Clip) event.getSource();
               c.close();
               c.removeLineListener(this);
     }
}

Upvotes: 2

Views: 183

Answers (1)

Dark Falcon
Dark Falcon

Reputation: 44191

As per my comment above, you should reuse the existing Clip object.

Map<String, Clip> clips = new HashMap<String, Clip>();

public synchronized void play(String url) {
    Clip clip = clips.get(url);
    if(clip == null) {
        AudioInputStream inputStream = AudioSystem.getAudioInputStream(
            TheGame.class.getResource(url));
        Clip clip = AudioSystem.getClip();
        clip.open(inputStream);
        clips.put(url, clip);
    }
    if(clip.isRunning())
        clip.stop();
    clip.setFramePosition(0);
    clip.start(); 
}

You will need to get a bit more creative if you need to overlap the same sound with itself. Note that I have placed this in a synchronized method to protect the Map from concurrent modification if you should happen to be calling this from multiple threads.

Upvotes: 3

Related Questions