Grunt Work
As of yesterday I've laid most of the groundwork to implement presets. As per my previous post re. them, I standardized on a byte for the ENCoder preset value, a byte for the preset TYPE (kind of an opcode), a 32 bit USE value, and a 4 byte LaBLe, all of which fit in a contiguous 10 byte space. I stuck all 50-ish of these structures together in memory to form a contiguous block that can be accessed with a stride of 10. ENC is the thing that gets manipulated by the associated encoder on a given page, USE is the value that gets used directly (or after something simple like a multiply, offset, or shift) by the code, LBL shows up on the LCD preceding the value, and TYPE helps the various parts of the code interpret what to do. ENC is the only thing that ends up in EEPROM as a preset, and being 8 bits limits any single adjustment to this number of discrete values.
It took me a while to settle on the TYPE encoding in terms of ENC limits. TYPE 0 is null, and displays a blank on the LCD. The TYPE values 1-127 are just that, limits of 0 to the value. The next 64 are +/- limits, the next 8 are 0 and 2^n - 1 limits, the next 8 are squared versions of those, and the next 8 are -2^n to 2^n - 1 limits. This leaves 3 remaining TYPEs if we want to adhere to the LSb decoding, or 3 * 32 = 96 types if we don't (I obviously chose not to adhere). I currently have two custom types, one for half note spacing of filter frequency and the like, and one with 2^12 dynamic range for volume attack and decay. To arrive at this encoding I did a quick inventory of the limits in use (most are 0 to 2^n - 1).
Having a TYPE means we can put anything we like on the LCD for the encoder value. It's nice to finally see Hz for filter frequencies (I need to do something similar for Q or damping). Handing the LCD string address directly to the display generator to stick the LBL and displayed value in there was the most straightforward way to do this.
And now all complex / time consuming conversion from ENC to USE values (LOG2, EXP2, INV, etc.) is out of the critical real-time path, and handled by a thread that gets interrupted at the LCD / encoder / refresh rate of 180Hz. With the core running at 180MHz and 8 threads sharing this, that means we've got 1,000,000 / 8 = 125,000 cycles to do it in, which is a lifetime.
All of the USE values on the displayed page get updated at this rate, but none of the others. So I've got a function that goes through and refreshes all USE values, and call it at start-up so I only have to put default values in one slot (ENC) and the rest auto-generate. I'll be using this function when a new preset is loaded. Limiting parameter loading from EEPROM to a subset, say past a certain point, can avoid changing system parameters that are placed lower than the limit. I'm thinking of having system parameters store to EEPROM only when the associated knob is pressed, and recall only at power-up. One system parameter will select 50/60Hz mains filtering, another will select the power-up preset to load, and there will be others for pitch and volume sensitivity, offset, linearity, etc.
A little more than 10% of RAM is left, which is probably OK though I wish there were more. Most of the subroutines are defined and in memory, so calling them a bunch more shouldn't need all that much code. It's kind of creepy making huge structural edits to the code at this point, as I need the prototype to remain in playable condition for practice and show-and-tell. Some weird external RF thing was going on last night around midnight which made me think I'd subtly broke the pitch side, but by sticking older loads in there I finally realized that I hadn't.