org.monome.pages.MIDIKeyboardJulienBPage.java Source code

Java tutorial

Introduction

Here is the source code for org.monome.pages.MIDIKeyboardJulienBPage.java

Source

/*
 *  MIDIKeyboardJulienBPage.java
 * 
 *  Copyright (c) 2008, Tom Dinchak, Julien Bayle
 * 
 *  This file is part of Pages.
 *
 *  Pages is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Pages is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  You should have received a copy of the GNU General Public License
 *  along with Pages; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

package org.monome.pages;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.apache.commons.lang.StringEscapeUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * The MIDI Faders page.  Usage information is available at:
 * 
 * http://code.google.com/p/monome-pages/wiki/MIDIKeyboardPage
 *   
 * @author Tom Dinchak, Julien Bayle
 *
 */
public class MIDIKeyboardJulienBPage implements Page, ActionListener {

    /**
     * The MonomeConfiguration that this page belongs to
     */
    MonomeConfiguration monome;

    /**
     * The index of this page (the page number) 
     */
    int index;

    /**
     * The GUI for this page
     */
    JPanel panel;

    /**
     * The selected MIDI channel (8x8 only)
     */
    private int midiChannel = 0;

    /**
     * The octave offset for each row (128 and 256 only) 
     */
    private int[] octave = new int[16];

    /**
     * The selected scale
     */
    private int myScale = 0;

    /**
     * The semitones between each note in the selected scale 
     */
    private int[][] scales = { { 2, 2, 1, 2, 2, 2, 1 }, // major
            { 2, 1, 2, 2, 1, 2, 2 }, // natural minor
            { 3, 1, 1, 2, 2, 1, 2 }, // blues/r&r
            { 1, 2, 1, 3, 1, 2, 2 }, // indianish
            { 1, 3, 2, 2, 2, 1, 1 }, // enigmatic
            { 1, 3, 2, 2, 2, 1, 1 }, // enigmatic2
            { 1, 3, 2, 2, 2, 1, 1 }, // enigmatic3
            { 1, 3, 2, 2, 2, 1, 1 }, // enigmatic4
    };

    /**
     * The selected key 
     */
    private int myKey = 0;

    /**
     * The starting note for each key (from C-2 to B-2) 
     */
    private int[] keys = { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59 };

    /**
     * Stores the note on / off state of all MIDI notes
     */
    private int[][] notesOn = new int[16][128];

    /**
     * The MIDI output devices
     */
    private ArrayList<Receiver> midiReceivers = new ArrayList<Receiver>();

    /**
     * The names of the MIDI output devices 
     */
    private ArrayList<String> midiDeviceNames = new ArrayList<String>();

    /**
     * The name of the page 
     */
    private String pageName = "MIDI Keyboard Julien";
    private JLabel pageNameLBL;

    /**
     * @param monome The MonomeConfiguration object this page belongs to
     * @param index The index of this page (the page number)
     */
    public MIDIKeyboardJulienBPage(MonomeConfiguration monome, int index) {
        this.monome = monome;
        this.index = index;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#handlePress(int, int, int)
     */
    public void handlePress(int x, int y, int value) {

        // if this is a 128 or 256 then handle presses differently from a 64 or 40h
        if (this.monome.sizeX > 8) {
            this.handlePress256(x, y, value);
        } else {
            this.handlePress64(x, y, value);
        }
    }

    /**
     * Handles a button press for a 64 / 40h monome
     * 
     * @param x The x value of the button press received
     * @param y The y value of the button press received
     * @param value The type of event (1 = press, 0 = release)
     */
    public void handlePress64(int x, int y, int value) {
        if (value == 1) {

            if (y >= 6) {
                if (y == 6) {// select the midi channel
                    this.midiChannel = x;
                    for (int i = 0; i < 8; i++) {
                        if (this.midiChannel == i) {
                            this.monome.led(i, y, 1, this.index);
                        } else {
                            this.monome.led(i, y, 0, this.index);
                        }
                    }
                } else if (y == 7) {// select scale
                    this.monome.led(this.myScale, y, 0, this.index);
                    this.myScale = x;
                    this.monome.led(this.myScale, y, 1, this.index);
                }
                this.stopNotes();

            } else {
                this.stopNotes(); // ???
            }
        }

        // for presses and releases in the keyboard area, send the note on message on press
        // and send the note off on message release
        if (y < 6 && x < 7) {
            int velocity = value * 127;
            int channel = this.midiChannel;
            int note_num = this.getNoteNumber(x) + ((y - 3) * 12);
            this.playNote(note_num, velocity, channel);
            this.notesOn[channel][note_num] = value;
            this.monome.led(x, y, value, this.index);

            if (value == 1) {
                System.out.println("Midi Channel: " + this.midiChannel);
                System.out.println("X: " + x);
                System.out.println("Y: " + y);
                System.out.println("Scale: " + this.myScale);
                System.out.println(" ");
            }
        }
    }

    /**
     * Handles a button press for a 128 / 256 monome
     * 
     * @param x The x value of the button press received
     * @param y The y value of the button press received
     * @param value The type of event (1 = press, 0 = release)
     */
    public void handlePress256(int x, int y, int value) {

        if (value == 1) {
            // bottom row - set the key or scale
            if (y == (this.monome.sizeY - 1)) {
                if (x < 12) {
                    this.myKey = x;
                }
                if (x >= 12) {
                    this.myScale = (x - 12);
                }
                this.stopNotes();
                // set the octave offset
            } else {

                // minus 2 octaves
                if (x == 14) {
                    if (this.octave[y] == -1) {
                        return;
                    } else {
                        this.octave[y] -= 1;
                    }
                    if (this.octave[y] == 0) {
                        this.monome.led(14, y, 0, this.index);
                        this.monome.led(15, y, 0, this.index);
                    }
                    if (this.octave[y] == -1) {
                        this.monome.led(14, y, 1, this.index);
                        this.monome.led(15, y, 0, this.index);
                    }
                    return;
                }

                // plus 2 octaves
                if (x == 15) {
                    if (this.octave[y] == 1) {
                        return;
                    } else {
                        this.octave[y] += 1;
                    }
                    if (this.octave[y] == 0) {
                        this.monome.led(14, y, 0, this.index);
                        this.monome.led(15, y, 0, this.index);
                    }
                    if (this.octave[y] == 1) {
                        this.monome.led(14, y, 0, this.index);
                        this.monome.led(15, y, 1, this.index);
                    }
                    return;
                }
                this.stopNotes();
            }
        }

        // play the note
        if (y != (this.monome.sizeY - 1) && x < 14) {
            int velocity = value * 127;
            int channel = (int) Math.floor(y / 3);
            int note_num = this.getNoteNumber(x) + (this.octave[y] * 24);
            this.playNote(note_num, velocity, channel);
            if (x < 14) {
                this.monome.led(x, y, value, this.index);
            }
        } else if (y == (this.monome.sizeY - 1)) {
            for (int i = 0; i < 12; i++) {
                if (this.myKey == i) {
                    this.monome.led(i, (this.monome.sizeY - 1), 1, this.index);
                } else {
                    this.monome.led(i, (this.monome.sizeY - 1), 0, this.index);
                }
            }
            for (int i = 0; i < 4; i++) {
                if (this.myScale == i) {
                    this.monome.led(i + 12, (this.monome.sizeY - 1), 1, this.index);
                } else {
                    this.monome.led(i + 12, (this.monome.sizeY - 1), 0, this.index);
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#handleTick()
     */
    public void handleTick() {
        return;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#handleReset()
     */
    public void handleReset() {
        this.redrawMonome();
    }

    private void stopNotes() {
        ShortMessage note_out = new ShortMessage();
        for (int chan = 0; chan < 16; chan++) {
            for (int i = 0; i < 128; i++) {
                if (this.notesOn[chan][i] == 1) {
                    try {
                        note_out.setMessage(ShortMessage.NOTE_OFF, chan, i, 0);
                        for (int j = 0; j < midiReceivers.size(); j++) {
                            midiReceivers.get(j).send(note_out, -1);
                        }
                    } catch (InvalidMidiDataException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * Plays a MIDI note.  0 velocity will send a note off, and > 0 velocity will send a note on.
     * 
     * @param note_num
     * @param velocity
     * @param channel
     */
    public void playNote(int note_num, int velocity, int channel) {
        ShortMessage note_out = new ShortMessage();
        try {
            if (velocity == 0) {
                note_out.setMessage(ShortMessage.NOTE_OFF, channel, note_num, velocity);
            } else {
                note_out.setMessage(ShortMessage.NOTE_ON, channel, note_num, velocity);
            }
            for (int i = 0; i < midiReceivers.size(); i++) {
                midiReceivers.get(i).send(note_out, -1);
            }
        } catch (InvalidMidiDataException e) {
            e.printStackTrace();
        }
    }

    /**
     * Convert a button press to a MIDI note number.
     * 
     * @param y The y value of the button pressed
     * @return The MIDI note number
     */
    public int getNoteNumber(int y) {
        int offset = 0;
        int note;

        if (y >= 7) {
            y -= 7;
            offset = 12;
        }

        note = this.keys[this.myKey];

        for (int i = 0; i < y; i++) {
            note += this.scales[this.myScale][i];
        }

        note += offset;
        return note;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#redrawMonome()
     */
    public void redrawMonome() {
        // for 128 / 256 monomes
        if (this.monome.sizeX > 8) {
            this.redrawMonome256();
            // for 64 / 40h monomes
        } else {
            this.redrawMonome64();
        }
    }

    /**
     * Redraw this page on a 64 or 40h monome.
     */
    public void redrawMonome64() {
        // everything off except the midi channel selection, the key and the scale
        for (int x = 0; x < this.monome.sizeX; x++) {
            for (int y = 0; y < this.monome.sizeY; y++) {
                if (x < 7 && y < 6) {
                    this.monome.led(x, y, 0, this.index);

                } else if (y >= 6) {
                    if (y == 7 && this.myScale == x) {
                        this.monome.led(x, y, 1, this.index);
                    } else if (y == 6 && this.midiChannel == x) {
                        this.monome.led(x, y, 1, this.index);
                    } else {
                        this.monome.led(x, y, 0, this.index);
                    }

                } else {
                    this.monome.led(x, y, 0, this.index);
                }
            }
        }
    }

    /**
     * Redraws this page for a 128 or 256 monome.
     */
    public void redrawMonome256() {
        // everything off except the key/scale selection and the octave offsets
        for (int x = 0; x < this.monome.sizeX; x++) {
            for (int y = 0; y < this.monome.sizeY; y++) {
                if (y == (this.monome.sizeY - 1)) {
                    if (x == this.myKey) {
                        this.monome.led(x, y, 1, this.index);
                    } else {
                        this.monome.led(x, y, 0, this.index);
                    }

                    if (x >= 12) {
                        if ((x - 12) == this.myScale) {
                            this.monome.led(x, y, 1, this.index);
                        } else {
                            this.monome.led(x, y, 0, this.index);
                        }
                    }
                } else if (y != (this.monome.sizeY - 1) && x == 14) {
                    if (this.octave[y] == -1) {
                        this.monome.led(x, y, 1, this.index);
                    }
                    if (this.octave[y] == 0) {
                        this.monome.led(x, y, 0, this.index);
                    }
                } else if (y != (this.monome.sizeY - 1) && x == 15) {
                    if (this.octave[y] == 1) {
                        this.monome.led(x, y, 1, this.index);
                    }
                    if (this.octave[y] == 0) {
                        this.monome.led(x, y, 0, this.index);
                    }
                } else {
                    this.monome.led(x, y, 0, this.index);
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#getName()
     */
    public String getName() {
        return pageName;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#setName()
     */
    public void setName(String name) {
        this.pageName = name;
        this.pageNameLBL.setText("Page " + (this.index + 1) + ": " + pageName);
        this.monome.setJMenuBar(this.monome.createMenuBar());
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#getPanel()
     */
    public JPanel getPanel() {
        if (this.panel != null) {
            return this.panel;
        }

        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));

        JPanel subPanel = new JPanel();
        pageNameLBL = new JLabel((this.index + 1) + ": MIDI Keyboard");
        subPanel.add(pageNameLBL);
        panel.add(subPanel);

        subPanel = new JPanel();
        JButton button = new JButton("Add MIDI Output");
        button.addActionListener(this);
        subPanel.add(button);
        panel.add(subPanel);

        this.panel = panel;
        return panel;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#send(javax.sound.midi.MidiMessage, long)
     */
    public void send(MidiMessage message, long timeStamp) {
        return;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#toXml()
     */
    public String toXml() {
        String xml = "";
        xml += "      <name>MIDI Keyboard JulienB Page</name>\n";
        xml += "      <pageName>" + this.pageName + "</pageName>\n";
        for (int i = 0; i < this.midiDeviceNames.size(); i++) {
            xml += "      <selectedmidioutport>" + StringEscapeUtils.escapeXml(this.midiDeviceNames.get(i))
                    + "</selectedmidioutport>\n";
        }
        return xml;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#actionPerformed(java.awt.event.ActionEvent)
     */
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.getActionCommand());
        if (e.getActionCommand().equals("Add MIDI Output")) {
            String[] midiOutOptions = this.monome.getMidiOutOptions();
            String deviceName = (String) JOptionPane.showInputDialog(this.monome, "Choose a MIDI Output to add",
                    "Add MIDI Output", JOptionPane.PLAIN_MESSAGE, null, midiOutOptions, "");

            if (deviceName == null) {
                return;
            }

            this.addMidiOutDevice(deviceName);

        }
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#addMidiOutDevice(java.lang.String)
     */
    public void addMidiOutDevice(String deviceName) {
        Receiver receiver = this.monome.getMidiReceiver(deviceName);

        for (int i = 0; i < this.midiReceivers.size(); i++) {
            if (this.midiReceivers.get(i).equals(receiver)) {
                System.out.println("Receiver already connected");
                return;
            }
        }
        this.midiReceivers.add(receiver);
        this.midiDeviceNames.add(deviceName);
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#getCacheDisabled()
     */
    public boolean getCacheDisabled() {
        return false;
    }

    /* (non-Javadoc)
     * @see org.monome.pages.Page#destroyPage()
     */
    public void destroyPage() {
        return;
    }

    public void clearPanel() {
        this.panel = null;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public void handleADC(int adcNum, float value) {
        // TODO Auto-generated method stub

    }

    public void configure(Element pageElement) {
        NodeList nameNL = pageElement.getElementsByTagName("pageName");
        Element el = (Element) nameNL.item(0);
        if (el != null) {
            NodeList nl = el.getChildNodes();
            String name = ((Node) nl.item(0)).getNodeValue();
            this.setName(name);
        }
    }

    public void handleADC(float x, float y) {
        // TODO Auto-generated method stub

    }

    public boolean isTiltPage() {
        // TODO Auto-generated method stub
        return false;
    }

    public ADCOptions getAdcOptions() {
        // TODO Auto-generated method stub
        return null;
    }

    public void setAdcOptions(ADCOptions options) {
        // TODO Auto-generated method stub

    }
}