Simple class for getting Midi natively with Processing

So I started learning a little bit of Processing and as a first exercise, I wanted to do a visualization of the midi data from one of my songs. The most common midi library used with processing was not able to detect midi inputs on my mac, so I had to write my own class. I’m putting the code in here hoping that it can help someone else.

Oh, and some of the code was taken from here:

http://www.jsresources.org/examples/MidiInDump.html

import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;

/*** MyMidi Class implementation ***/

class MyMidi implements Receiver {
  public int[] lastNoteOnByChannels = new int[16];
  public MyMidi()
  {
    
  }
  
  public long seByteCount = 0;
	public long smByteCount = 0;
	public long seCount = 0;
	public long smCount = 0;

	private final String[]		sm_astrKeyNames = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};

	private final String[]		sm_astrKeySignatures = {"Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#"};
	private  final String[]		SYSTEM_MESSAGE_TEXT =
	{
		"System Exclusive (should not be in ShortMessage!)",
		"MTC Quarter Frame: ",
		"Song Position: ",
		"Song Select: ",
		"Undefined",
		"Undefined",
		"Tune Request",
		"End of SysEx (should not be in ShortMessage!)",
		"Timing clock",
		"Undefined",
		"Start",
		"Continue",
		"Stop",
		"Undefined",
		"Active Sensing",
		"System Reset"
	};

	private final String[]		QUARTER_FRAME_MESSAGE_TEXT =
	{
		"frame count LS: ",
		"frame count MS: ",
		"seconds count LS: ",
		"seconds count MS: ",
		"minutes count LS: ",
		"minutes count MS: ",
		"hours count LS: ",
		"hours count MS: "
	};

	private  final String[]		FRAME_TYPE_TEXT =
	{
		"24 frames/second",
		"25 frames/second",
		"30 frames/second (drop)",
		"30 frames/second (non-drop)",
	};
private char hexDigits[] = 
	   {'0', '1', '2', '3', 
	    '4', '5', '6', '7', 
	    '8', '9', 'A', 'B', 
	    'C', 'D', 'E', 'F'};


  public String getKeyName(int nKeyNumber)
	{
		if (nKeyNumber > 127)
		{
			return "illegal value";
		}
		else
		{
			int	nNote = nKeyNumber % 12;
			int	nOctave = nKeyNumber / 12;
			return sm_astrKeyNames[nNote] + (nOctave - 1);
		}
	}


	public int get14bitValue(int nLowerPart, int nHigherPart)
	{
		return (nLowerPart & 0x7F) | ((nHigherPart & 0x7F) << 7);
	}
        
        private void noteOn(int note, int velocity, int channel){
            lastNoteOnByChannels[channel] = note;
            System.out.println(note + " " + velocity);
        }

  public String decodeMessage(ShortMessage message)
	{
		String	strMessage = null;
		switch (message.getCommand())
		{
		case 0x80:
			strMessage = "note Off " + getKeyName(message.getData1()) + " velocity: " + message.getData2();
			break;

		case 0x90:
			strMessage = "note On " + getKeyName(message.getData1()) + " velocity: " + message.getData2();
                        noteOn(message.getData1(), message.getData2(), message.getChannel());
			break;

		case 0xa0:
			strMessage = "polyphonic key pressure " + getKeyName(message.getData1()) + " pressure: " + message.getData2();
			break;

		case 0xb0:
			strMessage = "control change " + message.getData1() + " value: " + message.getData2();
			break;

		case 0xc0:
			strMessage = "program change " + message.getData1();
			break;

		case 0xd0:
			strMessage = "key pressure " + getKeyName(message.getData1()) + " pressure: " + message.getData2();
			break;

		case 0xe0:
			strMessage = "pitch wheel change " + get14bitValue(message.getData1(), message.getData2());
			break;

		case 0xF0:
			strMessage = SYSTEM_MESSAGE_TEXT[message.getChannel()];
			switch (message.getChannel())
			{
			case 0x1:
				int	nQType = (message.getData1() & 0x70) >> 4;
				int	nQData = message.getData1() & 0x0F;
				if (nQType == 7)
				{
					nQData = nQData & 0x1;
				}
				strMessage += QUARTER_FRAME_MESSAGE_TEXT[nQType] + nQData;
				if (nQType == 7)
				{
					int	nFrameType = (message.getData1() & 0x06) >> 1;
					strMessage += ", frame type: " + FRAME_TYPE_TEXT[nFrameType];
				}
				break;

			case 0x2:
				strMessage += get14bitValue(message.getData1(), message.getData2());
				break;

			case 0x3:
				strMessage += message.getData1();
				break;
			}
			break;

		default:
			strMessage = "unknown message: status = " + message.getStatus() + ", byte1 = " + message.getData1() + ", byte2 = " + message.getData2();
			break;
		}
		if (message.getCommand() != 0xF0)
		{
			int	nChannel = message.getChannel() + 1;
			String	strChannel = "channel " + nChannel + ": ";
			strMessage = strChannel + strMessage;
		}
		smCount++;
		smByteCount+=message.getLength();
		return "["+getHexString(message)+"] "+strMessage;
	}
private String intToHex(int i) {
		return ""+hexDigits[(i & 0xF0) >> 4]
		         +hexDigits[i & 0x0F];
	}

public String getHexString(ShortMessage sm)
	{
		// bug in J2SDK 1.4.1
		// return getHexString(sm.getMessage());
		int status = sm.getStatus();
		String res = intToHex(sm.getStatus());
		// if one-byte message, return
		switch (status) {
			case 0xF6:			// Tune Request
			case 0xF7:			// EOX
	    		// System real-time messages
			case 0xF8:			// Timing Clock
			case 0xF9:			// Undefined
			case 0xFA:			// Start
			case 0xFB:			// Continue
			case 0xFC:			// Stop
			case 0xFD:			// Undefined
			case 0xFE:			// Active Sensing
			case 0xFF: return res;
		}
		res += ' '+intToHex(sm.getData1());
		// if 2-byte message, return
		switch (status) {
			case 0xF1:			// MTC Quarter Frame
			case 0xF3:			// Song Select
					return res;
		}
		switch (sm.getCommand()) {
			case 0xC0:
			case 0xD0:
					return res;
		}
		// 3-byte messages left
		res += ' '+intToHex(sm.getData2());
		return res;
	}



public String getHexString(byte[] aByte)
	{
		StringBuffer	sbuf = new StringBuffer(aByte.length * 3 + 2);
		for (int i = 0; i < aByte.length; i++)
		{
			sbuf.append(' ');
			sbuf.append(hexDigits[(aByte[i] & 0xF0) >> 4]);
			sbuf.append(hexDigits[aByte[i] & 0x0F]);
			/*byte	bhigh = (byte) ((aByte[i] &  0xf0) >> 4);
			sbuf.append((char) (bhigh > 9 ? bhigh + 'A' - 10: bhigh + '0'));
			byte	blow = (byte) (aByte[i] & 0x0f);
			sbuf.append((char) (blow > 9 ? blow + 'A' - 10: blow + '0'));*/
		}
		return new String(sbuf);
	}


  public void send(MidiMessage message, long lTimeStamp){
    //System.out.println("Message received");
    String	strMessage = null;
		if (message instanceof ShortMessage)
		{
			strMessage = decodeMessage((ShortMessage) message);
                        System.out.println(strMessage);
		}

  }
  
  public void close(){
  }

  public void printMidiDevices() {
    MidiDevice.Info[] aInfos = MidiSystem.getMidiDeviceInfo();
    for (int i = 0; i < aInfos.length; i++) {
    try {
     MidiDevice device = MidiSystem.getMidiDevice(aInfos[i]);
       boolean bAllowsInput = (device.getMaxTransmitters() != 0);
       boolean bAllowsOutput = (device.getMaxReceivers() != 0);
       System.out.println("" + i + "  "
           + (bAllowsInput ? "IN " : "   ")
           + (bAllowsOutput ? "OUT " : "    ")
           + aInfos[i].getName() + ", " + aInfos[i].getVendor()
           + ", " + aInfos[i].getVersion() + ", "
           + aInfos[i].getDescription());
    }  
    catch (MidiUnavailableException e) {
       // device is obviously not available...
       System.err.println(e);
    }
  } 
  
    }
    MidiDevice inputDevice = null;
    Transmitter t = null;
    Receiver r = null;
    public void openForInput(int idx){
        try{
          MidiDevice.Info[] aInfos = MidiSystem.getMidiDeviceInfo();
          inputDevice = MidiSystem.getMidiDevice(aInfos[idx]);
          inputDevice.open();
          t = inputDevice.getTransmitter();
          t.setReceiver(this);
        }catch(MidiUnavailableException e){
          System.err.println(e);
          
        }
    }

}
/* end of MyMidi Class */


MyMidi myMidi;

void setup(){
  size(480,360);
  myMidi = new MyMidi();
  myMidi.printMidiDevices();
  // Set the index of the device to open for input according
  // to your system
  myMidi.openForInput(0);
}

void draw()
{
  background(255);
  if(myMidi.lastNoteOnByChannels[0] != 0){
    translate(width/2, height/2);
    rotate(myMidi.lastNoteOnByChannels[0]);
    rect(-10,-10,20,20);
  }
}