void (*usb_midi_handleProgramChange)(uint8_t ch, uint8_t program) = NULL; | void (*usb_midi_handleProgramChange)(uint8_t ch, uint8_t program) = NULL; | ||||
void (*usb_midi_handleAfterTouch)(uint8_t ch, uint8_t pressure) = NULL; | void (*usb_midi_handleAfterTouch)(uint8_t ch, uint8_t pressure) = NULL; | ||||
void (*usb_midi_handlePitchChange)(uint8_t ch, int pitch) = NULL; | void (*usb_midi_handlePitchChange)(uint8_t ch, int pitch) = NULL; | ||||
void (*usb_midi_handleSysEx)(const uint8_t *data, uint16_t length, uint8_t complete) = NULL; | |||||
void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete) = NULL; | |||||
void (*usb_midi_handleSysExComplete)(uint8_t *data, unsigned int size) = NULL; | |||||
void (*usb_midi_handleTimeCodeQuarterFrame)(uint8_t data) = NULL; | |||||
void (*usb_midi_handleSongPosition)(uint16_t beats) = NULL; | |||||
void (*usb_midi_handleSongSelect)(uint8_t songnumber) = NULL; | |||||
void (*usb_midi_handleTuneRequest)(void) = NULL; | |||||
void (*usb_midi_handleClock)(void) = NULL; | |||||
void (*usb_midi_handleStart)(void) = NULL; | |||||
void (*usb_midi_handleContinue)(void) = NULL; | |||||
void (*usb_midi_handleStop)(void) = NULL; | |||||
void (*usb_midi_handleActiveSensing)(void) = NULL; | |||||
void (*usb_midi_handleSystemReset)(void) = NULL; | |||||
void (*usb_midi_handleRealTimeSystem)(uint8_t rtb) = NULL; | void (*usb_midi_handleRealTimeSystem)(uint8_t rtb) = NULL; | ||||
void (*usb_midi_handleTimeCodeQuarterFrame)(uint16_t data) = NULL; | |||||
// Maximum number of transmit packets to queue so we don't starve other endpoints for memory | // Maximum number of transmit packets to queue so we don't starve other endpoints for memory | ||||
#define TX_PACKET_LIMIT 6 | #define TX_PACKET_LIMIT 6 | ||||
void static sysex_byte(uint8_t b) | void static sysex_byte(uint8_t b) | ||||
{ | { | ||||
// when buffer is full, send another chunk to handler. | |||||
if (usb_midi_msg_sysex_len == USB_MIDI_SYSEX_MAX) { | |||||
if (usb_midi_handleSysEx) { | |||||
(*usb_midi_handleSysEx)(usb_midi_msg_sysex, usb_midi_msg_sysex_len, 0); | |||||
usb_midi_msg_sysex_len = 0; | |||||
} | |||||
if (usb_midi_handleSysExPartial && usb_midi_msg_sysex_len >= USB_MIDI_SYSEX_MAX) { | |||||
// when buffer is full, send another chunk to partial handler. | |||||
(*usb_midi_handleSysExPartial)(usb_midi_msg_sysex, usb_midi_msg_sysex_len, 0); | |||||
usb_midi_msg_sysex_len = 0; | |||||
} | } | ||||
if (usb_midi_msg_sysex_len < USB_MIDI_SYSEX_MAX) { | if (usb_midi_msg_sysex_len < USB_MIDI_SYSEX_MAX) { | ||||
usb_midi_msg_sysex[usb_midi_msg_sysex_len++] = b; | usb_midi_msg_sysex[usb_midi_msg_sysex_len++] = b; | ||||
usb_midi_msg_data2 = (n >> 24); | usb_midi_msg_data2 = (n >> 24); | ||||
return 1; | return 1; | ||||
} | } | ||||
if (type1 == 0x02 || type1 == 0x03 || (type1 == 0x05 && type2 == 0x0F)) { | |||||
// system common or system realtime message | |||||
uint8_t type; | |||||
system_common_or_realtime: | |||||
type = n >> 8; | |||||
switch (type) { | |||||
case 0xF1: // TimeCodeQuarterFrame | |||||
if (usb_midi_handleTimeCodeQuarterFrame) { | |||||
(*usb_midi_handleTimeCodeQuarterFrame)(n >> 16); | |||||
} | |||||
break; | |||||
case 0xF2: // SongPosition | |||||
if (usb_midi_handleSongPosition) { | |||||
(*usb_midi_handleSongPosition)(n >> 16); | |||||
} | |||||
break; | |||||
case 0xF3: // SongSelect | |||||
if (usb_midi_handleSongSelect) { | |||||
(*usb_midi_handleSongSelect)(n >> 16); | |||||
} | |||||
break; | |||||
case 0xF6: // TuneRequest | |||||
if (usb_midi_handleTuneRequest) { | |||||
(*usb_midi_handleTuneRequest)(); | |||||
} | |||||
break; | |||||
case 0xF8: // Clock | |||||
if (usb_midi_handleClock) { | |||||
(*usb_midi_handleClock)(); | |||||
} else if (usb_midi_handleRealTimeSystem) { | |||||
(*usb_midi_handleRealTimeSystem)(0xF8); | |||||
} | |||||
break; | |||||
case 0xFA: // Start | |||||
if (usb_midi_handleStart) { | |||||
(*usb_midi_handleStart)(); | |||||
} else if (usb_midi_handleRealTimeSystem) { | |||||
(*usb_midi_handleRealTimeSystem)(0xFA); | |||||
} | |||||
break; | |||||
case 0xFB: // Continue | |||||
if (usb_midi_handleContinue) { | |||||
(*usb_midi_handleContinue)(); | |||||
} else if (usb_midi_handleRealTimeSystem) { | |||||
(*usb_midi_handleRealTimeSystem)(0xFB); | |||||
} | |||||
break; | |||||
case 0xFC: // Stop | |||||
if (usb_midi_handleStop) { | |||||
(*usb_midi_handleStop)(); | |||||
} else if (usb_midi_handleRealTimeSystem) { | |||||
(*usb_midi_handleRealTimeSystem)(0xFC); | |||||
} | |||||
break; | |||||
case 0xFE: // ActiveSensing | |||||
if (usb_midi_handleActiveSensing) { | |||||
(*usb_midi_handleActiveSensing)(); | |||||
} else if (usb_midi_handleRealTimeSystem) { | |||||
(*usb_midi_handleRealTimeSystem)(0xFE); | |||||
} | |||||
break; | |||||
case 0xFF: // SystemReset | |||||
if (usb_midi_handleSystemReset) { | |||||
(*usb_midi_handleSystemReset)(); | |||||
} else if (usb_midi_handleRealTimeSystem) { | |||||
(*usb_midi_handleRealTimeSystem)(0xFF); | |||||
} | |||||
break; | |||||
default: | |||||
return 0; // unknown message, ignore it | |||||
} | |||||
usb_midi_msg_type = type; | |||||
goto return_message; | |||||
} | |||||
if (type1 == 0x04) { | if (type1 == 0x04) { | ||||
sysex_byte(n >> 8); | sysex_byte(n >> 8); | ||||
sysex_byte(n >> 16); | sysex_byte(n >> 16); | ||||
sysex_byte(n >> 8); | sysex_byte(n >> 8); | ||||
if (type1 >= 0x06) sysex_byte(n >> 16); | if (type1 >= 0x06) sysex_byte(n >> 16); | ||||
if (type1 == 0x07) sysex_byte(n >> 24); | if (type1 == 0x07) sysex_byte(n >> 24); | ||||
usb_midi_msg_data1 = usb_midi_msg_sysex_len; | |||||
usb_midi_msg_data2 = usb_midi_msg_sysex_len >> 8; | |||||
uint16_t len = usb_midi_msg_sysex_len; | |||||
usb_midi_msg_data1 = len; | |||||
usb_midi_msg_data2 = len >> 8; | |||||
usb_midi_msg_sysex_len = 0; | usb_midi_msg_sysex_len = 0; | ||||
usb_midi_msg_type = 7; // 7 = Sys Ex | usb_midi_msg_type = 7; // 7 = Sys Ex | ||||
if (usb_midi_handleSysEx) | |||||
(*usb_midi_handleSysEx)(usb_midi_msg_sysex, usb_midi_msg_data1, 1); | |||||
if (usb_midi_handleSysExPartial) { | |||||
(*usb_midi_handleSysExPartial)(usb_midi_msg_sysex, len, 1); | |||||
} else if (usb_midi_handleSysExComplete) { | |||||
(*usb_midi_handleSysExComplete)(usb_midi_msg_sysex, len); | |||||
} | |||||
return 1; | return 1; | ||||
} | } | ||||
if (type1 == 0x0F) { | if (type1 == 0x0F) { | ||||
// TODO: does this need to be a full MIDI parser? | // TODO: does this need to be a full MIDI parser? | ||||
// What software actually uses this message type in practice? | // What software actually uses this message type in practice? | ||||
uint8_t b = n >> 8; | |||||
if (b >= 0xF8) { | |||||
// From Sebastian Tomczak, seb.tomczak at gmail.com | |||||
// http://little-scale.blogspot.com/2011/08/usb-midi-game-boy-sync-for-16.html | |||||
goto system_common_or_realtime; | |||||
} | |||||
if (usb_midi_msg_sysex_len > 0) { | if (usb_midi_msg_sysex_len > 0) { | ||||
// From David Sorlien, dsorlien at gmail.com, http://axe4live.wordpress.com | // From David Sorlien, dsorlien at gmail.com, http://axe4live.wordpress.com | ||||
// OSX sometimes uses Single Byte Unparsed to | // OSX sometimes uses Single Byte Unparsed to | ||||
// send bytes in the middle of a SYSEX message. | // send bytes in the middle of a SYSEX message. | ||||
sysex_byte(n >> 8); | sysex_byte(n >> 8); | ||||
} else { | |||||
// From Sebastian Tomczak, seb.tomczak at gmail.com | |||||
// http://little-scale.blogspot.com/2011/08/usb-midi-game-boy-sync-for-16.html | |||||
usb_midi_msg_type = 8; | |||||
if (usb_midi_handleRealTimeSystem) | |||||
(*usb_midi_handleRealTimeSystem)(n >> 8); | |||||
goto return_message; | |||||
} | } | ||||
//} else { | |||||
// usb_midi_msg_type = 8; | |||||
// if (usb_midi_handleRealTimeSystem) | |||||
// (*usb_midi_handleRealTimeSystem)(n >> 8); | |||||
// goto return_message; | |||||
//} | |||||
} | } | ||||
if (type1 == 0x02) { | |||||
// From Timm Schlegelmilch, karg.music at gmail.com | |||||
// http://karg-music.blogspot.de/2015/06/receiving-midi-time-codes-over-usb-with.html | |||||
usb_midi_msg_type = 9; | |||||
if (usb_midi_handleTimeCodeQuarterFrame) | |||||
(*usb_midi_handleTimeCodeQuarterFrame)(n >> 16); | |||||
return 1; | |||||
} | |||||
//if (type1 == 0x02) { | |||||
// From Timm Schlegelmilch, karg.music at gmail.com | |||||
// http://karg-music.blogspot.de/2015/06/receiving-midi-time-codes-over-usb-with.html | |||||
// usb_midi_msg_type = 9; | |||||
// if (usb_midi_handleTimeCodeQuarterFrame) | |||||
// (*usb_midi_handleTimeCodeQuarterFrame)(n >> 16); | |||||
// return 1; | |||||
//} | |||||
return 0; | return 0; | ||||
} | } | ||||
extern void (*usb_midi_handleProgramChange)(uint8_t ch, uint8_t program); | extern void (*usb_midi_handleProgramChange)(uint8_t ch, uint8_t program); | ||||
extern void (*usb_midi_handleAfterTouch)(uint8_t ch, uint8_t pressure); | extern void (*usb_midi_handleAfterTouch)(uint8_t ch, uint8_t pressure); | ||||
extern void (*usb_midi_handlePitchChange)(uint8_t ch, int pitch); | extern void (*usb_midi_handlePitchChange)(uint8_t ch, int pitch); | ||||
extern void (*usb_midi_handleSysEx)(const uint8_t *data, uint16_t length, uint8_t complete); | |||||
extern void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete); | |||||
extern void (*usb_midi_handleSysExComplete)(uint8_t *data, unsigned int size); | |||||
extern void (*usb_midi_handleTimeCodeQuarterFrame)(uint8_t data); | |||||
extern void (*usb_midi_handleSongPosition)(uint16_t beats); | |||||
extern void (*usb_midi_handleSongSelect)(uint8_t songnumber); | |||||
extern void (*usb_midi_handleTuneRequest)(void); | |||||
extern void (*usb_midi_handleClock)(void); | |||||
extern void (*usb_midi_handleStart)(void); | |||||
extern void (*usb_midi_handleContinue)(void); | |||||
extern void (*usb_midi_handleStop)(void); | |||||
extern void (*usb_midi_handleActiveSensing)(void); | |||||
extern void (*usb_midi_handleSystemReset)(void); | |||||
extern void (*usb_midi_handleRealTimeSystem)(uint8_t rtb); | extern void (*usb_midi_handleRealTimeSystem)(uint8_t rtb); | ||||
extern void (*usb_midi_handleTimeCodeQuarterFrame)(uint16_t data); | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
bool read(uint8_t channel=0) __attribute__((always_inline)) { | bool read(uint8_t channel=0) __attribute__((always_inline)) { | ||||
return usb_midi_read(channel); | return usb_midi_read(channel); | ||||
}; | }; | ||||
inline uint8_t getType(void) __attribute__((always_inline)) { | |||||
uint8_t getType(void) __attribute__((always_inline)) { | |||||
return usb_midi_msg_type; | return usb_midi_msg_type; | ||||
}; | }; | ||||
inline uint8_t getCable(void) __attribute__((always_inline)) { | |||||
uint8_t getCable(void) __attribute__((always_inline)) { | |||||
return usb_midi_msg_cable; | return usb_midi_msg_cable; | ||||
}; | }; | ||||
inline uint8_t getChannel(void) __attribute__((always_inline)) { | |||||
uint8_t getChannel(void) __attribute__((always_inline)) { | |||||
return usb_midi_msg_channel; | return usb_midi_msg_channel; | ||||
}; | }; | ||||
inline uint8_t getData1(void) __attribute__((always_inline)) { | |||||
uint8_t getData1(void) __attribute__((always_inline)) { | |||||
return usb_midi_msg_data1; | return usb_midi_msg_data1; | ||||
}; | }; | ||||
inline uint8_t getData2(void) __attribute__((always_inline)) { | |||||
uint8_t getData2(void) __attribute__((always_inline)) { | |||||
return usb_midi_msg_data2; | return usb_midi_msg_data2; | ||||
}; | }; | ||||
inline uint8_t * getSysExArray(void) __attribute__((always_inline)) { | |||||
uint8_t * getSysExArray(void) __attribute__((always_inline)) { | |||||
return usb_midi_msg_sysex; | return usb_midi_msg_sysex; | ||||
}; | }; | ||||
inline void setHandleNoteOff(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { | |||||
void setHandleNoteOff(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { | |||||
// type: 0x80 NoteOff | |||||
usb_midi_handleNoteOff = fptr; | usb_midi_handleNoteOff = fptr; | ||||
}; | }; | ||||
inline void setHandleNoteOn(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { | |||||
void setHandleNoteOn(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { | |||||
// type: 0x90 NoteOn | |||||
usb_midi_handleNoteOn = fptr; | usb_midi_handleNoteOn = fptr; | ||||
}; | }; | ||||
inline void setHandleVelocityChange(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { | |||||
void setHandleVelocityChange(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { | |||||
// type: 0xA0 AfterTouchPoly | |||||
usb_midi_handleVelocityChange = fptr; | usb_midi_handleVelocityChange = fptr; | ||||
}; | }; | ||||
inline void setHandleControlChange(void (*fptr)(uint8_t channel, uint8_t control, uint8_t value)) { | |||||
void setHandleAfterTouchPoly(void (*fptr)(uint8_t channel, uint8_t note, uint8_t pressure)) { | |||||
// type: 0xA0 AfterTouchPoly | |||||
usb_midi_handleVelocityChange = fptr; | |||||
}; | |||||
void setHandleControlChange(void (*fptr)(uint8_t channel, uint8_t control, uint8_t value)) { | |||||
// type: 0xB0 ControlChange | |||||
usb_midi_handleControlChange = fptr; | usb_midi_handleControlChange = fptr; | ||||
}; | }; | ||||
inline void setHandleProgramChange(void (*fptr)(uint8_t channel, uint8_t program)) { | |||||
void setHandleProgramChange(void (*fptr)(uint8_t channel, uint8_t program)) { | |||||
// type: 0xC0 ProgramChange | |||||
usb_midi_handleProgramChange = fptr; | usb_midi_handleProgramChange = fptr; | ||||
}; | }; | ||||
inline void setHandleAfterTouch(void (*fptr)(uint8_t channel, uint8_t pressure)) { | |||||
void setHandleAfterTouch(void (*fptr)(uint8_t channel, uint8_t pressure)) { | |||||
// type: 0xD0 AfterTouchChannel | |||||
usb_midi_handleAfterTouch = fptr; | usb_midi_handleAfterTouch = fptr; | ||||
}; | }; | ||||
inline void setHandlePitchChange(void (*fptr)(uint8_t channel, int pitch)) { | |||||
void setHandleAfterTouchChannel(void (*fptr)(uint8_t channel, uint8_t pressure)) { | |||||
// type: 0xD0 AfterTouchChannel | |||||
usb_midi_handleAfterTouch = fptr; | |||||
}; | |||||
void setHandlePitchChange(void (*fptr)(uint8_t channel, int pitch)) { | |||||
// type: 0xE0 PitchBend | |||||
usb_midi_handlePitchChange = fptr; | usb_midi_handlePitchChange = fptr; | ||||
}; | }; | ||||
inline void setHandleSysEx(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) { | |||||
usb_midi_handleSysEx = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr; | |||||
void setHandleSysEx(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) { | |||||
// type: 0xF0 SystemExclusive - multiple calls for message bigger than buffer | |||||
usb_midi_handleSysExPartial = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr; | |||||
} | } | ||||
inline void setHandleRealTimeSystem(void (*fptr)(uint8_t realtimebyte)) { | |||||
usb_midi_handleRealTimeSystem = fptr; | |||||
}; | |||||
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(uint16_t data)) { | |||||
void setHandleSystemExclusive(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) { | |||||
// type: 0xF0 SystemExclusive - multiple calls for message bigger than buffer | |||||
usb_midi_handleSysExPartial = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr; | |||||
} | |||||
void setHandleSystemExclusive(void (*fptr)(uint8_t *data, unsigned int size)) { | |||||
// type: 0xF0 SystemExclusive - single call, message larger than buffer is truncated | |||||
usb_midi_handleSysExComplete = fptr; | |||||
} | |||||
void setHandleTimeCodeQuarterFrame(void (*fptr)(uint8_t data)) { | |||||
// type: 0xF1 TimeCodeQuarterFrame | |||||
usb_midi_handleTimeCodeQuarterFrame = fptr; | usb_midi_handleTimeCodeQuarterFrame = fptr; | ||||
}; | }; | ||||
private: | |||||
void setHandleSongPosition(void (*fptr)(uint16_t beats)) { | |||||
// type: 0xF2 SongPosition | |||||
usb_midi_handleSongPosition = fptr; | |||||
} | |||||
void setHandleSongSelect(void (*fptr)(uint8_t songnumber)) { | |||||
// type: 0xF3 SongSelect | |||||
usb_midi_handleSongSelect = fptr; | |||||
} | |||||
void setHandleTuneRequest(void (*fptr)(void)) { | |||||
// type: 0xF6 TuneRequest | |||||
usb_midi_handleTuneRequest = fptr; | |||||
} | |||||
void setHandleClock(void (*fptr)(void)) { | |||||
// type: 0xF8 Clock | |||||
usb_midi_handleClock = fptr; | |||||
} | |||||
void setHandleStart(void (*fptr)(void)) { | |||||
// type: 0xFA Start | |||||
usb_midi_handleStart = fptr; | |||||
} | |||||
void setHandleContinue(void (*fptr)(void)) { | |||||
// type: 0xFB Continue | |||||
usb_midi_handleContinue = fptr; | |||||
} | |||||
void setHandleStop(void (*fptr)(void)) { | |||||
// type: 0xFC Stop | |||||
usb_midi_handleStop = fptr; | |||||
} | |||||
void setHandleActiveSensing(void (*fptr)(void)) { | |||||
// type: 0xFE ActiveSensing | |||||
usb_midi_handleActiveSensing = fptr; | |||||
} | |||||
void setHandleSystemReset(void (*fptr)(void)) { | |||||
// type: 0xFF SystemReset | |||||
usb_midi_handleSystemReset = fptr; | |||||
} | |||||
void setHandleRealTimeSystem(void (*fptr)(uint8_t realtimebyte)) { | |||||
// type: 0xF8-0xFF - if more specific handler not configured | |||||
usb_midi_handleRealTimeSystem = fptr; | |||||
}; | |||||
}; | }; | ||||
extern usb_midi_class usbMIDI; | extern usb_midi_class usbMIDI; |