| @@ -0,0 +1,250 @@ | |||
| /* 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; | |||
| } | |||
| } | |||