Reputation: 25
I've copied the code from chapter 13 of the book Head First Java, page 420 to study and play around with, but the code doesn't seem to work as advertised.
First of all, the beat does not start at the first input on the GUI. If I place a cymbal splash on beat one to kick off the beat, and a snare on beat 3, the program plays back: Snare - splash.
Secondly, most sounds are not there, instead playing hihat or snare sounds. The Whistle, cowbell and maracass sounds play back hi hats.
Is there something that has changed in Java since this book was released, or are there bugs in the code written in the book. How do I change this code to play correctly and with the right sounds?
Thanks
import java.awt.*;
import javax.swing.*;
import javax.sound.midi.*;
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));
checkboxList=new ArrayList<JCheckBox>();
Box buttonBox=new Box(BoxLayout.Y_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 upTempo=new JButton("Tempo Up");
upTempo.addActionListener(new MyUpTempoListener());
buttonBox.add(upTempo);
JButton downTempo=new JButton("Tempo Down");
downTempo.addActionListener(new MyDownTempoListener());buttonBox.add(downTempo);
Box nameBox=new Box(BoxLayout.Y_AXIS);
for(int i=0;i< 16;i++){
nameBox.add(new Label(instrumentNames[i]));
}
background.add(BorderLayout.EAST,buttonBox);
background.add(BorderLayout.WEST,nameBox);
theFrame.getContentPane().add(background);
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();
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.start();
sequencer.setTempoInBPM(120);
}catch(Exception e){
e.printStackTrace();
}
} // close buildTrackAndStart method
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();}
} // 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 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;
}
} // close class
Upvotes: 0
Views: 351
Reputation: 7910
I recall building this 10 years ago. I can't recall if it was 100% successful, and I can't find the project in any of my old Eclipse workspace archives. I think it mostly worked but also convinced me to avoid Java Midi. (Am more interested in synthesis, anyway.) I have a few miscellaneous notes and corrections I made to myself about the code in pencil, an old copy of the book, but I don't think any would cause the issues you are describing.
I am noticing that the code you are showing is different from the listing in the appendix of my edition (copyright 2003!). Maybe you have the second edition? Given that the code survived two editions, chances are that it was working pretty well at the time.
Have you checked their errata page? Be sure to check the unconfirmed errata as well. Also, did you type in the code yourself or import it?
Almost certainly the MIDI system that you are accessing with the program is laid out differently from the standard that used to be implemented on most PCs. I never did get a good handle on what sound cards or sound systems used which midi mappings to voices, but it sure sounds like the percussion mapping you are referencing is different. Your system may have more than one percussion set, so it could be a matter of checking out what you are accessing and whether it is a classic GM 1 Sound Set or not.
I seem to recall either or both authors Sierra and Bates had (or still have) an association with the forum Javaranch. You might have some luck cross posting there. If you get an answer from that site, please come back here and resolve this question.
Upvotes: 2