5

I am using the following code for loading a clip (javax.sound.sampled.Clip)

def createClip(file:String):Clip = {
    val clip = AudioSystem.getClip()
    clip.open(AudioSystem.getAudioInputStream(new File(s"sounds/$file.wav")))
    clip
}

Its written in Scala but I guess anyone not familiar with scala but java will see that its basically the same, as one would do with java.

My attempt was to make it effortless to play a sound like this

createClip("example_WAV_File").start()

Since I will create a new Clip, each time I call a sound file, I thought there shouldn't be any problems concerning rewinding a clip etc. However in an actual Application (a Game), I used this library, to handle the sounds resulting of a Punch and all those stuff. Now here is the thing, when (lets say e.g.) punching several times, some when I will get

Exception in thread "Thread-58" javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian not supported.

I once ran into a similar Error but that was about the bit encoding being above 16-bits. Since my file has 16-bit encoding I have no clue what this could be...

As said above, the Error only occurs when several clips are played. I figured, that this could indicate a memory problem. I naively thought, that the garbage collector would do all the work for me, as the clip would have lost any reference when leaving its scope.

My initial solution was, to hold a reference to any sound I am going to play, and only rewind it when another instance requires an already played sound. This solution would never break but it was not really able to play the same clip two times. Thats why I switched to loading a clip several times when required.

I figured, that I probably should use a close() method on the clip.

val clip = createClip("example_WAV_file")
clip.start()
clip.close()

This actually solved my Problem, enhancing my guess, that it is probably memory related, but also, now the clip would stop immediately after starting it.

Do I really need to create a thread for each clip, waiting for the clip.getMicrosecondLength() time to close it? That sounds rather crappy.

Only option left -> I am using the sound API really really wrongly, even though I thought, that I was using it just the way some tutorials showed me. So where is my particular mistake? Or what is the solution to being able to play the same clip two times, at the same time?

Julian
  • 525
  • 5
  • 19
  • Did you ever solve this problem? – Nelson Dec 12 '18 at 21:10
  • I don't think so, sry. I just gave my old project a lookup. I created each clip exactly once and set its frameposition whenever I played it. As I mentioned in my post, I am already quite sure, that its a memory problem, so chances might be, it could work if you declare the file as read only, when loading it. That way two seperate clips, loading the same data might get along. – Julian Dec 13 '18 at 08:21
  • please ignore whatever I said about threads at that time tho :D – Julian Dec 13 '18 at 08:23

1 Answers1

0

When you open() a Clip, you basically allocate a native audio line, i.e. a limited resource. This means, you must close it, when you are done with it. An easy way of doing this is via a LineListener.

For example:

Clip clip = createClip("some.wav");
clip.addLineListener(event -> {
    if (LineEvent.Type.STOP.equals(event.getType()) {
        event.getLine().close();
    }
});
clip.start();

This ensures that whenever a line is stopped (which happens when a clip has finished playing), the line is also closed and the used native resources are freed.

Note that this means that you cannot simply play that same clip again, but that you must create/open a new clip.

Hendrik
  • 5,085
  • 24
  • 56