|
|
|
|
|
|
|
|
|
|
|
/* 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; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|