Dev workflow for Native Plugins

I’m curious what the best workflow for developing native plugins might be? I have an idea for a spectral type plugin but keen to develop as close to the hardware as possible without the extra VCV pieces. Currently workflow is:

  1. compile module to .mmplugin
  2. put on usb stick and move across to MM
  3. test
  4. iterate

The remove usb stick part is too impractical really though. Can the module be copied / reloaded by JTAG etc (ideally without reflashing while firmware)? Or is it possible to test mmplugins in the simulator? Or in a similar thread there was discussion of loading patches over serial connection, maybe possible here too? Would the Wifi expander allow easier workflow for the development cycle?

I can always go back to VCV first then port (as it’s easier to debug) but curious if there’s a better way?

Good question!
I did a lot of testing in the simulator for plugins.
The thing I did was create a branch in the firmware repo, and then add the “plugin” as a built-in. Then you can run the simulator and play with it freely.

You can see an example of that with the rack-fundamental-builtin branch and/or the airwindows-builtin branch. The Wifi Module lets you transfer plugin files, too, though I haven’t tried that workflow for development.

Doing it as a built-in also lets you test on MM hardware via JTAG, though with JTAG speeds, it’s not much faster than unloading a plugin, swapping a USB Drive, and re-loading it.

The basic procedure for making a plugin into a built it is:

  • add BRAND to firmware/vcv_ports/brands.cmake
  • put a CMakeLists.txt file and the source files needed to build the plugin in firmware/vcv_ports/BRAND/
  • add a new section for the brand in firmware/vcv_ports/internal_plugin_manager.hh, just copy/paste from another brand and update the module and brand names.
  • add PNG assets to firmware/assets/BRAND/. Remember if running on hardware you will need to do an update for the assets image (use the build/metamodule-firmware-vx.x.x-assets.zip file to update). If running on simulator you can just do make asset-image from the firmware dir to regenerate the image that the simulator uses.

Another approach, which is what we did for the 4ms modules is to create a wrapper in VCV for the module code. Your native plugin is a class derived from CoreProcessor, and then you have a VCV rack plugin project that generates a VCV module when given the CoreProcessor and an array of MetaModule::Elements.
This is how we did it for the 4ms modules:

The Element list is in a constexpr struct called the info structs, you can see them here:

So then to create the model, it’s like this:
rack::Model* modelMyModel = GenericModule<MyModelInfo>::create()

The slug in MyModelInfo is used to query the registry (ModuleFactory) and get a pointer to the CoreProcessor type. But a more simple way would be to modify GenericModule a little bit and do something like:

rack::Model* modelMyModel = GenericModule<MyModelInfo, MyModelProcessor>::create()

Conceivably, you could have a JUCE or VST layer around the CoreProcessor, too.

2 Likes

I can happily build, and debug using vscode built-in plugins using the metamodule repo and the simulator subdirectory. My question is can I debug using simulator and compiled .mmplugin objects somehow?

Context is experimenting with compiling Befaco Oneiroi which will have to remain closed source, and which I’ll have to distribute as a .mmplugin eventually, so it gets a bit awkward debugging as built-in, copying out, building in SDK etc. There are a few other changes between “built in” and metamodule-example-plugins right? Maybe the actual modified vcv source stays the same, and we just have different bits of surrounding structure (vcv_ports/glue in one, and plugin-sdk specific cmakefiles in the other)? Hope this makes sense!

Yeah, that’s basically it. The simulator can’t load an .mmplugin file, but you can build the plugin as a built-in without too much trouble. I did this when developing a few brands (Bogaudio, Fundamental, Airwindows…)

Since Befaco is already a built-in brand, maybe you could just add Oneiroi as a module within the Befaco brand, and then run it in the simulator. Once it’s all working well, you could move the code to the plugin.

But-- I’ve been meaning to document the process of running a plugin as a built-in in the simulator. I’d like to make it more automatic – right now you have to a few manual steps:

  • Append the brand name (let’s call it NewBrand) to vcv_ports/brands.cmake

  • Add the VCV repo as a submodule to vcv_ports/NewBrand

  • Put the plugin’s CMakeLists.txt file (plus any support files if there are any) into a new dir: vcv_ports/glue/NewBrand

  • In the this file, put a if(BUILD_DYN_PLUGIN)...endif() around the include(....../plugin.cmake) and also the create_plugin() call. This is done in all the other vcv_ports/glue/**/CMakeLists.txt files already, so you can copy that.

  • You probably will need to adjust some of the paths in the CMakeLists file so it points to the vcv_ports/NewBrand sources.

  • Add something like this to vcv_ports/internal_plugin_manager.hh in load_internal_plugins():

#ifndef BUILD_DYN_PLUGIN_NewBrand
		auto &newbrand_plugin = internal_plugins.emplace_back("NewBrand");
		newbrand_plugin.slug = "NewBrand";
		pluginInstance = &newbrand_plugin;
		pluginInstance->addModel(modelOneiroi);
#endif

I think that’s all that’s needed to run it in the simulator. There’s a cmake option you can set to build the plugin from within the firmware build, too (that’s what the cmake BUILD_DYN_PLUGIN stuff is for). Once it’s running in the simulator and all the bugs are worked out, you will need to remove the if(BUILD_DYN_PLUGIN)...endif() lines in the CMakeLists before building it outside of the firmware repo.

1 Like