1

I'm writing a videogame and I want to stop the music when a player loses or quits (to the main menu).

Here's my code:

public class Music{
    private static Clip clip;
    private static AudioInputStream stream;

    private static void loadMusic(){
        if(clip != null) return;
        try {
            AudioFormat format;
            DataLine.Info info;
            stream = AudioSystem.getAudioInputStream(Music.class.getResource("/resources/music/music.wav"));
            format = stream.getFormat();
            info = new DataLine.Info(Clip.class, format);
            clip = (Clip) AudioSystem.getLine(info);
            clip.open(stream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void playMusic(boolean loop){
        loadMusic();
        if(clip == null) return;
        if(loop) clip.loop(Clip.LOOP_CONTINUOUSLY);
        else clip.start();
    }

    public static void stopMusic(){
        clip.stop();
        clip.setMicrosecondPosition(0);
    }
}

Whenever I call Music.stopMusic(), the game hangs for a few seconds then continues.

Tyzoid
  • 1,028
  • 14
  • 25
  • 1
    You probably want to use a [`static` block](http://stackoverflow.com/questions/2943556/static-block-in-java) [[2](http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html)] instead of a constructor to instantiate `static` fields. – Jeffrey Aug 20 '12 at 22:37
  • @Jeffrey In my application, I'm doing other things in my constructor, I just simplified it to make it easier to read. Plus, getClass().getResource("path") cannot be called from a static method. – Tyzoid Aug 20 '12 at 22:40
  • 2
    `Music.class.getResource("path")` can be. In my opinion, mixing `static` initialization with instance initialization can get confusing and reduce readability overall. – Jeffrey Aug 20 '12 at 22:43
  • Thanks for the suggestion; I couldn't find that code snippet. The original problem still remains, however. – Tyzoid Aug 20 '12 at 22:58

1 Answers1

2

From what i gather form your description, you are pressing a stop button on your GUI and this calls Music.stopMusic(). The result is that the audio clip stops playing, but you are still waiting 3 seconds before you GUI becomes responsive.

This is because the call you are making in Music.stopMusic() makes native calls to I/O resources, and this should not be called in the EDT. You should look at running up a worker thread to do this:

Thread t = new Thread(new Runnable() { 
     public void run() {
         Music.stop();
     }
});
t.start();

Or look to using a SwingWorker.

Interestingly, though I am not sure which implementation of Clip is returned, but a quick look at MixerClip shows a call to a native library, and then perhaps the smoking gun in your predicament- a 3 second wait for a callback!

// stop the sample.  this invalidates the sample voice id.
nStop(id);

// wait for the callback
synchronized(lock) {
    if (id!=0) {
    try {
        //long time=System.currentTimeMillis();
        lock.wait(3000);
        //if (System.currentTimeMillis()-time > 2500) {
        //System.out.println(" WAITING TIMED OUT!"); System.out.flush();
        //id=0;
        //}
    } catch (InterruptedException e) { }
    }
}
akf
  • 36,245
  • 8
  • 81
  • 94
  • I've tried a worker thread and that solves this issue, but brings a new one: If a user restarts the game quickly enough, music will not start. (That could probably be solved by having a static thread variable in Music that you wait to complete when you start the game again) – Tyzoid Aug 21 '12 at 12:30
  • The new thread worked. I just needed to Thread.join() when I needed to start the music again. – Tyzoid Aug 21 '12 at 21:36