1

I'm trying to load a .wav file in my EnviromentAudio object, but I received only an UnsupportedAudioFileException and I don't know why. Because the file is a wav and I've tried to encode it as an unsigned 8 bit, as a signed 16 bit, with a 44100 bit rate, as a GSM, as a A-law... long story short I've tried a lot of encoding, as many people suggested, but no one worked. Probably I'm not getting something, so, I want to ask what I'm doing wrong.

EDIT:
As pointed out I should have specified some things: first of all, to set some context, I am using Java 8 to create a little pc game for a project, which must uses the basics components of java. Said that, I'm using the ClassLoader , because I have a mess in the project folder. It does not follow the convention and I have to keep like that. It's structured like this:

-src  
    -app
        -audio
            EnviromentAudio.java // Class that need to load soundtrack.wav
-res  
    -audio
        Soundtrack.wav // Audio to be loaded

And I know that a getResource.. should start always with a /, but if I add that slash, then every attempt to get a resource results in a NPE. Probably that's caused by the folders disposition and, by the way, the resources folder is set as source folder, so I'm not even quite sure about that, cause, also, I've already used the getResource to get other files without problems.

In this case The getResource works fine, that is it retrieves the file, but the AudioSystem generates an error. I've tried to isolate the parties involved, but the only problem seems to be here. I'm adding the AudioManager class, the Audio class inherited by EnviromentAudio, and the whole EnviromentAudio, with the hope that it will be of help for a better understanding. I also provided a main in the AudioManager class, which should be enough to replicate the error.

Audio class:


package application.core.audio;

import java.util.ArrayList;

import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.swing.JOptionPane;

public abstract class Audio
{
    protected static final String AUDIOERROR="Error in loading audio. "
            + "Execution Failed, please, restart the game. "
    protected static final String AUDIOERRORTITLE="Audio loading error";

    protected ArrayList<Clip> multimedia;
    protected Clip currentAudio;

    protected FloatControl gainControl;

    public Audio()  {
        multimedia=new ArrayList<Clip>();
        currentAudio=null;
    }

    protected abstract void getResources();

    public void playAudio(int index)    {
        try
        {
            currentAudio=multimedia.get(index);
            gainControl=(FloatControl) currentAudio.getControl(
                    FloatControl.Type.MASTER_GAIN);
            currentAudio.open();
        } catch (LineUnavailableException e)
        {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, AUDIOERROR,
                    AUDIOERRORTITLE, JOptionPane.ERROR_MESSAGE);
        }
        currentAudio.start();
    }
    public void loopAudio(int index)    {
        currentAudio=multimedia.get(index);
//          gainControl=(FloatControl) currentAudio.getControl(
//                  FloatControl.Type.MASTER_GAIN);
//          currentAudio.open();
//      currentAudio.start();
        currentAudio.loop(Clip.LOOP_CONTINUOUSLY);
    }
    public void repeatAudio(int index, int times)   {
        try
        {
            currentAudio=multimedia.get(index);
            gainControl=(FloatControl) currentAudio.getControl(
                    FloatControl.Type.MASTER_GAIN);
            currentAudio.open();
        } catch (LineUnavailableException e)
        {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, AUDIOERROR,
                    AUDIOERRORTITLE, JOptionPane.ERROR_MESSAGE);
        }
        currentAudio.loop(times);
    }
    public void stopAudio(int index)    {
        multimedia.get(index).stop();
        multimedia.get(index).close();
    }

    public void setVolume(float volume) {
        float range=gainControl.getMaximum()-gainControl.getMinimum();
        float gain=(range-volume)+gainControl.getMinimum();
        gainControl.setValue(gain);
    }

    public boolean currentAudioIsOpen() {return currentAudio.isOpen();}
    public void openCurrentAudio()  {
        if (!currentAudio.isOpen())
            try
            {
                currentAudio.open();
            } catch (LineUnavailableException e)
            {
                e.printStackTrace();
                JOptionPane.showMessageDialog(null, AUDIOERROR,
                        AUDIOERRORTITLE, JOptionPane.ERROR_MESSAGE);
            }
    }
    public void openAndPlayCurrentAudio()   {
        if (!currentAudio.isOpen())
            openCurrentAudio();
        currentAudio.start();
    }

    public void playCurrentAudio()  {currentAudio.start();}
    public void loopCurrentAudio()  {currentAudio.loop(Clip.LOOP_CONTINUOUSLY);}
    public void repeatCurrentAudio(int times)   {currentAudio.loop(times);}
    public void stopCurrentAudio()  {currentAudio.stop();}
    public void stopAndCloseCurrentAudio()  {
        currentAudio.stop();
        currentAudio.close();
    }
}

This is my EnviromentAudio class that produce the exception:


package application.core.audio;

import java.io.File;
import java.io.IOException;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class EnviromentAudio extends Audio
{
    public static final int SOUNDTRACK=0;

    public EnviromentAudio()
    {
        super();
        getResources();
        this.gainControl=(FloatControl) currentAudio.getControl(FloatControl.Type.MASTER_GAIN);
        }

    @Override
    protected void getResources()
    {
        try
        {
        ClassLoader loader=EnviromentAudio.class.getClassLoader();
            multimedia.add(AudioSystem.getClip());
            multimedia.get(SOUNDTRACK).open(AudioSystem.getAudioInputStream( // here the exception is thrown (on getAudioInputStream)
                    loader.getResourceAsStream("resources"+File.separator+"audio"+File.separator+
                    "soundtrack"+File.separator+"igpeSoundtrack.wav")));
            currentAudio=multimedia.get(SOUNDTRACK);
        } catch (LineUnavailableException e)
        {
            e.printStackTrace();
        } catch (IOException | UnsupportedAudioFileException e1)
        {
            e1.printStackTrace();
        }
    }

}

AudioManager class:


    package application.core.audio;
    
    public class AudioManager
    {
        private static AudioManager instance=null;
    
        private EnviromentAudio soundtrack;
        private PlayerAudio playerAudio;
    
        private AudioManager()  {
            soundtrack=new EnviromentAudio();
    //      playerAudio=new PlayerAudio();
    
            soundtrack.loopAudio(EnviromentAudio.SOUNDTRACK);
        }
    
        public static AudioManager getInstance()    {
            if (instance==null)
                instance=new AudioManager();
            return instance;
        }
    
        public Audio getSoundtrack()    {return soundtrack;}
        public Audio getPlayerSounds()  {return playerAudio;}
    
        public void setVolume(float volume) {
            soundtrack.setVolume(volume);
            playerAudio.setVolume(volume);
        }
        public float getVolume()    {return soundtrack.gainControl.getValue();}
    
        public static void main(String[] args)
        {
            AudioManager a=AudioManager.getInstance();
        }
    
    }

And here is the error:

javax.sound.sampled.UnsupportedAudioFileException: Stream of unsupported format
    at java.desktop/javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1020)
    at application.core.audio.EnviromentAudio.getResources(EnviromentAudio.java:29)
    at application.core.audio.EnviromentAudio.<init>(EnviromentAudio.java:18)
    at application.core.audio.AudioManager.<init>(AudioManager.java:11)
    at application.core.audio.AudioManager.getInstance(AudioManager.java:19)
    at application.MainApplication.audioInitialize(MainApplication.java:44)
    at application.MainApplication.main(MainApplication.java:25)
CcmU
  • 490
  • 7
  • 14
  • 3
    When debugging these types of problems, separate out complex statements and test each in turn. In this case, I **suspect** the audio file (resource) is not even being found. E.G. Using `File.separator` will foul up any `getResource..` method on systems where it is a back slash. The `getResource..` methods always need `/`. ***Always.*** I'd also recommend using `getResource` for an URL over `getResourceAsStream` for a stream for several reasons. **Generally:** For better help sooner, [edit] to add a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). – Andrew Thompson Jul 24 '20 at 01:36
  • 1
    As Andrew already pointed out, if you know the file name and the extension, always use `getResource()`, not the stream version. It makes it much easier for the underlying framework to guess the format correctly. – Hendrik Jul 24 '20 at 07:02
  • Thanks to both of you for the suggestion and the explanation, I will correct this aspect. Therefore, I've tried to modify the `getResource`, but I've got another problem, I edited the question with more info. TLDR: if `getResource` is modified it produces a NPE – CcmU Jul 24 '20 at 10:38
  • It makes sense that you would get an NPE! If I'm reading correctly, the system is looking for /src/app/audio/resources/audio/soundtrack/igpeSoundtrack.WAV. I'm never sure what the system thinks the "root" of the project is. With a "/" is at the start of the URL, you would be looking for /root/resources/audio/etc... This may in fact be /src/resources/etc... which doesn't exist. You may need to change your file structure to accommodate creating a common root folder. For a test, perhaps make /resources a subfolder of /src and use the preceding / in the address. – Phil Freihofner Jul 24 '20 at 17:37

1 Answers1

1

This is more to help with troubleshooting than a solution (expanding on Andrew Thompson's suggestion of making an MRE. Are you using a particular framework? Or is it something of your own making? For a second I though it might be Android (due to presence of AudioManager).

Following is a more minimal example for play testing your .wav file. Put the wav file in the same folder as this class. Does your .wav file play when using this?

import java.io.IOException;
import java.net.URL;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class BasicClipExample {

    public static void main(String[] args) {
        try {
            BasicClipExample.run();
        } catch (UnsupportedAudioFileException | IOException 
                | LineUnavailableException | InterruptedException e) {
             e.printStackTrace();
        }
    }

    private static void run() throws UnsupportedAudioFileException, 
            IOException, LineUnavailableException, InterruptedException
    {
        String filename = "yourSound.wav";
        URL url = BasicClipExample.class.getResource(filename);
        AudioInputStream ais = AudioSystem.getAudioInputStream(url);
        DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
        Clip clip = (Clip) AudioSystem.getLine(info);
        clip.open(ais);
        clip.start();
        Thread.sleep(6000); // plays up to 6 seconds of sound before exiting
        clip.close();
    }
}

If it works, then something is odd about your framing code. From here you can progressively check if things like the file separator logic are working. You can also add some lines to print out the AudioFormat if the file loads.

Another way I sometimes inspect files is to load them into Audacity, which is free. Info about the file format is pretty easy to inspect with that tool. If I had to wager, and the issue IS the .wav format, I'm guessing that the file is recorded at a higher quality level than Java is set to work with, e.g., 48000 (maybe Java supports?) or 96000 fps or 24- or 32-bit encoding rather than 16-bit.

Phil Freihofner
  • 7,145
  • 1
  • 17
  • 38
  • 1
    I followed your suggestion, but I've encountered the same problem. I'm always more sure that it's a problem of the file. I used Audacity indeed, not only to control the file's info, but also to modify its property. But I still don't know why a wav 16 bit PCM at 44100 Hz doesn't work. – CcmU Jul 24 '20 at 10:14
  • I'm not clear from your comment. Did you try the exact code sample and get the same error message about unrecognized format? From the added info on your question, it's starting to look more like a problem providing a valid URL for the file. This is my best understanding of resource loading https://stackoverflow.com/questions/63020106/the-system-cannot-find-the-path-specified-in-jar/63025071#63025071 – Phil Freihofner Jul 24 '20 at 17:46
  • To trouble shoot, a starting step would be to have a file you know you can load and play, and do so. Then put in your problem file and see if the error occurs. If the error occurs, the only thing I can think is maybe its BigEndian instead of LittleEndian or something weird. I'd try putting it in Audacity and exporting it with a known, working format and trying that exported file. – Phil Freihofner Jul 24 '20 at 17:48
  • 1
    Ok, I found out what the error was or, better the errors. As you also guessed there were a file problem, for I don't know what reason, audacity couldn't export successfully in the wav signed 16 bit 44100 KHz format (oddly enough, it was exporting in unsigned 32 bit with a bit rate of 48000 Hz without considering the export settings), but I fixed that by reinstalling audacity. – CcmU Jul 24 '20 at 18:56
  • 1
    The second problem was the `getResource` problem. I still don't know how it fully works and so I've tried all the suggestion that I recieved. I saw that even in a sub folder of src, the NPE was lurking. That is almost surely caused by my poorly knowledge of the `getResource` methods, but I'm trying to learn more. In the end I resolved by using a File constructed from the file's path, because I can't figure out why my `getResource` works in a class and not in another one, especially when the path is the correct one. Still, thanks a lot. – CcmU Jul 24 '20 at 18:56
  • 1
    The rules around getResource have been a stumbling block for years for people. I'd like to see a clear explanation of what the "root" folder is that "/" uses for the absolute address form. Maybe it's hard to define because different IDE's have different file structures. Glad to hear you are making progress! – Phil Freihofner Jul 24 '20 at 21:15
  • 1
    I wonder if this would help: use ONLY "/yourAudioFile.wav" in the getResource() method and place copies of the file in various directories until it is found. That way you can learn what Java thinks is the "root" directory of the project. Once you know the root, you can add subfolders and create a better file folder structure. – Phil Freihofner Jul 24 '20 at 21:19
  • 1
    @PhilFreihofner *"I'd like to see a clear explanation of what the "root" folder is that "/" uses for the absolute address form."* It might have various directories (hate the term 'folder' given it's a GUI term unrelated o headless machines) if other Jars are specified in the class-path. But 'class-path' is the best way to think of the root. The `getResource(..)` method will check from the root of *every* Jar in the class-path until it finds the resource. – Andrew Thompson Jul 25 '20 at 23:24
  • Thanks @AndrewThompson. I've managed to do a fair bit and yet never been able to grasp this. I'm going to look for a tutorial on class-path for a project as a start. Your answer makes reference to much I have not come to terms with. But I very much appreciate your having replied with an answer. – Phil Freihofner Jul 28 '20 at 05:20
  • While looking at https://dev.to/martingaston/understanding-the-java-classpath-building-a-project-manually-3c3l I came across a diagram of the "Maven standard directory layout". I think the OP @CcmU might take a look to see if this would serve their needs. Note that /src is probably the root when using the absolute addressing form, and is above the both the code and the resources, allowing easy access to either via absolute addressing. – Phil Freihofner Jul 28 '20 at 05:28