Download images required for the metronome here.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Metronome extends Thread{
public static int defaultMsPbpm = 5000;
// by testing
/* (emperical)
* 5000ms = 1bpm
* Therefore,
* timerDelay(ms) = 5000/bpm
*/
public int wy = 342; // initial position of weight
public static int bpmInt;
public static int msPbpm = defaultMsPbpm; // number ms at 1bpm
public int timerDelay;
public static int sliderKnob_width = 42;
public static int sliderKnob_height = 19;
public static int weight_width = 84;
public static int weight_height = 68;
final static int MAX_WY = 550;
//public Point cursorPressedPosition;
public int X = 0;
public int Y = 0;
boolean volUprWasHit = false;
boolean volLwrWasHit = false;
boolean subBeatWasHit = false;
public int deltaX = 0;
boolean weightWasHit;
public int deltaY = 0;
public Timer timer;
public boolean hasStarted = false;
public boolean stopAnimation = false;
// Stuff for Midi
public int volUprX = (int)(110 +80*(140/127));
public int volLwrX = (int)(110 +80*(140/127));
public int subBeatX = 395;
public int numSubBeats;
private MidiChannel channel = null;
private static int majorVelocity = 80; // initial volume
private static int minorVelocity = 80; // initial volume
final static int HAND_CLAP = 39;
final static int RIM_SHOT = 37;
final static int COWBELL = 56;
final static int HIGH_BONGO = 60;
final static int LOW_BONGO = 61;
final static int HIGH_TIMBALE = 65;
final static int LOW_TIMBALE = 66;
final static int HIGH_AGOGO = 67;
final static int LOW_AGOGO = 68;
final static int CLAVES = 75;
final static int HIGH_WOOD_BLOCK = 76;
final static int LOW_WOOD_BLOCK = 77;
final static int CUICA = 78;
final static int MUTE_TRIANGLE = 81;
static int noteForMainBeat = CLAVES;
static int noteForSubBeat = HIGH_WOOD_BLOCK;
static int NUM_SUB_BEAT = 1;
private JFrame frame;
private JMenuBar menuBar;
private JMenuItem settingsMenuItem;
private JLabel soundLabel1;
private JLabel soundLabel2;
private JLabel timerDelayLabel = new JLabel();
private JMenuItem helpMenuItem;
public static void main(String[] args) {
(new Metronome()).start();
}
public Metronome() {
/**
* Get properties that were save when the app was last closed.
**/
Properties prop = new Properties();
InputStream input = null;
try {
input = new FileInputStream(“metronome.properties”);
// load a properties file
prop.load(input);
// get the property values that were save when application was closed
noteForMainBeat = Integer.parseInt(prop.getProperty(“noteForMainBeat”));
noteForSubBeat = Integer.parseInt(prop.getProperty(“noteForSubBeat”));
majorVelocity = Integer.parseInt(prop.getProperty(“majorVelocity”));
//System.out.println(“majorVelocity: “+majorVelocity);
volUprX = (int)(124 +majorVelocity*(140/127));
minorVelocity = Integer.parseInt(prop.getProperty(“minorVelocity”));
//System.out.println(“majorVelocity: “+majorVelocity);
volLwrX = (int)(124 +minorVelocity*(140/127));
subBeatX = Integer.parseInt(prop.getProperty(“subBeatX”));
numSubBeats = Integer.parseInt(prop.getProperty(“numSubBeats”));
msPbpm = Integer.parseInt(prop.getProperty(“msPerBPM”));
bpmInt = Integer.parseInt(prop.getProperty(“tempo”));
wy = (int)(bpmInt -24)*2 + 150;
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Get Synthesizer
**/
try {
final Synthesizer synthesizer = MidiSystem.getSynthesizer();
synthesizer.open();
channel = synthesizer.getChannels()[9];
} catch (MidiUnavailableException ex) {
//log.error(ex);
}
/**
* Java Frame set-up
*/
EventQueue.invokeLater(new Runnable() {
//@Override
public void run() {
JFrame frame = new JFrame(“DaveS Metronome”);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add icon for the frame
URL iconURL = getClass().getResource(“images/DavesMetronome.png”);
// iconURL is null when not found
ImageIcon icon = new ImageIcon(iconURL);
frame.setIconImage(icon.getImage());
// Add the menu
settingsMenuItem = new JMenuItem(“Settings”);
settingsMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Execute when UP button is pressed
SettingsDialog dialog = new SettingsDialog();
dialog.setModal(true);
dialog.setVisible(true);
}
});
helpMenuItem = new JMenuItem(“Help”);
helpMenuItem.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
helpMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Execute when UP button is pressed
HelpDialog dialog = new HelpDialog();
dialog.setModal(true);
dialog.setVisible(true);
}
});
// define the KeyListener
JTextField timerTrigger = new JTextField();
timerTrigger.addKeyListener(new MyKeyListener());
frame.add(timerTrigger);
// add graphics pane
frame.add(new AnimationPane());
menuBar = new JMenuBar();
// add menus to menubar
menuBar.add(settingsMenuItem);
menuBar.add(helpMenuItem);
// put the menubar on the frame
frame.setJMenuBar(menuBar);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev) {
// Save variables to File
Properties prop = new Properties();
OutputStream output = null;
try {
output = new FileOutputStream(“metronome.properties”);
// set the properties value
prop.setProperty(“noteForMainBeat”, Integer.toString(noteForMainBeat));
prop.setProperty(“noteForSubBeat”, Integer.toString(noteForSubBeat));
prop.setProperty(“majorVelocity”, Integer.toString(majorVelocity));
prop.setProperty(“minorVelocity”, Integer.toString(minorVelocity));
prop.setProperty(“tempo”, Integer.toString(bpmInt));
prop.setProperty(“subBeatX”, Integer.toString(subBeatX));
prop.setProperty(“numSubBeats”, Integer.toString(numSubBeats));
prop.setProperty(“msPerBPM”, Integer.toString(msPbpm));
// save properties to project root folder
prop.store(output, null);
} catch (IOException io) {
io.printStackTrace();
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
});
frame.setMinimumSize(new Dimension(600, 880));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
/**
* Help menu item was clicked
*/
public void actionPerformed(ActionEvent ev)
{
HelpDialog dialog = new HelpDialog();
dialog.setModal(true);
dialog.setVisible(true);
}
/**
* Java JPanel set-up
*/
public class AnimationPane extends JPanel {
private static final long serialVersionUID = 8113062312141634760L;
// Load Basic images
BufferedImage baseBack = loadImage(“base-back”);
BufferedImage baseLower = loadImage(“base-lower”);
BufferedImage baseUpper = loadImage(“base-upper”);
BufferedImage wand = loadImage(“wand”);
// Load Button images
ImageIcon upButtonIcon = createImageIcon(“images/up.gif”);
ImageIcon downButtonIcon = createImageIcon(“images/down.gif”);
protected JButton up = new JButton();
protected JButton down = new JButton();
// Load Slider images
BufferedImage volumePlate = loadImage(“volumePlate”);
BufferedImage volumeKnobUpper = loadImage(“volumeKnobUpper”);
BufferedImage volumeKnobLower = loadImage(“volumeKnobLower”);
// Sub-beat switch
BufferedImage subBeatPlate = loadImage(“subBeatPlate”);
BufferedImage subBeatKnob = loadImage(“subBeatKnob”);
// Here we go
private BufferedImage weight = loadImage(“weight”);
private int xPos = 258;
private int direction = 1;
int angle = 360;
int MAX_ANGLE = 378;
int MIN_ANGLE = 342;
int angleIncrement = 3;
// Here is the ActionListener and Animation definition
public AnimationPane() {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), “pressed.up”);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), “pressed.down”);
am.put(“pressed.up”, new MoveAction(“up”));
am.put(“pressed.down”, new MoveAction(“down”));
timer = new Timer(timerDelay, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Animation
switch (numSubBeats){
case 0:
angle = angle + angleIncrement*direction;
if (angle >= MAX_ANGLE){
direction = -1;
// Midi
if (angle == 378){
channel.noteOn(noteForMainBeat, majorVelocity);
}
}
else if (angle <= MIN_ANGLE){
direction = 1;
if (angle == 342){
channel.noteOn(noteForMainBeat, majorVelocity);
}
}
repaint();
break;
case 1:
angle = angle + angleIncrement*direction;
if (angle >= MAX_ANGLE){
direction = -1;
// Midi
if (angle == 378){
channel.noteOn(noteForMainBeat, majorVelocity);
}
}
else if (angle <= MIN_ANGLE){
direction = 1;
if (angle == 342){
channel.noteOn(noteForMainBeat, majorVelocity);
}
}
else if (angle == 360){
channel.noteOn(noteForSubBeat, minorVelocity);
}
repaint();
break;
case 2:
angle = angle + angleIncrement*direction;
if (angle >= MAX_ANGLE){
direction = -1;
// Midi
if (angle == 378){
channel.noteOn(noteForMainBeat, majorVelocity);
}
}
else if (angle <= MIN_ANGLE){
direction = 1;
if (angle == 342){
channel.noteOn(noteForMainBeat, majorVelocity);
}
}
else if (angle == 354){
channel.noteOn(noteForSubBeat, minorVelocity);
}
else if (angle == 366){
channel.noteOn(noteForSubBeat, minorVelocity);
}
repaint();
break;
}
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
// add buttons to fine tune tempo
up = new JButton(null, upButtonIcon);
up.setMnemonic(KeyEvent.VK_UP);
up.setPreferredSize(new Dimension(20, 26));
down = new JButton(null, downButtonIcon);
down.setMnemonic(KeyEvent.VK_DOWN);
down.setPreferredSize(new Dimension(20, 26));
up.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Execute when UP button is pressed
if (!hasStarted){
bpmInt = bpmInt + 1;
if (bpmInt > 224){bpmInt = 224;};
wy = (int)(bpmInt -24)*2 + 150;
timerDelay = msPbpm/bpmInt;
down.requestFocus(false);
repaint();
}
}
});
down.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Execute when DOWN button is pressed
if (!hasStarted){
bpmInt = bpmInt – 1;
if (bpmInt < 24){bpmInt = 24;};
wy = (int)(bpmInt -24)*2 + 150;
timerDelay = msPbpm/bpmInt;
repaint();
}
}
});
}
// Graphics definition
protected void paintComponent(Graphics g) {
super.paintComponent(g);
addMouseMotionListener(new ChangeTempo());
addMouseListener (new GetCurPos());
Graphics2D g2d = (Graphics2D) g;
AffineTransform original = g2d.getTransform(); // save for original animation state
// Static images
g2d.drawImage(baseBack, 0, 0, null);
g2d.drawImage(baseUpper, 0, 0, null);
// The BPM box
int thickness = 4;
Color myColor = Color.decode(“#905838”); //(“#C1E0FF”);
g2d.setColor(myColor);
g2d.fill3DRect(270, 70, 60, 26, false); //(X, Y, length, width)
for (int i = 1; i <= thickness; i++)
g2d.draw3DRect(270 – i, 70 – i, 60 + 2 * i – 1, 26 + 2 * i – 1, true);
// Display BPM
Font courierBold10 = new Font(“Droid”, Font.BOLD, 20);
Color textColor = Color.decode(“#ECEFFF”);//(“#62F35B”);
g2d.setColor(textColor);
g2d.setFont(courierBold10);
String textBpm = Integer.toString((wy-150)/2 + 24);
g2d.drawString(textBpm, 280, 90);
if (!hasStarted) {
g2d.setTransform(original);
g2d.drawImage(wand, 290, 116, this);
g2d.drawImage(weight, xPos, wy, this);
if (stopAnimation){ // spacebar was pressed to stop the animation
timer.stop();
}
}
else {
// Animated images
g2d.rotate(Math.toRadians(Math.abs(angle)), 298, 700); // 298, 700 is X, Y coordinate for center of rotation
g2d.drawImage(wand, 290, 116, null);
g2d.drawImage(weight, xPos, wy, this);
}
// Now draw the base with volume slider etc…
g2d.setTransform(original);
g2d.drawImage(baseLower, 0, 0, null);
g2d.drawImage(volumePlate, 100,670, null); // was 200
g2d.drawImage(volumeKnobUpper, volUprX,680, null);
g2d.drawImage(volumeKnobLower, volLwrX,702, null);
g2d.drawImage(subBeatPlate, 360, 685, null);
g2d.drawImage(subBeatKnob, subBeatX, 700, null);
// Add the buttons
up.setForeground(Color.decode(“#643D27”));
up.setBackground(Color.decode(“#CD7D50”));
up.setLocation(246, 70);
add(up);
down.setForeground(Color.decode(“#643D27”));
down.setBackground(Color.decode(“#CD7D50”));
down.setLocation(334, 70);
add(down);
}
public class GetCurPos extends MouseAdapter {
public void mousePressed(MouseEvent e){
X = e.getX();
Y = e.getY();
// What object was hit?
Rectangle weightRect = new Rectangle(290, wy, weight_width, weight_height);
Rectangle volUprRect = new Rectangle(volUprX, 680, sliderKnob_width, sliderKnob_height);
Rectangle volLwrRect = new Rectangle(volLwrX, 702, sliderKnob_width, sliderKnob_height);
Rectangle subBeatRect = new Rectangle(subBeatX, 700, sliderKnob_width, sliderKnob_height);
// Weight
if (weightRect.contains(X,Y)) {
weightWasHit = true;
deltaY = Y – wy;
}
// Main beat volume
else if (volUprRect.contains(X,Y)){
volUprWasHit = true;
deltaX = X – volUprX;
}
// Sub-Beat Volume
else if (volLwrRect.contains(X,Y)) {
volLwrWasHit = true;
deltaX = X – volLwrX;
}
// Sub-Beat switch
else if (subBeatRect.contains(X,Y)) {
subBeatWasHit = true;
deltaX = X – subBeatX;
}
}
public void mouseReleased(MouseEvent e){
volUprWasHit = false;
volLwrWasHit = false;
subBeatWasHit = false;
weightWasHit = false;
}
}
public class ChangeTempo extends MouseAdapter {
public void mouseDragged(MouseEvent e) {
if (weightWasHit){
if (!hasStarted){
//if (e.getY() < 570) {
wy = e.getY() – deltaY;
if (wy > MAX_WY){
wy = MAX_WY;
}
if (wy < 150){
wy = 150;
}
bpmInt = (int)((wy-150)/2 + 24);
timerDelay = msPbpm/bpmInt;
repaint();
}
}
else if (volUprWasHit){
volUprX = e.getX()-deltaX;
if (volUprX > 250){
volUprX = 250;
}
if (volUprX <= 110){
volUprX = 110;
}
majorVelocity = (int)((volUprX-110)*127/140);
repaint();
//}
}
else if (volLwrWasHit){
volLwrX = e.getX()-deltaX;
if (volLwrX > 250){
volLwrX = 250;
}
if (volLwrX <= 110){
volLwrX = 110;
}
minorVelocity = (int)((volLwrX-110)*127/140);
repaint();
}
else if (subBeatWasHit){
subBeatX = e.getX();
if (subBeatX <420){
subBeatX = 390;
numSubBeats = 0;
}
// number of sub-beats = 1
if (subBeatX >420 && subBeatX<445){
subBeatX = 412;
numSubBeats = 1;
}
// number of sub-beats = 2
if (subBeatX > 445){
subBeatX = 432;
numSubBeats = 2;
}
repaint();
}
}
}
public class MoveAction extends AbstractAction {
private static final long serialVersionUID = 3308815138155077750L;
private int direction;
public MoveAction(String whichWay) {
if (whichWay == “up”) {
direction = 1;
}
else if(whichWay == “down”){
direction = -1;
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (!hasStarted){
bpmInt = bpmInt + direction;
if (bpmInt > 224){bpmInt = 224;}
else if (bpmInt < 24) {bpmInt = 24;}
wy = (int)(bpmInt -24)*2 + 150;
timerDelay = msPbpm/bpmInt;
repaint();
}
}
}
}
private BufferedImage loadImage(String name) {
//BufferedImage bi;
String imgFileName = “images/Metronome-“+name+”.png”;
URL url = Metronome.class.getResource(imgFileName);
BufferedImage img = null;
try {
img = ImageIO.read(url);
} catch (Exception e) {
}
return img;
}
/**
* Returns an ImageIcon, or null if the path was invalid.
*/
protected static ImageIcon createImageIcon(String path) {
java.net.URL imgURL = Metronome.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
} else {
System.err.println(“Couldn’t find file: ” + path);
return null;
}
}
/**
* Start/Stop animation
**/
class MyKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent evt) {
if (evt.getKeyChar() == KeyEvent.VK_SPACE) {
if (!hasStarted){
bpmInt = (int)((wy-150)/2 + 24);
timerDelay = msPbpm/bpmInt;
timer.setDelay(timerDelay);
timer.start();
}
else {
stopAnimation = true;
}
hasStarted = !hasStarted;
}
// Sub-Beat Switch
else if (evt.getKeyChar() == KeyEvent.VK_0 && hasStarted) {
subBeatX = 390;
numSubBeats = 0;
}
else if (evt.getKeyChar() == KeyEvent.VK_1 && hasStarted) {
subBeatX = 412;
numSubBeats = 1;
}
else if (evt.getKeyChar() == KeyEvent.VK_2 && hasStarted) {
subBeatX = 432;
numSubBeats = 2;
}
}
}
// Help dialog box
private class HelpDialog extends JDialog implements ActionListener {
private static final long serialVersionUID = -273240361506739164L;
private HelpDialog() {
super(frame, “Help”, true);
// Stop the Metronome
if (hasStarted) {
timer.stop();
(hasStarted) = false;
}
JPanel panel = new JPanel(new FlowLayout());
getContentPane().add(panel);
JLabel helpText = new JLabel();
String helpContent = “<html>”
+”<head>”
+”<meta charset=’UTF-8′>”
+”<title>Insert title here</title>”
+”</head>”
+”<body>”
+”<div align=’center’ style=’font-size:1.2em’><b>How to use Metronome</b></div>”
+”<ul>”
+” <li>Drag the weight to set tempo.</li><br/>”
+” <li>Use up/down arrows on either side of the BPM display<br/>”
+” or use up/down arrow keys on your keyboard to fine adjust tempo.</li>”
+” <ul style=’margin-top: -0.5em’>”
+” <li>Up increases tempo – weight moves down</li>”
+” <li>Down decreases tempo – weight moves up</li>”
+” </ul>”
+” <li>Press spacebar to Start/Stop the metronome.</li><br/>”
+” <li>Volume Sliders:</li>”
+” <ul style=’margin-top: -0.5em’>”
+” <li>Top slider controls the major beat volume.</li>”
+” <li>Bottom slider controls the sub-beat volume.</li>”
+” </ul>”
+” <li>Select 0, 1 or 2 sub-beats with the Sub-Beat switch.<br/>”
+” Or while the metronome is running,<br/>”
+” use keypad number 0, 1, 2 to change number of sub-beats.</li><br/>”
+” <li>Major beat and sub-beat sounds can be changed in the ‘Settings’ menu item.</li>”
+”</ul>”
+”<ul>”
+”<li>Timer Delay (Optional adjustment in Settings panel.)</li></br>”
+”Tempo accuracy may vary by 1/4 beat per minute at 60BPM<br/>depending on your computer configuration.<br/>”
+”To calibrate:<br/>”
+ “<ul style=’margin-top: -0.5em’>”
+ “<li>Set tempo to 60BPM and use a stop watch<br/>to count the number of beats that you hear in one minute.</li>”
+ “<li>If you count more than 60 beats,<br/>then the metronome is running to fast.<br/>”
+ “Increase the delay by moving the slider to the right<br/>or use right arrow key on your keyboard.</li>”
+ “<li>If you count less than 60 beats,<br/>the metronome is running slow.<br/>”
+ “Decrease the delay by moving the slider to the left<br/>or use left arrow key on your keyboard.</li>”
+ “</ul>”
+”</ul>”
+”</body>”
+”</html>”;
helpText.setText(helpContent);
panel.add(helpText);
this.setBounds(300, 200, 640, 620);
panel.setLocation(20, 50);
}
public void actionPerformed(ActionEvent ev)
{
setVisible(false);
}
}
/**
* The Settings dialog box
**/
private class SettingsDialog extends JDialog implements ActionListener, ChangeListener {
private static final long serialVersionUID = 147052588095926828L;
private SettingsDialog()
{
// Stop Metronome
if (hasStarted) {
timer.stop();
hasStarted = false;
}
// Add main panel for Settings
JPanel contentPane = new JPanel();
contentPane.setLayout(new FlowLayout());
contentPane.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 2));
// panel for sound
JPanel soundsContainer = new JPanel();
soundsContainer.setLayout(new GridLayout(1, 2, 10, 10));
// LEFT side of panel
JPanel buttonPanel1 = new JPanel();
buttonPanel1.setLayout(new GridLayout(16, 1, 10, 10));
JLabel Left = new JLabel(“Major Beat”, JLabel.CENTER);
Left.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
buttonPanel1.add(Left);
// SELECT SOUND PANEL 1
soundLabel1 = new JLabel(getSoundName(noteForMainBeat), JLabel.CENTER);
buttonPanel1.add(soundLabel1);
// Sound buttons
JButton hc1 = new JButton(“Hand Clap”);
hc1.addActionListener(new soundButtonPanel1(“Hand Clap”, HAND_CLAP ));
buttonPanel1.add(hc1);
JButton rs1 = new JButton(“Side Stick/Rimshot”);
rs1.addActionListener(new soundButtonPanel1(“Side Stick/Rimshot”, RIM_SHOT));
buttonPanel1.add(rs1);
JButton cb1 = new JButton(“Cowbell”);
cb1.addActionListener(new soundButtonPanel1(“CowBell”, COWBELL ));
buttonPanel1.add(cb1);
JButton hb1 = new JButton(“High Bongo”);
hb1.addActionListener(new soundButtonPanel1(“High Bongo”, HIGH_BONGO ));
buttonPanel1.add(hb1);
JButton lb1 = new JButton(“Low Bongo”);
lb1.addActionListener(new soundButtonPanel1(“Low Bongo”, LOW_BONGO ));
buttonPanel1.add(lb1);
JButton ht1 = new JButton(“High Timbale”);
ht1.addActionListener(new soundButtonPanel1(“High Timbale”, HIGH_TIMBALE ));
buttonPanel1.add(ht1);
JButton lt1 = new JButton(“Low Timbale”);
lt1.addActionListener(new soundButtonPanel1(“Low Timbale”, LOW_TIMBALE ));
buttonPanel1.add(lt1);
JButton ha1 = new JButton(“High Agogo”);
ha1.addActionListener(new soundButtonPanel1(“High Agogo”, HIGH_AGOGO ));
buttonPanel1.add(ha1);
JButton la1 = new JButton(“Low Agogo”);
la1.addActionListener(new soundButtonPanel1(“Low Agogo”, LOW_AGOGO ));
buttonPanel1.add(la1);
JButton c1 = new JButton(“Claves”);
c1.addActionListener(new soundButtonPanel1(“Claves”, CLAVES ));
buttonPanel1.add(c1);
JButton hwb1 = new JButton(“High Wood Block”);
hwb1.addActionListener(new soundButtonPanel1(“High Wood Block”, HIGH_WOOD_BLOCK ));
buttonPanel1.add(hwb1);
JButton lwb1 = new JButton(“Low Wood Block”);
lwb1.addActionListener(new soundButtonPanel1(“Low Wood Block”, LOW_WOOD_BLOCK ));
buttonPanel1.add(lwb1);
JButton mc1 = new JButton(“Mute Cuica”);
mc1.addActionListener(new soundButtonPanel1(“Mute Cuica”, CUICA ));
buttonPanel1.add(mc1);
JButton mt1 = new JButton(“Mute Triangle”);
mt1.addActionListener(new soundButtonPanel1(“Mute Triangle”, MUTE_TRIANGLE ));
buttonPanel1.add(mt1);
soundsContainer.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
soundsContainer.add(buttonPanel1, BorderLayout.NORTH);
// RIGHT side of panel
JPanel buttonPanel2 = new JPanel();
buttonPanel2.setLayout(new GridLayout(16, 1, 10, 10));
JLabel Right = new JLabel(“Sub-Beat”, JLabel.CENTER);
Right.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
buttonPanel2.add(Right);
// Selected sound
soundLabel2 = new JLabel(getSoundName(noteForSubBeat), JLabel.CENTER);
buttonPanel2.add(soundLabel2);
// Sound buttons
JButton hc2 = new JButton(“Hand Clap”);
hc2.addActionListener(new soundButtonPanel2(“Hand Clap”, HAND_CLAP ));
buttonPanel2.add(hc2);
JButton rs2 = new JButton(“Side Stick/Rimshot”);
rs2.addActionListener(new soundButtonPanel2(“Side Stick/Rimshot”, RIM_SHOT ));
buttonPanel2.add(rs2);
JButton cb2 = new JButton(“Cowbell”);
cb2.addActionListener(new soundButtonPanel2(“Cowbell”, COWBELL ));
buttonPanel2.add(cb2);
JButton hb2 = new JButton(“High Bongo”);
hb2.addActionListener(new soundButtonPanel2(“High Bongo”, HIGH_BONGO ));
buttonPanel2.add(hb2);
JButton lb2 = new JButton(“Low Bongo”);
lb2.addActionListener(new soundButtonPanel2(“Low Bongo”, LOW_BONGO ));
buttonPanel2.add(lb2);
JButton ht2 = new JButton(“High Timbale”);
ht2.addActionListener(new soundButtonPanel2(“High Timbale”, HIGH_TIMBALE ));
buttonPanel2.add(ht2);
JButton lt2 = new JButton(“Low Timbale”);
lt2.addActionListener(new soundButtonPanel2(“Low Timbale”, LOW_TIMBALE ));
buttonPanel2.add(lt2);
JButton ha2 = new JButton(“High Agogo”);
ha2.addActionListener(new soundButtonPanel2(“High Agogo”, HIGH_AGOGO ));
buttonPanel2.add(ha2);
JButton la2 = new JButton(“Low Agogo”);
la2.addActionListener(new soundButtonPanel2(“Low Agogo”, LOW_AGOGO ));
buttonPanel2.add(la2);
JButton c2 = new JButton(“Claves”);
c2.addActionListener(new soundButtonPanel2(“Claves”, CLAVES ));
buttonPanel2.add(c2);
JButton hwb2 = new JButton(“High Wood Block”);
hwb2.addActionListener(new soundButtonPanel2(“High Wood Block”, HIGH_WOOD_BLOCK ));
buttonPanel2.add(hwb2);
JButton lwb2 = new JButton(“Low Wood Block”);
lwb2.addActionListener(new soundButtonPanel2(“Low Wood Block”, LOW_WOOD_BLOCK ));
buttonPanel2.add(lwb2);
JButton mc2 = new JButton(“Mute Cuica”);
mc2.addActionListener(new soundButtonPanel2(“Mute Cuica”, CUICA ));
buttonPanel2.add(mc2);
JButton mt2 = new JButton(“Mute Triangle”);
mt2.addActionListener(new soundButtonPanel2(“Mute Triangle”, MUTE_TRIANGLE ));
buttonPanel2.add(mt2);
soundsContainer.add(buttonPanel2, BorderLayout.NORTH);
contentPane.add(soundsContainer);
// Bottom Panel for Fine Timing Adjustment
JPanel timingPanel = new JPanel(new BorderLayout());
timingPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
timingPanel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
timingPanel.setLayout(new GridLayout(3, 1));
final JSlider timingSlider = new JSlider(JSlider.HORIZONTAL, 4500, 5500, msPbpm);
timingSlider.addChangeListener(this);
timingSlider.setMinorTickSpacing(50);
timingSlider.setMajorTickSpacing(500);
timingSlider.setPaintTicks(true);
timingSlider.setPaintLabels(true);
JPanel timerDelayPanel = new JPanel();
timerDelayLabel.setText(“Timer delay is “+Integer.toString(msPbpm)+”ms”);
timerDelayPanel.add(timerDelayLabel);
timingPanel.add(timerDelayPanel);
JButton reset = new JButton(“Reset to Default”);
reset.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
msPbpm = defaultMsPbpm;
timerDelayLabel.setText(“Timer delay is “+Integer.toString(msPbpm)+”ms”);
timingSlider.setValue(msPbpm);
}
});
timingPanel.add(timingSlider);
timingPanel.add(reset);
contentPane.add(timingPanel);
setContentPane(contentPane);
setSize(480, 800);
setLocationByPlatform(true);
}
public void stateChanged(ChangeEvent e) {
JSlider timingJSlider = (JSlider)e.getSource();
if (!timingJSlider.getValueIsAdjusting()) {
msPbpm = timingJSlider.getValue();
timerDelayLabel.setText(“Timer delay is “+Integer.toString(msPbpm)+”ms”);
}
}
private String getSoundName(int noteNumber) {
String retString = “not found”;
switch (noteNumber){
case 39:
retString = “Hand Clap”;
break;
case 37:
retString = “Sidestick/Rimshot”;
break;
case 56:
retString = “Cowbell”;
break;
case 60:
retString = “High Bongo”;
break;
case 61:
retString = “Low Bongo”;
break;
case 65:
retString = “High Timbale”;
break;
case 66:
retString = “Low Timbale”;
break;
case 67:
retString = “High Agogo”;
break;
case 68:
retString = “Low Agogo”;
break;
case 75:
retString = “Claves”;
break;
case 76:
retString = “High Wood Block”;
break;
case 77:
retString = “Low Wood Block”;
break;
case 78:
retString = “Mute Cuica”;
break;
case 81:
retString = “Mute Triangle”;
break;
}
return retString;
}
public void actionPerformed(ActionEvent ev){
setVisible(false);
}
}
class soundButtonPanel1 implements ActionListener{
private String noteDescription = null;
private int noteNumber = 0;
public soundButtonPanel1(String desc, int note) {
noteDescription = desc;
noteNumber = note;
}
public void actionPerformed(ActionEvent e) {
soundLabel1.setText(noteDescription);
channel.noteOn(noteNumber, majorVelocity);
noteForMainBeat = noteNumber;
}
}
class soundButtonPanel2 implements ActionListener{
private String noteDescription = null;
private int noteNumber = 0;
public soundButtonPanel2(String desc, int note) {
noteDescription = desc;
noteNumber = note;
}
public void actionPerformed(ActionEvent e) {
soundLabel2.setText(noteDescription);
channel.noteOn(noteNumber, minorVelocity);
noteForSubBeat = noteNumber;
}
}
}