Always consult the official Owners Manuals first!
March 2024: all pages have been checked and are up-to-date

Difference between revisions of "MIDI SysEx"

From Fractal Audio Wiki
Jump to navigation Jump to search
m (edited for readability)
m (edited for readability)
(12 intermediate revisions by the same user not shown)
Line 555: Line 555:
  
 
==GET_SCENE_NUMBER==
 
==GET_SCENE_NUMBER==
This is the same as SET_SCENE_NUMBER except you use 0x7F as the value for getting the scene number.
+
This is the same as [[#SET_SCENE_NUMBER|SET_SCENE_NUMBER]] except you use <tt>0x7F</tt> as the value for getting the scene number.
  
 
Message format:
 
Message format:
Line 573: Line 573:
 
:0xF7  SysEx End
 
:0xF7  SysEx End
 
</tt>
 
</tt>
 
  
 
==SET_SCENE_NUMBER==
 
==SET_SCENE_NUMBER==
Line 643: Line 642:
 
==MIDI_SET_PRESET==
 
==MIDI_SET_PRESET==
 
:Function number: 14
 
:Function number: 14
:Example: F0 0 1 74 3 14 0 1 13 0 FF  --> to set the current preset (edit buffer) to preset 0 (1 if DISPLAY OFFSET activated)
+
:Example:
:F0 0 1 74 3 14 1 0 13 0 FF  --> to set the current preset (edit buffer) to preset 128 (129 if DISPLAY OFFSET activated)
+
::<tt>F0 0 1 74 3 14 0 1 13 0 FF</tt> --> to set the current preset (edit buffer) to preset 0 (1 if DISPLAY OFFSET activated)
 +
::<tt>F0 0 1 74 3 14 1 0 13 0 FF</tt> --> to set the current preset (edit buffer) to preset 128 (129 if DISPLAY OFFSET activated)
 
:Send from AXE-FX to AXE-EDIT and MFC-101
 
:Send from AXE-FX to AXE-EDIT and MFC-101
 
 
  
 
==GET_BLOCK_PARAMETERS_LIST==
 
==GET_BLOCK_PARAMETERS_LIST==
This function will get you a list of all the parameters values of a specified block, each parameter with it's value is returned in a separate message.
+
This function will get you a list of all the parameters values of a specified block, each parameter with its value is returned in a separate message.
  
 
Message format:
 
Message format:
Line 693: Line 691:
 
:0xF7 SysEx End
 
:0xF7 SysEx End
 
</tt>
 
</tt>
 
 
  
 
==BATCH_LIST_REQUEST_START==
 
==BATCH_LIST_REQUEST_START==
Line 714: Line 710:
  
 
==BATCH_LIST_REQUEST_COMPLETE==
 
==BATCH_LIST_REQUEST_COMPLETE==
This message is sent to you after a batch of messages you requested was sent to you and it's complete. For Example if you request [[#GET_BLOCK_PARAMETERS_LIST|0x01 GET_BLOCK_PARAMETERS_LIST]], this message will reply confirming that the request has been complete.
+
This message is sent to you after a batch of messages you requested was sent to you and is complete.  
  
The "ID of function that it's replying to" is the ID of the function that requested the list, in the case of [[#GET_BLOCK_PARAMETERS_LIST|0x01 GET_BLOCK_PARAMETERS_LIST]] which is function 0x01 it will show this byte as 0x01 (on the AX8 the function ID will be 0x02 even tho the request was for 0x01).
+
For example, if you request [[#GET_BLOCK_PARAMETERS_LIST|0x01 GET_BLOCK_PARAMETERS_LIST]], this message will reply confirming that the request has been complete.
 +
 
 +
The "ID of function that it's replying to" is the ID of the function that requested the list. In the case of [[#GET_BLOCK_PARAMETERS_LIST|0x01 GET_BLOCK_PARAMETERS_LIST]], which is function 0x01, it will show this byte as 0x01 (on the AX8 the function ID will be 0x02 even though the request was for 0x01).
  
 
Message format:
 
Message format:
Line 727: Line 725:
 
:0xF7 SysEx End
 
:0xF7 SysEx End
 
</tt>
 
</tt>
 
 
  
 
==MULTIPURPOSE_RESPONSE==
 
==MULTIPURPOSE_RESPONSE==
Line 779: Line 775:
 
To do this you will use the Block ID that you want to bypass, Parameter ID 255, and the value would be 0 to Engage and 1 to Bypass.
 
To do this you will use the Block ID that you want to bypass, Parameter ID 255, and the value would be 0 to Engage and 1 to Bypass.
  
Example, this example will bypass the amp block, keep in mind the checksum must be calculated for the target device:
+
For example, this will bypass the amp block. Keep in mind the checksum must be calculated for the target device:
 
<tt>
 
<tt>
 
:[[#MIDI_SysEx:_Axe-Fx_II|HEADER BYTES]]
 
:[[#MIDI_SysEx:_Axe-Fx_II|HEADER BYTES]]
Line 794: Line 790:
 
:0xF7  SysEx End
 
:0xF7  SysEx End
 
</tt>
 
</tt>
 
 
  
 
==GET_CPU_USAGE==
 
==GET_CPU_USAGE==
Line 820: Line 814:
  
 
==GET_PRESET_EDITED_STATUS==
 
==GET_PRESET_EDITED_STATUS==
The Axe FX and AX8 both have an LED on the front panel labeled EDITED, this LED goes on whenever you make any changes to your preset that have not been saved. This message will request that status. It will reply with 1 if the LED on the front panel (preset has changes that need to be saved) is on and 0 if it's off
+
The Axe FX and AX8 both have an LED on the front panel labeled "EDITED", which lights whenever you make any changes to your preset that have not been saved. This message will request that status. It will reply with 1 if the LED is on because the preset has changes that need to be saved, and 0 if it's off.
  
 
Message format:
 
Message format:
Line 840: Line 834:
 
:0xF7 SysEx End
 
:0xF7 SysEx End
 
</tt>
 
</tt>
 
 
  
 
==SET_TARGET_BLOCK==
 
==SET_TARGET_BLOCK==
Line 862: Line 854:
  
 
==GET_MONITOR_GRAPH==
 
==GET_MONITOR_GRAPH==
This function responds with the Monitor Graph, this is the "MONITOR" section you see on Axe-Edit displaying the EQ curve of many blocks.<br>
+
This function responds with the Monitor Graph, this is the "MONITOR" section you see on Axe-Edit displaying the EQ curve of many blocks.
This function's response contains 256 bytes (aside from the header, checksum and sysex end bytes) representing 128 points on the graph, each point made up of 2 bytes.<br>
+
 
 +
This function's response contains 256 bytes, aside from the header, checksum and sysex end bytes, representing 128 points on the graph, where each point is made up of two bytes.
 +
 
 
Each pair of bytes is a 14-bit signed integer, the integer will range from -8191 to 8191 with 0 being the middle of the graph.
 
Each pair of bytes is a 14-bit signed integer, the integer will range from -8191 to 8191 with 0 being the middle of the graph.
 
The Controllers tab also makes use of this graph for ADSR1/2 but the integer will range from 0 to 8191.
 
The Controllers tab also makes use of this graph for ADSR1/2 but the integer will range from 0 to 8191.
  
When requesting this Graph you must specify the Index of the graph, all blocks have 1 graph (so use 0 as the Graph Index) with the exception of the Controllers which has 2 graphs, ADSR1 Index is 0, ADSR2 Index is 1.
+
When requesting this Graph you must specify the Index of the graph. All blocks have one graph, so use 0 as the Graph Index, with the exception of the Controllers which has two graphs, ADSR1 Index is 0, ADSR2 Index is 1.
  
 
You MUST send [[#SET_TARGET_BLOCK|SET_TARGET_BLOCK]] before requesting the graph since the graph request does not include a block id.
 
You MUST send [[#SET_TARGET_BLOCK|SET_TARGET_BLOCK]] before requesting the graph since the graph request does not include a block id.
  
TIP: you should be careful with how often you request this graph, you should only do it when after a set interval after the parameter for that block has been edited.
+
TIP: you should be careful with how often you request this graph. You should only do it when after a set interval after the parameter for that block has been edited.
  
 
Message format:
 
Message format:
Line 904: Line 898:
 
</tt>
 
</tt>
  
 +
==GET/SET_AX8_FOOTSWITCHES==
  
 +
This function returns the contents of both the per-preset and global foot-switches on the AX8.
  
==GET/SET_AX8_FOOTSWITCHES==
+
It includes eight 6-byte chunks:
  
This function returns the contents of both the per preset and global foot-switches on the AX8.<br>
+
* The first 2 bytes of the chunk represent the block of the local (per-preset) footswitch, the ID will be the Block ID or other IDs representing the function of the footswitch.
It includes Eight 6-byte chunks:<br>
+
* The next two bytes of the chunk represent the global footswitch block ID.
The first 2 bytes of the chunk represent the block of the local (per preset) footswitch, the ID will be the Block ID or other IDs representing the function of the footswitch.<br>
+
* The next byte represents the flags of the global footswitch, as in if the global footswitch is engaged, engaged+momentary & engaged+latching.
The next 2 bytes of the chunk represent the global footswitch block ID<br>
+
* The next byte represents the flags of the local footswitch.
The next byte represents the flags of the global footswitch, as in if the global footswitch is engaged, engaged+momentary & engaged+latching<br>
+
* When the Global Flag is 0 the per-preset footswitch is used. When the flag is 1, global is engaged in as a latching type. When it is 2 it's engaged as a momentary type.
The next byte represents the flags of the local footswitch.<br>
 
When the Global Flag is 0 the per preset footswitch is used, when the flag is 1, global is engaged in latching type, when it's 2 it's engaged in momentary type.<br>
 
  
 
Message format:
 
Message format:
Line 946: Line 940:
 
</tt>
 
</tt>
  
To edit footswitches you send the entire layout of footswitches with the QUERY being 1, you would include each 6-byte chunk after the Number of Foot-switches, each chunk would have to have the values you want to set for each footswitch.
+
To edit footswitches you send the entire layout of footswitches with the QUERY being <tt>1</tt>, you would include each 6-byte chunk after the Number of Foot-switches, each chunk would have to have the values you want to set for each footswitch.
 
 
 
 
  
 
=MIDI SysEx: Importing/Exporting Presets=
 
=MIDI SysEx: Importing/Exporting Presets=
Line 959: Line 951:
 
In order to calculate the Checksum, you basically have to XOR every byte from the start of the SysEx message, up to the character BEFORE the terminating F7 byte.  For example, to send the following SysEx message (to fetch a preset name):
 
In order to calculate the Checksum, you basically have to XOR every byte from the start of the SysEx message, up to the character BEFORE the terminating F7 byte.  For example, to send the following SysEx message (to fetch a preset name):
  
<tt>F0 00 01 74 03 0F F7</tt>
+
:<tt>F0 00 01 74 03 0F F7</tt>
  
 
We would have to XOR all the byte values from the starting 'F0' to the '0F' which is the second last byte:
 
We would have to XOR all the byte values from the starting 'F0' to the '0F' which is the second last byte:
  
<tt>0xF0 ^ 0x00 ^ 0x01 ^ 0x74 ^ 0x03 ^ 0x0F = '''0x89'''</tt>
+
:<tt>0xF0 ^ 0x00 ^ 0x01 ^ 0x74 ^ 0x03 ^ 0x0F = '''0x89'''</tt>
  
 
Then, we would need to strip the leftmost bit from the result (by ANDing it to 0x7F):
 
Then, we would need to strip the leftmost bit from the result (by ANDing it to 0x7F):
  
<tt>0x89 & 0x7F = '''0x09'''</tt>
+
:<tt>0x89 & 0x7F = '''0x09'''</tt>
  
 
And, we add this byte (actually, a septet now) to the end of the SysEx string, BEFORE the terminating F7:
 
And, we add this byte (actually, a septet now) to the end of the SysEx string, BEFORE the terminating F7:
  
<tt>F0 00 01 74 03 0F '''09''' F7</tt>
+
:<tt>F0 00 01 74 03 0F '''09''' F7</tt>
  
Obviously, in a 'static' SysEx message like above, you do not have to recalculate the Checksum each time as it will always be '09' as the rest of the message does not change, but if you are sending a SysEx string to change a parameter value etc. then you will have to calculate the Checksum on the fly as byte values towards the end of the SysEx string will be different each time.
+
Obviously, in a "static" SysEx message like above, you do not have to recalculate the Checksum each time as it will always be "09" as the rest of the message does not change, but if you are sending a SysEx string to change a parameter value, etc., then you will have to calculate the Checksum on the fly as byte values towards the end of the SysEx string will be different each time.
For Python users, here is a checksum function to return the checksum string
 
(it requires the functtools reduce function)
 
  
{code}
+
For Python users, here is a checksum function to return the checksum string:
from functools import reduce
 
  
def checksum(ip_str):
+
from functools import reduce
    outstr = ''  # Create an outstring object
+
    ip_list = ip_str.split()  # Split the string into a list
+
def checksum(ip_str):
    int_list = [int('0x' + str(x), 16) for x in ip_list[0:-1]]  #  Create an integer list
+
    outstr = ''  # Create an outstring object
    s = reduce(lambda x, y: x ^ y, int_list)  # Calculate the checksum
+
    ip_list = ip_str.split()  # Split the string into a list
    int_list.append(s & 0x7f)  # Append the checksum to the list with leftmost bit stripped
+
    int_list = [int('0x' + str(x), 16) for x in ip_list[0:-1]]  #  Create an integer list
    int_list.append(0xF7)  # Append the terminating byte to the list
+
    s = reduce(lambda x, y: x ^ y, int_list)  # Calculate the checksum
    for itm in int_list:  # Get each item from the list
+
    int_list.append(s & 0x7f)  # Append the checksum to the list with leftmost bit stripped
        a_str = str(hex(itm))[2:]  # Create a string without the first two characters
+
    int_list.append(0xF7)  # Append the terminating byte to the list
        outstr = outstr + a_str.zfill(2) + ' '  # Concatenate each item into a single string
+
    for itm in int_list:  # Get each item from the list
    return outstr.strip()  # Strip leading or trailing blanks and return the string
+
        a_str = str(hex(itm))[2:]  # Create a string without the first two characters
{code}
+
        outstr = outstr + a_str.zfill(2) + ' '  # Concatenate each item into a single string
 +
    return outstr.strip()  # Strip leading or trailing blanks and return the string
  
 
=MIDI SysEx: obtaining parameter values=
 
=MIDI SysEx: obtaining parameter values=
  
The old Standard/Ultra devices used to store the parameter values on *most* knobs as 0-254. This was split into two bytes, XX YY where XX was the lowest 4 bits of the value, and YY was the highest 4 bits. Pretty simple.
+
The old Standard/Ultra devices used to store the parameter values on *most* knobs as 0-254. This was split into two bytes, <tt>XX YY</tt> where <tt>XX</tt> was the lowest 4 bits of the value, and <tt>YY</tt> was the highest 4 bits. Pretty simple.
  
 
The new Axe-II now has more granular control over *most* knobs, with the values going from 0-65534. This requires a full 16 bits to store. The problem is that the MIDI specifications means you can only transmit values using 7 bits as the highest bit of each byte must be a zero.
 
The new Axe-II now has more granular control over *most* knobs, with the values going from 0-65534. This requires a full 16 bits to store. The problem is that the MIDI specifications means you can only transmit values using 7 bits as the highest bit of each byte must be a zero.
  
To get around this, the Axe-II uses THREE bytes to transmit the values XX YY ZZ where XX is the lowest 7 bits of the data, YY is the 8th to the 14th bit of the data, and ZZ is the remaining top two bits of the data.
+
To get around this, the Axe-II uses THREE bytes to transmit the values <tt>XX YY ZZ</tt> where <tt>XX</tt> is the lowest 7 bits of the data, <tt>YY</tt> is the 8th to the 14th bit of the data, and <tt>ZZ</tt> is the remaining top two bits of the data.
  
So, if you had the value 52421 on a knob, then this would be represented by the 16 bits: 1100110011000101
+
So, if you had the value 52421 on a knob, then this would be represented by the 16 bits: <tt>1100110011000101</tt>.
  
If you split this into segments of length 2/7/7 to store in ZZ/YY/XX:  11  0011001  1000101
+
If you split this into segments of length 2/7/7 to store in <tt>ZZ</tt>/<tt>YY</tt>/<tt>XX</tt><tt>11  0011001  1000101</tt>
  
So XX gets the last segment: 01000101
+
So <tt>XX</tt> gets the last segment: <tt>01000101</tt>
And YY gets the middle segment: 00011001
+
And <tt>YY</tt> gets the middle segment: <tt>00011001</tt>
And ZZ gets the first segment: 00000011
+
And <tt>ZZ</tt> gets the first segment: <tt>00000011</tt>
 
 
You will also have to back-calculate the respective knob values to the 16 bit values. For example, a knob that goes from 0 - 10, 0 would of course be 0, but 10 would be 65534. From there, you would work out that 5 is 32767 and 7 would be about 45871 etc.
 
 
 
To convert these bytes to the decimal value and visa-versa read [[#MIDI_Sysex:_converting_bytes_to_decimal_and_vise_versa_and_more|MIDI Sysex: converting bytes to decimal and vise versa and more]]
 
  
 +
You will also have to back-calculate the respective knob values to the 16 bit values. For example, a knob that goes from 0 - 10, 0 would of course be <tt>0</tt>, but 10 would be <tt>65534</tt>. From there, you would work out that 5 is <tt>32767</tt> and 7 would be about <tt>45871</tt> etc.
  
 +
To convert these bytes to the decimal value and visa-versa read [[#MIDI_Sysex:_converting_bytes_to_decimal_and_vise_versa_and_more|MIDI Sysex: converting bytes to decimal and vise versa and more]].
  
 
=MIDI SysEx: loading IRs=
 
=MIDI SysEx: loading IRs=
Line 1,095: Line 1,083:
 
=MIDI Sysex: converting bytes to decimal and vise versa and more=
 
=MIDI Sysex: converting bytes to decimal and vise versa and more=
  
All functions below written in C++
+
All the functions below are written in C++.
  
The following are for each 5 byte chunk of message 0x0E (PRESET_BLOCKS_DATA)
+
The following are for each 5 byte chunk of message <tt>0x0E</tt> [#PRESET_BLOCKS_DATA|PRESET_BLOCKS_DATA].
 +
 
 +
This function returns <tt>true</tt> if the block is in <tt>X</tt> state or <tt>false</tt> if the block is in <tt>Y</tt> state.
 +
<tt>x</tt> is the first byte of each 5 byte chunk of message <tt>0x0E</tt>.
  
This function returns true if the block is in X state or false if the block is in Y state
 
x is the first byte of each 5 byte chunk of message 0x0E<BR>
 
 
<pre>
 
<pre>
 
bool isBlockX(byte x){
 
bool isBlockX(byte x){
Line 1,110: Line 1,099:
 
</pre>
 
</pre>
  
This function returns true if the block is bypassed, false if it's not
+
This function returns <tt>true</tt> if the block is bypassed, <tt>false</tt> if it's not.
x is the first byte of each 5 byte chunk of message 0x0E<BR>
+
<tt>x</tt> is the first byte of each 5 byte chunk of message <tt>0x0E</tt>.
 +
 
 
<pre>
 
<pre>
 
bool isBlockBypassed(byte x){
 
bool isBlockBypassed(byte x){
Line 1,121: Line 1,111:
 
</pre>
 
</pre>
  
This function converts the 2 bytes for the block's bypass CC (IA CC Number LSB/MSB) into a decimal number
+
This function converts the two bytes for the block's bypass CC (IA CC Number LSB/MSB) into a decimal number.
LSB is the second byte and MSB is the third byte of each 5 byte chunk of message 0x0E<BR>
+
LSB is the second byte and MSB is the third byte of each 5 byte chunk of message <tt>0x0E</tt>.
 +
 
 
<pre>
 
<pre>
 
int toBlockCC(byte LSB, byte MSB){
 
int toBlockCC(byte LSB, byte MSB){
Line 1,129: Line 1,120:
 
</pre>
 
</pre>
  
This function converts the 2 bytes for the block's ID (Effect ID LSB/MSB) into a decimal number
+
This function converts the two bytes for the block's ID (Effect ID LSB/MSB) into a decimal number.
LSB is the fourth byte and MSB is the fifth byte of each 5 byte chunk of message 0x0E<BR>
+
LSB is the fourth byte and MSB is the fifth byte of each 5 byte chunk of message <tt>0x0E</tt>.
 +
 
 
<pre>
 
<pre>
 
int blockID(byte LSB, byte MSB){
 
int blockID(byte LSB, byte MSB){
Line 1,137: Line 1,129:
 
</pre>
 
</pre>
  
This function converts the effect ID bytes OR the parameter ID bytes into a decimal number<br>
+
This function converts the effect ID bytes OR the parameter ID bytes into a decimal number.
 +
 
 
<pre>
 
<pre>
 
int bytesToInt(byte byte1, byte byte2){
 
int bytesToInt(byte byte1, byte byte2){
Line 1,144: Line 1,137:
 
</pre>
 
</pre>
  
The reverse of the above to turn a decimal number into 2 bytes where x is the decimal number<BR>
+
This is the reverse of the above to turn a decimal number into two bytes where x is the decimal number.
 +
 
 
<pre>
 
<pre>
 
byte byte1 = (x & 0x7F);
 
byte byte1 = (x & 0x7F);
Line 1,151: Line 1,145:
 
</pre>
 
</pre>
  
This function converts the 3 parameter value bytes on message (0x02) into a decimal number<BR>
+
This function converts the 3 parameter value bytes on message (<tt>0x02</tt>) into a decimal number.
 +
 
 
<pre>
 
<pre>
 
int parameterValueBytesToInt(byte byte1, byte byte2, byte byte3){
 
int parameterValueBytesToInt(byte byte1, byte byte2, byte byte3){
Line 1,158: Line 1,153:
 
</pre>
 
</pre>
  
The reverse of the above to turn a decimal number into 3 bytes where x is the decimal number<BR>
+
This is the reverse of the above to turn a decimal number into 3 bytes where <tt>x</tt> is the decimal number.
 +
 
 
<pre>
 
<pre>
 
byte byte1 = x & 0x7F;
 
byte byte1 = x & 0x7F;
Line 1,167: Line 1,163:
 
</pre>
 
</pre>
  
To convert a decimal number into the 2 bytes for a preset change, where x is the decimal number<BR>
+
This will convert a decimal number into the two bytes for a preset change, where <tt>x</tt> is the decimal number.
 +
 
 
<pre>
 
<pre>
 
byte byte1 = x >> 7;
 
byte byte1 = x >> 7;
Line 1,174: Line 1,171:
 
</pre>
 
</pre>
  
To convert the 2 bytes received from a preset change to a decimal number:
+
To convert the two bytes received from a preset change to a decimal number use:
 +
 
 +
Axe FX II:
  
Axe FX II
 
 
<pre>
 
<pre>
 
int x = (byte1 & 0x7F) << 7 | byte2;
 
int x = (byte1 & 0x7F) << 7 | byte2;
 
</pre>
 
</pre>
  
AX8
+
AX8:
 +
 
 
<pre>
 
<pre>
 
int x = (byte1 & 0x7F) | ((byte2 & 0x7F) << 7);
 
int x = (byte1 & 0x7F) | ((byte2 & 0x7F) << 7);
 
</pre>
 
</pre>
  
To convert the decimal preset number into a Bank/Preset set. This number ranges from 0 to 383 on the Axe FX II, 0 to 767 on the XL/+ and 0 to 511 on the AX8
+
This will convert the decimal preset number into a Bank/Preset set. This number ranges from 0 to 383 on the Axe FX II, 0 to 767 on the XL/+ and 0 to 511 on the AX8.
 +
 
 +
On the Axe FX II or later,  where x is the decimal number from 0 to 383/767:
  
Axe FX II or later,  where x is the decimal number from 0 to 383/767<BR>
 
 
<pre>
 
<pre>
 
byte bankNumber = floor(x / 128); // banks are labeled A,B,C,D,E,F where bank A is 0 and bank F is 5
 
byte bankNumber = floor(x / 128); // banks are labeled A,B,C,D,E,F where bank A is 0 and bank F is 5
Line 1,195: Line 1,195:
 
</pre>
 
</pre>
  
AX8 where x is the decimal number from 0 to 511<BR>
+
For the AX8, where x is the decimal number from 0 to 511:
 +
 
 
<pre>
 
<pre>
 
byte bankNumber = floor(x / 8); // banks are labeled from 1 to 64, this functions will return a number from 0 to 63, add 1 when you display it
 
byte bankNumber = floor(x / 8); // banks are labeled from 1 to 64, this functions will return a number from 0 to 63, add 1 when you display it
Line 1,201: Line 1,202:
 
byte presetNumber = x - (bank * 8);
 
byte presetNumber = x - (bank * 8);
 
</pre>
 
</pre>
 
 
  
 
=MIDI SysEx: Blocks & Parameters IDs=
 
=MIDI SysEx: Blocks & Parameters IDs=
  
Read this: [[MIDI SysEx blocks and parameters IDs]]
+
Read [[MIDI SysEx blocks and parameters IDs]] for more information.
  
 
=MIDI SysEx: more information=
 
=MIDI SysEx: more information=
  
Read this: [http://forum.fractalaudio.com/threads/using-backing-tracks-to-trigger-preset-scene-changes-w-bandhelper.125461 Recalling presets and scenes in hex]
+
Read [http://forum.fractalaudio.com/threads/using-backing-tracks-to-trigger-preset-scene-changes-w-bandhelper.125461 Recalling presets and scenes in hex] for more information.
  
 
[[category:Axe-Fx2]]
 
[[category:Axe-Fx2]]

Revision as of 22:25, 31 December 2021

MIDI.png

MIDI SysEx: System Exclusive messages

If you're a MIDI expert, you can use SysEx messages (MIDI System Exclusive) to control Fractal Audio devices.

Learn more about SysEx

Discussion about Fractal Audio's SysEx implementation

Firmware Ares and later provides MIDI support for 3rd-party devices to set and/or get scene, channel, bypass, scene names, preset names, tuner data, tempo data, via System Exclusive messages. Read this Axe-Fx III MIDI for Third-Party Devices.

MIDI SysEx: Standard/Ultra

Ultra-specific SysEx information from forum member GM Arts

SysEx commands for retrieving Preset/Effect bypass states

MIDI SysEx: Axe-Fx III and FM3

SysEx commands for control of the Axe-Fx III and FM3 through third-party MIDI devices is documented in this guide:

Axe-Fx III MIDI for Third-Party Devices

MIDI SysEx: SysEx Model number per device

The numbers below refer to the processors.

  • 0x00 Axe-Fx Standard
  • 0x01 Axe-Fx Ultra
  • 0x02 MFC-101
  • 0x03 Axe-Fx II
  • 0x04 MFC-101 mk3
  • 0x05 FX8
  • 0x06 Axe-Fx II XL
  • 0x07 Axe-Fx II XL+
  • 0x08 AX8
  • 0x0A FX8 mk2
  • 0x10 Axe-Fx III
  • 0x11 FM3
  • 0x12 FM9

MIDI SysEx: Functions

Many Axe-Fx II SysEx messages are similar to Standard/Ultra messages documented here.

The messages documented here are compatible between Axe-Fx II, Axe-Fx II XL, Axe-Fx II XL+ and AX8, FX8 messages are not documented at the moment

Note, many examples on this page are using 3 for the Model #.

All messages you send must be sent with Checksums, but not all response messages will have a Checksum, those documented here will specify if a Checksum is not included.

All Axe-Fx II/AX8 SysEx messages have the same HEADER as follows:

0xF0 sysex start
0x00 Manf. ID byte0
0x01 Manf. ID byte1
0x74 Manf. ID byte2
0xdd Model #

The byte after the Model # will always be the Function ID.

The last byte of each message will be 0xF7, the Checksum is the byte before 0xF7

Here's an example of a sysex message, in this example the AX8 is responding with the preset number, function 0x14 GET_PRESET_NUMBER

[0xF0, 0x00, 0x01, 0x74, 0x08, 0x14, 0x00, 0x00, 0x19, 0xF7];

GET_FIRMWARE_VERSION

This message responds with the firmware version, also this acts as a "Greeting" message, only after the Axe FX II has received a firmware version request it will start sending message 0x21 FRONT_PANEL_CHANGE_DETECTED, if you're writing an app or foot controller that will need to be in sync then the first thing you want to do is request the firmware version so that message 0x21 is sent when changes happen on the front panel. Keep in mind, if you do this you will need to "Disconnect" from the hardware when your app closes by sending function 0x42 DISCONNECT_FROM_CONTROLLER

Message format:

HEADER BYTES
0x08 Function ID (0x08)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x08 Function ID (0x08)
0xdd Firmware Version major number
0xdd Firmware Version minor number
0xdd
0xdd
0xdd
0xdd
0xdd Checksum
0xF7 SysEx End

FRONT_PANEL_CHANGE_DETECTED

This message is sent to notify you that something may have changed on the hardware, and is the message that causes Axe-Edit/AX8-Edit to "Pause Communication". This message is notifies the editor that something changed and that it should reload the data for that preset, and is why you get the PAUSE overlay on the editor. When you click that overlay the Editor will re-request all the preset data it needs to keep the editor in sync with the hardware.

For example if you select a block on the Axe FX II, say, the Filter block, and you change the gain, then the Axe FX will send this message.

This message is sent about 400ms after the action was complete.

Another example, on the AX8, if you use the Amp Controls on the front panel to change the Gain then you will receive this message. This message is also sent if you press a footswitch that changes a scene/preset/block bypass/XY.

This message is only sent by the hardware if you requested the Firmware version. Read 0x08 GET_FIRMWARE_VERSION for more information.

Message format:

HEADER BYTES
0x21 Function ID (0x21)
0xdd Checksum
0xF7 SysEx End

DISCONNECT_FROM_CONTROLLER

This message should be sent when you quit an application connected to the Axe Fx if your application sent the 0x08 GET_FIRMWARE_VERSION message. See the 0x08 GET_FIRMWARE_VERSION section for more information.

Message format:

HEADER BYTES
0x42 Function ID (0x42)
0xdd Checksum
0xF7 SysEx End

GET_MIDI_CHANNEL

This message message replies with the MIDI channel set under SETUP > I/O > MIDI > MIDI CHANNEL.

Message format:

HEADER BYTES
0x17 Function ID (0x17)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x17 Function ID (0x17)
0xdd Midi Channel (single byte)
0xdd Checksum
0xF7 SysEx End

TUNER_INFO

This message is broadcast when the Tuner is engaged. Note that the Axe-FX II does not include the checksum byte in this message:

HEADER BYTES
0x0D Function ID (0x0D)
0xdd Note (0=A; 1=Bb; 2=B; etc.)
0xdd String number (0=high E string; 5=low E string)
0xdd Tuner data (value of 63 indicates 'perfectly in tune')
0xF7 SysEx End

TUNER_TOGGLE

To turn the tuner on/off you send a MIDI Control Change (CC). The CC value is the same that you assign on the Axe-Fx II in the same channel your Axe-Fx II is set to.

The default CC for the tuner is 15. Send the value 0 to turn the tuner off, any value of 64 or higher to turn it on.

You can get the MIDI Channel of your Axe-Fx with GET_MIDI_CHANNEL.

METRONOME_TOGGLE

To turn the metronome on/off you send a MIDI Control Change (CC). The CC value is the same you assign on the Axe-Fx II in the same channel your Axe-Fx II is set to.

Send the value 0 to turn the metronome off, any value of 64 or higher to turn it on.

You can get the MIDI channel of your Axe-Fx with GET_MIDI_CHANNEL.

NOTE that the metronome's level can not be edited via MIDI.

PRESET_BLOCKS_DATA

This message returns a 32-bit unsigned integer split into five MIDI words for each block loaded onto the preset. That is, if the amp block is loaded you will get five bytes with data for the amp block, etc.

The message contains five important pieces of data:

The block's current Bypass state (0 if bypassed, 1 if engaged).
The block's current X/Y state (0 if Y, 1 if X).
The block's ID (number ranging from 100 to 170).
The block's bypass CC# (if the value is 0 or more than 127, it means the block doesn't have a CC# assigned).
The block's XY CC# (if the value is 0 or more than 127, it means the block doesn't have a CC# assigned or doesn't have X/Y).


Request format:

HEADER BYTES
0x0E Function ID (0x0E)
0xdd Checksum
0xF7 SysEx End


Response format: (response doesn't have a checksum)

HEADER BYTES
0x0E Function ID (0x0E)
Information about each effect block in preset follows, using 5 bytes for each effect block in the preset
0xdd Byte 1 (flags) uint32_t LSB
Bit 0: 0 = Bypassed, 1 = Engaged
Bit 1: 0 = Y, 1 = X
Bit 2: unused
Bit 3: unused
Bit 4: unused
Bit 5: unused
Bit 6: unused
0xdd Byte 2 (bypass cc# number LSB)
Bit 0: unused (Bit 7 for the byte above)
Bit 1: Bypass CC# Bit 0
Bit 2: Bypass CC# Bit 1
Bit 3: Bypass CC# Bit 2
Bit 4: Bypass CC# Bit 3
Bit 5: Bypass CC# Bit 4
Bit 6: Bypass CC# Bit 5
0xdd Byte 3 (bypass cc# MSB and XY cc# LSB)
Bit 0: Bypass CC# Bit 6
Bit 1: Bypass CC# Bit 7
Bit 2: XY CC# Bit 0
Bit 3: XY CC# Bit 1
Bit 4: XY CC# Bit 2
Bit 5: XY CC# Bit 3
Bit 6: XY CC# Bit 4
0xdd Byte 4 (XY cc# MSB and Block ID LSB)
Bit 0: XY CC# Bit 5
Bit 1: XY CC# Bit 6
Bit 2: XY CC# Bit 7
Bit 3: Block ID Bit 0
Bit 4: Block ID Bit 1
Bit 5: Block ID Bit 2
Bit 6: Block ID Bit 3
0xdd Byte 5 (Block ID MSB) uint32_t MSB
Bit 0: Block ID Bit 4
Bit 1: Block ID Bit 5
Bit 2: Block ID Bit 6
Bit 3: Block ID Bit 7
Bit 4: unused
Bit 5: unused
Bit 6: unused

The following C++ code will iterate thru the sysex message array, take each five-byte chunk and convert them into a uint32_t:

// "data" is the entire sysex array, "lengthOfMessage" is the length/size of that array

for(uint16_t i = 6, n = lengthOfMessage - 1 ; i < n ; i += 5){
  // convert the 5 bytes into a uint32_t with some bit-shifting
  uint32_t blockData = (data[i] & 0x7F) | ((data[i+1] & 0x7F)<<7) | ((data[i+2] & 0x7F)<<14) | ((data[i+3] & 0x7F)<<21) | ((data[i+4] & 0x7F)<<28);

  bool isBlockEngaged = (bool) (blockData & 0x01); // true if the block is on, false if bypassed
  bool isX            = (bool) (blockData & 0x02); // true if the block is in X, false if Y
  uint8_t id          = (uint8_t) ((blockData & 0xFF000000) >> 24);
  uint8_t bypassCC    = (uint8_t) ((blockData & 0x0000FF00) >> 8); // if this number is 0 or higher than 127, then there's no cc# assigned
  uint8_t xyCC        = (uint8_t) ((blockData & 0x00FF0000) >> 16); // if this number is 0 or higher than 127, then there's no cc# assigned
}

GET/SET_BLOCK_PARAMETER_VALUE

This message will get or set the value of the specified block parameter, this value ranges from 0 to 65534, the response message will contain a string which is the label displayed by the Axe Fx II and its editor. For example, the Bass parameter of the AMP block may have a value of 32767 but the label will be "5.00", the value may be 65534 but the label will be "10.00".

Also, many parameters have a low/high limit and step. For example, the "Voices" parameter of the CHORUS block will have a low limit: 2, high limit: 8 and step: 2 that means you can send values of 2,4,6,8. If you send a value lower than 2 it will set the parameter to 2, if it's higher than 8 it will set the value to 8.

The Effect Type selector is also a parameter. For example, in the AMP block the effect type is parameter 0, most blocks with an effect type use parameter 0 as the effect type but not all. You should monitor the MIDI messages between the Axe FX II and Axe-Edit to make sure which parameter is which.

Bypassing/Engaging a Block is also done with this function with parameter 255. Send the value 0 to Engage, 1 to Bypass.

When you are getting a parameter value you still have to include a parameter value with your message but this value can be 0.

To convert these bytes to decimal and vice-versa see MIDI Sysex: converting bytes to decimal and vise versa and more.

Message format:

HEADER BYTES
0x02 Function ID (0x02)
0xdd effect ID bits 6-0
0xdd effect ID bits 13-7
0xdd parameter ID bits 6-0
0xdd parameter ID bits 13-7
0xdd parameter value bits 6-0
0xdd parameter value bits 13-7
0xdd parameter value bits 15-14
0x00 0=query value, 1=set value
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x02 Function ID (0x02)
0xdd effect ID bits 6-0
0xdd effect ID bits 13-7
0xdd parameter ID bits 6-0
0xdd parameter ID bits 13-7
0xdd parameter value bits 6-0
0xdd parameter value bits 13-7
0xdd parameter value bits 15-14
0xdd
0xdd
0xdd
0xdd
0xdd
0xdd null-terminated string byte0
0xdd byte1
...
0x00 null character
0xdd Checksum
0xF7 SysEx End

SET_TYPED_BLOCK_PARAMETER_VALUE

This is the function used by Axe-Edit when you type the value of a parameter. If you read this page you've learned that parameter values range from 0 to 65534, and, with that in mind, the Bass on the AMP block displays a value of 0.00 to 10.00 with 0.00 being 0 and 10.00 being 65534. If you want to set the value of that parameter to exactly 6.35 then you would use this function.

The parameter value is sent as a 32-bit Signed Float split into five 7-bit bytes, for example, for the Balance parameter on the AMP block you can send -50.6 as the parameter value since the knob displays a range from -100.0 to 100.0.

NOTE: this function behaves differently with the AMP's Level parameter which doesn't seem to respond with the proper value.

Message format:

HEADER BYTES
0x2E Function ID (0x2E)
0xdd Block ID bits 6-0
0xdd Block ID bits 13-7
0xdd Parameter ID bits 6-0
0xdd Parameter ID bits 13-7
0xdd Parameter Value bits 6-0
0xdd Parameter Value bits 13-7
0xdd Parameter Value bits 21-14
0xdd Parameter Value bits 29-22
0xdd Parameter Value bits 32-30
0xdd Checksum
0xF7 SysEx End

This responds in the same way as 0x02 GET/SET_BLOCK_PARAMETER_VALUE.

GET/SET_MODIFIER_VALUE

Message format:

HEADER BYTES
0x07 Function ID (0x07)
0xdd effect ID bits 6-0
0xdd effect ID bits 13-7
0xdd parameter ID bits 6-0
0xdd parameter ID bits 13-7
0xdd modifier parameter selector ID bits 6-0
0xdd 0
0xdd modifier value bits 6-0
0xdd modifier value bits 13-7
0xdd modifier value bits 15-14
0x00 0=query value, 1=set value
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x02 Function ID (0x02)
0xdd effect ID bits 6-0
0xdd effect ID bits 13-7
0xdd parameter ID bits 6-0
0xdd parameter ID bits 13-7
0xdd modifier parameter selector ID bits 6-0
0xdd 0
0xdd modifier value bits 6-0
0xdd modifier value bits 13-7
0xdd modifier value bits 15-14
0xdd null-terminated string byte0
0xdd byte1
...
0x00 null character
0xdd Checksum
0xF7 SysEx End

Modifier Parameter Selector IDs:

0x0 modifier source
0x1 min
0x2 max
0x3 start
0x4 mid
0x5 end
0x6 slope
0x7 damping
0x8 ?
0x9 ?
0xA auto engage
0xB pc reset
0xC off val
0xD scale
0xE offset


GET_PRESET_NAME

Message format:

HEADER BYTES
0x0F Function ID (0x0F)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x0F Function ID (0x0F)
0xdd null-terminated string byte0
0xdd byte1
...
0x00 null character
0xdd Checksum
0xF7 SysEx End


SET_PRESET_NAME

Message format:

HEADER BYTES
0x0F Function ID (0x09)
0xdd Each letter of the preset name sent as an ASCII byte
...
0xdd Checksum
0xF7 SysEx End

This responds with a 0x64 MULTIPURPOSE_RESPONSE.

MIDI_TEMPO_BEAT

This is the Tempo beat or "pulse" message sent by Axe-FX II. Note that the Axe-FX II does not include the Checksum byte in this message:

HEADER BYTES
0x10 Function ID (0x10)
0xF7 SysEx End

SET_TAP_TEMPO

If you want to send a BPM value to the Axe-Fx II/AX8, you can do this via the 0x02 GET/SET_BLOCK_PARAMETER_VALUE function.

To do this, send the parameter to the "CONTROLLERS" Block (ID 141), Parameter ID 32, and the value would be a number between 30 and 250.

GET_PRESET_NUMBER

Message format:

HEADER BYTES
0x14 Function ID (0x14)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x14 Function ID (0x14)
0xdd Preset Number bits 6-0
0xdd Preset Number bits 13-7
0xdd Checksum
0xF7 SysEx End


SET_PRESET_NUMBER

This message sets a preset number of your choice, it will respond with message 0x64 to confirm.

Message format:

HEADER BYTES
0x3C Function ID (0x3C)
0xdd Preset Number bits 6-0
0xdd Preset Number bits 13-7
0xdd Checksum
0xF7 SysEx End

This function will respond with 0x64 MULTIPURPOSE_RESPONSE.

GET_GRID_LAYOUT_AND_ROUTING

Message format:

HEADER BYTES
0x20 Function ID (0x20)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x20 Function ID (0x20)
Information about each grid location follows. The first 4-byte chunk corresponds to the top-left grid location (column 1, row 1). The next four-byte chunk corresponds to the grid location at column 1, row 2. Following this pattern, each of the 48 grid locations are described.
0xdd Block/Shunt ID bits 6-0
0xdd Block/Shunt ID bits 13-7
0xdd routing flags
Bit 0: connect row 1 to effect input
Bit 1: connect row 2 to effect input
Bit 2: connect row 3 to effect input
Bit 3: connect row 4 to effect input
Bit 4:
Bit 5:
Bit 6:
0xdd (unused?)
... (47 additional four-byte chunks)
0xdd Checksum
0xF7 SysEx End

About Shunts: Each shunt has a unique ID just like blocks, there are 36 shunts, on the Axe-Fx II the Shunt IDs start at 200 up to 235, on the AX8 they start at 300 to 335.


MIDI_LOOPER_STATUS_ENABLE, MIDI_LOOPER_STATUS

Message format:

HEADER BYTES
0x23 Function ID (0x23)
0xdd 0=Disable looper status messages, 1=Enable looper status messages
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x23 Function ID (0x23)
0xdd Looper Status Bits
Bit 0: Record
Bit 1: Play
Bit 2: Once
Bit 3: Overdub
Bit 4: Reverse
Bit 5: Half
Bit 6: Undo
0xdd Looper Position (range: 0 to 99)
0xdd Checksum
0xF7 SysEx End


GET_SCENE_NUMBER

This is the same as SET_SCENE_NUMBER except you use 0x7F as the value for getting the scene number.

Message format:

HEADER BYTES
0x29 Function ID (0x29)
0x7F Indicator that you want to read the scene number
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x29 Function ID (0x29)
0xdd Scene Number (range: 0 to 7)
0xdd Checksum
0xF7 SysEx End

SET_SCENE_NUMBER

Message format:

HEADER BYTES
0x29 Function ID (0x29)
0xdd Scene Number (range: 0 to 7)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x29 Function ID (0x29)
0xdd Scene Number (range: 0 to 7)
0xdd Checksum
0xF7 SysEx End


GET_CAB_NAME

Message format:

HEADER BYTES
0x12 Function ID (0x12)
0xdd Cab Number
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x12 Function ID (0x12)
0xdd null-terminated string byte0
0xdd byte1
...
0x00 null character
0xdd Checksum
0xF7 SysEx End


GET_ALL_CAB_NAMES

Message format:

HEADER BYTES
0x12 Function ID (0x12)
0x7F Get all cabs identifier byte 1
0x7F Get all cabs identifier byte 2
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x12 Function ID (0x12)
0xdd null-terminated string byte0
0xdd byte1
...
0x00 null character
0xdd Checksum
0xF7 SysEx End


MIDI_SET_PRESET

Function number: 14
Example:
F0 0 1 74 3 14 0 1 13 0 FF --> to set the current preset (edit buffer) to preset 0 (1 if DISPLAY OFFSET activated)
F0 0 1 74 3 14 1 0 13 0 FF --> to set the current preset (edit buffer) to preset 128 (129 if DISPLAY OFFSET activated)
Send from AXE-FX to AXE-EDIT and MFC-101

GET_BLOCK_PARAMETERS_LIST

This function will get you a list of all the parameters values of a specified block, each parameter with its value is returned in a separate message.

Message format:

HEADER BYTES
0x01 Function ID (0x01)
0xdd block ID bits 6-0
0xdd block ID bits 13-7
0xdd Checksum
0xF7 SysEx End

This message will respond with 0x32 BATCH_LIST_REQUEST_START confirming that the batch of messages will be sent and how many will be sent.

After that message you will receive messages with function ID 0x01, these messages are identical to function 0x02 with the exception of the function ID being 0x01

Once complete it will respond with 0x33 BATCH_LIST_REQUEST_COMPLETE confirming that they were all sent.

Message 0x01 response format:

HEADER BYTES
0x01 Function ID (0x01)
0xdd block ID bits 6-0
0xdd block ID bits 13-7
0xdd parameter ID bits 6-0
0xdd parameter ID bits 13-7
0xdd parameter value bits 6-0
0xdd parameter value bits 13-7
0xdd parameter value bits 15-14
0xdd
0xdd
0xdd
0xdd
0xdd
0xdd null-terminated string byte0
0xdd byte1
...
0x00 null character
0xdd Checksum
0xF7 SysEx End

BATCH_LIST_REQUEST_START

This message is sent to you when you request a list of certain items, this includes when you request a list of parameters of a block (0x01 GET_BLOCK_PARAMETERS_LIST) or when you request a list of factory/user cab names, you can treat this message as confirmation that those parameters are about to be sent to you. The "Response Length" below represents how many messages will be sent as part of that batch.

Message format:

HEADER BYTES
0x32 Function ID (0x32)
0xdd ID of function that it's replying to
0xdd Response Length bits 6-0
0xdd Response Length bits 13-7
0xdd Checksum
0xF7 SysEx End


BATCH_LIST_REQUEST_COMPLETE

This message is sent to you after a batch of messages you requested was sent to you and is complete.

For example, if you request 0x01 GET_BLOCK_PARAMETERS_LIST, this message will reply confirming that the request has been complete.

The "ID of function that it's replying to" is the ID of the function that requested the list. In the case of 0x01 GET_BLOCK_PARAMETERS_LIST, which is function 0x01, it will show this byte as 0x01 (on the AX8 the function ID will be 0x02 even though the request was for 0x01).

Message format:

HEADER BYTES
0x33 Function ID (0x33)
0xdd ID of function that it's replying to
0xdd Checksum
0xF7 SysEx End

MULTIPURPOSE_RESPONSE

This message is sent by the hardware as a response to certain requests, the first byte after the function id is the requested function id (for example if you change the preset using function id 0x3C the first byte after 0x64 will be 0x3C). The next byte is a response code for the requested function, this code can mean different things, it could mean that the message you first sent had an error, usually if the response code is 0 it means that the task you requested is valid.

Message format:

HEADER BYTES
0x64 Function ID (0x64)
0xdd Response Function ID
0xdd Response Code
0xdd Checksum
0xF7 SysEx End


GET_BLOCK_XY, SET_BLOCK_XY

Message format:

HEADER BYTES
0x11 Function ID (0x11)
0xdd effect ID bits 6-0
0xdd effect ID bits 13-7
0xdd 0 = set to X, 1 = set to Y
0xdd 0 = get value, 1 = set value
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x11 Function ID (0x11)
0xdd effect ID bits 6-0
0xdd effect ID bits 13-7
0xdd 0 = effect is X, 1 = effect is Y
0xdd Checksum
0xF7 SysEx End


SET_BLOCK_BYPASS_STATE

You can Bypass/Engage a block using Function 0x02 GET/SET_BLOCK_PARAMETER_VALUE. To do this you will use the Block ID that you want to bypass, Parameter ID 255, and the value would be 0 to Engage and 1 to Bypass.

For example, this will bypass the amp block. Keep in mind the checksum must be calculated for the target device:

HEADER BYTES
0x02 Function ID (2)
0x6A effect ID bits 6-0
0x00 effect ID bits 13-7
0x7F parameter ID bits 6-0
0x01 parameter ID bits 13-7
0x01 parameter value bits 6-0
0x00 parameter value bits 13-7
0x00 parameter value bits 15-14
0x01 Set Value
0xXX Checksum
0xF7 SysEx End

GET_CPU_USAGE

Message format:

HEADER BYTES
0x13 Function ID (0x13)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x13 Function ID (0x13)
0xdd CPU % (single byte from 0 to 100)
0xdd Checksum
0xF7 SysEx End


GET_PRESET_EDITED_STATUS

The Axe FX and AX8 both have an LED on the front panel labeled "EDITED", which lights whenever you make any changes to your preset that have not been saved. This message will request that status. It will reply with 1 if the LED is on because the preset has changes that need to be saved, and 0 if it's off.

Message format:

HEADER BYTES
0x2A Function ID (0x2A)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x2A Function ID (0x2A)
0xdd 0 = NO, 1 = YES
0xdd Checksum
0xF7 SysEx End

SET_TARGET_BLOCK

This function is sent when Axe-Edit opens a block for editing, this message should be sent if you will be requesting Real Time modifiers or the GET_MONITOR_GRAPH.

Message format:

HEADER BYTES
0x37 Function ID (0x37)
0xdd Block ID bits 6-0
0xdd Block ID bits 13-7
0xdd Checksum
0xF7 SysEx End

Responds with 0x64 MULTIPURPOSE_RESPONSE


GET_MONITOR_GRAPH

This function responds with the Monitor Graph, this is the "MONITOR" section you see on Axe-Edit displaying the EQ curve of many blocks.

This function's response contains 256 bytes, aside from the header, checksum and sysex end bytes, representing 128 points on the graph, where each point is made up of two bytes.

Each pair of bytes is a 14-bit signed integer, the integer will range from -8191 to 8191 with 0 being the middle of the graph. The Controllers tab also makes use of this graph for ADSR1/2 but the integer will range from 0 to 8191.

When requesting this Graph you must specify the Index of the graph. All blocks have one graph, so use 0 as the Graph Index, with the exception of the Controllers which has two graphs, ADSR1 Index is 0, ADSR2 Index is 1.

You MUST send SET_TARGET_BLOCK before requesting the graph since the graph request does not include a block id.

TIP: you should be careful with how often you request this graph. You should only do it when after a set interval after the parameter for that block has been edited.

Message format:

HEADER BYTES
0x36 Function ID (0x36)
0xdd Graph Index (0 for most blocks, see the above info)
0xdd Checksum
0xF7 SysEx End

HEADER BYTES
0x36 Function ID (0x36)
256 bytes to follow, representing 128 14-bit Signed Integers
0xdd Point 1 bits 6-0
0xdd Point 1 bits 13-7
0xdd Point 2 bits 6-0
0xdd Point 2 bits 13-7
0xdd Point 3 bits 6-0
0xdd Point 3 bits 13-7
...
0xdd Checksum
0xF7 SysEx End

GET/SET_AX8_FOOTSWITCHES

This function returns the contents of both the per-preset and global foot-switches on the AX8.

It includes eight 6-byte chunks:

  • The first 2 bytes of the chunk represent the block of the local (per-preset) footswitch, the ID will be the Block ID or other IDs representing the function of the footswitch.
  • The next two bytes of the chunk represent the global footswitch block ID.
  • The next byte represents the flags of the global footswitch, as in if the global footswitch is engaged, engaged+momentary & engaged+latching.
  • The next byte represents the flags of the local footswitch.
  • When the Global Flag is 0 the per-preset footswitch is used. When the flag is 1, global is engaged in as a latching type. When it is 2 it's engaged as a momentary type.

Message format:

HEADER BYTES
0x48 Function ID (0x48)
0xdd QUERY, 0=Get Value, 1=Set Value
0xdd Number of Foot-switches (0 to request all)
0xdd Checksum
0xF7 SysEx End

Response format:

HEADER BYTES
0x48 Function ID (0x48)
0xdd QUERY, 0=Get Value, 1=Set Value
0xdd Number of Foot-switches (0x08 on the AX8)
Eight 6-byte chunks to follow (with the first chunk representing footswitch 1, last one footswitch 8)
0xdd Local ID bits 6-0
0xdd Local ID bits 13-7
0xdd Global ID bits 6-0
0xdd Global ID bits 13-7
0xdd Global Flag, 0=Global Disabled, 1=Global Engaged/Latching, 2=Global Engaged/Momentary
0xdd Local Flag, 1=Local Latching, 2=Local Momentary
...
0xdd Checksum
0xF7 SysEx End

To edit footswitches you send the entire layout of footswitches with the QUERY being 1, you would include each 6-byte chunk after the Number of Foot-switches, each chunk would have to have the values you want to set for each footswitch.

MIDI SysEx: Importing/Exporting Presets

MIDI SysEx: calculating the SysEx Checksum

As mentioned above, the Axe-FX II units require a Checksum at the end of the SysEx string that is sent to it (before the terminating F7 byte) as a verification step.

In order to calculate the Checksum, you basically have to XOR every byte from the start of the SysEx message, up to the character BEFORE the terminating F7 byte. For example, to send the following SysEx message (to fetch a preset name):

F0 00 01 74 03 0F F7

We would have to XOR all the byte values from the starting 'F0' to the '0F' which is the second last byte:

0xF0 ^ 0x00 ^ 0x01 ^ 0x74 ^ 0x03 ^ 0x0F = 0x89

Then, we would need to strip the leftmost bit from the result (by ANDing it to 0x7F):

0x89 & 0x7F = 0x09

And, we add this byte (actually, a septet now) to the end of the SysEx string, BEFORE the terminating F7:

F0 00 01 74 03 0F 09 F7

Obviously, in a "static" SysEx message like above, you do not have to recalculate the Checksum each time as it will always be "09" as the rest of the message does not change, but if you are sending a SysEx string to change a parameter value, etc., then you will have to calculate the Checksum on the fly as byte values towards the end of the SysEx string will be different each time.

For Python users, here is a checksum function to return the checksum string:

from functools import reduce

def checksum(ip_str):
    outstr =   # Create an outstring object
    ip_list = ip_str.split()  # Split the string into a list
    int_list = [int('0x' + str(x), 16) for x in ip_list[0:-1]]  #  Create an integer list
    s = reduce(lambda x, y: x ^ y, int_list)  # Calculate the checksum
    int_list.append(s & 0x7f)  # Append the checksum to the list with leftmost bit stripped
    int_list.append(0xF7)  # Append the terminating byte to the list
    for itm in int_list:  # Get each item from the list
        a_str = str(hex(itm))[2:]  # Create a string without the first two characters
        outstr = outstr + a_str.zfill(2) + ' '  # Concatenate each item into a single string
    return outstr.strip()  # Strip leading or trailing blanks and return the string

MIDI SysEx: obtaining parameter values

The old Standard/Ultra devices used to store the parameter values on *most* knobs as 0-254. This was split into two bytes, XX YY where XX was the lowest 4 bits of the value, and YY was the highest 4 bits. Pretty simple.

The new Axe-II now has more granular control over *most* knobs, with the values going from 0-65534. This requires a full 16 bits to store. The problem is that the MIDI specifications means you can only transmit values using 7 bits as the highest bit of each byte must be a zero.

To get around this, the Axe-II uses THREE bytes to transmit the values XX YY ZZ where XX is the lowest 7 bits of the data, YY is the 8th to the 14th bit of the data, and ZZ is the remaining top two bits of the data.

So, if you had the value 52421 on a knob, then this would be represented by the 16 bits: 1100110011000101.

If you split this into segments of length 2/7/7 to store in ZZ/YY/XX: 11 0011001 1000101

So XX gets the last segment: 01000101 And YY gets the middle segment: 00011001 And ZZ gets the first segment: 00000011

You will also have to back-calculate the respective knob values to the 16 bit values. For example, a knob that goes from 0 - 10, 0 would of course be 0, but 10 would be 65534. From there, you would work out that 5 is 32767 and 7 would be about 45871 etc.

To convert these bytes to the decimal value and visa-versa read MIDI Sysex: converting bytes to decimal and vise versa and more.

MIDI SysEx: loading IRs

The information below was provided by former forum member LMO.

The Axe Fx II supports 2040-point impulse responses that are packaged for download in a series of 66 MIDI SysEx messages, as follows:

MIDI_START_IR_DOWNLOAD

Prepare the Axe-Fx II to receive impulse response data

Message Format:

HEADER BYTES
0x7A function ID
0x20
0x00
0x10
0xdd Checksum
0xF7 SysEx End

MIDI_G2_IR_DATA

There are 64 SysEx messages, each containing 32 chunks of data. Each chunk consists of five bytes and can hold either four text characters or one 32-bit IR data sample.

The first data message sent includes 8 chunks of text that specify the 32-character IR name, and 24 chunks of IR data. The subsequent 63 data messages each contain 32 data samples for a total of 2040 samples.

Message Format:

HEADER BYTES
0x7B function ID
0x20
0x00
0xdd data chunk byte 0
0xdd data chunk byte 1
0xdd data chunk byte 2
0xdd data chunk byte 3
0xdd data chunk byte 4
--- 31 additional five byte data chunks ---
0xdd Checksum
0xF7 SysEx End

MIDI_CLOSE_IR_DOWNLOAD

Terminate the IR download sequence

Message Format:

HEADER BYTES
0x7C function ID
0xdd encoded Checksum byte 0 for IR data
0xdd encoded Checksum byte 1 for IR data
0xdd encoded Checksum byte 2 for IR data
0xdd encoded Checksum byte 3 for IR data
0xdd encoded Checksum byte 4 for IR data
0xdd Checksum
0xF7 SysEx End

Data Chunk Encoding Scheme

The data encoding scheme translates four octets into five septets. Each septet occupies the lower seven bits of a byte, with the most significant bit set to 0.

  • octet is one byte containing 8 bits of data
  • septet is one byte containing 7 bits of data
byte_chunk = (data[0] & 0xFF )<< 24 | (data[1] & 0xFF )<< 16 | (data[2] & 0xFF )<< 8 (data[3] & 0xFF;
  • convert four octets to five septets
septet[0] = byte_chunk & 0xFF;
septet[1] = byte_chunk >> 7 & 0xFF;
septet[2] = byte_chunk >> 14 & 0xFF;
septet[3] = byte_chunk >> 21 & 0xFF;
septet[4] = byte_chunk >> 28 & 0xFF;

MIDI Sysex: converting bytes to decimal and vise versa and more

All the functions below are written in C++.

The following are for each 5 byte chunk of message 0x0E [#PRESET_BLOCKS_DATA|PRESET_BLOCKS_DATA].

This function returns true if the block is in X state or false if the block is in Y state. x is the first byte of each 5 byte chunk of message 0x0E.

bool isBlockX(byte x){
  if(x==0x03||x==0x02){
    return true;
  }
  return false;
}

This function returns true if the block is bypassed, false if it's not. x is the first byte of each 5 byte chunk of message 0x0E.

bool isBlockBypassed(byte x){
  if(x==0x03||x==0x01){
    return false;
  }
  return true;
}

This function converts the two bytes for the block's bypass CC (IA CC Number LSB/MSB) into a decimal number. LSB is the second byte and MSB is the third byte of each 5 byte chunk of message 0x0E.

int toBlockCC(byte LSB, byte MSB){
  return ((LSB & 0x7E) >> 1) + ((MSB & 3) << 6);
}

This function converts the two bytes for the block's ID (Effect ID LSB/MSB) into a decimal number. LSB is the fourth byte and MSB is the fifth byte of each 5 byte chunk of message 0x0E.

int blockID(byte LSB, byte MSB){
  return ((LSB & 0x78) >> 3) + ((MSB & 0x0F) << 4);
}

This function converts the effect ID bytes OR the parameter ID bytes into a decimal number.

int bytesToInt(byte byte1, byte byte2){
  return (byte1 & 0x7F) | ((byte2 & 0x7F)<<7);
}

This is the reverse of the above to turn a decimal number into two bytes where x is the decimal number.

byte byte1 = (x & 0x7F);

byte byte2 = ((x >> 7) & 0x7F);

This function converts the 3 parameter value bytes on message (0x02) into a decimal number.

int parameterValueBytesToInt(byte byte1, byte byte2, byte byte3){
  return (byte1 & 0x7F) | ((byte2 & 0x7F)<<7) | ((byte3 & 0x7F)<<14);
}

This is the reverse of the above to turn a decimal number into 3 bytes where x is the decimal number.

byte byte1 = x & 0x7F;

byte byte2 = (x >> 7) & 0x7F;

byte byte3 = (x >> 14) & 0x7F);

This will convert a decimal number into the two bytes for a preset change, where x is the decimal number.

byte byte1 = x >> 7;

byte byte2 = x & 0x7F;

To convert the two bytes received from a preset change to a decimal number use:

Axe FX II:

int x = (byte1 & 0x7F) << 7 | byte2;

AX8:

int x = (byte1 & 0x7F) | ((byte2 & 0x7F) << 7);

This will convert the decimal preset number into a Bank/Preset set. This number ranges from 0 to 383 on the Axe FX II, 0 to 767 on the XL/+ and 0 to 511 on the AX8.

On the Axe FX II or later, where x is the decimal number from 0 to 383/767:

byte bankNumber = floor(x / 128); // banks are labeled A,B,C,D,E,F where bank A is 0 and bank F is 5

byte presetNumber = x - (bankNumber*128); // preset number with index of 0, to display offset it to 1 just add 1 to this number when displayed

For the AX8, where x is the decimal number from 0 to 511:

byte bankNumber = floor(x / 8); // banks are labeled from 1 to 64, this functions will return a number from 0 to 63, add 1 when you display it

byte presetNumber = x - (bank * 8);

MIDI SysEx: Blocks & Parameters IDs

Read MIDI SysEx blocks and parameters IDs for more information.

MIDI SysEx: more information

Read Recalling presets and scenes in hex for more information.