java - sonidos - Reproducción de múltiples clips de sonido con objetos Clip
programacion android pdf 2018 (2)
El clip por botón debería estar bien. Cuando el usuario hace clic en el botón, ejecuta esto para reiniciar el clip:
sound.stop();
sound.setFramePosition(0);// set the location to the start of the file
sound.play();// restart your sound
Estoy desarrollando un programa que tiene numerosos objetos JButton, y quiero que cada uno corresponda con su propio archivo
.wav
.
Además, quiero que los sonidos funcionen de manera tal que puedan superponerse con los sonidos de otros botones, pero no puede superponerse consigo mismo (al hacer clic en un botón mientras se reproduce su sonido se reiniciará el sonido).
Intenté usar un solo objeto Clip pero tuve problemas para lograr lo que dije anteriormente. Como resultado, recurrí a declarar un nuevo objeto Clip para cada botón, pero tengo la sensación de que esta es una solución bastante ineficiente para mi problema.
¿Cómo puedo lograr lo que dije en el primer párrafo de la manera más eficiente?
Hay un par de formas en que puede lograr esto, pero la idea básica es que desea registrar un
LineListener
en un
Clip
y monitorear el evento
LineEvent.Type.STOP
y volver a habilitar el botón
Por ejemplo.
Esto busca todos los archivos
.wav
en un directorio dado y crea un botón para cada uno.
Cuando se hace clic, el botón (o más importante, la
Action
subyacente) se desactiva y se reproduce el audio.
Cuando se
STOP
, la
Action
(y el botón por extensión) se vuelve a habilitar.
Sound API puede reproducir múltiples sonidos simultáneamente de todos modos
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();
}
}
}
}
Nota: Intenté usar
Clip#drain
(en un hilo de fondo), pero solo funcionó para el primer clip, los clips posteriores básicamente omitieron el método, por lo que
LineListener
Ahora con mejor gestión de recursos
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;
}
}
}
}