Reproducir múltiples clips de sonido usando objetos Clip

Estoy desarrollando un progtwig que tiene numerosos objetos JButton, y quiero que cada uno se corresponda con su propio archivo .wav . Además, quiero que los sonidos funcionen de manera tal que puedan superponerse con los sonidos de otros botones, pero no se superponen entre sí (al hacer clic en un botón mientras se reproduce el sonido, se reiniciará el sonido).

Intenté usar un solo objeto Clip, pero tuve problemas para lograr lo que dije anteriormente. Como resultado, recurrí a declarar un nuevo objeto Clip para cada botón, pero tengo la sensación de que esta es una solución bastante ineficiente para mi problema.

¿Cómo puedo lograr lo que dije en el primer párrafo de la manera más eficiente?

Hay un par de formas en que puede lograr esto, pero la idea básica es, desea registrar un LineListener en un Clip y monitor para el evento LineEvent.Type.STOP y volver a habilitar el botón

Por ejemplo. Esto busca todos los archivos .wav en un directorio determinado y crea un botón para cada uno. Cuando se hace clic, el botón (o más importante, la Action subyacente) se desactiva y se reproduce el audio. Cuando se STOP , la Action (y el botón por extensión) se vuelve a habilitar.

El Sound API puede reproducir múltiples sonidos simultáneamente de todos modos

 import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Test { public static void main(String[] args) { new Test(); } public Test() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { public TestPane() { File[] musicFiles = new File("a directory somewhere").listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getName().toLowerCase().endsWith(".wav"); } }); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.fill = GridBagConstraints.HORIZONTAL; for (File music : musicFiles) { try { JButton btn = new JButton(new AudioAction(music.getName(), music.toURI().toURL())); add(btn, gbc); } catch (MalformedURLException ex) { ex.printStackTrace(); } } } } public class AudioAction extends AbstractAction { private URL audio; public AudioAction(String name, URL audioSource) { super(name); this.audio = audioSource; } public URL getAudioSource() { return audio; } @Override public void actionPerformed(ActionEvent e) { setEnabled(false); try (InputStream is = getAudioSource().openStream()) { AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(is); Clip play = AudioSystem.getClip(); play.addLineListener(new LineListener() { @Override public void update(LineEvent event) { System.out.println(event.getFramePosition()); if (event.getType().equals(LineEvent.Type.STOP)) { setEnabled(true); } } }); play.open(audioInputStream); play.start(); } catch (IOException | LineUnavailableException | UnsupportedAudioFileException exp) { exp.printStackTrace(); } } } } 

nb: Intenté usar Clip#drain (en un hilo de fondo), pero solo funcionó para el primer clip, los clips subsecuentes básicamente salteados sobre el método, por lo tanto, el motivo por el que fui para el LineListener

Ahora con una mejor gestión de recursos

 import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Test { public static void main(String[] args) { new Test(); } public Test() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { public TestPane() { File[] musicFiles = new File("...").listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getName().toLowerCase().endsWith(".wav"); } }); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.fill = GridBagConstraints.HORIZONTAL; for (File music : musicFiles) { try { JButton btn = new JButton(new AudioAction(music.getName(), music.toURI().toURL())); add(btn, gbc); } catch (MalformedURLException exp) { exp.printStackTrace(); } } } } public class AudioAction extends AbstractAction { private AudioPlayer player; public AudioAction(String name, URL audioSource) { super(name); player = new AudioPlayer(audioSource); } @Override public void actionPerformed(ActionEvent e) { if (player.isPlaying()) { player.stop(); } else { try { player.play(); } catch (IOException | LineUnavailableException | UnsupportedAudioFileException ex) { ex.printStackTrace(); } } } } public class AudioPlayer { private Clip clip; private URL url; public AudioPlayer(URL url) { this.url = url; } public boolean isPlaying() { return clip != null && clip.isRunning(); } protected void open() throws IOException, LineUnavailableException, UnsupportedAudioFileException { clip = AudioSystem.getClip(); clip.open(AudioSystem.getAudioInputStream(url.openStream())); } public void play() throws IOException, LineUnavailableException, UnsupportedAudioFileException { if (clip == null || !clip.isRunning()) { open(); clip.setFramePosition(0); clip.start(); } } public void stop() { if (clip != null && clip.isRunning()) { clip.stop(); clip.flush(); dispose(); } } public void dispose() { try { clip.close(); } finally { clip = null; } } } } 

El clip por botón debería estar bien. Cuando el usuario haga clic en el botón, ejecute esto para reiniciar el clip:

 sound.stop(); sound.setFramePosition(0);// set the location to the start of the file sound.play();// restart your sound