Kuroi Akuma
Kuroi Akuma

Reputation: 37

Playing audio from my Loop a single time without halting the rest of loop

Let's say I've a main GameLoop

Inside this loop I have my Game Updates

I'm handling my Events for Sprite collision testing with each Iteration

If collision is true, play audio file

Here's where the problem occurs

The Audio clip will either play Rapidly, while the game is frozen

or

It will play w/ delay like I want but the entire Game comes to a halt other than the Audio Clip.

I'm just looking for some tips on Threading Basically. As far as i'm aware it'll be the best way to handle this problem and I can't seem to get it running correctly.

Note I would extend Thread on main class but already extends Canvas, needed.

    public Main()
    {

        boolean running = true;

        while(running)
        {

            // check for collision (returns boolean)
            // if true proceed to execute Entity.doLogic()
            // this then activates the AudioClip class' .playAudioClip(this, path)
            // the audio Clip is then played and once it's done it'll return
            // returns and instantly goes back to playing again
            // meanwhile the loop Freezes up on me.

        }

    }

And This is the actual Sound.class

public class Sounds
{

    public void startSound()
    {
        String path = "path";
        playAudioClip(game, path);
    }

    public void playAudioClip(String path)
    {
        try
        {
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(new File(path)));
            clip.start();
        }
        catch(Exception e)
        {
            System.out.println("Problem loading audio file");
        }
        try{Thread.sleep(500);}catch(Exception ex){System.out.println("Problem with Sleep");};
    }
}

I've tried the below and same situation (Calling it by s.start() and s.run() no difference) using .start() would throw err in thread, will recreate real quick and share)

public class Sounds extends Thread
{
    @Override
    public void run()
    {
        String path = "path";
        playAudioClip(game, path);
    }

    public void playAudioClip(String path)
    {
        try
        {
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(new File(path)));
            clip.start();
        }
        catch(Exception e)
        {
            System.out.println("Problem loading audio file");
        }
        try{Thread.sleep(500);}catch(Exception ex){System.out.println("Problem with Sleep");};
    }
}

throws to console "java.lang.IllegalThreadStateException" invoking with start() only defining run() inside of this object

enter image description here Note to self. Don't do that.

Multithreading in the wrong way prime example 243 max threads going at any point there

Upvotes: 0

Views: 452

Answers (1)

Phil Freihofner
Phil Freihofner

Reputation: 7910

When the start() method of a Clip executes, the sound is played via a daemon thread created just for that purpose. There is no need for you to create a Thread and no need to include the Thread.sleep() command. As long as your program is running, the sound will execute. If your program stops while the sound is still playing, because it's on a daemon, the sound will not finish, but will stop along with the program.

My guess is that your code came from an example program who's only task is to play a sound. A better demo example would have shown how to manage a Clip in the context of a larger program.

Here is a class to try. I've omitted the Exception handling. I use URL because that allows one to get resources even if the program has been compiled into a jar. In terms of file structure, the code assumes that the sound files are in a subfolder relative to the location of your Sounds class.

public class Sounds
{
    private Clip clip;

    public Sounds(String soundFileName)
    {
        URL url = this.class.getResource("audio/" + soundFileName);
        AudioInputStream ais = AudioSystem.getAudioInputStream(url);
        DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
        clip = (Clip) AudioSystem.getLine(info);
        clip.open(ais);
    }

    public void playAudioClip()
    {
        clip.setFramePosition(0);
        clip.start();
    } 
}

The Clip class was designed for sounds that are held in memory. If you don't want to hold you sound file in memory, you should use SourceDataLine, as it will launch more quickly and only holds a buffer's worth of data at a time in memory instead of the entire file.

Thus, we make the Clip an instance variable, and load and open it as part of the instantiation process.

The command clip.setFramePosition(0) is there so that any time you call the Clip, it will start playing from the beginning of the file. With this, you can call and play the Clip at any time. Worst case scenario, if it is in the middle of playing when you call it, the sound will jump back to the beginning and start anew.

Upvotes: 0

Related Questions