6
\$\begingroup\$

This program offers 8 notes C, D, ..., A, B, C on keyboard keys 1, 2, ..., 8. Also, by typing Left or Right, you can cycle over MIDI instruments.

KeyboardPiano.java:

package net.coderodde.music;

import java.awt.Canvas;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.sound.midi.Instrument;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Soundbank;
import javax.sound.midi.Synthesizer;
import javax.swing.JFrame;

public class KeyboardPiano {

    private static final String APP_TITLE = "Keyboard Piano";

    private final JFrame frame = new JFrame(APP_TITLE);
    private final Canvas canvas = new Canvas();
    private Synthesizer synthesizer;
    private final MidiChannel[] midiChannels;
    private final Instrument[] instruments;
    private int instrumentIndex = 0;

    KeyboardPiano() {
        try {
            synthesizer = MidiSystem.getSynthesizer();
            synthesizer.open();
        } catch (MidiUnavailableException ex) {
            ex.printStackTrace();
            System.exit(1);
        }   

        this.midiChannels = synthesizer.getChannels();

        Soundbank bank = synthesizer.getDefaultSoundbank();

        synthesizer.loadAllInstruments(bank);


        this.instruments = synthesizer.getAvailableInstruments();
        synthesizer.loadAllInstruments(synthesizer.getDefaultSoundbank());
        synthesizer.getChannels()[0].programChange(instrumentIndex);

        System.out.println("[STATE] MIDI channels: " + midiChannels.length);
        System.out.println("[STATE] Instruments: " + instruments.length);
    }

    private void init() {   
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(canvas);
        frame.setSize(100, 100);
        frame.setResizable(false);
        canvas.addKeyListener(new KeyboardPianoListener());
        canvas.setFocusable(true);
        canvas.requestFocus();
    }

    private void show() {
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        KeyboardPiano keyboardPiano = new KeyboardPiano();
        keyboardPiano.init();
        keyboardPiano.show();
    }

    private final class KeyboardPianoListener implements KeyListener {

        @Override
        public void keyTyped(KeyEvent e) {

        }

        @Override
        public void keyPressed(KeyEvent e) {
            int keyCode = e.getExtendedKeyCode();
            int noteNumber = -1;

            switch (keyCode) {
                case KeyEvent.VK_1: {
                    noteNumber = 60;
                    break;
                }

                case KeyEvent.VK_2: {
                    noteNumber = 62;
                    break;
                }

                case KeyEvent.VK_3: {
                    noteNumber = 64;
                    break;
                }

                case KeyEvent.VK_4: {
                    noteNumber = 65;
                    break;
                }

                case KeyEvent.VK_5: {
                    noteNumber = 67;
                    break;
                }

                case KeyEvent.VK_6: {
                    noteNumber = 69;
                    break;
                }

                case KeyEvent.VK_7: {
                    noteNumber = 71;
                    break;
                }

                case KeyEvent.VK_8: {
                    noteNumber = 72;
                    break;
                }

                case KeyEvent.VK_LEFT: {
                    if (instrumentIndex == 0) {
                        instrumentIndex = instruments.length - 1;
                    } else {
                        instrumentIndex--;
                    }

                    synthesizer.getChannels()[0].programChange(instrumentIndex);
                    System.out.println("Switched to " + 
                                       instruments[instrumentIndex].getName());
                    break;
                }

                case KeyEvent.VK_RIGHT: {
                    if (instrumentIndex == instruments.length - 1) {
                        instrumentIndex = 0;
                    } else {
                        instrumentIndex++;
                    }

                    synthesizer.getChannels()[0].programChange(instrumentIndex);
                    System.out.println("Switched to " + 
                                       instruments[instrumentIndex].getName());
                    break;
                }
            }

            if (noteNumber != -1) {
                midiChannels[0].noteOn(noteNumber, 600);
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
            int keyCode = e.getExtendedKeyCode();
            int noteNumber = -1;

            switch (keyCode) {
                case KeyEvent.VK_1: {
                    noteNumber = 60;
                    break;
                }

                case KeyEvent.VK_2: {
                    noteNumber = 62;
                    break;
                }

                case KeyEvent.VK_3: {
                    noteNumber = 64;
                    break;
                }

                case KeyEvent.VK_4: {
                    noteNumber = 65;
                    break;
                }

                case KeyEvent.VK_5: {
                    noteNumber = 67;
                    break;
                }

                case KeyEvent.VK_6: {
                    noteNumber = 69;
                    break;
                }

                case KeyEvent.VK_7: {
                    noteNumber = 71;
                    break;
                }

                case KeyEvent.VK_8: {
                    noteNumber = 72;
                    break;
                }
            }

            if (noteNumber != -1) {
                midiChannels[0].noteOff(noteNumber, 600);
            }
        }
    }
}

Any critique much appreciated.

\$\endgroup\$
6
\$\begingroup\$

Repetition, specially when it occurs with switch cases is both bad for you and for your code. The switch (keyCode) is present two times in your code and all it does (almost) is just to map an integer. And I bolded map because that if that is what you want to do then we can use the HashMap to specify the note number that a key matches to.

private HashMap<Integer, Integer> mapNotes = new HashMap<Integer, Integer>();

KeyboardPiano() {
    try {
        synthesizer = MidiSystem.getSynthesizer();
        synthesizer.open();
    } catch (MidiUnavailableException ex) {
        ex.printStackTrace();
        System.exit(1);
    }   

    mapNotes.put(KeyEvent.VK_1, 60);
    mapNotes.put(KeyEvent.VK_2, 62);
    mapNotes.put(KeyEvent.VK_3, 64);
    mapNotes.put(KeyEvent.VK_4, 65);
    mapNotes.put(KeyEvent.VK_5, 67);
    mapNotes.put(KeyEvent.VK_6, 69);
    mapNotes.put(KeyEvent.VK_7, 71);
    mapNotes.put(KeyEvent.VK_8, 72);
    //...
}

And then use this same map

 public void keyPressed(KeyEvent e) {
    int keyCode = e.getExtendedKeyCode();
    int noteNumber = -1;

    if(mapNotes.containsKey(keyCode)){
        noteNumber = mapNotes.get(keyCode);
    }
    //...
 }

The switch statment in keyPressed can also be simplified because there is common code. The following code is executed in both left and right key press.

synthesizer.getChannels()[0].programChange(instrumentIndex);
System.out.println("Switched to " + 
                 instruments[instrumentIndex].getName());

With everything put togheter the keyPressedbecomes the following:

public void keyPressed(KeyEvent e) {
    int keyCode = e.getExtendedKeyCode();
    int noteNumber = -1;

    if(mapNotes.containsKey(keyCode)){
        noteNumber = mapNotes.get(keyCode);
    }else{
        switch (keyCode) {
            case KeyEvent.VK_LEFT: {
                if (instrumentIndex == 0) {
                    instrumentIndex = instruments.length - 1;
                } else {
                    instrumentIndex--;
                }
                break;
            }
            case KeyEvent.VK_RIGHT: {
                if (instrumentIndex == instruments.length - 1) {
                    instrumentIndex = 0;
                } else {
                    instrumentIndex++;
                }
                break;
            }
        }
        synthesizer.getChannels()[0].programChange(instrumentIndex);
        System.out.println("Switched to " + 
                           instruments[instrumentIndex].getName());
    }

    if (noteNumber != -1) {
        midiChannels[0].noteOn(noteNumber, 600);
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.