I'm a beginner working on a little drum machine project based off of Head First Java's code in Chapter 13.
At present, the code creates a sequence based on my input on the checkbox grid that the code draws to the GUI.
However I want the program to function like a real drum machine where I can add or subtract hits while the sequence plays.
My idea was to have all channels play on every tick of the sequence per default, and have the checkboxes unmute the channel at that specific tick. However I'm having a hard time figuring out how to implement that.
Is it possible? Is there an easier or better method that I'm not seeing to achieve what I'm aiming for?
import java.awt.*;
import javax.swing.*;
import javax.sound.midi.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.util.*;
import java.awt.event.*;
public class BeatBox {
JPanel mainPanel;
ArrayList<JCheckBox> checkboxList;
Sequencer sequencer;
Sequence sequence;
Track track;
JFrame theFrame;
String[] instrumentNames = {"Bass Drum", "Closed Hi-Hat", "Open Hi-Hat","Acoustic Snare",
"Crash Cymbal", "Hand Clap", "High Tom", "Hi Bongo", "Maracas", "Whistle", "Low Conga", "Cowbell",
"Vibraslap", "Low-mid Tom", "High Agogo", "Open Hi Conga" };
int[]instruments={35,42,46,38,49,39,50,60,70,72,64,56,58,47,67,63};
public static void main(String[]args){
new BeatBox().buildGUI();
}
public void buildGUI(){
theFrame=new JFrame("Cyber BeatBox");
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BorderLayout layout=new BorderLayout();
JPanel background=new JPanel(layout);
background.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
//Build tempo slider
JSlider slider = new JSlider();
slider.setOrientation(JSlider.HORIZONTAL);
slider.setMinimum(50);
slider.setMaximum(200);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(1);
slider.setValue(120);
JLabel label = new JLabel();
label.setText("Tempo: " + slider.getValue());
slider.addChangeListener(e -> {
JSlider source = (JSlider) e.getSource();
//float tempoFactor=sequencer.getTempoFactor();
sequencer.setTempoFactor((float)source.getValue()/100);
label.setText("Tempo: " + String.valueOf(source.getValue()));
});
checkboxList=new ArrayList<JCheckBox>();
Box buttonBox=new Box(BoxLayout.X_AXIS);
JButton start=new JButton("Start");
start.addActionListener(new MyStartListener());
buttonBox.add(start);
JButton stop=new JButton("Stop");
stop.addActionListener(new MyStopListener());
buttonBox.add(stop);
JButton resetButton = new JButton("Reset Patch");
resetButton.addActionListener(new MyResetListener());
buttonBox.add(resetButton);
Box nameBox=new Box(BoxLayout.Y_AXIS);
for(int i=0;i< 16;i++){
nameBox.add(new Label(instrumentNames[i]));
}
background.add(BorderLayout.NORTH,buttonBox);
background.add(BorderLayout.WEST,nameBox);
theFrame.getContentPane().add(background);
background.add(BorderLayout.SOUTH, slider);
buttonBox.add(BorderLayout.EAST, label);
GridLayout grid=new GridLayout(16,16);grid.setVgap(1);
grid.setHgap(2);
mainPanel=new JPanel(grid);
background.add(BorderLayout.CENTER,mainPanel);
for(int i=0;i< 256;i++){
JCheckBox c=new JCheckBox();
c.setSelected(false);
checkboxList.add(c);
mainPanel.add(c);
} // end loop
setUpMidi();
theFrame.setBounds(50,50,300,300);
theFrame.pack();
theFrame.setVisible(true);
} // close method
public void setUpMidi(){
try{
sequencer=MidiSystem.getSequencer();
sequencer.open();
sequence=new Sequence(Sequence.PPQ,4);
track=sequence.createTrack();
sequencer.setTempoInBPM(120);
}catch(Exception e){ e.printStackTrace(); }
} // close method
public void buildTrackAndStart(){
int[]trackList=null;
sequence.deleteTrack(track);
track=sequence.createTrack();
sequencer.setTickPosition(0);
for(int i=0;i<16;i++){
trackList=new int[16];
int key=instruments[i];
for(int j=0;j<16;j++){
JCheckBox jc=(JCheckBox)checkboxList.get(j+(16*i));
if(!jc.isSelected()){
trackList[j]=key;
} /*else{
trackList[j]=0;
}*/
} // close inner loop
makeTracks(trackList);
track.add(makeEvent(176,1,127,0,16));
} // close outer
track.add(makeEvent(192,9,1,0,15));
try{
sequencer.setSequence(sequence);
sequencer.setLoopCount(sequencer.LOOP_CONTINUOUSLY);
sequencer.setMasterSyncMode(Sequencer.SyncMode.MIDI_SYNC);
sequencer.start();
sequencer.setTempoInBPM(120);
}catch(Exception e){
e.printStackTrace();
}
} // close buildTrackAndStart method
//Inner Classes
public class MyStartListener implements ActionListener{
public void actionPerformed(ActionEvent a){
buildTrackAndStart(); }
} // close inner class
public class MyStopListener implements ActionListener{
public void actionPerformed(ActionEvent a){
sequencer.stop();
sequencer.setTickPosition(0);}
} // close inner class
public class MyUpTempoListener implements ActionListener{
public void actionPerformed(ActionEvent a){
float tempoFactor=sequencer.getTempoFactor();
sequencer.setTempoFactor((float)(tempoFactor*1.03));
}
} // close inner class
public class MyDownTempoListener implements ActionListener{
public void actionPerformed(ActionEvent a){
float tempoFactor=sequencer.getTempoFactor();
sequencer.setTempoFactor((float)(tempoFactor*.97));
}
} // close inner class
public class MyResetListener implements ActionListener{
public void actionPerformed(ActionEvent a){
for(int i = 0; i<256; i++){
JCheckBox loopBox = checkboxList.get(i);
loopBox.setSelected(false);
}
}
} // close inner class
//Makers
public void makeTracks(int[]list){
for(int i=0;i<16;i++){
int key=list[i];
if(key!=0){
track.add(makeEvent(144,9,key,100,i));
track.add(makeEvent(128,9,key,100,i+1));
}
}
}
public MidiEvent makeEvent(int comd,int chan,int one,int two,int tick){
MidiEvent event=null;
try{
ShortMessage a=new ShortMessage();
a.setMessage(comd,chan,one,two);
event=new MidiEvent(a,tick);
}catch(Exception e){
e.printStackTrace();}
return event;
}
}