0

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;
                }
}
NMard
  • 25
  • 7
  • A real drum machine generates the notes in real time. I do not know if the sequencer allows adding to the sequence on the fly. – CL. May 30 '20 at 09:41
  • I see, I'll look into seeing it that is possible, but is there another approach you would reckon might be smoother than using the sequencer? @CL. – NMard May 30 '20 at 09:46
  • 1
    You could send the messages in real time, but that would require a high-resolution timer. – CL. May 30 '20 at 14:01

0 Answers0