使用 Clip 对象播放多个声音片段
Playing multiple sound clips using Clip objects
我正在开发一个包含大量 JButton 对象的程序,我希望每个对象都对应于它自己的 .wav
文件。另外,我希望声音能够与其他按钮的声音重叠,但不能与自身重叠(在播放声音时单击按钮将重新启动声音)。
我尝试使用单个 Clip 对象,但我无法完成上述内容。因此,我求助于为每个按钮声明一个新的 Clip 对象,但我觉得这是解决我的问题的一个相当低效的解决方案。
我怎样才能以最有效的方式完成我在第一段中所说的内容?
每个按钮一个剪辑应该没问题。当用户单击按钮 运行 以重新启动剪辑时:
sound.stop();
sound.setFramePosition(0);// set the location to the start of the file
sound.play();// restart your sound
有几种方法可以实现这一点,但基本思想是,您想要将 LineListener
注册到 Clip
并监视 LineEvent.Type.STOP
事件并重新启用按钮
例如。这将查找给定目录中的所有 .wav
文件并为每个文件创建一个按钮。单击时,按钮(或更重要的是,底层 Action
)被禁用并播放音频。当它 STOP
时,Action
(以及扩展按钮)被重新启用。
声音 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;
}
}
}
}
我正在开发一个包含大量 JButton 对象的程序,我希望每个对象都对应于它自己的 .wav
文件。另外,我希望声音能够与其他按钮的声音重叠,但不能与自身重叠(在播放声音时单击按钮将重新启动声音)。
我尝试使用单个 Clip 对象,但我无法完成上述内容。因此,我求助于为每个按钮声明一个新的 Clip 对象,但我觉得这是解决我的问题的一个相当低效的解决方案。
我怎样才能以最有效的方式完成我在第一段中所说的内容?
每个按钮一个剪辑应该没问题。当用户单击按钮 运行 以重新启动剪辑时:
sound.stop();
sound.setFramePosition(0);// set the location to the start of the file
sound.play();// restart your sound
有几种方法可以实现这一点,但基本思想是,您想要将 LineListener
注册到 Clip
并监视 LineEvent.Type.STOP
事件并重新启用按钮
例如。这将查找给定目录中的所有 .wav
文件并为每个文件创建一个按钮。单击时,按钮(或更重要的是,底层 Action
)被禁用并播放音频。当它 STOP
时,Action
(以及扩展按钮)被重新启用。
声音 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;
}
}
}
}