Create MetaModule plugins from Puredata patches (beta)

I’m looking for beta testers and feedback for the new Heavy Compiler Generator for MetaModule!

The past few weeks I’ve been working on this new wrapper for MetaModule Native plugins: start with MetaModule generator by dromer · Pull Request #405 · Wasted-Audio/hvcc · GitHub

It will convert exposed parameters to knobs and LEDs and audio-i/o to jack sockets. I’ve added a way to include custom assets, but if you omit this config it will generate a panel automatically as well (you can still set the panel color if you want). A screenshot.png is generated either way for convenience.

panel

Audio i/o is converted from -10:10 to -1:1 range. With parameters you can optionally supply scaling values (by default they are 0:1).

Since it is still on a feature branch you can install it using this command:

pip install git+https://github.com/Wasted-Audio/hvcc.git@feature/metamodule

An example export command would be:

hvcc metatest.pd -m metatest.json -g meta -o bla -p ../../heavylib

There are a few “hacks” in the code, like I ended up needing to define _memalign_r() which is required for heavy but missing from the sdk build.

Right now there are definitely still limitations as mentioned in the ADR and documentation, but we can already generate functional modules!

4 Likes

Alternatively you can download the self-contained executable here: start with MetaModule generator · Wasted-Audio/hvcc@fb8814f · GitHub

Use with Heavy or Heavy.exe instead of hvcc.

1 Like

@danngreen right now I have included the jack/knob/led.png from the NativeExample.
I hope this is OK (no license restriction), or should I include more unique assets?

1 Like

Oh that’s fine, I realize I didn’t put a license on those images in the Native Example. I’ll add a CC0 license. You’re free to use it.

Super cool you have this working! I’m going to try this out now!

1 Like

Awesome, I made a simple patch, and it worked! I didn’t play with the custom graphics, but the auto-generated one looked fine. All the element names were ok. It even survived a sample rate change without dropping the pitch.
This is great.

Re: the _memalign_r, I recall something like that coming up when I was spinning SDK 2.2.
I tried seeing what would break if I removed the stub, but my super-simple project compiled with the function in compat.cpp commented out anyways. Can you point me what relies on it so I can see about rolling that into the next SDK?

Edit: errr nevermind, I figured it out. I think we can add that symbol

1 Like

Ok, I got to the bottom of the memalign_r issue. The API exports memalign but not the reent version, which was just an accidental omission. I had to take it out of the newlib archive .a in the SDK because we route plugin allocations to a plugin arena in firmware, and the way newlib does it won’t work with our allocator.
In any case I added a shim in the SDK on the main branch for now. It’s a weak function so it won’t conflict with your workaround. I’ll remove it when we bump the API up next (api-v2.4) because I can add it to the symbol list. It’ll still work with plugins built with your workaround or any SDK v2.3.

One thing, though, is in compat.cpp, it returns std::malloc(bytes), but that drops the alignment. I don’t know if hvcc can generate over-aligned types, but if it ever does, this will crash in runtime on the random chance of alignment for the allocation being wrong. I hit this issue when working with NEON types, since they need alignment greater than their size.

Since I didn’t forget to export memalign in the api, you can just use that:

extern void *memalign(size_t align, size_t size);

void* _memalign_r(struct _reent* r, size_t alignment, size_t bytes) {
    (void)r;
    return memalign(alignment, bytes);
}

Thnx! a lot of this memory stuff tends to go over my head. This was just the quick-n-dirty solution I ended up with. I’ll try with memalign, but if we can remove this route entirely it would be even nicer :wink:

For now I’ll leave it so that people can test, but when api-2.4 gets released it sounds like a plan to clean it up.

Thank you for testing, anyway! Can also still use some feedback on the data-format. I’m not 100% sure to use all of these separate coords/size sub-objects. The main reason was so that I could simplify the validation side in pydantic, but for the user it maybe adds a bit of friction and with a lot of objects it makes the json a bit verbose ..

One thing I found is that the version isn’t shown in the plugin overview, even though it’s set in various places (the root CMakeLists.txt and plugin.json).

It needs to be in the mmplugin file name for the Plugins tab to show it (it just scans the filenames)

Do you mean the assets key in the metadata.json? The json would be pretty verbose to type in all the coordinates manually. Possble solution: both VCV and MM both have python scripts that read SVGs and look for certain shapes with certain colors, and generate the component positions and types from that. Something similar could be done for this. It’s a nice workflow with Inkscape to create a layer for all the component positions and another layer called “artwork” for the panel artwork, it’s easy to keep the art and positions in sync that way.

oh, right now it only creates <name>.mmplugin , but I think this is autogenerated?

and yeah I meant like the separate coordinates and size objects. could just be x/y on the same level:


        "knobs": [
            {
                "param": "<receiver_name>",
                "coords": {
                    "x": 20,
                    "y": 20
                },
                "image": "path/to/knob_image.png"
            }
        ]

vs


        "knobs": [
            {
                "param": "<receiver_name>",
                "x": 20,
                "y": 20
                "image": "path/to/knob_image.png"
            }
        ]

I’m not too keen on adding more dependencies for SVG processing though.

We need to be able to tie each specific parameter and socket to the asset and location, so I don’t really think we could do this using a full svg with layers.