2

I have the following code to play my sounds in a game:

protected void playSound(final String sound, final int playTime){
        try {
            sounds.get(sound).open();
        } 
        catch(Exception e) {
            System.out.println(e);
        }
        new Thread(new Runnable() {
            public void run() {
                Clip actualClip = sounds.get(sound);
                actualClip.setFramePosition(0);
                if(playTime < 0){
                    actualClip.loop(Clip.LOOP_CONTINUOUSLY);
                }
                else{
                    actualClip.loop(playTime - 1);
                }
            }
        }).start();
    }

The sounds are saved in an hashmap:

private HashMap<String, Clip> sounds;

When i play two different sounds "in the same time" (with a difference of 1 ms ;) ), they are playing parallel to each other ; so i can hear two sounds in the same time. Looks like this:

playSound("sound1", 1);
playSound("sound2", 1);

But when i try to play the same sound twice, it doesnt work:

playSound("sound1", 1);
//here its waiting in my programm
playSound("sound1", 1);

The thing is, i want to add an "death" sound - but two mobs can also die in the same time, or just one second before another. When this happens either nothing happens, or the sound just plays one time.

Why? I think im creating a new AudioClip of the same file, in an own thread? So why it isnt working?

keyser
  • 18,829
  • 16
  • 59
  • 101
T_01
  • 1,256
  • 3
  • 16
  • 35
  • 1
    Question have to be concise, and their contents must lack remarks such as *Good day!* or *Thank you very much!*. That may be the reason someone edited out your question. You can use the comments for those. – Georgian Aug 10 '13 at 16:25
  • Minor suggestion, you should use the interface `Map` instead of using the implementation! – Marc-Andre Aug 10 '13 at 16:28
  • 2
    @GGrec "their contents must lack remarks such as Good day! or Thank you very much!." Is it written somewhere in SO rules? If you have come across then pls share the link. – rahulserver Aug 10 '13 at 16:28
  • @rahulserver I've seen it in the previous FAQ, and around meta. [Here's](http://meta.stackexchange.com/a/97137/182213) one. – keyser Aug 10 '13 at 16:31
  • @marcAndre is there any special reason for this? But okay ill change this. Knowone any idea why i have this problem? :/ – T_01 Aug 10 '13 at 16:32
  • @Keyser it would be better if I get some link from SO.The link u have given does not bear any concrete link from SO rules. I think it must be there otherwise there is no harm in including Thanx as its courtesy to me :) – rahulserver Aug 10 '13 at 16:37
  • 1) For better help sooner, post an [SSCCE](http://sscce.org/). 2) But it seems you are trying to get the same `Clip` instance to play 'twice in parallel'. That won't work. Create 2 clips with the same data. – Andrew Thompson Aug 10 '13 at 16:39
  • @rahulserver Is is **noise.** (And it is spelled 'thanks'. A better way to show your appreciation than adding platitudes is to spell words properly.) – Andrew Thompson Aug 10 '13 at 16:41
  • sounds.get(sound).open(); is wrong since open() method takes AudioInputStream stream as an argument.See documentation http://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/Clip.html#open(javax.sound.sampled.AudioInputStream) Does the code compile? – rahulserver Aug 10 '13 at 16:52
  • Yes it runs like this. – T_01 Aug 10 '13 at 16:57
  • @T_01 .. Do you get any exception on second time when you try to call `playSound("Sound1",1)` ? check your console as you are just printing it using `System.out.println(e)` .. – Kinaan Khan Sherwani Aug 10 '13 at 17:00
  • No, there arent any exceptions... – T_01 Aug 10 '13 at 17:02
  • if you read the documentation of `Clip.open` method here http://docs.oracle.com/javase/1.5.0/docs/api/javax/sound/sampled/Clip.html , it says > It Opens the clip, meaning that it should acquire any required system resources and become operational. The clip is opened with the format and audio data indicated. If this operation succeeds, the line is marked as open and an OPEN event is dispatched to the line's listeners. > Invoking this method on a line which is already open is illegal and may result in an IllegalStateException. – Kinaan Khan Sherwani Aug 10 '13 at 17:05
  • So it isnt possible to play the same sound twice? That cannot be... – T_01 Aug 10 '13 at 18:14
  • @T_01 There is goods reason ;) read this question to have a bit of information http://stackoverflow.com/questions/147468/why-should-the-interface-for-a-java-class-be-prefered – Marc-Andre Aug 10 '13 at 18:46

3 Answers3

1

First off, can you debug the method to show if 'sound' was played to make sure it's just not overlapping so you can't hear if the identical sounds were actually played at the same time?

What I think might be happening here though is that while playing the SAME clip, one of the threads will be trying to access the same data that the other thread is currently accessing, causing the error you're describing. You might want to look into synchronization in Java.

Ben
  • 36
  • 2
  • Synchronisation.... i didnt know about that anything, but i know a method can be synchronizes. Is this that? – T_01 Aug 10 '13 at 18:13
1

The reason is very simple: read the Javadoc of Clip.play(). It clearly states that whenever playing is ongoing, the method won't do anything. Javadoc of setFramePosition says "When the clip begins playing the next time, it will start by playing the frame at this position." Thus, it doesn't make any guarantee that the frame pointer moves immediately at request time; it only says the pointer will be where you want it to be, whenever the clip is played the next time.

Clip uses an InputStream internally, which will point to the current audio frame whilst playing. What should that pointer do if two requests are made to Clip.play()? The developer chose to do nothing, which is a good design choice as it is much more preferred than playing random audio frames, due to parallel access to the same input stream.

Solution: for each request to play audio, construct a new Clip and feed it a new InputStream (wrapped into an AudioInputStream). It also reduces extra work such as resetting the frame pointer because you use a Clip to play only once.

To make this a lot more efficient, read the audio file once and store it into a byte[]. Then, construct a ByteArrayInputStream using that byte[] and feed that into a new clip. This way you don't need disk access as you read the audio data directly from memory.

byte[] arr = ...;                 // audio data (e.g. read from disk)
Clip clip = AudioSystem.getClip();
ByteArrayInputStream bis = new ByteArrayInputStream(arr);
AudioInputStream ais = AudioSystem.getAudioInputStream(bis);
clip.open(ais);
clip.start();

Beware that resources also require cleanup, which is not shown in the example.

Timmos
  • 3,215
  • 2
  • 32
  • 40
0

When you play your same sound twice is the volume louder? Playing the exact sound at the exact same time would be the same as just doubling the amplitude of a single sound. I think with two identical sounds played that close together you may not be able to hear the individual sounds when listening.

Ehz
  • 2,027
  • 1
  • 12
  • 11
  • But that also happens when a monster dies and 1 second another monster dies... i think i could here this difference. – T_01 Aug 11 '13 at 15:08