okay, I have this problem: my audio starts playing correctly, but it wont stop even after "clip.stop()" or "clip.close()" ... do you have any idea what to do to stop it? (i could accept even muting it, I am really desperate)
public class Main {
//audio playing
public static void audio(boolean a) {
try {
File file = new File("textures/Main_theme.wav");
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(file));
if(a == true){
// this loads correctly, but wont stop music
clip.stop();
System.out.println(a);
}
else{
clip.start();
}
} catch (Exception e) {
System.err.println("Put the music.wav file in the sound folder if you want to play background music, only optional!");
}
}
private static String arg;
public static void main(String[] args){
//picture loading ... ignorable now
arg = "textures/ccc.gif";
JFrame f = new JFrame();
JPanel p = new JPanel();
JLabel l = new JLabel();
ImageIcon icon = new ImageIcon(arg);
f.setSize(480, 360);
f.setVisible(true);
l.setIcon(icon);
p.add(l);
f.getContentPane().add(p);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//calling audio method to play sound (works)
audio(false);
//should stop music and run another class
KeyListener action = new KeyListener()
{
@Override
public void keyPressed(KeyEvent e) {
//trying to stop music
f.dispose();
try {
Menu.menu(args);
Main.audio(true);
} catch (IOException e1) {
//rest of code ... ignorable
e1.printStackTrace();
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
};
f.addKeyListener( action );
}
}
You need to step back for a second and think about what you're doing.
You're creating a Clip
and you're playing it. At some point in the future, you create a new Clip
and try and stop. What connection does these two Clip
s have in common? How are they connected? The answer is, they're not. It's quite reasonable to load the same file into to sepearate Clip
s and play them individually.
Instead, you need to stop the instance of the Clip
you started earlier.
Because I'm lazy, I'd start by encapsulating the audio functionality into a simple class.
public class Audio {
private Clip clip;
protected Audio() {
}
public Audio(File source) throws LineUnavailableException, MalformedURLException, IOException, UnsupportedAudioFileException {
this(source.toURI().toURL());
}
public Audio(URL source) throws LineUnavailableException, IOException, UnsupportedAudioFileException {
this(source.openStream());
}
public Audio(InputStream source) throws LineUnavailableException, IOException, UnsupportedAudioFileException {
init(source);
}
protected void init(File source) throws LineUnavailableException, MalformedURLException, IOException, UnsupportedAudioFileException {
init(source.toURI().toURL());
}
protected void init(URL source) throws IOException, LineUnavailableException, UnsupportedAudioFileException {
init(source.openStream());
}
protected void init(InputStream source) throws LineUnavailableException, IOException, UnsupportedAudioFileException {
clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(source));
}
public void setRepeats(boolean repeats) {
clip.loop(repeats ? Clip.LOOP_CONTINUOUSLY : 1);
}
public void reset() {
clip.stop();
clip.setFramePosition(0);
}
public void play() {
clip.start();
}
public void stop() {
clip.stop();
}
public boolean isPlaying() {
return clip.isActive();
}
}
"Why?" you ask, because now I can create subclass which represent specific sounds and load them, without needing to care or remember what the source of the audio is, for example...
public class MainTheme extends Audio {
public MainTheme() throws LineUnavailableException, MalformedURLException, IOException, UnsupportedAudioFileException {
init(getClass().getResource("textures/Main_theme.wav"));
}
}
Now, I can easily create the "main theme" audio whenever I need to without having to care about what the source of it is
This also means I can pass the MainTheme
to other parts of the program which expect an instance of Audio
and off load the management simply
Then you just need to create an instance of the class and start/stop it as required, for example...
package test;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
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.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
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() {
JButton btn = new JButton("Click");
btn.addActionListener(new ActionListener() {
private Audio audio;
@Override
public void actionPerformed(ActionEvent e) {
try {
if (audio == null) {
audio = new MainTheme();
}
if (audio.isPlaying()) {
audio.stop();
} else {
audio.play();
}
} catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) {
ex.printStackTrace();
}
}
});
add(btn);
}
}
}