Search code examples
javaaudioclipjavasound

How can a Java application play a sound clip?


The following is a link to the most likely explanation I've seen, but I still have questions.

How can I play sound in Java?

I'll quote the code here:

public static synchronized void playSound(final String url) {
new Thread(new Runnable() {
  public void run() {
    try {
      Clip clip = AudioSystem.getClip();
      AudioInputStream inputStream = AudioSystem.getAudioInputStream(Main.class.getResourceAsStream("/path/to/sounds/" + url));
      clip.open(inputStream);
      clip.start(); 
    } catch (Exception e) {
      System.err.println(e.getMessage());
    }
  }
}).start();

}

  1. Does this work in an application, as opposed to an Applet?
  2. The method Main.class.getResourceAsStream() seems to require import com.sun.tools.apt.Main; but I cannot find documentation for that, and I don't know what it does. For instance, is "/path/to/sounds/" absolute, or relative, and if the latter, relative to where?

I've spent many hours now trying to play a simple sound effect. It's unbelievable how difficult it is. I hope that the above code can be made to work. Thanks for any help.

Chap


Solution

  • Although I drew heavily from @Andrew's code, I did have to make some tweaks here and there. The following is a demo of my solution, complete except for a sample .wav file.

    // Developed in Eclipse, YMMV regarding resource location.
    import java.net.URL;
    import javax.sound.sampled.AudioInputStream;
    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.Clip;
    
    class ClipPlayer {
    
    public static void main(String[] args) {
        // First, instantiate ourselves so we can call demoSam which
        // needs to be able to do a wait().
        ClipPlayer cp = new ClipPlayer();
        // Now run the actual demo
        cp.demoSam();
    }
    
    private void demoSam() {
    
        /**
         * Construct a Sam, capable of playing the "Chook.wav", a 0.1 sec sound.
         * NOTE: it's very tricky debugging an incorrectly-located
         * resource file, and I'm unable to give a general rule
         * here.  But in this example, Chook.wav is expected to be in the same
         * directory as the .class file, and there is no surrounding
         * package (i.e. we're taking the default package name).  If you
         * are using a package, you may have to write "myPackage/Chook.wav"
         * instead.
         */
    
        Sam sam;
        try {
            sam = new Sam("Chook.wav"); // or whatever, but it has to be .wav
        }
        catch (Exception e) {
            say("Exception thrown by Sam: " + e.getMessage());
            System.exit(1); // scoot
            return; // get rid of warning about sam possib not init'd
        }
        int countDown = 20;
        do {
            say("Doing something requiring accompanying sound effect...");
            try {
                sam.playIt();
            }
            catch (Exception e) {
                say("Caught exception from playIt: " + e.getMessage());
                System.exit(1);
            }
    
            // Now wait a human-scale duration, like 1/8 second.  In
            // practice we may be processing, since the sound is playing
            // asynchronously.
    
            synchronized (this) {
                try {
                    wait(125); // wait 1/8 sec
                }
                catch (Exception e2) {
                    say("huh?");
                }
            }
        } while (--countDown > 0);
    
    }
    
    /**
     * 'Sam' is a class that implements one method, playIt(), that simply
     * plays the .wav file clip it was instantiated with.  Just using an
     * inner class here for simplicity of demo.
     */
    final class Sam {
    
        AudioInputStream ais;
        Clip             clip;
    
        /**
         * Constructor: prepare clip to be played. Do as much here as 
         * possible, to minimize the overhead of playing the clip, 
         * since I want to call the play() method 5-10 times a second.
         */
        Sam(String clipName) throws Exception {
    
            // Resource is in same directory as this source code.  
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            URL url = classLoader.getResource(clipName);
            ais = AudioSystem.getAudioInputStream(url);
            clip = AudioSystem.getClip();
            clip.open(ais);
        }
    
        /**
         * playIt(): Start the clip playing once, asynchronously, and exit. 
         */
        public void playIt() throws Exception {
            clip.setFramePosition(0);  // Must always rewind!
            clip.loop(0);
            clip.start();
        }
    }
    
    private static void say(String s) {
        System.out.println(s);
    }
    }