So I have been making a stop button recently and have been wondering how can I stop a button sound instantly when already playing.
The problem : When you click the stop sound button, it only stops the next button you press.
What Im Trying to Achieve : When you click the stop sound button, it stops all playing sounds.
Here is the main button class sound :
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; //JButtons Class public class Buttons extends JButton implements ActionListener{ private int locX = 0; private int locY = 0; //Sets the basic features of the buttons and adds an action listener public Buttons(String title){ super(title); setBounds(locX,locY,100,100); setOpaque(true); setBorderPainted(false); setBorder(BorderFactory.createLineBorder(Color.WHITE)); addActionListener(this); } //Sets the dimentions of the buttons public void setDimentions(int x, int y){ this.locX = x; this.locY = y; setBounds(locX,locY,100,100); } //Maps button colors to sting values static Map<String, Color> colorMap = Map.ofEntries(Map.entry("WHITE", Color.WHITE), Map.entry("GRAY", Color.GRAY), Map.entry( "BLACK", Color.BLACK), Map.entry( "RED", Color.RED), Map.entry( "ORANGE", new Color(255,121,0)), Map.entry( "YELLOW", Color.YELLOW), Map.entry( "GREEN", Color.GREEN), Map.entry( "BLUE", Color.BLUE), Map.entry( "MAGENTA", Color.MAGENTA), Map.entry( "PINK", Color.PINK), Map.entry( "CYAN", Color.CYAN)); //Gets the color from the map and returns it static Color getColor(String col){ return colorMap.get(col.toUpperCase()); } //Sets the color of the button and repaints it public void setColors(String colorBack, String colorFront){ setBackground(getColor(colorBack)); setForeground(getColor(colorFront)); repaint(); } public String[] listFilesForFolder(final File folder) { String[] f = new String[25]; int count = 0; for(int i = 0; i < 25; i++){ f[i] = ""; } for (final File fileEntry : folder.listFiles()) { if (fileEntry.isDirectory()) { listFilesForFolder(fileEntry); } else { if(fileEntry.getName().equals(".DS_Store")){ }else{ f[count] = fileEntry.getName(); count++; } } } return f; } public void playSound(String url, boolean loop, boolean stop){ try{ AudioInputStream audioIn = AudioSystem.getAudioInputStream(Launchpad.class.getResource("soundFiles/" + url)); Clip clip = AudioSystem.getClip(); clip.open(audioIn); clip.start(); if(loop == true){ clip.loop(Clip.LOOP_CONTINUOUSLY); } if(stop == true){ stopSound(clip); } } catch(Exception e){ System.out.println("Error"); } } public void stopSound(Clip clip){ if(clip.isActive()){ clip.stop(); clip.flush(); clip.close(); } } //Event Handler / Action Listener @Override public void actionPerformed(ActionEvent e){ if(e.getSource() == this){ String sNum = this.getText(); int num = Integer.parseInt(sNum); final File folder = new File("/Users/ethanbowles/Desktop/idk/programing/java/Launchpad/soundFiles"); String[] names =listFilesForFolder(folder); System.out.println(names[num - 1]); System.out.println(num); boolean fullStop = StopButton.stop; playSound(names[num - 1], LoopButton.loop, fullStop); StopButton.stop = false; LoopButton.loop = false; } } }
Here is the main sound stop button :
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; public class StopButton extends JButton implements ActionListener{ public static boolean stop = false; public StopButton(){ super("Stop"); setBounds(10,10,100,50); addActionListener(this); } @Override public void actionPerformed(ActionEvent e){ if(e.getSource() == this){ if(stop == true){ stop = false; }else{ stop = true; } super.repaint(); } } }
Advertisement
Answer
The problem is due to the logic with the .stop field in one JButton modified by the other JButton. Also reusing playSound() to play or stop is not a good design.
Here is a solution with a much cleaner design with 2 buttons, one for play one for stop.
The MusicController is independent from the UI:
class MusicController { // A property for the state of the controller public final static String PROP_STATE = "StateProperty"; enum State { NOT_READY, STOPPED, PLAYING }; State state = State.NOT_READY; boolean loop; // Manage property change listeners private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); public void loadSound() { // Initialize music data, load clip from file etc. ... State oldState = state; state = State.STOPPED; // We can actually play a sound only from the STOPPED state pcs.firePropertyChange(PROP_STATE, oldState, state); } public State getState() { return state; } public void play() { switch (state) { case NOT_READY: // Error "Not ready" ... break; case STOPPED: // Start playback (looped if loop is true) ... State oldState = state; state = State.PLAYING; pcs.firePropertyChange(PROP_STATE, oldState, state); // Notify listeners break; case PLAYING: // Already playing, do nothing break; default: throw new AssertionError(state.name()); } } public void stop() { // Same code structure than play(), but adapted to stop playback if current state is PLAYING. ... } public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } }
The View manages the UI and just listens to the MusicController state.
class View implements PropertyChangeListener { JButton playButton, stopButton; MusicController controller = new MusicController(); public View() { // Listen to music controller state changes controller.addPropertyChangeListener(this); // Create UI Action playAction = new AbstractAction("Play") { public void actionPerformed(ActionEvent ae) { controller.play(); } }; playButton = new JButton(playAction); // Same for stopButton with controller.stop(); ... // Add buttons to UI etc. ... updateUI(controller.getState()); } /** * Update the user interface depending on the music controller state. * * @param state */ private void updateUI(State state) { switch (state) { case NOT_READY: playButton.setEnabled(false); stopButton.setEnabled(false); break; case STOPPED: playButton.setEnabled(true); stopButton.setEnabled(false); break; case PLAYING: playButton.setEnabled(false); stopButton.setEnabled(true); break; default: throw new AssertionError(state.name()); } } /** * Called when a MusicController property has changed. */ public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource() == controller && MusicController.PROP_STATE.equals(evt.getPropertyName())) { // State has changed, update UI accordingly State state = (State) evt.getNewValue(); updateUI(state); } } }