2009-02-17 4 views
6

Ich verwende den folgenden Code, um eine Sounddatei mit der Java-Sound-API abzuspielen.Wie kann ich auf die Wiedergabe eines Java-Soundclips warten?

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

Der Methodenaufruf an das Clip.start() Methode gibt sofort, und das System Playbacks die Tondatei in einem Hintergrund-Thread. Ich möchte, dass meine Methode anhält, bis die Wiedergabe abgeschlossen ist.

Gibt es einen schönen Weg, es zu tun?

EDIT: Für alle, die sich in meine endgültige Lösung, auf die Antwort von Uri basiert, habe ich den folgenden Code:

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); 
} 

Antwort

4

ein Soundclip ein Typ oder Line ist und unterstützt daher Line-Hörer.

Wenn Sie addLineListener verwenden, sollten Sie Ereignisse erhalten, wenn die Wiedergabe startet und stoppt; Wenn Sie nicht in einer Schleife sind, sollten Sie aufhören, wenn der Clip endet. Wie bei allen Ereignissen kann es jedoch zu Verzögerungen vor dem eigentlichen Ende der Wiedergabe und dem Anhalten kommen.

Das Warten der Methode ist ein wenig komplizierter. Sie können entweder busy-warten (keine gute Idee) oder andere Synchronisationsmechanismen verwenden. Ich denke, es gibt ein Muster (nicht sicher) für das Warten auf eine lange Operation, um ein Abschluss-Event zu werfen, aber das ist eine allgemeine Frage, die Sie vielleicht separat zu SO veröffentlichen möchten.

6

können Sie einfach diesen Code setzen statt:

nehmen Ihre clip1 spielt, und Sie wollen ein clip2 direkt danach gespielt werden, können Sie:

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

und diese Arbeit zu machen, ohne Verzögerung Ihrer Hauptaufgabe (Ich sage dies, weil die While-Schleife die Arbeit warten auf Clip1 zu beenden, egal was die nächste Arbeit ist ...) Sie können einen neuen Thread in dem Punkt, wo Sie es wollen, und einfach machen setze den Code in seine run() Methode ... GOOD GLUCK!

+0

Das funktionierte wie ein Zauber für eine einfache Text-to-Speech-Anwendung, die ich gerade arbeite. – progyammer

+0

Ich bezweifle, dass dies eine Prozessor-Ressource-hungrige Technik ist. – sancho21

0

Ab JDK8, Clip ‚s Hörer Art Buggy ist (dh es kann passieren, dass es die Type.STOP zweimal für eine einzelne Wiedergabe des Audio (.start()) abfeuert.

Ich möchte daher etwas verwenden wie :

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. 

Darüber hinaus könnten Sie einen Blick auf die neue AudioClip Klasse nehmen, die JFX8 bietet (hat einige nette Methoden mit Geige)

0

ich eine Wartezeit benutzt haben/notify dies zu implementieren Nur fo. Deine Referenz.

 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(); 
1

Ich bevorzuge diese Art und Weise 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();