-2

I know this question has been asked many times and I've looked at many in hopes to figure out my problems, but it didn't really help much. I was given this class to use to play WAV files on my GUI and it works when retrieving local files. But it doesn't work in a JAR file. I know this is because the music class only works for retrieving local files, so I need help changing the code to retrieve music files from the JAR.

Here's the SoundPlayer class:

package extra;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.pscode.xui.sound.bigclip.BigClip;

/**
 * A class to play audio clips. Caches previously-played clips,
 * allowing fast re-playback of previously played sounds.
 * 
 * @author Anon
 * @version 1.51
 */

public class SoundPlayer {

/** A cache of previously-played audio clips. */
private final Map<String, Clip> myClips = new HashMap<String, Clip>();

/**
 * Plays the audio file with the given file name.
 * This method returns instantly, without waiting for the clip to finish playing.
 * 
 * @param theFilename The name of the file to play.
 * @return a Clip object representing the sound played.
 * @throws IllegalArgumentException if there is a problem reading from the sound file.
 */
public Clip play(final String theFilename) throws IllegalArgumentException {
    return loop(theFilename, 1);
}

/** 
 * Plays the clip with the given file name in a continuous loop.
 * The clip keeps looping until it is later stopped by calling the 
 * stop() method. This function returns instantly
 *    
 * @param theFilename The name of the file to play.
 * @return a Clip object representing the sound played.
 * @throws IllegalArgumentException if there is a problem reading from the sound file.
 */
public Clip loop(final String theFilename) throws IllegalArgumentException {
    return loop(theFilename, Clip.LOOP_CONTINUOUSLY);
}

/** 
 * Plays the clip with the given file name in a loop.
 * The clip loops until it has played the specified number of times,
 * or until it is later stopped by calling the stop() method.
 * This function returns instantly, without waiting for the clip to finish looping.
 *
 * @param theFilename The name of the file to play.
 * @param theNumberOfTimes The number of times to loop the clip.
 * @return a Clip object representing the sound played.
 * @exception IllegalArgumentException if there is a problem reading from the sound file.
 */
public Clip loop(final String theFilename, final int theNumberOfTimes) 
    throws IllegalArgumentException {

    final Clip clip = getClip(theFilename);

    if (clip != null) {
        clip.loop(theNumberOfTimes);
    }

    return clip;
}

/**
 * Pauses the clip with the given file name.
 * If the clip is later played, it will resume from where it was paused.
 * Calling this method does not resume a thread that is 
 * suspended on a playAndWait() or a loopAndWait().
 * 
 * If stop() is called on a paused clip, it will reset to the
 * beginning of the clip for the next play.
 * 
 * @param theFilename The name of the file to pause.
 * @exception IllegalArgumentException if there is a problem reading from
 * or playing the sound file.
 */
public void pause(final String theFilename) throws IllegalArgumentException {
    final Clip clip = getClip(theFilename);

    if (clip != null) {
        final int pos = clip.getFramePosition();
        clip.stop();
        clip.setFramePosition(pos);
    }
}

/** 
 * Stops the clip with the specified filename
 * (and wakes up any threads waiting for it to finish playing).
 * 
 * @param theFilename The name of the file to stop.
 * @return a Clip object representing the sound stopped.
 * @exception IllegalArgumentException if there is a problem reading from the sound file.
 */
public Clip stop(final String theFilename) 
    throws IllegalArgumentException {
    final Clip clip = getClip(theFilename);
    stopClip(clip);

    return clip;
}

/** 
 * Stops all currently playing sound clips
 * (and wakes up the threads waiting for them to finish playing).
 */
public void stopAll() {
    for (final Clip clip : myClips.values()) {
        stopClip(clip);
    }
}   

/** 
 * Preloads the clip at the given file name.
 * This means the clip will be available faster, when requested for playing the first time.
 * @param theFilename The name of the file to preload.
 * @return a Clip object representing the preloaded sound.
 * @exception IllegalArgumentException if there is a problem reading from the sound file.
 */
public Clip preLoad(final String theFilename) 
    throws IllegalArgumentException {
    return getClip(theFilename);
}


/**
 * Returns a Clip object for a filename, either by creating
 * a new one or loading it from the cache.
 * 
 * @param theFilename The name of the file to load.
 * @return a Clip object, or null if one is not found.
 * @exception IllegalArgumentException if there is a problem reading from the sound file.
 */
private Clip getClip(final String theFilename) throws IllegalArgumentException {
    BigClip clip = null;
    AudioInputStream ais = null;

    if (myClips.containsKey(theFilename)) {
        clip = (BigClip) myClips.get(theFilename);
    } else {
        // read audio file from disk
        try {
            ais = AudioSystem.getAudioInputStream(new File(theFilename));


            clip = new BigClip();
            clip.open(ais);
            clip.addLineListener(new LineListener() {
                /** 
                 * Responds to audio events generated by clips. 
                 * 
                 * @param theEvent The event generated.
                 */
                public void update(final LineEvent theEvent) {
                    if (theEvent.getType() == LineEvent.Type.STOP) {
                        // clip is done playing
                        stopClip((Clip) theEvent.getSource());
                    }
                }
            });
            myClips.put(theFilename, clip);
        } catch (final UnsupportedAudioFileException uafe) {
            throw new IllegalArgumentException
            ("Not a valid supported audio file: \"" + theFilename + "\"", uafe);
        } catch (final LineUnavailableException lue) {
            lue.printStackTrace();
            throw new IllegalArgumentException
            ("Line is not available to play sound \"" + theFilename + " \"", lue);
        } catch (final IOException ioe) {
            throw new IllegalArgumentException
            ("I/O error while reading file: \"" + theFilename + "\" ", ioe);
        }
    }

    return clip;
}

/**
 * Stops the playing of the specified clip.
 * 
 * @param theClip The clip.
 */
private void stopClip(final Clip theClip) {
    if (theClip != null) {
        synchronized (theClip) {
            theClip.stop();
            theClip.setFramePosition(0);
            theClip.notifyAll();  // awaken threads waiting for this Clip
        }
    }
}
}

// end of class SoundPlayer

In the getClip() method, I think that's where there needs to be a change since that's where the AudioInputStream is grabbing local files. I'm just not sure how to change it though.

Jasmine
  • 313
  • 2
  • 23
  • 2
    *"I know this question has been asked many times and I've looked at many in hopes to figure out my problems,"* Link to the top 5, and.. *"but it didn't really help much."* ..explain ***why*** that did not solve this exact same problem. `import org.pscode.xui.sound.bigclip.BigClip;` Huh.. haven't seen that in a while. :) Why are you using [`BigClip`](http://stackoverflow.com/a/5668510/418556) as opposed to the standard `Clip` from the Java Sound API? – Andrew Thompson Jun 06 '16 at 00:19
  • @AndrewThompson I actually have no idea why BigClip is being used, my professor provided the class. However, it works perfectly in Eclipse, so it seems like it's an okay choice. – Jasmine Jun 06 '16 at 00:54
  • 1
    The reason I recognized `BigClip` is because *I wrote it*. (But for a very specific purpose - i.e. large clips.) – Andrew Thompson Jun 06 '16 at 08:47

2 Answers2

0

Files in a jar file can't actually be retrieved as a file. Instead, you need to use the Class object (usually the this variable)'s .getResource to retrieve a URL or .getResourceAsStream to retrieve an InputStream for it.

You can then pass either of these into one of AudioSystem.getAudioInputStream's overloads as it has one for InputStreams and one for URLs.

Powerlord
  • 87,612
  • 17
  • 125
  • 175
  • I just noticed that what you provided is what I tried previously and it didn't work. I forgot to include that in my post. It looks like this: `ais = AudioSystem.getAudioInputStream(this.getClass(). getResourceAsStream("/" + theFilename));` – Jasmine Jun 06 '16 at 00:56
0

Use ClassLoader.getSystemResource("path/inside/of/jar").

If the goal is to simply play sound:

Try TinySound. I recently used this in a project I'm working on, and it's mindblowingly easy to use. Make sure you use either .wav or .ogg files though, you can use http://media.io/ to convert to both of those.

Dan95363
  • 11
  • 3
  • `ClassLoader.getSystemResource(..` Resources included in our Jars are **not** system resources. Instead use `this.getClass().getResource(..`. – Andrew Thompson Jun 06 '16 at 09:03
  • Actually, I was able to get it working using `ClassLoader.getSystemResource()`. – Dan95363 Jun 06 '16 at 22:43
  • *"Actually, I was able to get it working.."* I'm glad you were able to see it working on one machine/environment using `getSystemResource`. The problem is that it might not work on the next machine/environment. Don't mistake a single positive result for the correct way of going about a task. – Andrew Thompson Jun 06 '16 at 22:46
  • Oh, I didn't realize this wasn't a cross-platform thing. I have however tried this both on Linux and on Windows, and they have worked. The nice thing about this is that it doesn't need to be used in a static context. – Dan95363 Jun 07 '16 at 23:33