Arduino Axe-Fx control library

Release Candidate 1!

This version is ready to roll. The only thing not yet implemented is switching effect channels.

This version has been toughened against very fast preset changes (eg spinning the wheel). I have also added more fine-grained callbacks for the preset number/name, scene number/name, and effect list, in case you only care about some of the data (eg just printing the preset name on an LCD).

I have also added another filter callback. As well as handling/overriding the sysex response, you can also filter which effects will be added to the preset. This lets you filter out effects you are not interested in (or keep exotic things like multiplexers).

https://github.com/tysonlt/AxeFxControl/releases/tag/v0.5-rc1
 
I have no experience programming for Arduino, but I was perusing your GitHub project out of curiosity.

Looking at the function AxeEffect::copyEffectNameAndTag from https://github.com/tysonlt/AxeFxControl/blob/master/src/AxeEffect.cpp and I was curious if the language supports hashes or dictionaries?

The reason I ask is I always feel like large case statements are inefficient. If you had a data structure keyed on effectId, you could simply perform a lookup.

Anyway, the code looks very clean... Although as an outsider, I would ask for more comments. At least a comment on each function describing its purpose.

For example, from the same file above, I'm not sure what AxeEffect::isSwitchable is supposed to be for. I would assume this is about whether the block supports channel switching... But if that is correct, then I think it's wrong because the "false case" includes all the Amp and Cab blocks.

I should probably get an Arduino "starter kit" because this kind of stuff has always intrigued me: 20+ years ago, I built my own in-car MP3 player with a bare motherhood, an IDE flash drive, a 2-line "smart" LCD display and a PS2 10-key keypad... Driven by Perl on a mini Linux distro and living in a shoe box!

This was before embedded Linux, micro-computers on a stick, etc, etc :)
 
I have no experience programming for Arduino, but I was perusing your GitHub project out of curiosity.

Looking at the function AxeEffect::copyEffectNameAndTag from https://github.com/tysonlt/AxeFxControl/blob/master/src/AxeEffect.cpp and I was curious if the language supports hashes or dictionaries?

The reason I ask is I always feel like large case statements are inefficient. If you had a data structure keyed on effectId, you could simply perform a lookup.

Anyway, the code looks very clean... Although as an outsider, I would ask for more comments. At least a comment on each function describing its purpose.

For example, from the same file above, I'm not sure what AxeEffect::isSwitchable is supposed to be for. I would assume this is about whether the block supports channel switching... But if that is correct, then I think it's wrong because the "false case" includes all the Amp and Cab blocks.

I should probably get an Arduino "starter kit" because this kind of stuff has always intrigued me: 20+ years ago, I built my own in-car MP3 player with a bare motherhood, an IDE flash drive, a 2-line "smart" LCD display and a PS2 10-key keypad... Driven by Perl on a mini Linux distro and living in a shoe box!

This was before embedded Linux, micro-computers on a stick, etc, etc :)

There are dictionaries, in fact moving from Java to C++ this is the biggest thing I missed. However on Arduino it's best to avoid anything that uses dynamic memory. When I played with linked lists and hashes I found them a tad unstable. Switches and arrays are certainly tedious to code, but once they're done they can't break.

Funny thing about the comments, I usually add a comment header to every method in the Java idiom... until a grumpy C++ told me off for useless comments everywhere!!! So I went through and deleted all of them!:tearsofjoy:

The isSwitchable is probably poorly named. It is just an attempt to filter out effects that you probably don't want displayed in an effects list, eg when printing them on a screen. I figured you'd want a list of things you are likely to switch, hence the name. Of course outputs etc ARE switchable so it needs to be renamed. I added a filter callback for this so that users can filter effects and ignore this method.

Thanks for checking it out! Definitely grab an arduino, but be warned, it's a time-sink!
 
This was before embedded Linux, micro-computers on a stick, etc, etc :)

Nice... cool before it was cool! It is a lot of fun and you get a great sense of achievement making it yourself, but it is seldom cheaper. This project started as a vanilla midi controller before I got the Axe3 and my whole musical world changed! :)

The one problem with hacking around with code and chips is that if you're not careful, you can spend more time with a microcontroller plugged into the axe than you do a guitar.
 
Nice... cool before it was cool! It is a lot of fun and you get a great sense of achievement making it yourself, but it is seldom cheaper. This project started as a vanilla midi controller before I got the Axe3 and my whole musical world changed! :)

The one problem with hacking around with code and chips is that if you're not careful, you can spend more time with a microcontroller plugged into the axe than you do a guitar.
The cool thing is that if you want something to work a certain way, you get to make it happen and the only thing stopping you is you!
 
I doubt I will be able to replicate the FC12, but I will be able to do all that is in the midi spec. I thought about going into the custom foot controller business, but who has the time?
 
I doubt I will be able to replicate the FC12, but I will be able to do all that is in the midi spec. I thought about going into the custom foot controller business, but who has the time?
@voes and @simonp54 do... @GM Arts writes custom firmware for the BJ Devices Controllers.

@oson00 has created a custom "translator" for using the MFC with the Axe Fx III.

Several other folks here have created their own, too.
 
In one week I will try out the Library.
Maybe I could give some ideas and we build a great library together! I really like the idea for a library!!
 
Anyway, the code looks very clean... Although as an outsider, I would ask for more comments. At least a comment on each function describing its purpose.

Ok Mr U-G, I defer to your wisdom. I have added a ton of comments, restoring what I had and expanding it a lot.

I have also been thinking about the case statement. The problem with a map is that you still have to initialise it, which is actually less efficient than a simple switch statement, which introduces almost no overhead. The other option is to read the data from an SD card, but it gets complicated. Massive switches like that are tedious to code, and brittle when the underlying struct changes (although I doubt fractal will change the effect list), but they are actually the most efficient way to retrieve the data.
 
In one week I will try out the Library.
Maybe I could give some ideas and we build a great library together! I really like the idea for a library!!
Sounds great man! I was interested to see your translation unit. I dig neat little solutions like that!
 
Ok Mr U-G, I defer to your wisdom. I have added a ton of comments, restoring what I had and expanding it a lot.

I have also been thinking about the case statement. The problem with a map is that you still have to initialise it, which is actually less efficient than a simple switch statement, which introduces almost no overhead. The other option is to read the data from an SD card, but it gets complicated. Massive switches like that are tedious to code, and brittle when the underlying struct changes (although I doubt fractal will change the effect list), but they are actually the most efficient way to retrieve the data.
Cool. I think with the idea of a library, the comments will be invaluable.

I understand that initializing the map may have more overhead than the case, but I would suspect that the case gets called potentially many times, where the initialization would only happen (presumably) once?

Anyway, it's your code and as I said, no experience with Arduino, so I don't know the memory requirements, etc. :)
 
Yeah I’m not sure how the switch statement boils down in assembler, but a map lookup has to do a hash compare to search for the key, so it’s really something that’s better suited for a bigger processor. I thought of creating a bunch of individual classes for each effect with the strings hard-coded, but it works, it’s fast, so I closed the lid on the box!
 
Yeah I’m not sure how the switch statement boils down in assembler, but a map lookup has to do a hash compare to search for the key, so it’s really something that’s better suited for a bigger processor. I thought of creating a bunch of individual classes for each effect with the strings hard-coded, but it works, it’s fast, so I closed the lid on the box!
The thing is, the hash once initialized should be very fast to lookup - that's a primary purpose of a hash.

Whereas the case statement has to process through each test case until it finds a match or doesn't.

For tests that are early in the list it's fast, but for items near the end it is going to be less so.

Anyway... I'll shut up now because I feel like I'm talking out of turn :)

If I decide to start into this coding, I'll try to test it.
 
The thing is, the hash once initialized should be very fast to lookup - that's a primary purpose of a hash.

Whereas the case statement has to process through each test case until it finds a match or doesn't.

For tests that are early in the list it's fast, but for items near the end it is going to be less so.

Hmm, that is true.

...

Just went down a stackoverflow rabbit-hole about how the compiler generates the lookup table and pointer arrays and sequential vs non-sequential indexes.... and decided to firmly close that box and weld it shut. :tearsofjoy::tearsofjoy::tearsofjoy: Ah, coding... it's a tender trap... time to pick up my guitar! :guitar:

Good times.
 
The thing is, the hash once initialized should be very fast to lookup - that's a primary purpose of a hash.

Whereas the case statement has to process through each test case until it finds a match or doesn't.

For tests that are early in the list it's fast, but for items near the end it is going to be less so.

Anyway... I'll shut up now because I feel like I'm talking out of turn :)

If I decide to start into this coding, I'll try to test it.

And in this particular case, since the ID's are sequential and in order, the hash is super simple and you don't need to deal with collisions (as you know all the IDS).

I experimented in compiler explorer using AVR gcc - but the version hosted there seems fairly old (no C++11 support)

In any case, here's a lookup table solution that also minimizes the amount of strings that have to be stored:
https://godbolt.org/z/CdYUCj

Compare with the original:
https://godbolt.org/z/anhQL0

Code size goes down by quite a bit.
 
And in this particular case, since the ID's are sequential and in order, the hash is super simple and you don't need to deal with collisions (as you know all the IDS).

I experimented in compiler explorer using AVR gcc - but the version hosted there seems fairly old (no C++11 support)

In any case, here's a lookup table solution that also minimizes the amount of strings that have to be stored:
https://godbolt.org/z/CdYUCj

Compare with the original:
https://godbolt.org/z/anhQL0

Code size goes down by quite a bit.
Not being a C++ coder at all, I just spent about 30 minutes looking at that and being confused... Until I realized that you "consolidated" a number of things into your code sample from other bits in the project :)

I'd need to spend more time really evaluating the original code to understand how it works before I understand your example.

But maybe the OP can get something from it?
 
Back
Top Bottom