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); } }
Post a Comment
You must be logged in to post a comment.