Question

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

Was it helpful?

Solution 3

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);
}
}

OTHER TIPS

  1. That should work in an application.
  2. That line of code is most likely referencing the class that method is in. So that method was originally in class Main, if you put the method in class FooBar, you should change it to FooBar.class.getResourceAsStream().
  3. It is a relative path. It will look for the resource outside of every package. Example: Let's say the class that's running this piece of code is located at C:\Users\Jeffrey\bin\foo\bar\SoundPlayer.class and the class is in package foo.bar. This means that the ClassLoader will look for the resources inside the C:\Users\Jeffrey\bin\ folder. (In your case, it will look for the resource at C:\Users\Jeffrey\bin\path\to\sounds\ + url)

I always loaded sounds like this:

 Clip sound = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
 sound.open(AudioSystem.getAudioInputStream(file));

but your method should also work.

Does this work in an application, as opposed to an Applet?

It works in either.

The method Main.class.getResourceAsStream() seems to require import com.sun.tools.apt.Main;

Where did you get that idea? I've made plenty of sound examples, and never heard of that class that you should not be using.

..but I cannot find documentation for that,..

No, the com.sun classes are not only undocumented, but might change in the next micro-version.

..and I don't know what it does. For instance, is "/path/to/sounds/" absolute, or relative, and if the latter, relative to where?

It is relative to the root of the class-path.

..It's unbelievable how difficult it is.

Media handling in general, is tricky.


BTW - I'm not much impressed with the code on the linked thread. The Thread wrapper is unnecessary, as mentioned in several of the comments, even for playing multiple Clip instances simultaneously.

Instead see this code that I (wrote &) personally recommend.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top