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() & 0×0F;
if (nQType == 7)
{
nQData = nQData & 0×1;
}
strMessage += QUARTER_FRAME_MESSAGE_TEXT[nQType] + nQData;
if (nQType == 7)
{
int nFrameType = (message.getData1() & 0×06) >> 1;
strMessage += “, frame type: ” + FRAME_TYPE_TEXT[nFrameType];
}
break;
case 0×2:
strMessage += get14bitValue(message.getData1(), message.getData2());
break;
case 0×3:
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 & 0×0F];
}
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] & 0×0F]);
/*byte bhigh = (byte) ((aByte[i] & 0xf0) >> 4);
sbuf.append((char) (bhigh > 9 ? bhigh + ‘A’ - 10: bhigh + ‘0′));
byte blow = (byte) (aByte[i] & 0×0f);
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);
}
}






