Search code examples
javaswingaudiojavasoundaudio-player

java audio playing wont stop


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 );

        }
    }

Solution

  • 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 Clips 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 Clips 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);
            }
        }
    }