diff options
| author | Donald Delmar Davis <don@suspectdevices.com> | 2013-04-24 22:15:21 -0700 |
|---|---|---|
| committer | Donald Delmar Davis <don@suspectdevices.com> | 2013-04-24 22:15:21 -0700 |
| commit | aa917d1d8fd5dde9934264bdaa0891e08a08fd1d (patch) | |
| tree | 665fcd4d8fdcee9501ff52f17734ac0af0f6546c /wirish | |
| parent | 3db89a7ef64570933fa152cd616c93424c8a5d50 (diff) | |
I am told that I need to commit at least once a day so people know I am alive...
Diffstat (limited to 'wirish')
| -rw-r--r-- | wirish/include/wirish/usb_midi.h | 186 | ||||
| -rw-r--r-- | wirish/usb_midi.cpp | 440 |
2 files changed, 597 insertions, 29 deletions
diff --git a/wirish/include/wirish/usb_midi.h b/wirish/include/wirish/usb_midi.h index 3ee1989..99f2ad8 100644 --- a/wirish/include/wirish/usb_midi.h +++ b/wirish/include/wirish/usb_midi.h @@ -35,29 +35,183 @@ #include <wirish/Print.h> #include <wirish/boards.h> -/** - * @brief Virtual serial terminal. +/* + * This is the Midi class. If you are just sending Midi data, you only need to make an + * instance of the class, passing it your serial port -- in most cases it looks like + * + * USBMidi midi(channel); + * + * then you don't need to do anything else; you can start using it to send Midi messages, + * e.g. + * + * midi.sendNoteOn(1, note, velocity); + * + * If you are using it to receive Midi data, it's a little more complex & you will need + * to subclass the Midi class. + * + * For people not used to C++ this may look confusing, but it's actually pretty easy. + * Note that you only need to write the functions for event types you want to handle. + * They should match the names & prototypes of the functions in the class; look at + * the functions in the Midi class below that have the keyword "virtual" to see which + * ones you can use. + * + * Here's an example of one that takes NOTE ON, NOTE OFF, and CONTROL CHANGE: + * + * class MyMidi : public USBMidi { + * public: + * + * // Need this to compile; it just hands things off to the Midi class. + * MyMidi(HardwareSerial &s) : Midi(s) {} + * + * void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) + * { + * if (note == 40) {digitalWrite(13, HIGH); } + * } + * + * void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) + * { + * if (note == 40) { digitalWrite(13, LOW); } + * } + * + * void handleControlChange(unsigned int channel, unsigned int controller, + * unsigned int value) + * { + * analogWrite(6, value * 2); + * } + * + * Then you need to make an instance of this class: + * + * MyMidi midi(); + * + * If receiving Midi data, you also need to call the poll function every time through + * loop(); e.g. + * + * void loop() { + * midi.poll(); + * } + * + * This causes the Midi class to read data from the serial port and process it. */ -class USBMidi : public Print { -public: - USBMidi(void); - - void begin(void); - void end(void); +class USBMidi { +private: + // The serial port used by this Midi instance (it takes complete control over the port) + + /* Private Receive Parameters */ + + // The channel this Midi instance receives data for (0 means all channels) + int channelIn_; + + // These are for keeping track of partial Midi messages as bytes come in + int recvMode_; + int recvByteCount_; + int recvEvent_; + int recvArg0_; + int recvBytesNeeded_; + int lastStatusSent_; + + /* Private Send Parameters */ + + // This controls whether every Midi message gets a command byte sent with it + // (Midi can just send a single command byte and then stream events without + // sending the command each time) + bool sendFullCommands_; + + /* Internal functions */ + + // Called whenever data is read from the serial port + void dispatchPacket(uint32 packet); + + // This doesn't work -- by making it private, we ensure nobody ever calls it + //Midi(); + +public: + // Set this parameter to anything other than 0 to cause every MIDI update to + // include a copy of the current event (e.g. for every note off to include + // the NOTE OFF event) -- by default, if an event is the same type of event as + // the last one sent, the event byte isn't sent; just the new note/velocity + static const unsigned int PARAM_SEND_FULL_COMMANDS = 0x1000; + + // Use this parameter to update the channel the MIDI code is looking for messages + // to. 0 means "all channels". + static const unsigned int PARAM_CHANNEL_IN = 0x1001; + + + // Constructor -- generally just use e.g. "Midi midi(Serial);" + USBMidi(); + + // Call to start the serial port, at given baud. For many applications + // the default parameters are just fine (which will cause messages for all + // MIDI channels to be delivered) + void begin(unsigned int channel = 0); + //void begin(); + void end(); + uint32 available(void); - - uint32 read(void *buf, uint32 len); - uint8 read(void); - - void write(uint8); - void write(const char *str); - void write(const void*, uint32); - + + uint32 readPackets(void *buf, uint32 len); + uint32 readPacket(void); + + void writePacket(uint32); +// void write(const char *str); + void writePackets(const void*, uint32); + uint8 isConnected(); uint8 pending(); + + + // Changes the updateable parameters (params are Midi::PARAM_SEND_FULL_COMMANDS or + // Midi::PARAM_CHANNEL_IN -- see above for what they mean) + void setParam(unsigned int param, unsigned int val); + + // Get current values for the user updateable parameters (params, etc same as above) + unsigned int getParam(unsigned int param); + + + // poll() should be called every time through loop() IF dealing with incoming MIDI + // (if you're only SENDING MIDI events from the Arduino, you don't need to call + // poll); it causes data to be read from the serial port and processed. + void poll(); + + // Call these to send MIDI messages of the given types + void sendNoteOff(unsigned int channel, unsigned int note, unsigned int velocity); + void sendNoteOn(unsigned int channel, unsigned int note, unsigned int velocity); + void sendVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity); + void sendControlChange(unsigned int channel, unsigned int controller, unsigned int value); + void sendProgramChange(unsigned int channel, unsigned int program); + void sendAfterTouch(unsigned int channel, unsigned int velocity); + void sendPitchChange(unsigned int pitch); + void sendSongPosition(unsigned int position); + void sendSongSelect(unsigned int song); + void sendTuneRequest(void); + void sendSync(void); + void sendStart(void); + void sendContinue(void); + void sendStop(void); + void sendActiveSense(void); + void sendReset(void); + + // Overload these in a subclass to get MIDI messages when they come in + virtual void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity); + virtual void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity); + virtual void handleVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity); + virtual void handleControlChange(unsigned int channel, unsigned int controller, unsigned int value); + virtual void handleProgramChange(unsigned int channel, unsigned int program); + virtual void handleAfterTouch(unsigned int channel, unsigned int velocity); + virtual void handlePitchChange(unsigned int pitch); + virtual void handleSongPosition(unsigned int position); + virtual void handleSongSelect(unsigned int song); + virtual void handleTuneRequest(void); + virtual void handleSync(void); + virtual void handleStart(void); + virtual void handleContinue(void); + virtual void handleStop(void); + virtual void handleActiveSense(void); + virtual void handleReset(void); }; + + #if BOARD_HAVE_MIDIUSB extern USBMidi MidiUSB; #endif diff --git a/wirish/usb_midi.cpp b/wirish/usb_midi.cpp index 54ce7a9..6926639 100644 --- a/wirish/usb_midi.cpp +++ b/wirish/usb_midi.cpp @@ -3,6 +3,7 @@ * * Copyright (c) 2010 Perry Hung. * Copyright (c) 2013 Magnus Lundin. + * Copyright (c) 2013 Donald Delmar Davis, Suspect Devices. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -23,6 +24,28 @@ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. + * + * Midi.cpp: Code for MIDI processing library, Arduino version + * + * (c) 2003-2008 Tymm Twillman <tymm@booyaka.com> + * + * This file is part of Tymm's Arduino Midi Library. + * + * Tymm's Arduino Midi Library is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 2.1 + * of the License, or (at your option) any later version. + * + * Tymm's Arduino Midi Library is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Tymm's Arduino Midi Library. If not, see + * <http://www.gnu.org/licenses/>. + * + * *****************************************************************************/ /** @@ -33,7 +56,6 @@ #include <string.h> #include <stdint.h> - #include <libmaple/nvic.h> #include <libmaple/usb_midi_device.h> #include <libmaple/usb.h> @@ -52,9 +74,27 @@ USBMidi::USBMidi(void) { #endif } -void USBMidi::begin(void) { +// Constructor -- set up defaults for variables, get ready for use (but don't +// take over serial port yet) + +void USBMidi::begin(unsigned int channel) { #if BOARD_HAVE_MIDIUSB usb_midi_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); + /* Not in proprietary stream */ + recvMode_ = 0; + /* No bytes recevied */ + recvByteCount_ = 0; + /* Not processing an event */ + recvEvent_ = 0; + /* No arguments to the event we haven't received */ + recvArg0_ = 0; + /* Not waiting for bytes to complete a message */ + recvBytesNeeded_ = 0; + // There was no last event. + lastStatusSent_ = false; + // Don't send the extra bytes; just send deltas + sendFullCommands_ = false; + #endif } @@ -64,15 +104,15 @@ void USBMidi::end(void) { #endif } -void USBMidi::write(uint8 ch) { - this->write(&ch, 1); +void USBMidi::writePacket(uint32 p) { + this->writePackets(&p, 1); } -void USBMidi::write(const char *str) { - this->write(str, strlen(str)); -} +//void USBMidi::write(const char *str) { +// this->write(str, strlen(str)); +//} -void USBMidi::write(const void *buf, uint32 len) { +void USBMidi::writePackets(const void *buf, uint32 len) { if (!this->isConnected() || !buf) { return; } @@ -105,7 +145,7 @@ uint32 USBMidi::available(void) { return usb_midi_data_available(); } -uint32 USBMidi::read(void *buf, uint32 len) { +uint32 USBMidi::readPackets(void *buf, uint32 len) { if (!buf) { return 0; } @@ -119,10 +159,10 @@ uint32 USBMidi::read(void *buf, uint32 len) { } /* Blocks forever until 1 byte is received */ -uint8 USBMidi::read(void) { - uint8 b; - this->read(&b, 1); - return b; +uint32 USBMidi::readPacket(void) { + uint32 p; + this->readPackets(&p, 1); + return p; } uint8 USBMidi::pending(void) { @@ -136,3 +176,377 @@ uint8 USBMidi::isConnected(void) { #if BOARD_HAVE_MIDIUSB USBMidi MidiUSB; #endif + + +// This is used for tracking when we're processing a proprietary stream of data +// The assigned value is arbitrary; just for internal use. +static const int MODE_PROPRIETARY = 0xff; + + +// These are midi status message types as sent on the wire +static const int STATUS_EVENT_NOTE_OFF = 0x80; +static const int STATUS_EVENT_NOTE_ON = 0x90; +static const int STATUS_EVENT_VELOCITY_CHANGE = 0xA0; +static const int STATUS_EVENT_CONTROL_CHANGE = 0xB0; +static const int STATUS_EVENT_PROGRAM_CHANGE = 0xC0; +static const int STATUS_AFTER_TOUCH = 0xD0; +static const int STATUS_PITCH_CHANGE = 0xE0; +static const int STATUS_START_PROPRIETARY = 0xF0; +static const int STATUS_SONG_POSITION = 0xF2; +static const int STATUS_SONG_SELECT = 0xF3; +static const int STATUS_TUNE_REQUEST = 0xF6; +static const int STATUS_END_PROPRIETARY = 0xF7; +static const int STATUS_SYNC = 0xF8; +static const int STATUS_START = 0xFA; +static const int STATUS_CONTINUE = 0xFB; +static const int STATUS_STOP = 0xFC; +static const int STATUS_ACTIVE_SENSE = 0xFE; +static const int STATUS_RESET = 0xFF; + +union EVENT_t { + uint32 i; + uint8 b[4]; + MIDI_EVENT_PACKET_t p; +}; + +// Handle decoding incoming MIDI traffic a byte at a time -- remembers +// what it needs to from one call to the next. +// +// This is a private function & not meant to be called from outside this class. +// It's used whenever data is available from the serial port. +// +void USBMidi::dispatchPacket(uint32 p) +{ + union EVENT_t e; + + e.i=p; + // !!!!!!!!!!!!!!!! FIX THIS VERY VERY SHORTLY !!!!!!!!!!!!!! + if (recvMode_ & MODE_PROPRIETARY + && CIN_IS_SYSEX(e.p.cin)) + { + /* If proprietary handling compiled in, just pass all data received + * after a START_PROPRIETARY event to proprietary_decode + * until get an END_PROPRIETARY event + */ + +#ifdef CONFIG_MIDI_PROPRIETARY + proprietaryDecode(byte); +#endif + + return; + } + + switch (e.p.cin) { + case CIN_3BYTE_SYS_COMMON: + if (e.p.midi0 == MIDIv1_SONG_POSITION_PTR) { + handleSongPosition(((uint16)e.p.midi2)<<7|((uint16)e.p.midi1)); + } + break; + + case CIN_2BYTE_SYS_COMMON: + switch (e.p.midi0) { + case MIDIv1_SONG_SELECT: + handleSongSelect(e.p.midi1); + break; + case MIDIv1_MTC_QUARTER_FRAME: + break; + } + break; + case CIN_NOTE_OFF: + handleNoteOff(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2); + break; + case CIN_NOTE_ON: + handleNoteOn(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2); + break; + case CIN_AFTER_TOUCH: + handleVelocityChange(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2); + break; + case CIN_CONTROL_CHANGE: + handleControlChange(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2); + break; + case CIN_PROGRAM_CHANGE: + handleProgramChange(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1); + break; + case CIN_CHANNEL_PRESSURE: + handleAfterTouch(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1); + break; + + case CIN_PITCH_WHEEL: + handlePitchChange(((uint16)e.p.midi2)<<7|((uint16)e.p.midi1)); + break; + case CIN_1BYTE: + switch (e.p.midi0) { + case MIDIv1_CLOCK: + handleSync(); + break; + case MIDIv1_TICK: + break; + case MIDIv1_START: + handleStart(); + break; + case MIDIv1_CONTINUE: + handleContinue(); + break; + case MIDIv1_STOP: + handleStop(); + break; + case MIDIv1_ACTIVE_SENSE: + handleActiveSense(); + break; + case MIDIv1_RESET: + handleReset(); + break; + case STATUS_TUNE_REQUEST: + handleTuneRequest(); + break; + + default: + break; + } + break; + } +} + + +// Try to read data at serial port & pass anything read to processing function +void USBMidi::poll(void) +{ while(available()) { + dispatchPacket(readPacket()); + } +} + +static union EVENT_t outPacket; + +// Send Midi NOTE OFF message to a given channel, with note 0-127 and velocity 0-127 +void USBMidi::sendNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_NOTE_OFF; + outPacket.p.midi0=MIDIv1_NOTE_OFF|(channel & 0x0f); + outPacket.p.midi1=note; + outPacket.p.midi2=velocity; + writePacket(outPacket.i); + +} + + +// Send Midi NOTE ON message to a given channel, with note 0-127 and velocity 0-127 +void USBMidi::sendNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_NOTE_ON; + outPacket.p.midi0=MIDIv1_NOTE_ON|(channel & 0x0f); + outPacket.p.midi1=note; + outPacket.p.midi2=velocity; + writePacket(outPacket.i); + +} + +// Send a Midi VELOCITY CHANGE message to a given channel, with given note 0-127, +// and new velocity 0-127 +// Note velocity change == polyphonic aftertouch. +// Note aftertouch == channel pressure. +void USBMidi::sendVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_AFTER_TOUCH; + outPacket.p.midi0=MIDIv1_AFTER_TOUCH |(channel & 0x0f); + outPacket.p.midi1=note; + outPacket.p.midi2=velocity; + writePacket(outPacket.i); + +} + + +// Send a Midi CC message to a given channel, as a given controller 0-127, with given +// value 0-127 +void USBMidi::sendControlChange(unsigned int channel, unsigned int controller, unsigned int value) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_CONTROL_CHANGE; + outPacket.p.midi0=MIDIv1_CONTROL_CHANGE |(channel & 0x0f); + outPacket.p.midi1=controller; + outPacket.p.midi2=value; + writePacket(outPacket.i); + +} + +// Send a Midi PROGRAM CHANGE message to given channel, with program ID 0-127 +void USBMidi::sendProgramChange(unsigned int channel, unsigned int program) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_PROGRAM_CHANGE; + outPacket.p.midi0=MIDIv1_PROGRAM_CHANGE |(channel & 0x0f); + outPacket.p.midi1=program; + writePacket(outPacket.i); + +} + +// Send a Midi AFTER TOUCH message to given channel, with velocity 0-127 +void USBMidi::sendAfterTouch(unsigned int channel, unsigned int velocity) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_CHANNEL_PRESSURE; + outPacket.p.midi0=MIDIv1_CHANNEL_PRESSURE |(channel & 0x0f); + outPacket.p.midi1=velocity; + writePacket(outPacket.i); + +} + +// Send a Midi PITCH CHANGE message, with a 14-bit pitch (always for all channels) +void USBMidi::sendPitchChange(unsigned int pitch) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_PITCH_WHEEL; + outPacket.p.midi0=MIDIv1_PITCH_WHEEL; + outPacket.p.midi1= (uint8) pitch & 0x07F; + outPacket.p.midi2= (uint8) (pitch<<7) & 0x7f; + writePacket(outPacket.i); + +} + +// Send a Midi SONG POSITION message, with a 14-bit position (always for all channels) +void USBMidi::sendSongPosition(unsigned int position) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_3BYTE_SYS_COMMON; + outPacket.p.midi0=MIDIv1_SONG_POSITION_PTR; + outPacket.p.midi1= (uint8) position & 0x07F; + outPacket.p.midi2= (uint8) (position<<7) & 0x7f; + writePacket(outPacket.i); + +} + +// Send a Midi SONG SELECT message, with a song ID of 0-127 (always for all channels) +void USBMidi::sendSongSelect(unsigned int song) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_2BYTE_SYS_COMMON; + outPacket.p.midi0=MIDIv1_SONG_SELECT; + outPacket.p.midi1= (uint8) song & 0x07F; + writePacket(outPacket.i); + +} + +// Send a Midi TUNE REQUEST message (TUNE REQUEST is always for all channels) +void USBMidi::sendTuneRequest(void) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_1BYTE; + outPacket.p.midi0=MIDIv1_TUNE_REQUEST; + writePacket(outPacket.i); +} + + +// Send a Midi SYNC message (SYNC is always for all channels) +void USBMidi::sendSync(void) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_1BYTE; + outPacket.p.midi0=MIDIv1_CLOCK; + writePacket(outPacket.i); +} + +// Send a Midi START message (START is always for all channels) +void USBMidi::sendStart(void) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_1BYTE; + outPacket.p.midi0=MIDIv1_START ; + writePacket(outPacket.i); +} + + +// Send a Midi CONTINUE message (CONTINUE is always for all channels) +void USBMidi::sendContinue(void) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_1BYTE; + outPacket.p.midi0=MIDIv1_CONTINUE ; + writePacket(outPacket.i); +} + + +// Send a Midi STOP message (STOP is always for all channels) +void USBMidi::sendStop(void) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_1BYTE; + outPacket.p.midi0=MIDIv1_STOP ; + writePacket(outPacket.i); +} + +// Send a Midi ACTIVE SENSE message (ACTIVE SENSE is always for all channels) +void USBMidi::sendActiveSense(void) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_1BYTE; + outPacket.p.midi0=MIDIv1_ACTIVE_SENSE ; + writePacket(outPacket.i); +} + +// Send a Midi RESET message (RESET is always for all channels) +void USBMidi::sendReset(void) +{ + outPacket.p.cable=DEFAULT_MIDI_CABLE; + outPacket.p.cin=CIN_1BYTE; + outPacket.p.midi0=MIDIv1_RESET ; + writePacket(outPacket.i); +} + + +// Open the serial port and begin processing. +//void Midi::begin(unsigned int channel, unsigned long baud) +//{ +// channelIn_ = channel; +// serial_.begin(baud); +//} + + +// Set (package-specific) parameters for the Midi instance +void USBMidi::setParam(unsigned int param, unsigned int val) +{ + if (param == PARAM_SEND_FULL_COMMANDS) { + if (val) { + sendFullCommands_ = true; + } else { + sendFullCommands_ = false; + } + } else if (param == PARAM_CHANNEL_IN) { + channelIn_ = val; + } +} + + +// Get (package-specific) parameters for the Midi instance +unsigned int USBMidi::getParam(unsigned int param) +{ + if (param == PARAM_SEND_FULL_COMMANDS) { + return sendFullCommands_; + } else if (param == PARAM_CHANNEL_IN) { + return channelIn_; + } + + return 0; +} + + +// Placeholders. You should subclass the Midi base class and define these to have your +// version called. +void USBMidi::handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) {} +void USBMidi::handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) {} +void USBMidi::handleVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity) {} +void USBMidi::handleControlChange(unsigned int channel, unsigned int controller, unsigned int value) {} +void USBMidi::handleProgramChange(unsigned int channel, unsigned int program) {} +void USBMidi::handleAfterTouch(unsigned int channel, unsigned int velocity) {} +void USBMidi::handlePitchChange(unsigned int pitch) {} +void USBMidi::handleSongPosition(unsigned int position) {} +void USBMidi::handleSongSelect(unsigned int song) {} +void USBMidi::handleTuneRequest(void) {} +void USBMidi::handleSync(void) {} +void USBMidi::handleStart(void) {} +void USBMidi::handleContinue(void) {} +void USBMidi::handleStop(void) {} +void USBMidi::handleActiveSense(void) {} +void USBMidi::handleReset(void) {} + |
