Tips
September 5, 2025 ยท View on GitHub
Memory Allocations
The MetaModule has hard real-time requirements in order to acheive low-latency audio.
There is an audio "thread" which has the highest priority of any task. The
audio thread calls your module's process() or update() function for each sample frame.
Therefore any code in the process() function must be optimized for efficient
execution.
This means you cannot make any memory allocations in the process() function
(or any other function that's called by process(). This includes:
-
Do not create, destroy, or re-size a
std::string. Writing characters to an already allocated string is OK, as long as it does not change length. -
Do not
push_backoremplace_backor otherwise change the length of a std::vector (or any std container such as list, deque, etc...) You can read/write data in a vector, but don't change the capacity of the vector. If you do need a dynamically-sized vector in the audio thread, one useful technique is to usereserve()when you construct your module. Then you can safely callpush_backin the audio thread as long as you are 100% certain you will not push more items than you reserved.
I've seen a few examples of VCV Rack plugins that allocate memory in the audio thread. The most common example is allocating channels when a cable is patched into a jack. Often, the number of polyphonic channels is read from the port and then a vector of some data structure is allocated to accomodate that many poly channels. This is not allowed in the MetaModule. I suspect it's OK in VCV Rack because typically Rack is run on modern computers which can handle spikes in CPU usage coming from allocations. But the MetaModule will behave erratically, sometimes doing OK and sometimes failing when you patch a cable.
In order to address this pattern in several VCV Rack modules, the MetaModule
runs process once for each module when it first loads the patch. Sometimes
modules will allocate only if they detect their data structures have not been
set up.
If you are designing a new module, I would put forth a suggestion to set up all
data structures in the module constructor. That includes anything that requires
a heap allocation. Use reserve() if you still need dynamic re-sizing.
Then, in the process() function, make sure there is no situation in which a
memory allocation can happen.
Memory
The MetaModule has about 300MB of RAM dedicated for plugins to use. This includes the plugin code (but not data such as PNG files), and any memory individual modules in the patch will use. Be mindful that this is a shared resource, so try to keep the memory footprint low if your module deals with large buffers (several MB or more) or is able to load files of arbitrary sizes. Remember that users will often want duplicates of a module in a patch to make it 4-voice, 8-voice, etc.
Disk Access
File system access is permitted on the MetaModule. However, it is much slower than
on a desktop computer. Also keep in mind that file paths differ on the computer
than they do on the MetaModule.
Use the helper function translate_path_to_local() to convert a computer
path to a MetaModule path (see Filesystem Calls
Block sizes
The minimum block size the MetaModule supports is 16 frames. So if you are optimizing your module to process in blocks, this would be the ideal size to use.
Using a larger size would mean the CPU usage would spike, which can be upsetting and confusing to users.
UnInitialized data
Make sure any variables you use are initialized to a valid value. I do not have hard data to back this up, but my experience has been that often a desktop OS will return zero-intialized memory when you request an allocation. Perhaps this is a security feature? In any case, the MetaModule does not do this (and good coding practices tell us to always initialized a variable before reading it).
Bugs that result from using uninitialized data might not show themselves until you port to the MetaModule.
For example:
class MyModule : rack::Module {
float increment;
void process() {
int index = std::floor(increment);
float output = wavetable[index];
Clearly, if increment is some value greater than the size of the wavetable,
the module might crash. On desktop system, you are more likely to get a value
of 0 for increment, therefore the bug will not show itself.
MetaModule platform vs. Computer (VCV Rack) platform
The MetaModule runs without an operating system, and so has some different requirements than code written to be run on a desktop computer for VCV Rack. Also, subtle bugs or or less-than-ideal practices might rarely cause a problem on a fast computer with GB of RAM, but might cause frequent issues with the MetaModule's constrained resources. So, the process of porting can bring to light these issue.