Trying to play a sound in java 8 using clip is throwing java.lang.NullPointerException

76 views Asked by At

I'm currently programming a small game, and am trying to implement background music. I'm quite new to visual/audio in Java, and after doing some research online I found that Clip would be an option that would be compatible with how I'm doing my graphics. So, I followed a tutorial of how to set it up/use it, resulting in this 'Sound' class:

import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.*;

public class Sound implements LineListener {
    
    boolean isPlaybackCompleted;
    int playMode;
    
    public Sound(int mode) {
        playMode = mode; // 0 = background music
    }
    
    @Override
    public void update(LineEvent event) {
        if (LineEvent.Type.START == event.getType()) {
            isPlaybackCompleted = false;
            System.out.println("Playback started.");
        } else if (LineEvent.Type.STOP == event.getType()) {
            isPlaybackCompleted = true;
            System.out.println("Playback completed.");
        }
    }
    
    public void playSound(String soundName) throws LineUnavailableException, IOException, UnsupportedAudioFileException {
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("./Sounds/facilityIndoors.wav");
        AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
        AudioFormat audioFormat = audioStream.getFormat();
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
        Clip audioClip = (Clip) AudioSystem.getLine(info);
        audioClip.addLineListener(this);
        audioClip.open(audioStream);
        audioClip.start();
        switch(playMode){
        case 0:
            if(isPlaybackCompleted == true) {
                
            }
        }
        audioClip.close();
        audioStream.close();
    }
}

(Note that the 'soundName' argument for playSound is something I'm hoping to implement later so I can switch between tracks based on a variable shared with the current map name and is currently unused, I've just hard coded the path to a file that already exists for now for testing)

And I have and instantiation and function call in my main

        Sound music = new Sound(0);
        music.playSound(map.currentMap);

When I try and call the playSound function though, this error throws:

Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:209)
    at java.desktop/javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1006)
    at Sound.playSound(Sound.java:37)
    at Program.main(Program.java:22)

At first I thought I had spelt the file path wrong, but after double checking my spelling seems to be correct, as shown here: Image of file path and file name 'Global Research FacilityJava8' is my project folder, and my image references seem to start from that level, so maybe Clip doesn't start from the project folder and that could be the issue, but if it is I don't know how to resolve it.

I have tried checking the file path for spelling errors, and have tried omitting some of the symbols from the start (E.G just 'Sounds/facilityIndoors.wav' or '/Sounds/facilityIndoors.wav'), but the error still persisted. While checking the list of possible duplicates before posting this, I have also found a post talking about putting all of my resource folders into one 'resources' folder and add it to the build path, which I have now tried, but to no avail, still throwing the same error. (file path is now 'resources/Sounds/facilityIndoors.wav')

2

There are 2 answers

3
Grinding For Reputation On BEST ANSWER

I had faced this same issue, but instead of finding a way to fix it, I decided to work around it by copying the audio file out onto a location on my PC.

try this code to copy the file

File audio = new File("C://somewhere//audio.wav").mkdir();
Files.copy(getClass().getResourceAsStream("resources/Sounds/facilityIndoors.wav"), Paths.get("C://somewhere//audio.wav"), StandardCopyOption.REPLACE_EXISTING);

and now just use the audio file like so

AudioInputStream audioStream = AudioSystem.getAudioInputStream(audio));
Clip audioClip  = AudioSystem.getClip();
audioClip.open(audioStream);
0
Phil Freihofner On

Getting the address of the sound file correct is often tricky. If you don't specify a starting "/" in the address, the search is relative to the class being referenced by the .setResource or .getResourceAsStream method. In what you posted, did you have a subfolder /Sounds in the folder that holds the class Sound? If not, you'd get a null error.

The "/" as a prefix to the address is supposed to start the search from the project folder, but I confess I am somewhat fuzzy as to how exactly that works. (I usually generate sounds on the fly rather than reference resources, so I don't use this very often). I think, for example, a Maven-based project uses the /src/main/resources folder, whereas other Java projects use the /src. But I'd have to test this to confirm.

It might no longer make any difference, but as a side note, I think it's better to use .getResource(), which returns a URL, than .getResourceAsStream(), which returns an InputStream. The latter is pickier, for example, AudioSystem.getAudioInputStream() will reject the InputStream if the underlying file does not support mark or reset operations. If you supply a URL to .getAudioInputStream(), support for mark and reset is not a requirement. You can read about this in the API for AudioSystem.getAudioStream(), if you compare the overloads. Using File as the argument (the third overload) is often not a good choice because Java cannot locate a file within a Jar.