6
« Last post by dx100 on November 12, 2026, 03:21:21 AM »
For future reference this was done with Fable I believe but maybe Opus 5.5, I don't recall. I'm a long time developer but Claude surely makes things I've always wanted to find time to do much more feasible.
I asked Claude for more details and got this:
"The RAM came from about 11.5 KB that was still free in main RAM, made possible by moving msfa's large tables into flash. And yes, DX7 and native preenfm2 patches play at the same time, on different parts.
Where the RAM came from
Stock 2.21b leaves about 22 KB of main RAM free; the 64 KB CCM is nearly full and was not touched. The DX7 engine uses about 11.7 KB of that, leaving about 10 KB free:
┌───────────────────────────────────────────────────────────────────────────────┬────────┐
│ Item │ RAM │
├───────────────────────────────────────────────────────────────────────────────┼────────┤
│ DX7 voice pool: 8 msfa voices plus per-part LFO and controller state │ 6.6 KB │
├───────────────────────────────────────────────────────────────────────────────┼────────┤
│ msfa frequency table, which depends on the sample rate so is computed at boot │ 4 KB │
├───────────────────────────────────────────────────────────────────────────────┼────────┤
│ The four parts' DX7 voices (155 bytes each) plus one copy for menu undo │0.8 KB │
└───────────────────────────────────────────────────────────────────────────────┴───_─────┘
The trick that made it fit: msfa normally builds its sine and exp2 tables in RAM at startup, 16 KB together. In this port they're generated offline and stored as const arrays in flash, where there was plenty of space. Flash use went from about 274 KB to about 357 KB, out of 768 KB.
Playing both at once?
Yes. The engine is chosen per part by the patch loaded into it. Part 1 can play a DX7 patch on the msfa engine while parts 2 to 4 play preenfm2 patches on the native engine. Everything is summed into the same output, and each part keeps its own filter and effect, gain, pan and arpeggiator.
Caveats worth mentioning:
- The 8 DX7 voices are one pool shared by all DX7 parts.
- A DX7 part still reserves its native voices and operators in the existing budget, even though the msfa engine renders it. That's conservative but wasteful; a DX7 part could be budgeted separately.
- CPU load with 8 DX7 voices plus native voices has not been measured yet. A DEBUG_CPU_USAGE build is the next step. If it's tight, the DX7 pool can drop to 6."
Are we hurting the flash by storing there? or do we just store there once?
"No harm. The tables are written to flash once, as part of the firmware image, and after that they're only ever read.
- Written once. The sine and exp2 tables are const data compiled into the .bin. They're written to flash in the same erase-and-program pass as the rest of the firmware, when you flash it from the bootloader. Nothing at runtime ever writes to them.
- Reading doesn't wear flash. Flash wears from erase and program cycles, rated at a minimum of 10,000 per sector on this chip. Reads are unlimited. The audio engine reads these tables millions of times per second, the same way it reads the program code itself, which also lives in flash.
- So the only wear is per firmware update, exactly as with stock firmware. The tables add a few kilobytes to an image that's rewritten anyway.
The one cost of flash versus RAM is speed. Flash has wait states at 168 MHz, but the STM32F4's flash accelerator caches recent reads, and the inner loops hit the same small regions of the tables repeatedly. It's how the stock firmware already stores most of its own tables.
Nothing in our changes writes to flash at runtime. Patches, DX7 banks and settings all go to the USB key."