| /* Receive Incoming USB Host MIDI using functions. As usbMIDI | |||||
| reads incoming messages, handler functions are run. | |||||
| See the InputRead example for the non-function alterative. | |||||
| This very long example demonstrates all possible handler | |||||
| functions. Most applications need only some of these. | |||||
| This example is meant to allow easy copy-and-paste of the | |||||
| desired functions. | |||||
| Use the Arduino Serial Monitor to view the messages | |||||
| as Teensy receives them by USB MIDI | |||||
| You must select MIDI from the "Tools > USB Type" menu | |||||
| This example code is in the public domain. | |||||
| */ | |||||
| #include <USBHost_t36.h> | |||||
| USBHost myusb; | |||||
| USBHub hub1(myusb); | |||||
| USBHub hub2(myusb); | |||||
| MIDIDevice midi1(myusb); | |||||
| void setup() { | |||||
| Serial.begin(115200); | |||||
| // Wait 1.5 seconds before turning on USB Host. If connected USB devices | |||||
| // use too much power, Teensy at least completes USB enumeration, which | |||||
| // makes isolating the power issue easier. | |||||
| delay(1500); | |||||
| Serial.println("USB Host InputFunctions example"); | |||||
| delay(10); | |||||
| myusb.begin(); | |||||
| midi1.setHandleNoteOn(myNoteOn); | |||||
| midi1.setHandleNoteOff(myNoteOff); | |||||
| midi1.setHandleAfterTouchPoly(myAfterTouchPoly); | |||||
| midi1.setHandleControlChange(myControlChange); | |||||
| midi1.setHandleProgramChange(myProgramChange); | |||||
| midi1.setHandleAfterTouchChannel(myAfterTouchChannel); | |||||
| midi1.setHandlePitchChange(myPitchChange); | |||||
| // Only one of these System Exclusive handlers will actually be | |||||
| // used. See the comments below for the difference between them. | |||||
| midi1.setHandleSystemExclusive(mySystemExclusiveChunk); | |||||
| midi1.setHandleSystemExclusive(mySystemExclusive); | |||||
| midi1.setHandleTimeCodeQuarterFrame(myTimeCodeQuarterFrame); | |||||
| midi1.setHandleSongPosition(mySongPosition); | |||||
| midi1.setHandleSongSelect(mySongSelect); | |||||
| midi1.setHandleTuneRequest(myTuneRequest); | |||||
| midi1.setHandleClock(myClock); | |||||
| midi1.setHandleStart(myStart); | |||||
| midi1.setHandleContinue(myContinue); | |||||
| midi1.setHandleStop(myStop); | |||||
| midi1.setHandleActiveSensing(myActiveSensing); | |||||
| midi1.setHandleSystemReset(mySystemReset); | |||||
| // This generic System Real Time handler is only used if the | |||||
| // more specific ones are not set. | |||||
| midi1.setHandleRealTimeSystem(myRealTimeSystem); | |||||
| } | |||||
| void loop() { | |||||
| // The handler functions are called when midi1 reads data. They | |||||
| // will not be called automatically. You must call midi1.read() | |||||
| // regularly from loop() for midi1 to actually read incoming | |||||
| // data and run the handler functions as messages arrive. | |||||
| myusb.Task(); | |||||
| midi1.read(); | |||||
| } | |||||
| void myNoteOn(byte channel, byte note, byte velocity) { | |||||
| // When a USB device with multiple virtual cables is used, | |||||
| // midi1.getCable() can be used to read which of the virtual | |||||
| // MIDI cables received this message. | |||||
| Serial.print("Note On, ch="); | |||||
| Serial.print(channel, DEC); | |||||
| Serial.print(", note="); | |||||
| Serial.print(note, DEC); | |||||
| Serial.print(", velocity="); | |||||
| Serial.println(velocity, DEC); | |||||
| } | |||||
| void myNoteOff(byte channel, byte note, byte velocity) { | |||||
| Serial.print("Note Off, ch="); | |||||
| Serial.print(channel, DEC); | |||||
| Serial.print(", note="); | |||||
| Serial.print(note, DEC); | |||||
| Serial.print(", velocity="); | |||||
| Serial.println(velocity, DEC); | |||||
| } | |||||
| void myAfterTouchPoly(byte channel, byte note, byte velocity) { | |||||
| Serial.print("AfterTouch Change, ch="); | |||||
| Serial.print(channel, DEC); | |||||
| Serial.print(", note="); | |||||
| Serial.print(note, DEC); | |||||
| Serial.print(", velocity="); | |||||
| Serial.println(velocity, DEC); | |||||
| } | |||||
| void myControlChange(byte channel, byte control, byte value) { | |||||
| Serial.print("Control Change, ch="); | |||||
| Serial.print(channel, DEC); | |||||
| Serial.print(", control="); | |||||
| Serial.print(control, DEC); | |||||
| Serial.print(", value="); | |||||
| Serial.println(value, DEC); | |||||
| } | |||||
| void myProgramChange(byte channel, byte program) { | |||||
| Serial.print("Program Change, ch="); | |||||
| Serial.print(channel, DEC); | |||||
| Serial.print(", program="); | |||||
| Serial.println(program, DEC); | |||||
| } | |||||
| void myAfterTouchChannel(byte channel, byte pressure) { | |||||
| Serial.print("After Touch, ch="); | |||||
| Serial.print(channel, DEC); | |||||
| Serial.print(", pressure="); | |||||
| Serial.println(pressure, DEC); | |||||
| } | |||||
| void myPitchChange(byte channel, int pitch) { | |||||
| Serial.print("Pitch Change, ch="); | |||||
| Serial.print(channel, DEC); | |||||
| Serial.print(", pitch="); | |||||
| Serial.println(pitch, DEC); | |||||
| } | |||||
| // This 3-input System Exclusive function is more complex, but allows you to | |||||
| // process very large messages which do not fully fit within the midi1's | |||||
| // internal buffer. Large messages are given to you in chunks, with the | |||||
| // 3rd parameter to tell you which is the last chunk. This function is | |||||
| // a Teensy extension, not available in the Arduino MIDI library. | |||||
| // | |||||
| void mySystemExclusiveChunk(const byte *data, uint16_t length, bool last) { | |||||
| Serial.print("SysEx Message: "); | |||||
| printBytes(data, length); | |||||
| if (last) { | |||||
| Serial.println(" (end)"); | |||||
| } else { | |||||
| Serial.println(" (to be continued)"); | |||||
| } | |||||
| } | |||||
| // This simpler 2-input System Exclusive function can only receive messages | |||||
| // up to the size of the internal buffer. Larger messages are truncated, with | |||||
| // no way to receive the data which did not fit in the buffer. If both types | |||||
| // of SysEx functions are set, the 3-input version will be called by midi1. | |||||
| // | |||||
| void mySystemExclusive(byte *data, unsigned int length) { | |||||
| Serial.print("SysEx Message: "); | |||||
| printBytes(data, length); | |||||
| Serial.println(); | |||||
| } | |||||
| void myTimeCodeQuarterFrame(byte data) { | |||||
| static char SMPTE[8]={'0','0','0','0','0','0','0','0'}; | |||||
| static byte fps=0; | |||||
| byte index = data >> 4; | |||||
| byte number = data & 15; | |||||
| if (index == 7) { | |||||
| fps = (number >> 1) & 3; | |||||
| number = number & 1; | |||||
| } | |||||
| if (index < 8 || number < 10) { | |||||
| SMPTE[index] = number + '0'; | |||||
| Serial.print("TimeCode: "); // perhaps only print when index == 7 | |||||
| Serial.print(SMPTE[7]); | |||||
| Serial.print(SMPTE[6]); | |||||
| Serial.print(':'); | |||||
| Serial.print(SMPTE[5]); | |||||
| Serial.print(SMPTE[4]); | |||||
| Serial.print(':'); | |||||
| Serial.print(SMPTE[3]); | |||||
| Serial.print(SMPTE[2]); | |||||
| Serial.print('.'); | |||||
| Serial.print(SMPTE[1]); // perhaps add 2 to compensate for MIDI latency? | |||||
| Serial.print(SMPTE[0]); | |||||
| switch (fps) { | |||||
| case 0: Serial.println(" 24 fps"); break; | |||||
| case 1: Serial.println(" 25 fps"); break; | |||||
| case 2: Serial.println(" 29.97 fps"); break; | |||||
| case 3: Serial.println(" 30 fps"); break; | |||||
| } | |||||
| } else { | |||||
| Serial.print("TimeCode: invalid data = "); | |||||
| Serial.println(data, HEX); | |||||
| } | |||||
| } | |||||
| void mySongPosition(uint16_t beats) { | |||||
| Serial.print("Song Position, beat="); | |||||
| Serial.println(beats); | |||||
| } | |||||
| void mySongSelect(byte songNumber) { | |||||
| Serial.print("Song Select, song="); | |||||
| Serial.println(songNumber, DEC); | |||||
| } | |||||
| void myTuneRequest() { | |||||
| Serial.println("Tune Request"); | |||||
| } | |||||
| void myClock() { | |||||
| Serial.println("Clock"); | |||||
| } | |||||
| void myStart() { | |||||
| Serial.println("Start"); | |||||
| } | |||||
| void myContinue() { | |||||
| Serial.println("Continue"); | |||||
| } | |||||
| void myStop() { | |||||
| Serial.println("Stop"); | |||||
| } | |||||
| void myActiveSensing() { | |||||
| Serial.println("Actvice Sensing"); | |||||
| } | |||||
| void mySystemReset() { | |||||
| Serial.println("System Reset"); | |||||
| } | |||||
| void myRealTimeSystem(uint8_t realtimebyte) { | |||||
| Serial.print("Real Time Message, code="); | |||||
| Serial.println(realtimebyte, HEX); | |||||
| } | |||||
| void printBytes(const byte *data, unsigned int size) { | |||||
| while (size > 0) { | |||||
| byte b = *data++; | |||||
| if (b < 16) Serial.print('0'); | |||||
| Serial.print(b, HEX); | |||||
| if (size > 1) Serial.print(' '); | |||||
| size = size - 1; | |||||
| } | |||||
| } | |||||