-
Notifications
You must be signed in to change notification settings - Fork 2
umpProcessor
Processing of UMP messages.
This class allows the application to set up a set of callbacks relevant to the applications needs.
Example Setup
umpProcessor UMPHandler;
UMPHandler.setUtility(utilityCallback);
UMPHandler.setCVM(handleChannelVoiceMessages);
UMPHandler.setSystem(handleSystemMessages);
UMPHandler.setSysEx(processUMPSysex);
UMPHandler.setMidiEndpoint(midiEndpointCallback);
UMPHandler.setFunctionBlock(functionblockCallback);
...
UMP messages of Message Type 0x2 and 0x4 are presented in this format. See below for the value meaning for each status.
struct umpCVM{
uint8_t umpGroup;
uint8_t messageType;
uint8_t status;
uint8_t channel;
uint8_t note;
uint32_t value;
uint16_t index;
uint8_t bank;
bool flag1;
bool flag2;
};
UMP messages of Message Type 0x0 and 0x1 are presented in this format. See below for the value meaning for each status.
struct umpGeneric{
uint8_t umpGroup;
uint8_t messageType;
uint8_t status;
uint16_t value;
};
Note: Utility Messages (MT 0x0) are groupless and therefore the umpGproup
value is ignored.
struct umpData{
uint8_t umpGroup;
uint8_t messageType;
uint8_t status;
uint8_t form;
uint8_t* data;
uint8_t dataLength;
};
Process incoming UMP messages broken up into 32bit words.
Reset the current processing of incoming UMP.
void utilityCallback(struct umpGeneric mess){
printf("->Utility Message: status %d value: %d", mess.status, mess.value);
}
Values in the umpGeneric struct:
status | value |
---|---|
UTILITY_NOOP | - |
UTILITY_JRCLOCK | sender clock time |
UTILITY_JRTS | sender clock timestamp |
UTILITY_DELTACLOCKTICK | # of ticks PQN |
UTILITY_DELTACLOCKSINCE | # of ticks since last event |
Set the callable function when a Channel Voice Message is processed by processUMP
void handleChannelVoiceMessages(struct umpCVM mess){
printf("->CVM: Group %d CH %d Note: %d status: %d", mess.umpGroup, mess.channel, mess.status);
}
Values in the umpCVM struct:
status | note | value | bank | index | flag1 | flag2 |
---|---|---|---|---|---|---|
NOTE_OFF | note | velocity♠ | attributeType | attributeData | ||
NOTE_ON | note | velocity♠ | attributeType | attributeData | ||
KEY_PRESSURE | note | value* | ||||
CC | value* | index | ||||
PROGRAM_CHANGE | program | bank | index | bank valid | ||
CHANNEL_PRESSURE | value* | |||||
PITCH_BEND | value* | |||||
RPN †♥ | value | bank | index | |||
NRPN †♥ | value | bank | index | |||
RPN_RELATIVE † | value‡ | bank | index | |||
NRPN_RELATIVE † | value‡ | bank | index | |||
PITCH_BEND_PERNOTE † | note | value | ||||
RPN_PERNOTE † | note | value | index | |||
NRPN_PERNOTE † | note | value | index | |||
PERNOTE_MANAGE † | note | detach | reset |
† MIDI 2.0 Protocol messages only
* M1 Values are scaled to 32 bit value
♠ M1 Values are scaled to 16 bit value
♥ Message is only triggered when a MIDI 2.0 RPN message is sent. This is not triggered when a MIDI 1.0 (N)RPN
messages are sent. Those messages are processed using the function set by setControlChange
‡ These values are twos complement and will need to cast e.g:
int32_t relativeValue = (int32_t)mess.value;
The umpProcessor
makes some distinction between different Protocols. This means that Channel Voice Messages (e.g.
Note On) handlers are called the same way regardless if is a MIDI 1.0 Channel Voice Message (Message Type 0x2) or a MIDI
2.0 Channel Voice Message (Message Type 0x4). MIDI 1.0 Channel Voice Message values are scaled to match MIDI 2.0 Messages.
This allows for umpProcessor
to process both types of Channel Voice Messages simultaneously.
It is up to the application to manage the combination of JR messages and other UMP messages.
Set the callable function when a System Message is processed by processUMP
void handleSystemMessages(struct umpGeneric mess){
printf("->CVM: Group %d status: %d", mess.umpGroup, mess.status);
}
Values in the umpGeneric struct:
status | value |
---|---|
TIMING_CODE | time code |
SPP | position |
SONG_SELECT | song |
TUNEREQUEST | |
TIMINGCLOCK | |
SEQSTART | |
SEQCONT | |
SEQSTOP | |
ACTIVESENSE | |
SYSTEMRESET |
midiCIProcessor midiciMain1;
bool isProcMIDICI = false;
void processUMPSysex(struct umpData mess){
//Example of Processing UMP into MIDI-CI processor
if(mess.form==1 && mess.data[0] == S7UNIVERSAL_NRT && mess.data[2] == S7MIDICI){
if(mess.umpGroup==0) {
midiciMain1.startSysex7(mess.umpGroup, mess.data[1]);
isProcMIDICI = true;
}
}
for (int i = 0; i < mess.dataLength; i++) {
if(mess.umpGroup==0 && isProcMIDICI){
midiciMain1.processMIDICI(mess.data[i]);
}else{
//Process other SysEx
}
}
if((mess.form==3 || mess.form==0) && isProcMIDICI){
midiciMain1.endSysex7();
isProcMIDICI = false;
}
}
inline void setFlexTimeSig(void (*fptr)(uint8_t group, uint8_t numerator, uint8_t denominator, uint8_t num32Notes))
inline void setFlexMetronome(void (*fptr)(uint8_t group, uint8_t numClkpPriCli, uint8_t bAccP1, uint8_t bAccP2, uint8_t bAccP3, uint8_t numSubDivCli1, uint8_t numSubDivCli2))
inline void setFlexKeySig(void (*fptr)(uint8_t group, uint8_t addrs, uint8_t channel, uint8_t sharpFlats, uint8_t tonic))
inline void setFlexChord(void (*fptr)(uint8_t group, uint8_t addrs, uint8_t channel, uint8_t chShrpFlt, uint8_t chTonic, uint8_t chType, uint8_t chAlt1Type, uint8_t chAlt1Deg, uint8_t chAlt2Type, uint8_t chAlt2Deg, uint8_t chAlt3Type, uint8_t chAlt3Deg, uint8_t chAlt4Type, uint8_t chAlt4Deg, uint8_t baShrpFlt, uint8_t baTonic, uint8_t baType, uint8_t baAlt1Type, uint8_t baAlt1Deg, uint8_t baAlt2Type, uint8_t baAlt2Deg))
inline void setFlexPerformance(void (fptr)(uint8_t group, uint8_t form, uint8_t addrs, uint8_t channel, uint8_t status, uint8_t text, uint8_t textLength))
inline void setFlexLyric(void (fptr)(uint8_t group, uint8_t form, uint8_t addrs, uint8_t channel, uint8_t status, uint8_t text, uint8_t textLength))
Example Processing of Endpoint Get:
void midiEndpointCallback(uint8_t majVer, uint8_t minVer, uint8_t filter){
//Upon Recieving the filter it is important to return the information requested
if(filter & 0x1){ //Endpoint Info Notification
std::array<uint32_t, 4> ump = mtFMidiEndpointInfoNotify(3, false, true, false, false);
sendUMP(ump.data(),4);
}
if(filter & 0x2) {
std::array<uint32_t, 4> ump = mtFMidiEndpointDeviceInfoNotify({DEVICE_MFRID}, {DEVICE_FAMID}, {DEVICE_MODELID}, {DEVICE_VERSIONID});
sendUMP( ump.data(), 4);
}
if(filter & 0x4) {
uint8_t friendlyNameLength = strlen(DEVICE_MIDIENPOINTNAME);
for(uint8_t offset=0; offset<friendlyNameLength; offset+=14) {
std::array<uint32_t, 4> ump = mtFMidiEndpointTextNotify(MIDIENDPOINT_NAME_NOTIFICATION, offset, (uint8_t *) DEVICE_MIDIENPOINTNAME,friendlyNameLength);
sendUMP(ump.data(),4);
}
}
if(filter & 0x8) {
int8_t piiLength = strlen(PRODUCT_INSTANCE_ID);
for(uint8_t offset=0; offset<piiLength; offset+=14) {
std::array<uint32_t, 4> ump = mtFMidiEndpointTextNotify(PRODUCT_INSTANCE_ID, offset, (uint8_t *) buff,piiLength);
sendUMP(ump.data(),4);
}
}
if(filter & 0x10){
std::array<uint32_t, 4> ump = mtFNotifyProtocol(0x1,false,false);
sendUMP(ump.data(),4);
}
}