Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (524)
Games in Android Showcase (127)
games submitted by our members
Games in WIP (592)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2
  ignore  |  Print  
  Can't figure out how to do Java Audio properly.  (Read 3999 times)
0 Members and 1 Guest are viewing this topic.
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Posted 2013-10-04 17:35:46 »

This is driving me insane. I have asked on Reddit and StackOverflow, and besides the fact that everyone is quick to jump down my throat about supposedly not "searching beforehand" (which I have, by the way... extensively), nobody has given me an answer.

The problem being, that I cannot seem to make the Clip class in Java's sampled sound package work the way I need it to. So I either need someone to look at the code for my Sounds class and tell me what fatal problem is causing my game to freeze up when I try to play the same sound multiple times simultaneously, or refer me to another library/package I can use.

So anyway, I'm trying to make use of the Clip class. (http://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/Clip.html)
I'm pre-loading the various sounds into an enumerator class and calling them statically as needed (ie: Sounds.WHATEVER.play()), but once I added sounds that would be played several times at once, simultaneously, the game started freezing up. I can only assume that the Clip class is not capable of doing this, but just in case I'm just doing something wrong... can someone look at my Sounds class and tell me if that is the case, or if I'm just doing something wrong?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
/**
    This file is part of Generic Zombie Shooter.

    Generic Zombie Shooter is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Generic Zombie Shooter is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Generic Zombie Shooter.  If not, see <http://www.gnu.org/licenses/>.
 **/

package genericzombieshooter.misc;

import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

/**
 * Contains all pre-loaded sounds.
 * @author Darin Beaudreau
 */

public enum Sounds {
    // Weapon-Related
    POPGUN("shoot2.wav", false),
    RTPS("shoot1.wav", false),
    BOOMSTICK("shotgun1.wav", false),
    FLAMETHROWER("flamethrower.wav", true),
    THROW("throw2.wav", false),
    EXPLOSION("explosion2.wav", false),
    LANDMINE_ARMED("landmine_armed.wav", false),
    TELEPORT("teleport.wav", false),
   
    // Zombie-Related
    MOAN1("zombie_moan_01.wav", false),
    MOAN2("zombie_moan_02.wav", false),
    MOAN3("zombie_moan_03.wav", false),
    MOAN4("zombie_moan_04.wav", false),
    MOAN5("zombie_moan_05.wav", false),
    MOAN6("zombie_moan_06.wav", false),
    MOAN7("zombie_moan_07.wav", false),
    MOAN8("zombie_moan_08.wav", false),
    POISONCLOUD("poison_cloud.wav", false),
   
    // Game Sounds
    POWERUP("powerup.wav", false),
    PURCHASEWEAPON("purchase_weapon.wav", false),
    BUYAMMO("buy_ammo2.wav", false),
    POINTBUY("point_buy.wav", false),
    PAUSE("pause.wav", false),
    UNPAUSE("unpause.wav", false);
   
    private Clip clip;
    private boolean looped;

    Sounds(String filename, boolean loop) {
        openClip(filename, loop);
    }

    private synchronized void openClip(String filename, boolean loop) {
        try {
            URL audioFile = Sounds.class.getResource("/resources/sounds/" + filename);

            AudioInputStream audio = AudioSystem.getAudioInputStream(audioFile);
            AudioFormat format = audio.getFormat();
            DataLine.Info info = new DataLine.Info(Clip.class, format);
            clip = (Clip) AudioSystem.getLine(info);

            clip.open(audio);
        } catch (UnsupportedAudioFileException uae) {
            System.out.println(uae);
        } catch (IOException ioe) {
            System.out.println(ioe);
        } catch (LineUnavailableException lue) {
            System.out.println(lue);
        }
        looped = loop;
    }

    public synchronized void play() {
        play(1.0);
    }
   
    public synchronized void play(final double gain) {
        Runnable soundPlay = new Runnable() {
            @Override
            public void run() {
                Clip clipCopy = (Clip)clip;
                FloatControl gainControl = (FloatControl)clipCopy.getControl(FloatControl.Type.MASTER_GAIN);
                float dB = (float)(Math.log(gain) / Math.log(10.0) * 20.0);
                gainControl.setValue(dB);
                if(!looped) reset(clipCopy);
                clipCopy.loop((looped)?Clip.LOOP_CONTINUOUSLY:0);
               
            }
        };
        new Thread(soundPlay).start();
    }
   
    public synchronized void reset() {
        reset(clip);
    }
   
    public synchronized void reset(Clip clipCopy) {
        clipCopy.setFramePosition(0);
    }

    public static void init() {
        values();
    }
}


If I need to use a different package or library, I will, but I really want to stick to core Java, at least for this project.

So, is there some fatal error with the way I'm handling this? Or is the Clip class just not capable of playing sound this way?
I can provide information on how I'm playing the sound if need be, but really, all I'm doing is statically calling the play method for the required sound when that sound is supposed to be played (ie: firing a weapon, zombies moaning, etc). I think the reason this freezing never occurred before is because I was never playing the same sound multiple times simultaneously.

So... can someone spare me the insanity and tell me what it is exactly that I'm doing wrong?
Offline gouessej
« Reply #1 - Posted 2013-10-04 17:42:12 »

Hi

Maybe try to use another Mixer. If you want to use a sound library, you can try Paul Lamb's Sound System or TinySound. Personally, I use Paul Lamb's Sound System with its plugin based on JOAL and my game works very well both with OpenJDK and Oracle Java  Grin

Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #2 - Posted 2013-10-04 19:42:42 »

Hi

Maybe try to use another Mixer. If you want to use a sound library, you can try Paul Lamb's Sound System or TinySound. Personally, I use Paul Lamb's Sound System with its plugin based on JOAL and my game works very well both with OpenJDK and Oracle Java  Grin

I gave TinySound a look and all I could find was a Github page. I downloaded the ZIP of the project, but I can't figure out how to include it in my project in NetBeans and use the library. What do I do with the ZIP? I've tried including it as a library, adding it to the project, etc... but I can't import anything from the kuusisto package, nor can NetBeans find the TinySound class.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #3 - Posted 2013-10-04 20:11:51 »

Why do you create a Thread? Clips always play in the background, you don't need Threads for that.
The easiest way to play audio clips is this.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
public static void play(String name) {
   try{
     AudioInputStream sounds = AudioSystem.getAudioInputStream(Sounds.class.getResource(name));
     final Clip clip = AudioSystem.getClip();
     clip.addLineListener(new LineListener() {
         public void update(LineEvent e) {
             LineEvent.Type type = e.getType();
             if(type == type.STOP) clip.close();
         }
     });
     clip.open(sounds);
     clip.start();
   } catch(Exception e){
       e.printStackTrace();
   }
}


If you want to play two clips at the same time you just call it twice.
e.g.
1  
2  
play("shoot.wav");
play("explosion.wav");
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #4 - Posted 2013-10-04 20:45:55 »

Except that if I close the sound, I have to reload the data, which isn't good for sounds that are played a lot. Also, when I said playing two sounds at the same time, I meant the SAME sound, not two different ones. So two calls to the same sound overlapping each other. THAT is why I use threads. Or at least, that's what some page on Google suggested.

Clip can't seem to do that, which is why it's not going to work out.
Offline Jeremy
« Reply #5 - Posted 2013-10-04 21:06:08 »

Except that if I close the sound, I have to reload the data, which isn't good for sounds that are played a lot. Also, when I said playing two sounds at the same time, I meant the SAME sound, not two different ones. So two calls to the same sound overlapping each other. THAT is why I use threads. Or at least, that's what some page on Google suggested.

Clip can't seem to do that, which is why it's not going to work out.

I don't think playing a clip in a new thread allows you to play them simultaneously, to play two audio files simultaneously, you'll need two clips.

That said, it isn't very difficult to do. You'll want to cache all open clips with SoftReferences (or WeakReferences, but SoftReferences are more appropriate for caching.) When they're playing keep a hard references to them, and when they aren't keep a softreferences (via some caching module) This allows you to very quickly reset an unused to clip if you want to play it again and it's not currently being played (but it doesn't solve the problem of playing two clips simultaneously yet.) If the JRE decides it needs more memory and destroys your clip instance, you'll need to make sure you close it before giving it away to the JRE.

You'll also want to softreferences their raw data (for clips you want to play simultaneously) that you use to initialize the clip. By doing this, you minimize filesystem IO by caching a copy of the file in memory (with a byte buffer etc) which will allow you to very quickly initialize a new clip instance.

If you use this method, it is then important that you use an uncompressed audio format (like WAV) to minimize the pre-processing overhead induced by the compression. Which is fine since compressing small audio files (like the moan of a zombie, or gun fire) is almost pointless to do individually.

DON'T do this for large audio files like background music since it will probably kick everything out of the cache eventually. If you want to do this for compressed audio formats you need cache their decompressed raw data, otherwise initializing the clip will be expensive (due to the redundant process of decompressing the cached data.)

There are other more clever ways of doing it, but you'd have to ask someone more experienced with the Java Sound API.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline Zeta

Junior Devvie


Medals: 1
Exp: 1 year



« Reply #6 - Posted 2013-10-04 21:12:03 »

I think DrZoidberg's solution is the best option and the easiest way to achieve overlapping sounds (two sounds at the same time).
There is no need for threads.

A clever person solves a problem. A wise person avoids it. -Albert Einstein
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #7 - Posted 2013-10-04 21:14:38 »

Except that if I close the sound, I have to reload the data, which isn't good for sounds that are played a lot. Also, when I said playing two sounds at the same time, I meant the SAME sound, not two different ones.

Of course you can also play the same sound twice at the same time.
1  
2  
play("shoot.wav");
play("shoot.wav");

You can also add a delay to get an echo effect.
1  
2  
3  
play("shoot.wav");
Thread.sleep(500);
play("shoot.wav");

That the data has to be loaded twice should not be a problem since the OS has a file cache. But if you think it's an issue you could load the wav files into byte arrays and then create clips from those.
Offline Jeremy
« Reply #8 - Posted 2013-10-04 21:16:46 »

True, I think OPs point was that it causes a lot of traffic to the filesystem, which isn't good if you're playing a machine gun for example.

I've tried it, and it was an issue for me.

It's also very inefficient for compressed audio formats.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #9 - Posted 2013-10-04 23:12:27 »

Except that if I close the sound, I have to reload the data, which isn't good for sounds that are played a lot. Also, when I said playing two sounds at the same time, I meant the SAME sound, not two different ones. So two calls to the same sound overlapping each other. THAT is why I use threads. Or at least, that's what some page on Google suggested.

Clip can't seem to do that, which is why it's not going to work out.

I don't think playing a clip in a new thread allows you to play them simultaneously, to play two audio files simultaneously, you'll need two clips.

That said, it isn't very difficult to do. You'll want to cache all open clips with SoftReferences (or WeakReferences, but SoftReferences are more appropriate for caching.) When they're playing keep a hard references to them, and when they aren't keep a softreferences (via some caching module) This allows you to very quickly reset an unused to clip if you want to play it again and it's not currently being played (but it doesn't solve the problem of playing two clips simultaneously yet.) If the JRE decides it needs more memory and destroys your clip instance, you'll need to make sure you close it before giving it away to the JRE.

You'll also want to softreferences their raw data (for clips you want to play simultaneously) that you use to initialize the clip. By doing this, you minimize filesystem IO by caching a copy of the file in memory (with a byte buffer etc) which will allow you to very quickly initialize a new clip instance.

If you use this method, it is then important that you use an uncompressed audio format (like WAV) to minimize the pre-processing overhead induced by the compression. Which is fine since compressing small audio files (like the moan of a zombie, or gun fire) is almost pointless to do individually.

DON'T do this for large audio files like background music since it will probably kick everything out of the cache eventually. If you want to do this for compressed audio formats you need cache their decompressed raw data, otherwise initializing the clip will be expensive (due to the redundant process of decompressing the cached data.)

There are other more clever ways of doing it, but you'd have to ask someone more experienced with the Java Sound API.

I don't want to seem stupid, but I didn't understand a lot of what you just said. Like I said, I don't have to stick to the Clip class... I was just hoping I could stay within the core Java packages.

I checked out TinySound, but I don't know how to include it in my project. I'm using NetBeans. I downloaded a ZIP of the TinySound repository on Github, but I'm not sure what to do with it. There aren't any JARs inside, so I can't use it as a library. How do I incorporate the .java files as a library?

Also, I removed the threads for playing the sound.

But yeah, loading the sound files on the fly isn't going to work. It would cause too much latency. Like Jeremy said, for a machine gun (and my game happens to have an Assault Rifle), it just won't work. My sounds are used too often.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Jeremy
« Reply #10 - Posted 2013-10-05 01:42:59 »

You should stick to the Java Sound Library if you can, otherwise you don't get the advantage of plugging in other SPIs for different audio formats etc. Also, if you can it is always best to avoid any native dependencies.

I've put some code for loading audio files in the paste-bin. It is hacked up and it may not be 100% proper, but it should work fine. Any missing code you can write pretty easily.
http://www.java-gaming.org/?action=pastebin&id=730


JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #11 - Posted 2013-10-05 02:05:01 »

You should stick to the Java Sound Library if you can, otherwise you don't get the advantage of plugging in other SPIs for different audio formats etc. Also, if you can it is always best to avoid any native dependencies.

I've put some code for loading audio files in the paste-bin. It is hacked up and it may not be 100% proper, but it should work fine. Any missing code you can write pretty easily.
http://www.java-gaming.org/?action=pastebin&id=730



I only use WAV files, so I don't see why I would need to worry about other audio formats. Also, I took a look at that Audio class you posted. Does it support the multiple plays of the same Clip simultaneously? Is it streamed? Like I said, streaming is absolutely not an option.
Offline Jeremy
« Reply #12 - Posted 2013-10-05 02:15:55 »

You should stick to the Java Sound Library if you can, otherwise you don't get the advantage of plugging in other SPIs for different audio formats etc. Also, if you can it is always best to avoid any native dependencies.

I've put some code for loading audio files in the paste-bin. It is hacked up and it may not be 100% proper, but it should work fine. Any missing code you can write pretty easily.
http://www.java-gaming.org/?action=pastebin&id=730



I only use WAV files, so I don't see why I would need to worry about other audio formats. Also, I took a look at that Audio class you posted. Does it support the multiple plays of the same Clip simultaneously? Is it streamed? Like I said, streaming is absolutely not an option.

Yeah, it will let you play the same audio file twice, and it adapts the ByteBuffer object to an InputStream for the JavaSound API, but it reuses the ByteBuffer - it doesn't load the same resource more than once and it caches the raw data to initialize a new clip if once is needed. It also caches the clip objects for re-use.

It works for machine guns etc.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #13 - Posted 2013-10-05 02:30:40 »

Also, I noticed a couple of weird imports at the top... specifically, the "jeva" package. Was that a typo, or is that an external library? Can I just include this file and use it as is, or do I need a library to use with it?
Offline Jeremy
« Reply #14 - Posted 2013-10-05 02:39:32 »

Also, I noticed a couple of weird imports at the top... specifically, the "jeva" package. Was that a typo, or is that an external library? Can I just include this file and use it as is, or do I need a library to use with it?

Those are external libraries, just delete them. You'll get a problem with AudioException not being found, and Core.getService(IResourceLibrary.class).openResourceRaw() or something like that. It's pretty easy to guess what they're supposed to do. Just write your own AudioException and openResourceRaw(string resource) is supposed to read the resource into a bytebuffer.

You should be able to just copy and paste it, noting the package will probably need to be changed.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #15 - Posted 2013-10-05 02:47:24 »

How do I do that? Is it a simple function that you could just post here?
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #16 - Posted 2013-10-05 03:13:55 »

But yeah, loading the sound files on the fly isn't going to work. It would cause too much latency. Like Jeremy said, for a machine gun (and my game happens to have an Assault Rifle), it just won't work. My sounds are used too often.

Then do it like this

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
public class AudioData {
    byte[] data;
    AudioFormat format;
    AudioData(String fileName) {
       try {
           AudioInputStream sounds = AudioSystem.getAudioInputStream(AudioData.class.getResource(fileName));
           format = sounds.getFormat();
           int size = sounds.available();
           data = new byte[size];
           sounds.read(data);
           sounds.close();
       } catch(Exception e) {
           e.printStackTrace();
       }
    }
    public Clip createClip() {
        try {
            final Clip clip = AudioSystem.getClip();
            clip.addLineListener(new LineListener() {
             public void update(LineEvent e) {
                 LineEvent.Type type = e.getType();
                 if(type == type.STOP) clip.close();
             }
            });
            clip.open(format, data, 0, data.length);
            return clip;
        } catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
}


and then you can make your machine gun with this code
1  
2  
3  
4  
5  
AudioData data = new AudioData("shoot.wav");
for(int i = 0; i < 100; i++) {
    data.createClip().start();
    Thread.sleep(100);
}


However of course it would be better to use Clip.loop for a machine gun but then the audio file may need some editing to make it sound correctly.
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #17 - Posted 2013-10-05 03:17:50 »

Why would I loop the sound for my Assault Rifle? It's a single shot that plays whenever a bullet is fired.

So anyway, what you're saying is that I could have a class with final static AudioData objects, and then when I need to play the clip, call the createClip() method and start it?
Offline SHC
« Reply #18 - Posted 2013-10-05 03:20:29 »

I'm using a similar way of DrZoidberg but had some small difference. My classes are located here.See classes WavSound, SoundState, WavPlayer.

Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #19 - Posted 2013-10-05 03:32:04 »

You could use your Sounds enum but put AudioData objects in there instead of clips.
Or use SHC's classes, they look good too. Although I'm not sure if you can make a machine gun with them.
SHC, maybe you could post a machine gun example using your library?
Offline SHC
« Reply #20 - Posted 2013-10-05 04:04:00 »

Those classes will just play the sound once or plays them in a loop.

1  
2  
3  
4  
5  
6  
7  
8  
9  
// Load the clip
WavSound sound = WavPlayer.loadSound("shoot.wav");

// Play it once
sound.play();

// Play looping
sound.setLooping(true);
sound.play();

If you want to have effects, I think you can manipulate the data by getting the samples data and create a new WavSound object with the modified samples.

Offline philfrei
« Reply #21 - Posted 2013-10-05 06:17:58 »

Hi packetpirate. I tried answering you earlier at StackOverflow.

According to this link, TinySound will support concurrent playbacks of Clips:
http://www.java-gaming.org/topics/need-a-really-simple-library-for-playing-sounds-and-music-try-tinysound/25974/msg/260120/view.html#msg260120

What exactly are you trying to load from the TinySound project? Can you point to the location of the zip file?

Have you done the whole shebang of "forking" "cloning" "pulling" to get a copy onto your desktop?

It's easier for me to reply here than StackOverflow.

"It's after the end of the world! Don't you know that yet?"
Offline nsigma
« Reply #22 - Posted 2013-10-05 12:10:26 »

I checked out TinySound, but I don't know how to include it in my project. I'm using NetBeans. I downloaded a ZIP of the TinySound repository on Github, but I'm not sure what to do with it. There aren't any JARs inside, so I can't use it as a library. How do I incorporate the .java files as a library?

You need to build TinySound into a JAR yourself.  Try creating a second project in NetBeans for TinySound, and copying all of the src folder contents from the zip file into it.  You'll probably have to link the JARs in the lib folder into that project to get it to build.  Either compile a JAR from the new TinySound project and include it in your existing project, or you should be able to link the TinySound project as a library directly.

I'd seriously get your head around the Java build process and creating / using JARs before you try playing with sound further.

And really, really, really DO NOT use Clip  Wink .  It's implementation is not set up for doing what you want to do, or can end up working on one OS and not another.

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #23 - Posted 2013-10-05 15:08:23 »

But yeah, loading the sound files on the fly isn't going to work. It would cause too much latency. Like Jeremy said, for a machine gun (and my game happens to have an Assault Rifle), it just won't work. My sounds are used too often.

Then do it like this

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
public class AudioData {
    byte[] data;
    AudioFormat format;
    AudioData(String fileName) {
       try {
           AudioInputStream sounds = AudioSystem.getAudioInputStream(AudioData.class.getResource(fileName));
           format = sounds.getFormat();
           int size = sounds.available();
           data = new byte[size];
           sounds.read(data);
           sounds.close();
       } catch(Exception e) {
           e.printStackTrace();
       }
    }
    public Clip createClip() {
        try {
            final Clip clip = AudioSystem.getClip();
            clip.addLineListener(new LineListener() {
             public void update(LineEvent e) {
                 LineEvent.Type type = e.getType();
                 if(type == type.STOP) clip.close();
             }
            });
            clip.open(format, data, 0, data.length);
            return clip;
        } catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
}


and then you can make your machine gun with this code
1  
2  
3  
4  
5  
AudioData data = new AudioData("shoot.wav");
for(int i = 0; i < 100; i++) {
    data.createClip().start();
    Thread.sleep(100);
}


However of course it would be better to use Clip.loop for a machine gun but then the audio file may need some editing to make it sound correctly.

Ok, I've implemented this, but now I have one other question about how to use it... for sounds that I want to loop, but to stop when certain things happen (ie: flamethrower sound loop until player stops firing the weapon), how would I do that?

EDIT: I suppose answering the previous question might give me an idea on how to solve this one, but this is also a huge problem... there is no delay between the fire() calls to the Flamethrower weapon (whose sound is the only one in the game that loops), so they all play together REALLY rapidly, and it comes out as a raucous jumble of noise.
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #24 - Posted 2013-10-05 18:45:16 »

The clip class has a loop and a stop method. But after you call stop the clip will get closed automatically so don't call start or loop on it after you stopped it.
For a machine gun effect you need a delay. You probably have to create another Thread for that. You can communicate with that Thread through a volatile variable.

EDIT:
I updated my AudioData class

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
public class AudioData {
    private byte[] data;
    private AudioFormat format;
    private volatile boolean machineGunActive = false;
    private HashSet<Clip> clipsPlaying = new HashSet<>();
   
    private synchronized void removeClip(Clip clip) {
        clipsPlaying.remove(clip);
    }
    private synchronized void addClip(Clip clip) {
        clipsPlaying.add(clip);
    }
    private synchronized void stopAllClips() {
        for(Clip clip: clipsPlaying) clip.stop();
        clipsPlaying.clear();
    }
   
    AudioData(String fileName) {
       try {
           AudioInputStream sounds = AudioSystem.getAudioInputStream(AudioData.class.getResource(fileName));
           format = sounds.getFormat();
           int size = sounds.available();
           data = new byte[size];
           sounds.read(data);
           sounds.close();
       } catch(Exception e) {
           e.printStackTrace();
       }
    }
    public Clip createClip() {
        try {
            final Clip clip = AudioSystem.getClip();
            clip.addLineListener(new LineListener() {
             public void update(LineEvent e) {
                 LineEvent.Type type = e.getType();
                 if(type == type.STOP) {
                     clip.close();
                     removeClip(clip);
                 }
             }
            });
            clip.open(format, data, 0, data.length);
            return clip;
        } catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
    public void play() {
        Clip clip = createClip();
        addClip(clip);
        clip.start();
    }
    public void loop(int n) {
        Clip clip = createClip();
        addClip(clip);
        clip.loop(n);
    }
    public void loopForever() {
        Clip clip = createClip();
        addClip(clip);
        clip.loop(Clip.LOOP_CONTINUOUSLY);
    }
    public void stop() {
        machineGunActive = false;
        stopAllClips();
    }
    public void stopMachineGun() {
        machineGunActive = false;
    }
    public void playMachineGun(final int delay) {
        machineGunActive = true;
        new Thread() {
            public void run() {
                while(machineGunActive) {
                    play();
                    try {
                        Thread.sleep(delay);
                    } catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

Try this
1  
2  
3  
4  
AudioData data = new AudioData("shoot.wav");
data.playMachineGun(100);
Thread.sleep(2000);
data.stopMachineGun();

The difference between stop() and stopMachineGun() is that stop() will stop playing immediately, while stopMachineGun() allows the sound to fade out naturally.
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #25 - Posted 2013-10-05 19:18:45 »

The clip class has a loop and a stop method. But after you call stop the clip will get closed automatically so don't call start or loop on it after you stopped it.
For a machine gun effect you need a delay. You probably have to create another Thread for that. You can communicate with that Thread through a volatile variable.

Why would I need a delay? The sound is played when the fire() method is called on the gun, which already has a cooldown.
Anyway, I don't think you get what I'm saying. Whereas most weapons in my game have a cooldown before they can be used again, the Flamethrower just fires particles continuously as long as the player holds down the fire button. The problem being that the sound is played when it's fired, and since it's being looped, that means dozens, or even hundreds, of the Flamethrower sound clip are being created and looped all at once. I tried making it so it wasn't looped and just relied on the fire() event from the weapon to play the sound, but there are A LOT of particles coming out of this thing, so it still sounds like a machine gun of noise on crack. So what I'm asking is... how can I make it so the sound only plays if there is no Flamethrower sound currently being played? The play() method doesn't return the clip that is generated to play the audio, so I can't use that to determine if it's running.

I thought of using a boolean, but how would I determine when the last clip generated by the play() method stops so I can generate a new one?
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #26 - Posted 2013-10-05 19:28:38 »

Wait, so you want to play the flamethrower sound only if it's not playing anymore. Can't you use loop()? Then it will automatically start playing again once it stopped.
Also, I edited my previous post. Take a look at the new code.
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #27 - Posted 2013-10-05 19:39:02 »

Wait, so you want to play the flamethrower sound only if it's not playing anymore. Can't you use loop()? Then it will automatically start playing again once it stopped.
Also, I edited my previous post. Take a look at the new code.

You're missing the point. I already said that when it was looped, I couldn't stop it because there is no way to stop the Clip once it has been created and started because that Clip only exists in the scope of the play() method in my sounds class. I would need access to that clip in my framework so I can call the stop() method on it under certain conditions. But simply returning the Clip from the play() method wouldn't work because it would return to the weapon class. So unless I made a global variable for the Flamethrower Clip that changed whenever it's played, which I think would be a hacky solution, but a bad idea, I can't get it to stop when the player lets go of the fire button.

Also, forget the "machine gun"... that weapon is not the issue, and works just fine. It's the Flamethrower that's the problem, because there is no delay (or rather, only 20 ms delay) in between creation of each particle, which causes the sound to be played very rapidly, causing the noise in question.

EDIT: Just looked at your modified solution, and that MAY work, but I still feel as if there might be a better option. I'll try it and get back to you.
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #28 - Posted 2013-10-05 19:46:42 »

If there is only a 20ms delay I guess it's best to use loop(). And then call stop(), once the fire button has been released. You can do that by either making the flamethrower AudioData object globally accessible or by adding a MouseListener when the fire button is pressed. You can add as many MouseListeners to your game as you want. That listener will then stop the sound and remove itself.
Offline packetpirate

Junior Devvie


Medals: 1
Projects: 1



« Reply #29 - Posted 2013-10-05 21:31:17 »

If there is only a 20ms delay I guess it's best to use loop(). And then call stop(), once the fire button has been released. You can do that by either making the flamethrower AudioData object globally accessible or by adding a MouseListener when the fire button is pressed. You can add as many MouseListeners to your game as you want. That listener will then stop the sound and remove itself.

Haha, oh wow... my new solution works perfectly for the Flamethrower, but now no other sounds will play... will I ever get this right?
Here's the code I have now for the play() method in the AudioData class.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
public void play(final double gain, final boolean smoothLoop) {
    final Clip clip = createClip();
    FloatControl gainControl = (FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);
    float dB = (float)(Math.log(gain) / Math.log(10.0) * 20.0);
    gainControl.setValue(dB);
    active = true;
    new Thread(new Runnable() {
        @Override
        public void run() {
            if(!smoothLoop) clip.start();
            else clip.loop(Clip.LOOP_CONTINUOUSLY);
            while(active) {
                if(!clip.isRunning() && !smoothLoop) active = false;
            }
            clip.stop();
        }
    }).start();
}


Basically, I had a boolean set on each AudioData called active that determines if the sound is currently playing. It's set to true right before the clip starts playing. It then starts the clip and goes into a loop saying "if active, check to see if it's running, and if it's looped. If it's no longer active and isn't looped, set active to false". This ensures that when a clip is finished with playback, it automatically sets active to false and then stops the clip. The looped parameter is provided by the Sounds class. So basically, for normal clips that aren't looped, it sets active to true, then starts a thread in which it constantly checks that clip to see if it's still running. If it's no longer running, active is set to false, and then the clip stops. But for looped clips, like the Flamethrower, it will never set the active boolean to false, meaning it will continue to allow it to loop, never stopping. I then put in the setActive() method to set it to inactive when the mouse is released (among other conditions). So now the Flamethrower starts and stops like it's supposed to, but none of the other clips even play.

What am I doing wrong?

EDIT: Nevermind, I just had to check if the clip was looped and only call stop() if it was looped.
Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

toopeicgaming1999 (57 views)
2014-11-26 15:22:04

toopeicgaming1999 (50 views)
2014-11-26 15:20:36

toopeicgaming1999 (10 views)
2014-11-26 15:20:08

SHC (24 views)
2014-11-25 12:00:59

SHC (24 views)
2014-11-25 11:53:45

Norakomi (26 views)
2014-11-25 11:26:43

Gibbo3771 (24 views)
2014-11-24 19:59:16

trollwarrior1 (37 views)
2014-11-22 12:13:56

xFryIx (75 views)
2014-11-13 12:34:49

digdugdiggy (52 views)
2014-11-12 21:11:50
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!