how can I wait for a java sound clip to finish playing back?
Asked Answered
W

6

10

I am using the following code to play a sound file using the java sound API.

    Clip clip = AudioSystem.getClip();
    AudioInputStream inputStream = AudioSystem.getAudioInputStream(stream);
    clip.open(inputStream);
    clip.start();

The method call to the Clip.start() method returns immediately, and the system playbacks the sound file in a background thread. I want my method to pause until the playback has completed.

Is there any nice way to do it?

EDIT: For everyone interested in my final solution, based on the answer from Uri, I used the code below:

private final BlockingQueue<URL> queue = new ArrayBlockingQueue<URL>(1);

public void playSoundStream(InputStream stream) {
    Clip clip = AudioSystem.getClip();
    AudioInputStream inputStream = AudioSystem.getAudioInputStream(stream);
    clip.open(inputStream);
    clip.start();
    LineListener listener = new LineListener() {
        public void update(LineEvent event) {
                if (event.getType() != Type.STOP) {
                    return;
                }

                try {
                    queue.take();
                } catch (InterruptedException e) {
                    //ignore this
                }
        }
    };
clip.addLineListener(listener );
}
Wot answered 17/2, 2009 at 17:36 Comment(0)
N
5

A sound clip is a type or Line and therefore supports Line listeners.

If you use addLineListener, you should get events when play starts and stops; if you're not in a loop, you should get a stop when the clip ends. However, as with any events, there might be a lag before the actual end of playback and the stopping.

Making the method wait is slightly trickier. You can either busy-wait on it (not a good idea) or use other synchronization mechanisms. I think there is a pattern (not sure about it) for waiting on a long operation to throw a completion event, but that's a general question you may want to post separately to SO.

No answered 17/2, 2009 at 17:51 Comment(0)
B
10

I prefer this way in Java 8:

CountDownLatch syncLatch = new CountDownLatch(1);

try (AudioInputStream stream = AudioSystem.getAudioInputStream(inStream)) {
  Clip clip = AudioSystem.getClip();

  // Listener which allow method return once sound is completed
  clip.addLineListener(e -> {
    if (e.getType() == LineEvent.Type.STOP) {
      syncLatch.countDown();
    }
  });

  clip.open(stream);
  clip.start();
}

syncLatch.await();
Buiron answered 13/10, 2017 at 7:38 Comment(0)
V
8

you can just put this code instead:

assume your clip1 is playing and you want a clip2 to be played right after that, you can:

clip1.start();
while(clip1.getMicrosecondLength() != clip1.getMicrosecondPosition())
{
}
clip2.loop(some int here);

and, to make this work without delaying your main task (I say this because the while loop makes the work wait for clip1 to finish, no matter what the next work is...) you can make a new thread in the point where you want it to happen, and just put the code in its run() method... GOOD LUCK!

Venatic answered 25/6, 2013 at 15:12 Comment(2)
This worked like a charm for a simple text-to-speech application I was working on.Abhorrence
I doubt this one is a processor-resource-hungry technique.Buiron
N
5

A sound clip is a type or Line and therefore supports Line listeners.

If you use addLineListener, you should get events when play starts and stops; if you're not in a loop, you should get a stop when the clip ends. However, as with any events, there might be a lag before the actual end of playback and the stopping.

Making the method wait is slightly trickier. You can either busy-wait on it (not a good idea) or use other synchronization mechanisms. I think there is a pattern (not sure about it) for waiting on a long operation to throw a completion event, but that's a general question you may want to post separately to SO.

No answered 17/2, 2009 at 17:51 Comment(0)
T
1

I have used a wait/notify to implement this. Just for your reference.

        final Clip clip = AudioSystem.getClip(mixerInfo);
        clip.addLineListener(new LineListener(){

            @Override
            public void update(LineEvent e) {
                if (e.getType()==LineEvent.Type.STOP){
                    synchronized(clip){
                        clip.notifyAll();
                    }
                    System.err.println("STOP!");
                }

            }

        });
        clip.open(as);
        clip.start();
        while (true){
            synchronized(clip){
                clip.wait();
            }
            if (!clip.isRunning()){
                break;
            }
        }
        clip.drain();
        clip.stop();
        clip.close();
Theotokos answered 11/7, 2017 at 16:39 Comment(0)
C
0

As of JDK8, Clip's listener is kind of buggy (i.e. it can happen that it fires the Type.STOP twice for a single reproduction of the audio (.start()).

I would therefore use something like:

Clip[] myBunchOfClipsToPlaySequentially=new Clip[A_BUNCH];
//
// [load actual clips...]
// [...]
//
for(Clip c: myBunchOfClipsToPlaySequentially)
    while(c.getFramePosition()<c.getFrameLength())
        Thread.yield(); // Note that if we simply put a NO-OP instead (like a
                        // semicolon or a {}), we make our CPU
                        // cry). Thread.yield() allows for almost 0% CPU consumption.

In addition, you could take a look to the new AudioClip class that JFX8 provides (has some nifty methods to fiddle with)

Cavalierly answered 6/10, 2016 at 17:28 Comment(0)
P
0

Not sure if I can speak for anyone else and this thread is very old. But I used the clip library to play my .wav file and then just slept the thread for the amount of time the sound file runs for:

Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
System.out.println("Clip Started");
Thread.sleep(162000); //Length of time in milliseconds
System.out.println("Clip finished");

Worked like a charm for me and I was monitoring my processor whilst it was going on, the whole program used maximum 4%!

Planetstruck answered 24/2, 2021 at 13:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.