I am making a small game in Java. For this game I just added sounds. So I want to have all my images and audio files in the jar. For pictures this was easy:
new ImageIcon(Main.class.getResource("images/machgd2.png")).getImage()
But for audio it only works when I run the program in Eclipse but not from a jar. I use:
File soundFile = new File(Main.class.getResource(filename).getFile());
So how can I get this file from inside the .jar file?
Update:
OK, got it working, thanks to Andrew!
To play the sound I used a class I found on the net, and I found out that class just uses File to get an AudioInputStream, so I dropped the File thing.
When it's in a jar file, it isn't a file on the file system, is it? You'll either have to copy the file out of the jar file into some temporary location, or use APIs which don't require a file (e.g. ones which only need an InputStream or a URL, both of which are easily available from jar files using getResourceAsStream or getResource.).
You haven't shown where you're using soundFile - if you show us which APIs you're trying to use, we can try to suggest an appropriate alternative.
See "Playing a Clip" from the Java Sound info page here at SO to see..
import java.net.URL;
import javax.swing.*;
import javax.sound.sampled.*;
public class LoopSound {
public static void main(String[] args) throws Exception {
URL url = new URL(
"http://pscode.org/media/leftright.wav");
Clip clip = AudioSystem.getClip();
// getAudioInputStream() also accepts a File or InputStream
AudioInputStream ais = AudioSystem.
getAudioInputStream( url );
clip.open(ais);
// loop continuously
clip.loop(-1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// A GUI element to prevent the Clip's daemon Thread
// from terminating at the end of the main()
JOptionPane.showMessageDialog(null, "Close to exit!");
}
});
}
}
Which, you might notice, uses an URL (as returned by getResource()) rather than a File. The method is overloaded to also accept File or InputStream, but I use the URL based version most commonly.
Related
I am trying to play a .wav file but having trouble doing so with a relative path function.
public class Main {
public static void main(String[] args) {
playSound();
}
public static void playSound() {
try {
File file = new File(Main.class.getResource("notification.wav").getFile());
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
} catch(Exception ex) {
System.out.println("Error playing sound.");
ex.printStackTrace();
}
}
}
If I run the code above in IntelliJ, I receive the error:
java.io.FileNotFoundException: C:\Users\Dominik\Documents\IntelliJ%20Projects\nbbot\out\production\nbbot\com\company\notification.wav (The system cannot find the path specified)
If I run my built .jar application, I receive the error:
java.io.FileNotFoundException: file:\C:\Users\Dominik\Documents\IntelliJ%20Projects\nbbot\nbbot_jar\nbbot.jar!\com\company\notification.wav (The filename, directory name, or volume label syntax is incorrect) in Java?
The .wav file an the Main class, where I run the code snipped are in the same folder:
nbbot > src > com > company > Main.java; notification.wav
It's strange, because the paths are correct and there should be no problem to just read the file.
I also tried Main.class.getClass().getResource("notification.wav").getFile(); and .getPath() but no luck either. If I just use new File("notification.wav") and put the .wav the project folder nbbot the audio plays as intendet, but only in the IDE.
Your plan is broken. resources aren't usually files. Calling .getFile() on a resource is usually going to get you broken code. For example, when pack up your app into a jar, boom, breaks. a java.io.File object can only represent an actual file.
Just about every API also lets you pass a URL or an InputStream instead of a File, use those - those are abstractions that apply to all resources.
getAudioInputStream is just such a method, fortunately. It has overrides for em. Write this:
try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(
Main.class.getResource("notification.wav"))) {
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
}
This will probably fix this problem and will definitely mean that your code will continue to work once your files are packed up into a jar.
NB: InputStreams are resources that must be closed, hence, you need to use try-with-resources, or your app will eventually hard-crash due to running out of OS handles after playing enough audio.
Apologies if this has been asked before, but I was unable to find an answer that worked for us. I am also a beginner so please bear with me.
Essentially, after jarring our code the audio stopped working.
clip = AudioSystem.getClip();
File file = new File(musicLocation);
clip.open(AudioSystem.getAudioInputStream(file.getAbsoluteFile()));
clip.start();
I have tried using getResourceAsStream and getResource as apparently files don't work properly when jarred but it still does not work even in Intellij.
My code is located in the src folder and the music location is being passed to the code like so:
filepath = "src/Images/music/click.wav";
musicObject.playMusic(filepath);
Images are working properly in the jar file.
Edit: it appear that the jar file is unable to take the audio files, which are within another folder that is otherwise being accessed, because the file size of the jar does not change after the deletion of the .wav files.
Edit 2:
public class Music {
Clip loop;
void loopMusic(String musicLocation) {
try {
loop = AudioSystem.getClip();
InputStream is = getClass().getResourceAsStream(musicLocation);
AudioInputStream audioS = AudioSystem.getAudioInputStream(is);
loop = AudioSystem.getClip();
loop.open(audioS);
loop.start();
loop.loop(Clip.LOOP_CONTINUOUSLY);
} catch (Exception e) {
System.out.println(e);
}
}
void stopLoop(){
if (loop != null) {
loop.stop();
loop.close();
}
loop=null;
}
}
This is the coe we are attempting to use. The musicLocation String is passed in the format of: /folder/file.wav
After manually putting the .wav files into the jar through winrar, it still is unable to load the music in the jar file.
Edit 3:Attempting to use URL Class, receiving NullPointerException
URL musicLocation = this.getClass().getResource("/Images/music/battle.wav");
AudioInputStream inputStream = AudioSystem.getAudioInputStream(musicLocation);
When passed into the AudioInputStream as a file with the "src" before the location name, it does not pass a null.
Edit 4: Attempting to use URL Class with the file inside of folder in Music class package
URL musicLocation = this.getClass().getResource("audio/battle.wav");
AudioInputStream inputStream = AudioSystem.getAudioInputStream(musicLocation);
With the audio folder above being inside the classes (containing all the .java files) package, this returns a NullPointerException. After adding a "Classes/" to the front, hovering over the string in my IDE allows me to "see" that the file is correctly being sourced if you will but it still returns a NullPointerException to the .wav file.
Edit 5:
Receiving this error after implementing Phil's code
Exception in thread "main" java.lang.ExceptionInInitializerError
at Classes.Frame.<init>(Frame.java:15)
at Classes.GraphicsRunner.main(GraphicsRunner.java:15)
Caused by: java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:222)
at java.desktop/javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1032)
at Classes.Music.<init>(Music.java:16)
at Classes.Panel.<clinit>(Panel.java:24)
... 2 more
Edit 6: Displaying class that resulted in error above
package Classes;
import javax.sound.sampled.*;
import javax.swing.*;
import java.io.*;
import java.net.URL;
public class Music {
Clip clip;
// Constructor, create looping audio resource, hold in memory.
public Music() {
URL url = this.getClass().getResource("audio/battle.wav");
AudioInputStream ais;
try {
ais = AudioSystem.getAudioInputStream(url);
//line 16 above
DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
clip = (Clip) AudioSystem.getLine(info);
clip.open(ais);
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
e.printStackTrace();
}
}
public void play() {
clip.setFramePosition(0);
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
}
Edit 7: Phils identical set of code worked upon moving project to Eclipse. I'm still unsure of why it didn't work in Intellij but my problem was solved nonetheless. Thanks to everyone who offered their help!
The java.io.File object is not a good choice for addressing files that are packed in jars. I don't know the correct technical way to explain this, but in simple language, it can only address files that are in file folders. It has no ability to "see" within jar files.
For this reason, it's more usual to access the file by getting its URL using the Class.getResource method. A URL can identify a file that has been compressed and is located within a jar.
It is also possible to address and load a sound file in a jar using the .getResourceAsStream method. This method returns an InputStream, not a URL. But this is a dicier option. If you look at the API for the overloaded AudioSystem.getAudioInputStream, and compare the versions, you'll see that if the argument is an InputStream, the file will be subjected to tests to determine if "mark" and "reset" are supported. A sizable number of audio files fail these tests. For this reason, it's safer to use the method with a URL argument.
Problems can also arise in how the file name is provide in the getResource method, but usually if the name String works in the DAW it will also work in a jar (assuming you are obtaining a URL and not a File). The specifics about "relative" and "root" addressing aren't the easiest to explain. But we can go there if needed.
EDIT: Code example for troubleshooting.
public class Music {
Clip clip;
// Constructor, create looping audio resource, hold in memory.
public Music() {
URL url = this.getClass().getResource("audio/battle.wav");
AudioInputStream ais;
try {
ais = AudioSystem.getAudioInputStream(url);
DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
clip = (Clip) AudioSystem.getLine(info);
clip.open(ais);
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
e.printStackTrace();
}
}
public void play() {
clip.setFramePosition(0);
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
}
The code assumes the following file folder structure:
/src/.../folderwithMusic/Music
/src/.../folderwithMusic/audio/battle.wav
Note that the file name is case sensitive. A null value for the URL indicates that the file is not at the expected location. First get this working in your IDE, and maybe prefer either your console or File Explorer to verify the file structure and file location.
If you try running the above and have problems, the exact code as entered and stack trace would be helpful.
I have this directory structure:
root
resources
.png files
Music.mid
Sound.wav
src
.java files
and I play the sounds from the resources folder using this code
public void play() {
try {
URL defaultSound = getClass().getResource(filename);
AudioInputStream audioInputStream =
AudioSystem.getAudioInputStream(defaultSound);
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start( );
while (clip.isRunning()) {}
}
catch (Exception e) {
e.printStackTrace();
}
}
I pass to it filenames = /resources/Music.mid and /resources/Sound.wav.
It works fine when I run it in IDE, but when I package it into jar (all music files are inside the jar, I checked) it doesn't play sound and throws exception:
FileNotFoundException: syntax error in file name, directory name or disk name.
Why does this happen and how do I fix it?
getClass() returns a URL and not a File object. It does that because classloaders (which is what .getResource uses) are an abstract concept, and 'a resource' can be anything. It does not have to be a file. It can be an entry in a jar file (which is really a zip). It can be an entry in a jmod file (which is a custom container format introduced with JDK9). It can be a blob in a database column. A file on a webdav server. Or even generated on the fly every time you ask for the resource. Anything goes.
Clearly, your audio player library is obsolete or crappy. It DOES allow you to pass a URL to it, but the audio player library you're using will just crash out if that URL is anything except one that represents an actual file on disk.
When running in the IDE, it IS a file on disk. When running as a jar, it is as valid a resource as anything a classloader produces, but due to the failure of this player library to deal with any URL (other than file:// URLs), it crashes.
There are only two solutions:
Get a better library.
Find a temp dir, unpack the resource into it, then hand the temp file (or possibly a URL representing this temp file) to your library.
Option #2 is quite hairy. You need to find a location with write access, and you need to take care of deleting the file after. It's also a waste of disk cycles. But if you must, you can, of course, do so:
public void play() throws Exception {
File f = File.createTempFile("Sound.wav");
f.deleteOnExit();
try (InputStream in = YourPlayer.class.getResourceAsStream("/resources/Sound.wav");
OutputStream out = new FileOutputStream(f)) {
in.transferTo(out);
}
AudioInputStream audioInputStream =
AudioSystem.getAudioInputStream(f.toURI().toURL());
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start( );
while (clip.isRunning()) {}
}
NB: getClass().getResource() is slightly wrong; the right syntax is ClassThisCodeIsIn.class.getResource. Usually both work, but there are cases where the latter works but the former doesn't (such as when you're subclassing). It's a good habit to never write code that is strictly inferior to something that's just as simple, even if in this particular case it probably doesn't matter.
I am making an simple application to play sounds in Java. I am able to do that when I keep the audio files in D: disk. Here is the code
in = new FileInputStream("D:\\"+selectedSounds[position]+".wav");
//Some code for playing audio
Then I placed the audio files in same package where the Jframe class is present. But when I run it prompts fileNotFound exception. Can some one tell me why this is happening.
in = new FileInputStream(selectedSounds[position]+".wav");
// I have also tried
new FileInputStream("./"+selectedSounds[position]+".wav");
Here is the file path
Your wave file, contained within the "Source Packages" won't be accessible once the program is packaged as a Jar, as the files will be embedded within the Jar itself and no longer accessible as files.
Instead, you should be using Class#getResourceAsStream, for example...
try (InputStream in = getClass().getResourceAsStream("/PlayAudio/" + selectedSounds[position]+".wav")) {
// You now have an InputStream to your resource, have fun
} catch (IOException | NullPointerException exp) {
exp.printStackTrace();
}
I am working on sound code for a game. And I was using the following code:
import java.io.*;
import java.net.URL;
import javax.sound.sampled.*;
/**
* This enum encapsulates all the sound effects of a game, so as to separate the sound playing
* codes from the game codes.
* 1. Define all your sound effect names and the associated wave file.
* 2. To play a specific sound, simply invoke SoundEffect.SOUND_NAME.play().
* 3. You might optionally invoke the static method SoundEffect.init() to pre-load all the
* sound files, so that the play is not paused while loading the file for the first time.
* 4. You can use the static variable SoundEffect.volume to mute the sound.
*/
public enum SoundEffect {
EXPLODE("explode.wav"), // explosion
GONG("gong.wav"), // gong
SHOOT("shoot.wav"); // bullet
// Nested class for specifying volume
public static enum Volume {
MUTE, LOW, MEDIUM, HIGH
}
public static Volume volume = Volume.LOW;
// Each sound effect has its own clip, loaded with its own sound file.
private Clip clip;
// Constructor to construct each element of the enum with its own sound file.
SoundEffect(String soundFileName) {
try {
// Use URL (instead of File) to read from disk and JAR.
URL url = this.getClass().getClassLoader().getResource(soundFileName);
// Set up an audio input stream piped from the sound file.
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
// Get a clip resource.
clip = AudioSystem.getClip();
// Open audio clip and load samples from the audio input stream.
clip.open(audioInputStream);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
// Play or Re-play the sound effect from the beginning, by rewinding.
public void play() {
if (volume != Volume.MUTE) {
if (clip.isRunning())
clip.stop(); // Stop the player if it is still running
clip.setFramePosition(0); // rewind to the beginning
clip.start(); // Start playing
}
}
// Optional static method to pre-load all the sound files.
static void init() {
values(); // calls the constructor for all the elements
}
}
Now when I replace one of the listed *.wav files from the code with my own or even name one of my own to the file name listed from the above code. I receive the follow error:
Exception in thread "main" java.lang.ExceptionInInitializerError
at soundTest.main(soundTest.java:19)
Caused by: java.lang.NullPointerException
at com.sun.media.sound.StandardMidiFileReader.getSequence(Unknown Source)
at javax.sound.midi.MidiSystem.getSequence(Unknown Source)
at com.sun.media.sound.SoftMidiAudioFileReader.getAudioInputStream(Unknown Source)
at javax.sound.sampled.AudioSystem.getAudioInputStream(Unknown Source)
at SoundEffect.<init>(sfx.java:75)
at SoundEffect.<clinit>(sfx.java:55)
From following the stack URL url is null going in, which is telling me the *.wav file itself is not being read.
I have tried the following lines and yes the *.wav file was present (I am aware that I cannot have three items named the same, I've used one played with it, then commented it out and try again with another one, I just removed the "//" to make is easier to read):
TEST("file://C:/shoot.wav");
TEST("/soundTest/shoot.wav");
TEST("shoot.wav");
As well as, placing a copy of the file in the directory with the package (default), in the src folder, and of course in the root (c:).
How I am envoking the enum is in my main statement where it is all the standard java, main code but with:
SoundEffect.SHOOT.play();
Where exactly does the *.wav file need to be at int he directory? Or if there is another issue I am missing please point it out. I am also using the Eclipse IDE "Kepler," on Windows 8.1. I would like to note the posted code is all I have thus far.
There is some discussion in the comments that Eclipse might be the problem. I seriously doubt Eclipse is the problem. I have loaded hundreds of audio files using Eclipse. Usually I put the files in a sub-directory "audio" that is one level below the calling code, and use the relative address form: "audio/mySound.wav".
Here is how I load my URL:
URL url = this.getClass().getResource("audio/" & fileName);
I am puzzled as to why there are MIDI references in your stack trace. MIDI has nothing to do with loading .wav files, and really should not be involved at all. Are you sure this is the correct stack trace? Why are we seeing references to MIDI?
Sometimes an unrecognizable .wav file format will throw unexpected errors. The most common .wav is 16-bit encoding, 44100 bps, stereo, little-endian. Are your .wav files of this format?
There are some aspects of your code that I haven't seen implemented in this manner, particularly the use of ENUMS. I'll take your word all that has been tested and verified. I tend to just name individual sound objects (using a wrapper for wav files with Clip or SourceDataLine for playback) and use them that way. Could be what you have with that is a good organizational tool, but I'm not clear if it is working as intended.
For example, with Static use of SoundEffect, as in SoundEffect.SHOOT.play(), are you sure it is pointing to the shoot.wav? Have you written a test to verify this? Combining ENUMS and static invocations--it's getting a little tricky for me to follow.