Play multiple audio clips using Clip objects

I am developing a program with many JButton objects and I want each one to correspond to its own file .wav

. Also, I want the sounds to work in such a way that they can overlap with other button sounds, but cannot overlap themselves (pressing the button while the sound is playing will restart the sound).

I tried using a single Clip object, but I found it difficult to accomplish what I said above. As a result, I have resorted to declaring a new Clip object for each button, but I have a feeling that this is a rather ineffective solution to my problem.

How can I accomplish what I have outlined in the first paragraph in the most efficient way?

+1


source to share


2 answers


There are several ways you could achieve this, but the basic idea is that you want to register LineListener

in Clip

and track the event LineEvent.Type.STOP

and reuse the button

For example. It searches for all files .wav

in a given directory and creates a button for each one. When the button is pressed (or more importantly, the main one Action

) is muted and the sound is played. When it's STOP

s, Action

(and the expand button) turns on again.

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

(on a background thread) but it only worked for the first clip, subsequent clips mostly skipped the method, so the reason I went forLineListener

Now with 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(), 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;
            }
        }

    }

}

      

+4


source


One clip per button should be fine. When the user clicks the button, run this to restart the clip:



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

      

0


source







All Articles