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