Reputation: 1359
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?
Upvotes: 2
Views: 946
Reputation: 3324
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.
Upvotes: 1
Reputation: 2067
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.
Upvotes: 0
Reputation: 36
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.
Upvotes: 1