Jar won't play sounds from resources folder loaded through getClass() - java

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.

Related

file not found or syntax incorrect when using Main.class.getResource("notification.wav").getFile()

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.

Trouble with .wav playing in jar file created from Intellij IDEA

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.

(Java) Trying to load a clip from JAR file using getResourceAsStream(), but works only on my IDE

I'm trying to load a clip from my JAR file, but it only works in my IDE (NetBeans), and not when I run its JAR executable.
Here's the code I'm using that returns a clip:
public static Clip getClipFromJar(String filePath) {
try {
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(FileLoader.class.getResourceAsStream(filePath)));
return clip;
} catch (Exception error) {
System.exit(-1);
return null;
}
}
I'm really stuck, using getResourceAsStream() seemed to work for other things such as images, but it's not working for getting a clip.
Thanks for all the help everyone! The tips I got from you guys are good for programming in general, so thanks! :)
Get the URL , take care of the AudioInputStream and AudioSytem and then return the clip . And the clip can be started like clip.start() after that
try{
URL soundURL = getClass().getResource(filePath);
AudioInputStream AIS = AudioSystem.getAudioInputStream(soundURL);
Clip clip = AudioSystem.getClip();
clip.open(AIS);
return clip;
} catch (Exception ex) {
ex.printStackTrace();
}
Note : I tried this code and it worked properly both in IDE and outside
getResourceAsStream() looks for its resource on the classpath, starting with the location of the class itself -- so if your clip (or whatever resource) is in the same folder as the class, it'll find it there. If it isn't there, you have to make sure you have it in the classpath somewhere, and it sounds like that might be difference between your IDE environment and your program runtime.
Look at the 'filepath' you have also -- if it starts with a slash of course, it is absolute, etc.; I'm assuming you know about the absolute and relative paths.
A suggestion for debugging -- eliminate all the 'stacking' of calls. Make one call to get the stream; make another call to get the audio input stream, and a third to open it. Then you can at least put in trace statements to see which one of them is failing (since you can't debug outside of the IDE, and it isn't failing there).

url for audioInputStream seems empty

I am trying the following code to play an mp3. The file is in the correct folder. An exception is thrown when opening the file.
Any ideas what could be wrong?
When trying to print the url on the third line it gives me a null pointer exception.
I am compiling with 1.8 to a min compat version of 1.6. Could this be related?
AudioInputStream audioIn = null;
URL url = this.getClass().getResource("./data/1.wav");
System.out.print(url.getFile());
try {
audioIn = AudioSystem.getAudioInputStream(url);
//audioInputStream = AudioSystem.getAudioInputStream(this.getClass().getResource(soundFile));
}
catch(Exception ex) {System.out.print("exception opening file");}
try{
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
}
catch(Exception ex)
{
System.out.print("exception mediaplayer");
}
}
Instead of using a String var, you could use a Clip object. With Clip, it worked for me.
Oh I had to put the data folder in the src directory.
Still a problem though: when I compile as a jar file, it does not find the data folder. And I want the user to be able to put different things in there.
A URL can see into Jar, unlike a File address. So, you should be fine using the URL to obtain the file.
Two possible problems:
(1) if the file is an .mp3 and you are trying to load a .wav, there will of course be a mismatch as 1.mp3 <> 1.wav.
(2) putting the data into the src folder is not enough. It must also be in a subfolder of the folder that contains the "this" that you refer to. For example, if your code is in package "foo", the place to put the file would be src/foo/data/1.wav.
Do you have a library to decode the .mp3? If so, something like JavaZoom should work.

sound not playing in jar

I have packed all the class files and resources as a jar but on execution the sound files wont play. My package structure is:
+project
|_classes
|_ _*.class
|_resources
|_ _ *.jpg,*.wav
Code:
AudioInputStream inputStream = AudioSystem.getAudioInputStream(kidsClassRoom.class.getResourceAsStream("../resources/"+file));
getting a null when this line is executed!!
An alternate theory to those already presented. Often successful getResource() calls depend on the class loader instance that is called to locate them. For this reason, I would recommend to use an instance of a user defined object, from which to call getResource(). E.G.
// Sanity check
System.out.println("The value of 'file' is: " + file);
// Presuming kidsClassRoom1 is an instance of kidsClassRoom
AudioInputStream inputStream = AudioSystem.
getAudioInputStream(
kidsClassRoom1.
getClass().
getResourceAsStream("/resources/"+file));
You might also note that snippet uses the prefix of "/" for the resource. Contrary to what others are saying, I am confident that means 'from the root' of the resource path, in whatever Jar on the run-time class-path it is found. Leaving the '/' or '../' out will have the class loader searching for the resource in a sub-path of the class that this is occurring in.
Of course - make sure the Wav ends up in the Jar! Copy/rename the .jar to a .zip and double click it is the 'quick & dirty' way to examine the archive contents on Windows.
Create a package named resources as shown below
then
AudioSystem.getAudioInputStream(kidsClassRoom.class.getResourceAsStream("resources/"+file));
This is my function for playing a looping sound file in jars, it works fine for me.
It appears that getResourceAsStream() doesn't work with jars. however, getResource() does.
public synchronized void alarm() {
try {
crit = AudioSystem.getClip();
AudioInputStream inputStream1 = AudioSystem.getAudioInputStream(this.getClass().getResource("critical.wav"));
crit.open(inputStream1);
crit.loop(Clip.LOOP_CONTINUOUSLY);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
When you do getResourceAsStream, it is not relative to the current class, but the root of the archive. That is, first try to remove ../.
The important thing to note is that in the exported jar the resources are not stored as files (Read this somewhere, someone more knowledgeable please input). So it's best to get the resource as a URL Object first then pass that to the AudioInputStream Object.
URL url = YourClass.class.getResource(filename);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
If the resource is in a subfolder, remember to add it to your filename path.

Categories

Resources