【问题标题】:Playing multiple sound clips using Clip objects使用 Clip 对象播放多个声音片段
【发布时间】:2015-04-23 23:40:32
【问题描述】:

我正在开发一个包含大量 JButton 对象的程序,我希望每个对象都对应于它自己的 .wav 文件。另外,我希望声音能够与其他按钮的声音重叠,但不能与自身重叠(在播放声音时单击按钮将重新启动声音)。

我尝试使用单个 Clip 对象,但无法完成上述操作。结果,我求助于为每个按钮声明一个新的 Clip 对象,但我觉得这对我的问题来说是一个相当低效的解决方案。

我怎样才能以最有效的方式完成我在第一段中所说的内容?

【问题讨论】:

    标签: java swing audio javasound clip


    【解决方案1】:

    您可以通过多种方式实现此目的,但基本思想是,您希望将 LineListener 注册到 Clip 并监视 LineEvent.Type.STOP 事件并重新启用按钮

    例如。这将查找给定目录中的所有.wav 文件,并为每个文件创建一个按钮。单击时,按钮(或更重要的是,底层Action)被禁用并播放音频。当它STOPs 时,Action(以及扩展的按钮)被重新启用。

    Sound API 无论如何都可以同时播放多个声音

    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:我尝试使用Clip#drain(在后台线程中),但它只适用于第一个剪辑,后续剪辑基本上跳过了该方法,因此我选择了LineListener

    现在有了更好的资源管理

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

    【讨论】:

    • 理论上,Clip 不能在外部管理并没有真正的原因,但是您正在进一步增加复杂性,使用这种方法,您有一个封装工作单元,这使其自我管理。通常我会创建一个“音频”类,它具有播放/停止/恢复功能,允许您创建多个音频文件,这些文件都由单个类管理。在这个例子中,不太可能显着增加内存使用量,因为剪辑变得“无法访问”,它将符合 GC 条件,但这是一个关于如何让它播放的示例;)
    • 要“重启”Clip,首先需要维护Clip的类实例字段,当actionPerformed方法被触发时,你会检查@ 987654336@是否为null,如果是则创建,如果不是,则在其上调用stop,然后再次调用start
    • 您是否打开了多个音频流,或者只有一个音频流也会出现?
    • 我已经修改了示例(或添加了一个新示例),当您停止它时,它基本上会关闭并遵循 Clip 实例。由于我只有大约 3 个波形文件,因此很难在很大程度上进行测试
    • 我认为最好在需要时延迟加载它们,并在不需要时删除它们。像这样使用延迟加载可能会遇到的唯一“重大”性能问题是每次加载音频都需要时间,但我在测试时没有看到任何明显的延迟
    【解决方案2】:

    每个按钮一个剪辑应该没问题。当用户单击按钮时,运行此按钮以重新启动剪辑:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-20
      • 1970-01-01
      • 2016-11-19
      • 1970-01-01
      • 1970-01-01
      • 2017-09-28
      相关资源
      最近更新 更多