我正在开发一个包含大量JButton对象的程序,我希望每个对象都有自己的.wav
文件。此外,我希望声音的工作方式使得它们可以与其他按钮的声音重叠,但它不能与自身重叠(在播放声音时单击按钮将重新启动声音)。
我尝试使用单个Clip对象,但我无法完成上面所说的内容。结果,我使用每个按钮声明一个新的Clip对象,但我觉得这对我的问题来说是一个相当低效的解决方案。
如何以最有效的方式完成我在第一段中所说的内容?
答案 0 :(得分:4)
您可以通过多种方式实现此目标,但基本的想法是,您希望将LineListener
注册到Clip
并监控LineEvent.Type.STOP
事件,重新启用按钮
例如。这将查找给定目录中的所有.wav
个文件,并为每个文件创建一个按钮。单击时,按钮(或更重要的是,基础Action
)将被禁用并播放音频。当它STOP
时,重新启用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
< / p>
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;
}
}
}
}
答案 1 :(得分:0)
每个按钮一个剪辑应该没问题。当用户单击该按钮时,运行此命令以重新启动剪辑:
sound.stop();
sound.setFramePosition(0);// set the location to the start of the file
sound.play();// restart your sound