Java – play multiple sound clips using clip objects
I'm developing a program that contains a large number of JButton objects. I hope each object has its own Wav file In addition, I want sounds to work in such a way that they can overlap the sounds of other buttons, but it cannot overlap itself (clicking the button when playing the sound will restart the sound)
I tried to use a clip object, but I couldn't finish what I said above As a result, I use each button to declare a new clip object, but I think it is a rather inefficient solution to my problem
How can I complete what I said in the first paragraph in the most effective way?
Solution
There are several ways to do this, but the basic idea is that you want to register linelistener with clip and monitor lineevent Type. Stop event and re enable the button
For example This will find all in the given directory Wav file and create a button for each file When clicked, the button (or, more importantly, the basic operation) is disabled and the audio is played When it stops, re enable action (and extension buttons)
In any case, the sound API can play multiple sounds at the same time
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: I tried to use clip#drain (in the background thread), but it only applies to the first clip, and subsequent clips basically skip the method, so I chose linelistener
Now there is better resource management
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(),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,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; } } } }