Arduino Axe-Fx control library

tysonit wrote <"........Having MIDI 2.0 won’t change the fact that we don’t have the secret MIDI codes......">

For MIDI 2.0 to work it would require - firstly - Fractal SW engineers to add MIDI 2.0 support to either their current firmware for - say the currently sold modellers ( AND foot controllers ideally - as that would greatly expand the possibilities for the FC12/6 etc ) - so that all parameters for their modellers become available for query and change via the MIDI 2.0 MIDI CI protocol.

Or maybe Fractal would choose to - for marketing/sales reasons - to only add MIDI 2.0 to their new upcoming unreleased next-gen products. Clearly this would be a bummer for owners of existing products - but hey- they are a business. But one can dream :)

Then once this was done - then all manner of controllers - software or hardware - can begin to be designed to control such MIDI 2.0 based products.

Its very early days at the moment - MIDI 2.0 is only just really "out the door" if that - see here

https://midi.org/community/midi-specifications

and here

https://midi2.dev/

and even here ...

https://amenote.com

and for an app or plugin that already acts as a MIDI 2.0 controller/control surface see here -

http://www.woodmansimmaculatemaplesyrupstudio.be/?ref=soundonsoundnews

Best

Nonchai
 
tysonit wrote <"........Having MIDI 2.0 won’t change the fact that we don’t have the secret MIDI codes......">

For MIDI 2.0 to work it would require - firstly - Fractal SW engineers to add MIDI 2.0 support to either their current firmware for - say the currently sold modellers ( AND foot controllers ideally - as that would greatly expand the possibilities for the FC12/6 etc ) - so that all parameters for their modellers become available for query and change via the MIDI 2.0 MIDI CI protocol.

Or maybe Fractal would choose to - for marketing/sales reasons - to only add MIDI 2.0 to their new upcoming unreleased next-gen products. Clearly this would be a bummer for owners of existing products - but hey- they are a business. But one can dream :)

Then once this was done - then all manner of controllers - software or hardware - can begin to be designed to control such MIDI 2.0 based products.

Its very early days at the moment - MIDI 2.0 is only just really "out the door" if that - see here

https://midi.org/community/midi-specifications

and here

https://midi2.dev/

and even here ...

https://amenote.com

and for an app or plugin that already acts as a MIDI 2.0 controller/control surface see here -

http://www.woodmansimmaculatemaplesyrupstudio.be/?ref=soundonsoundnews

Best

Nonchai

Dream guys - dream!



As the great Bjork once sung: ;)

One day
It'll happen
One day, one day
It'll all come true

One day
When you're ready
One day, one day
When you're up to it
..........
One day
It'll happen
One day, one day
It'll all make sense

Cheers !
 
Had to create a dedicated sniffer, which is integrated into fractool ;)

Wonder why Fractal make it so difficult…

That's probably going to be a long wait... ;)

You could always sniff the sysex and try and figure it out. Others have done this in the past (I'm pretty sure most of FracPad was done this way).

So are you saying that the communication is still happening via sysex messages?

Been scratching my head about how to make bidirectional sync with hardware controller knobs possible.

Don’t plan to use Arduino, want to understand if it is at all possible to create something like a c++ library that could enable using off the shelf controllers for this. If not for all parameters then at least for those assigned to Performance Pages.

Wasn’t a big deal when I used an Axe-FX but now I’m planning to move to a floor unit, and it’s just a pain to adjust things with knobs on the floor. :)
 
Hi, yes you can send and receive sysex, but only what is publicly published in the fractal MIDI guide.

FracPad can query the internal parameters because the author has access to the proprietary/private sysex protocol that AxeEdit/FC uses, so unfortunately no, you can’t easily query or set individual effect parameters.

What you CAN do is assign controllers to some parameters and set the controller values via MIDI, so if you set that up in advance then yes, you can get some control over effects.

That doesn't work for bidirectional sync though, does it? It's fine for automation from a DAW, but if I switch presets and knobs on a controller aren't automatically updated to new values, how's that supposed to work? There will be huge jumps in volume and such when you try to change anything.
 
Wonder why Fractal make it so difficult…



So are you saying that the communication is still happening via sysex messages?

Been scratching my head about how to make bidirectional sync with hardware controller knobs possible.

Don’t plan to use Arduino, want to understand if it is at all possible to create something like a c++ library that could enable using off the shelf controllers for this. If not for all parameters then at least for those assigned to Performance Pages.

Wasn’t a big deal when I used an Axe-FX but now I’m planning to move to a floor unit, and it’s just a pain to adjust things with knobs on the floor. :)
Because they only have to design a sysex protocole meeting the editing requirements, not the 3rd party ones...
Yes it's still sysex : but sysex is just a container, embedded data is free to be what you want
 
Because they only have to design a sysex protocole meeting the editing requirements, not the 3rd party ones...
Yes it's still sysex : but sysex is just a container, embedded data is free to be what you want

Well at least it’s sniffable in principle and not encrypted or something of that sort, and the overall structure of the sysex wrapper is known, sort of.

Does it send data only when an editor is detected, would I need to fake presence of an Edit application for this to work?
 
Well at least it’s sniffable in principle and not encrypted or something of that sort, and the overall structure of the sysex wrapper is known, sort of.

Does it send data only when an editor is detected, would I need to fake presence of an Edit application for this to work?
It's not encrypted but considering the large amount of data flow, it's pretty hard to sniff ;)
That's why i had to develop a dedicated sniffer, filtering the redundant data etc...
 
It's not encrypted but considering the large amount of data flow, it's pretty hard to sniff ;)
That's why i had to develop a dedicated sniffer, filtering the redundant data etc...
Sounds kinda depressing

And I believe since you have a commercial product you’re not going to share that on any terms?
 
Sounds kinda depressing

And I believe since you have a commercial product you’re not going to share that on any terms?
You could ask RJM, since they also interact w Fractal devices, but almost certainly the same caveat applies about commercialware they spent time/money to develop.

It's also possible it's a violation of something to publish that info, regardless of where it came from. It is proprietary Fractal intellectual property.
 
You could ask RJM, since they also interact w Fractal devices, but almost certainly the same caveat applies about commercialware they spent time/money to develop.

It's also possible it's a violation of something to publish that info, regardless of where it came from. It is proprietary Fractal intellectual property.
The challenge for me is that I have zero interest in selling anything, I need to solve my personal problem, and if I succeed in doing it I'll only share it for free. Not sure RJM would be interested in doing anything for such a case.

I went away from the hardware world for a couple of years due to some quite drastic life changes (moving a lot from country to country), and use plugins now hosted in Gig Performer, where I developed a ton of scripts to have hardware controllers adjust key parameters. I love that setup but got immensely sick and tired of plugins themselves, so ordered an FM3. Yet want to have the convenience of using my controllers (a Behringer X-Touch Mini and a Korg Nanokontrol) with it for quick adjustments, switching between songs, mixing, playing backing tracks etc.

Obviously this isn't going to give me a revenue stream to pay licensing costs to somebody, although I'd be happy to pay once maybe, depending on how much that would cost. I also need to assess whether the effort is something I want to undertake at all.
 
Unofficial statement from just me, if the specs aren't public, you're in for a lot of research, the results of which will continue to evolve. Doing it bidirectionally seems challenging too.

Midi is a classic example of the huge value created by public communication specs, ideally standardized. The only reason i can think of for why Fractal doesn't release them is that they're a small company with limited bandwidth, and they'd probably end up handholding a bunch of developers looking for a clue.

Good luck in any case :)
 
The only reason i can think of for why Fractal doesn't release them is that they're a small company with limited bandwidth, and they'd probably end up handholding a bunch of developers looking for a clue.
Yes, that makes sense, maintaining a public interface with documentation to accompany it may be a product in and of itself.

However, it doesn’t have to go all the way there, there can be some middle ground as well, the communication protocols can be open with no support or warranties.

Come to think of it, there are whole micro industries around some products including Fractal that exist with the sole purpose of making things a bit easier to users. Fracpad exists because of it, people sell courses, preset packs, block libraries etc, there are no guarantees of backward compatibility for them, I think. So it’s okay in some cases.
 
Last edited:
I'm working in a controller for my FM3 posted in forum.



I'm using Arduino Mega. Thanks to tysonIt for his glorious library.
I got problems getting the preset number and name when changing it in my FM3. When I change preset with the controller, everything is working. I have tried many ways of doing it, but without success.

Does anybody get an idea about it?

Here is the code:

C++:
#include <Arduino.h>

#include <Wire.h>

#include "AxeFxControl.h"

#include "LiquidCrystal_I2C.h"

#include "OneButton.h"

#define SCENE_1   1 // Scene 1

#define SCENE_2   2 // Scene 2

#define SCENE_3   3 // Scene 3

#define SCENE_4   4 // Scene 4

#define SCENE_5   5 // Scene 5

#define SCENE_6   6 // Scene 6

#define SCENE_7   7 // Scene 7

#define SCENE_8   8 // Scene 8

#define MODES1 "1 Scenes            "

#define MODES2 "2 Effects           "

#define MODES3 "3 Config            "

#define MODES4 "4 Looper            "

// Create an instance of the AxeSystem class

AxeSystem Axe;

// Create the lcd object with address 0x27 and 16 columns x 2 rows

LiquidCrystal_I2C lcd(0x27,20,4);

// Variable that stores the last button pressed

int lastButton = 0;

// Variable thar stores mode

String mode = "Scn";

// Variable that stores if mode is changing

bool modeChanging = false;

// Create buttons

OneButton button1(53);

OneButton button2(52);

OneButton button3(51);

OneButton button4(50);

OneButton button5(49);

OneButton button6(48);

OneButton button7(47);

OneButton button8(46);

// Function executed when button is pressed

void click1();

void click2();

void click3();

void click4();

void click5();

void click6();

void click7();

void click8();

void printLCD(String message1 = "",String message2 = "",String message3 = "",String message4 = "");

void clearLcd();

void modeScreen();

void onPresetChange(AxePreset preset);

void PresetDecrement();

void PresetIncrement();

void down10Presets();

void up10Presets();

void down100Presets();

void up100Presets();

void sceneMode();

void effectMode();

void configMode();

void loopMode();



void setup() {

 

  // Initialize serial port for debugging

  Serial.begin(9600);

  button3.setLongPressIntervalMs(300);

  button6.setLongPressIntervalMs(300);

 

  // Procedures when button is pressed

  button1.attachClick(click1);

  button2.attachClick(click2);

  button3.attachClick(click3);

  button4.attachClick(click4);

  button5.attachClick(click5);

  button6.attachClick(click6);

  button7.attachClick(click7);

  button8.attachClick(click8);

  // Prodecure when longpressing occurs

  button1.attachLongPressStart(modeScreen);

  button4.attachLongPressStart(PresetDecrement);

  button8.attachLongPressStart(PresetIncrement);

  button4.attachDoubleClick(down10Presets);

  button8.attachDoubleClick(up10Presets);

  button4.attachMultiClick(down100Presets);

  button8.attachMultiClick(up100Presets);

 

  // Initialize the LCD

  lcd.init();

  // Turn on the backlight

  lcd.backlight();

  printLCD("** Piggy MidiBox **","","","  An FM3 Controller");

  delay(1000);

  printLCD("Fractal FM III     ","Firmware 1.4","AxeFxControl","by David Granados   ");

  delay(750);

  clearLcd();

  sceneMode(); // Set the initial mode to "Scenes"

  // Initialize the AxeSystem with Serial1

 

  Axe.begin(Serial1,AxeSystem::MIDI_CHANNEL_OMNI);

  Axe.registerPresetChangeCallback(onPresetChange);

  Axe.requestPresetDetails();

  Axe.fetchEffects(true);

  Axe.enableRefresh(3000,500);

  Axe.refresh(true);     

}

void loop() {

  // Update the AxeSystem to process incoming data

  Axe.update();

  // Update the state of the buttons

  button1.tick();

  button2.tick();

  button3.tick();

  button4.tick();

  button5.tick();

  button6.tick();

  button7.tick();

  button8.tick();

}

void click1()

{

  Serial.println("Click 1");

  if (modeChanging)

    {     

      sceneMode(); // Change to scene mode

      onPresetChange(Axe.getCurrentPreset()); // Update the LCD with the current preset and scene   

    }

  else

    {     

      if (lastButton != 1)

      {

        lastButton = 1;       

        Axe.sendSceneChange(SCENE_1);               

      }

    }       

}

void click2()

{

  Serial.println("Click 2");

  if (modeChanging)

  {

    effectMode(); // Change to effect mode

    onPresetChange(Axe.getCurrentPreset()); // Update the LCD with the current preset and scene   

  }

  else

  {

    if (lastButton != 2)

    {

      lastButton = 2;     

      Axe.sendSceneChange(SCENE_2); // Send the effect change command to the AxeFX

    }   

  }

    

}

void click3()

{

  Serial.println("Click 3");

  if (modeChanging)

    {     

      configMode(); 

      onPresetChange(Axe.getCurrentPreset()); // Update the LCD with the current preset and scene           

    }

  else

    {     

      if (lastButton != 3)

      {

        lastButton = 3;       

        Axe.sendSceneChange(SCENE_3);

      }

    } 

}

void click4()

{

 

  Serial.println("Click 4");

  if (modeChanging)

    {     

      loopMode();       

      onPresetChange(Axe.getCurrentPreset()); // Update the LCD with the current preset and scene     

    }

  else

  {

    if (lastButton != 4)

    {

      lastButton = 4;     

      Axe.sendSceneChange(SCENE_4); // Send the effect change command to the AxeFX

    }   

  }

}

void click5()

{

  Serial.println("Click 5");

  if (modeChanging)

    {   

      modeChanging = false;     

    }

  else

  {

    if (lastButton != 5)

    {

      lastButton = 5;     

      Axe.sendSceneChange(SCENE_5); // Send the effect change command to the AxeFX

    }   

  }

}

void click6()

{

  Serial.println("Click 6");

  if (modeChanging)

    {             

      modeChanging = false;     

    }

  else

  {

    if (lastButton != 6)

    {

      lastButton = 6;     

      Axe.sendSceneChange(SCENE_6); // Send the effect change command to the AxeFX

    }   

  }

}

void click7()

{

  Serial.println("Click 7");

  if (modeChanging)

    {             

      modeChanging = false;     

    }

  else

  {

    if (lastButton != 7)

    {

      lastButton = 7;     

      Axe.sendSceneChange(SCENE_7); // Send the effect change command to the AxeFX

    }   

  }

}

void click8()

{

  Serial.println("Click 8");

  if (modeChanging)

    {             

      modeChanging = false;     

    }

  else

  {

    if (lastButton != 8)

    {

      lastButton = 8;     

      Axe.sendSceneChange(SCENE_8); // Send the effect change command to the AxeFX

    }   

  }

}

void down10Presets()

{

  PresetNumber preset = Axe.getCurrentPreset().getPresetNumber();

  if (preset < 10) {

    preset = 0;

  } else {

    preset -= 10;

  }

  Axe.sendPresetChange(preset);

  Serial.print("Retrocede 10 presets: ");

}

void up10Presets()

{

  PresetNumber preset = Axe.getCurrentPreset().getPresetNumber();

  if (preset > 500) {

    preset = 511;

  } else {

    preset += 10;

  }

  Axe.sendPresetChange(preset);

  Serial.print("Avanza 10 presets: ");

}

void down100Presets()

{

  PresetNumber preset = Axe.getCurrentPreset().getPresetNumber();

  if (preset < 100) {

    preset = 0;

  } else {

    preset -= 100;

  }

  Axe.sendPresetChange(preset);

  Serial.print("Retrocede 100 presets: ");

}

void up100Presets()

{

  PresetNumber preset = Axe.getCurrentPreset().getPresetNumber();

  if (preset > 410) {

    preset = 511;

  } else {

    preset += 100;

  }

  Axe.sendPresetChange(preset);

  Serial.print("Avanza 100 presets: ");

}

void modeScreen()

{

  clearLcd();

  printLCD(MODES1,MODES2,MODES3,MODES4);

  modeChanging = true;

  lastButton = 0;

}

void clearLcd() {

  lcd.setCursor(0,0);

  lcd.print("                ");

  lcd.setCursor(0,1);

  lcd.print("                ");

  lcd.setCursor(0,2);

  lcd.print("                ");

  lcd.setCursor(0,3);

  lcd.print("                ");

}

void printLCD(String message1, String message2, String message3, String message4)

{

  if (message1.length() > 0)

    {

      lcd.setCursor(0,0);lcd.print(message1);

    }

  if (message2.length() > 0)

    {

      lcd.setCursor(0,1);lcd.print(message2);

    }

  if (message3.length() > 0)

    {

      lcd.setCursor(0,2);lcd.print(message3);

    }

  if (message4.length() > 0)

    {

      lcd.setCursor(0,3);lcd.print(message4);

    }

}



void PresetDecrement(){ Axe.sendPresetDecrement();}

void PresetIncrement(){ Axe.sendPresetIncrement();}

void onPresetChange(AxePreset preset) {       

 

   // Preset and scene Number & Name to LCD display

 

   printLCD("P"+String(preset.getPresetNumber())+" "+String(preset.getPresetName()).substring(0,10)+" "+String(mode),

            "S"+String(preset.getSceneNumber())+" "+String(preset.getSceneName()).substring(0,15)+" ","","");

 

}

void sceneMode()

{

    mode = " Scn";   

    modeChanging = false;

    printLCD("","","5    6    7    8    ","1    2    3    4    ");

}

void effectMode()

{

    mode = " Fx";   

    modeChanging = false;

    printLCD("",""," Cmp  Drv  Flg  Trm "," Cmp  Drv  Flg  Trm ");

}

void configMode()

{

    mode = " Cfg";   

    modeChanging = false;

    printLCD("","","Contrast ","Midi Channel ");

}

void loopMode()

{

    mode = " loop";   

    modeChanging = false;

    printLCD("",""," ","Play Record Undo");

}
 
Last edited:
You shouldn’t have to call onPresetChange manually, the library should be calling that for you. Start with the examples in the library to test whether basic preset change callbacks are working
 
It's true, not necessary. It's been a desperate way of trying how to show preset name when changed in the FM3. I can't find how to achieve it. The scene changes, but not preset name and number. Thanks for your replies.
 
Try the SwitchPreset or PresetName examples, and let me know what happens.

Don’t try to fix your current code until you see those examples working. You will save a lot of time if you can isolate the most basic things first.

Also you will need to pass FRACTAL_PRODUCT_FM3 when you first create the AxeSystem object.
 
You shouldn’t have to call onPresetChange manually, the library should be calling that for you. Start with the examples in the library to test whether basic preset change callbacks are working
This is what i do too

Void onPresetChange(AxePreset preset)
{preset.getPresetName(); }

you can print it directly on your screen or place it in a variable for use later.
Without this, i get the previous presetname on my screen
 
Last edited:
Back
Top Bottom