Java 使用剪辑对象播放多个声音剪辑

Java 使用剪辑对象播放多个声音剪辑,java,swing,audio,javasound,clip,Java,Swing,Audio,Javasound,Clip,我正在开发一个有许多JButton对象的程序,我希望每个对象都对应于它自己的.wav文件。此外,我希望声音能够与其他按钮的声音重叠,但不能与自身重叠。在按钮声音播放时单击按钮将重新启动声音 我试着使用一个剪辑对象,但我很难实现我上面所说的。结果,我为每个按钮声明了一个新的Clip对象,但我觉得这是一个相当低效的解决方案 如何才能以最有效的方式完成第一段中所述的内容?每个按钮一个剪辑就可以了。当用户单击按钮时,运行此命令以重新启动剪辑: sound.stop(); sound.setFramePo

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

我试着使用一个剪辑对象,但我很难实现我上面所说的。结果,我为每个按钮声明了一个新的Clip对象,但我觉得这是一个相当低效的解决方案


如何才能以最有效的方式完成第一段中所述的内容?

每个按钮一个剪辑就可以了。当用户单击按钮时,运行此命令以重新启动剪辑:

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

每个按钮一个夹子就可以了。当用户单击按钮时,运行此命令以重新启动剪辑:

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

有两种方法可以实现这一点,但基本思想是,您希望将LineListener注册到剪辑并监视LineEvent.Type.STOP事件,然后重新启用按钮

比如说。这将查找给定目录中的所有.wav文件,并为每个文件创建一个按钮。单击按钮或更重要的是,基本操作将被禁用并播放音频。当它停止时,将重新启用操作和扩展按钮

声音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();
            }
        }

    }

}
注意:我尝试在背景线程中使用Clipdrain,但它只对第一个剪辑有效,后续剪辑基本上跳过了该方法,因此我选择LineListener

现在有了更好的资源管理
有两种方法可以实现这一点,但基本思想是,您希望将LineListener注册到剪辑并监视LineEvent.Type.STOP事件,然后重新启用按钮

比如说。这将查找给定目录中的所有.wav文件,并为每个文件创建一个按钮。单击按钮或更重要的是,基本操作将被禁用并播放音频。当它停止时,将重新启用操作和扩展按钮

声音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();
            }
        }

    }

}
注意:我尝试在背景线程中使用Clipdrain,但它只对第一个剪辑有效,后续剪辑基本上跳过了该方法,因此我选择LineListener

现在有了更好的资源管理
理论上,没有真正的理由不能从外部管理剪辑,但是您进一步增加了复杂性,使用这种方法,您有一个封装工作单元,这使得它可以自我管理。通常我制作一个音频类,它具有播放/停止/恢复功能,允许您创建多个音频文件,这些文件都由一个类管理。在本例中,当剪辑变得不可访问时,不太可能显著增加内存使用量,因为它将符合GC的条件,但这是一个关于如何使其播放的示例;要重新启动剪辑,首先需要维护剪辑的类实例字段,当触发actionPerformed方法时,检查剪辑是否为null,如果为null,则创建,调用stop,然后再次调用start。您是否打开了多个音频流,或者如果只有一个音频流,它是否也会出现?我修改了示例或添加了一个新的示例,它基本上会在停止时关闭并遵从Clip实例。因为我只有大约3个wave文件,所以很难在很大程度上进行测试。我认为最好在需要的时候懒洋洋地加载它们,不需要的时候就把它们扔掉。使用像这样的延迟加载可能会带来的唯一显著性能问题是每次加载音频都可能需要时间,但我在测试时没有看到任何明显的延迟。理论上,没有真正的理由不能从外部管理剪辑,但通过这种方法,您会进一步增加复杂性,您有一个单独的封装工作单元,这使得它可以自我管理。通常我制作一个音频类,它具有播放/停止/恢复功能,允许您创建多个音频文件,这些文件都由一个类管理。在本例中,当剪辑变得不可访问时,不太可能显著增加内存使用量,因为它将符合GC的条件,但这是一个关于如何使其播放的示例;要重新启动剪辑,首先需要维护剪辑的类实例字段,当触发actionPerformed方法时,检查剪辑是否为null,如果为null,则创建,调用stop,然后再次调用start。您是否打开了多个音频流,或者如果只有一个音频流,它是否也会出现?我修改了示例或添加了一个新的示例,它基本上会在停止时关闭并遵从Clip实例。因为我只有大约3波fi
les,很难在很大程度上进行测试我认为最好是在需要的时候懒洋洋地加载它们,在不需要的时候把它们扔掉。使用像这样的延迟加载,您可能会遇到的唯一重要性能问题是每次加载音频都可能需要时间,但我在测试时没有看到任何明显的延迟