Learning to port - getting a lot of build errors with libstdc++-v3 in the metamodule-plugin-lib

Hi @danngreen - wondering if you have some guidance or pointers on what happened here:

Errors (warning, massive list)

CMakeLists:

Am trying my hand at porting over SurgeXT. I got most of it working, with some modified build commands:

cmake -B build -G Ninja -DTOOLCHAIN_BASE_DIR=/Applications/ArmGNUToolchain/14.2.rel1/arm-none-eabi/bin -DCMAKE_CXX_STANDARD=17

then

cmake --build build

Thanks!

Hi! Very cool you’re working on this!
The first thing I see is the arm-gcc toolchain v14.2. The libstdc++ for the SDK is v12.3, so you must use the arm-gcc v12.3 compiler.

Also, I would guess -DCMAKE_CXX_STANDARD=17 will cause a problem. The sdk uses c++20, so that might break things having that line. In fact, it might be that line that’s causing the libstdc++ errors.

If you do end up needing to compile the Serge sources with c++17, then the best way to do that would be to set it in the CMakeLists.txt file, something like this:

set_source_files_properties(
  ${SOURCE_DIR}/src/SurgeFileName.cpp
  PROPERTIES COMPILE_OPTIONS "-std=c++17"
)

But I would first try to compile it all without changing the c++ standard.

Thanks! That fixed it. Just wanted to be sure I’m not going in the wrong direction… do I basically just keep trying to compile until all the dependencies are compiled? I’m a little concerned with the sheer number of libs that surge has, see: surge/libs at main · surge-synthesizer/surge · GitHub - I’m starting to get compilation errors about missing headers from these so I’m adding them in, but I have no idea if they are compatible with the cortex m7 or what to look for.

update: got it to fully build, but not seeing anything in metamodule-plugin output directory

Logs above

From the log, it looks like you build SurgeXT but didn’t build any metamodule stuff. At least, in that cmake output there’s no mention of building the plugin SDK sources or creating a plugin.
Maybe you’re not in the right directory? You should be in the same dir as the CMakeLists.txt file you send earlier (the one with the create_plugin call at the end)

1 Like

Thanks, a lot of newbie mistakes so thanks for bearing with me. I ended up getting to a dark alleyway with SurgeXT with some Filesystem code that wouldn’t build and felt like it needed way too much intuitive knowledge about what was compatible with the cortex, so I’m temporarily pausing that work.

In good news though I’ve made a tremendous amount of progress on another port attempt, just a single Filter Module from SquinkyLabs. I’ve also gotten the simulator to build (note, I could not build on anything except for clang 16 on Mac Sonoma, every single version of gcc or newer versions of clang had errors with missing headers, etc.)

Probably fastest to just show a quick video - I can see the plugin in my plugins list, but unfortunately not able to find or load it when starting a new patch? Not sure if there’s something I missed.

https://www.dropbox.com/scl/fi/wolq1g3wksiid2d1r842m/Squinky.zip?rlkey=dybcm002a2cytuyn95dllzvk8&st=boaodg2x&dl=0

I’m sharing the following zip of my repo above as well as some logs

Simulator:

(base) ericgao@Erics-MBP-2 simulator % build/simulator
[Warn]  (448183.906, +448183906)         lv_init: Style sanity checks are enabled that uses more RAM    (in lv_obj.c line #181)
SDL: 1 audio devices found
0: MacBook Pro Speakers (selected)
SDL: Audio device MacBook Pro Speakers opened at 48000Hz, 2 channels, 512 blocksize, 8 bytes per frame, 8120 format code
HostFileIO: foreach_dir_entry() in "."
HostFileIO: read ./EnvVCA.yml
HostFileIO: read ./QuadEnosc.yml
HostFileIO: read ./TapoSaveTest.yml
HostFileIO: read ./BraidsLFOs.yml
HostFileIO: read ./evenenv.yml
HostFileIO: read ./MappingRangeTest.yml
HostFileIO: read ./ChaosPlaits.yml
HostFileIO: read ./Djembe4verb_extgate.yml
HostFileIO: read ./EnsembleBasic.yml
HostFileIO: read ./FlipPan.yml
HostFileIO: read ./Orcas_Heart_Octo_Djembe.yml
HostFileIO: foreach_dir_entry() in "./metamodule-plugins"
HostFileIO: foreach_dir_entry() in "."
HostFileIO: read ./EnOscPoly4.yml
HostFileIO: read ./Djembe4verb.yml
HostFileIO: read ./EnOsc_step_seq.yml
HostFileIO: read ./UntwistedBraid.yml
HostFileIO: read ./PlaygroundBefaco.yml
HostFileIO: read ./KarplusStereo.yml
HostFileIO: read ./SpringsintoCaves.yml
HostFileIO: read ./SlothDrone.yml
HostFileIO: read ./WanderVoices.yml
HostFileIO: read ./Braids-Quad.yml
HostFileIO: read ./DualEnvEnosc.yml
HostFileIO: read ./QuadDrum.yml
HostFileIO: read ./settings.yml
HostFileIO: read ./MIDI_Poly4.yml
HostFileIO: read ./Befaco4msPlayground.yml
HostFileIO: read ./EnOsc_8_step_seq.yml
HostFileIO: read ./Orcas_Heart_Octo_Djembe.yml
Raw image is 3567104, first byte is 2e
Starting LVGL
Trying to load settings.yml from NorFlash
HostFileIO: get file size settings.yml
HostFileIO: read settings.yml
Read patch id settings.yml 859 bytes
HostFileIO: foreach_dir_entry() in "metamodule-plugins"
HostFileIO: foreach_dir_entry() in "./"
HostFileIO: foreach_dir_entry() in "metamodule-plugins"
Error: filesystem error: in directory_iterator::directory_iterator(...): No such file or directory ["metamodule-plugins"]
HostFileIO: foreach_dir_entry() in "./"
UI: buffers have # frames: in: 512, out: 512
HostFileIO: foreach_dir_entry() in "metamodule-plugins"
HostFileIO: foreach_dir_entry() in "./"
HostFileIO: foreach_dir_entry() in "metamodule-plugins"
Error: filesystem error: in directory_iterator::directory_iterator(...): No such file or directory ["metamodule-plugins"]
HostFileIO: foreach_dir_entry() in "./"
Trying to load metamodule-plugins/SquinkyLabs.mmplugin from SD Card
HostFileIO: get file size metamodule-plugins/SquinkyLabs.mmplugin
HostFileIO: read metamodule-plugins/SquinkyLabs.mmplugin
Read patch id metamodule-plugins/SquinkyLabs.mmplugin 668672 bytes
Plugin was loaded and symbols resolved
Pretending plugin has same version as firmware
Plugin init_plugin was called

From the Squinky metamodule plugin build:

-- The CXX compiler identification is GNU 12.3.1
-- The ASM compiler identification is GNU
-- Found assembler: /Applications/ArmGNUToolchain/12.3.rel1/arm-none-eabi/bin/arm-none-eabi-gcc
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Applications/ArmGNUToolchain/12.3.rel1/arm-none-eabi/bin/arm-none-eabi-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/ArmGNUToolchain/12.3.rel1/arm-none-eabi/bin/arm-none-eabi-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.6s)
-- Generating done (0.1s)
-- Build files have been written to: /Users/ericgao/Documents/Projects/Squinky/build
[931/940] Building CXX object CMakeFiles/SquinkyLabs.dir/squinky/src/FiltModule.cpp.obj
In file included from /Users/ericgao/Documents/Projects/Squinky/squinky/src/FiltModule.cpp:12:
/Users/ericgao/Documents/Projects/Squinky/squinky/src/ctrl/PopupMenuParamWidget.h: In member function 'virtual void PopupMenuParamWidget::onAction(const rack::event::Action&)':
/Users/ericgao/Documents/Projects/Squinky/squinky/src/ctrl/PopupMenuParamWidget.h:195:27: warning: 'this' pointer is null [-Wnonnull]
  195 |             menu->addChild(new PopupMenuItem(i, this));
      |             ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/widget/OpaqueWidget.hpp:2,
                 from /Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/ui/TextField.hpp:4,
                 from /Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/app/LedDisplay.hpp:3,
                 from /Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/app/MidiDisplay.hpp:2,
                 from /Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/componentlibrary.hpp:3,
                 from /Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/rack.hpp:24,
                 from /Users/ericgao/Documents/Projects/Squinky/squinky/src/Squinky.hpp:2,
                 from /Users/ericgao/Documents/Projects/Squinky/squinky/src/FiltModule.cpp:3:
/Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/widget/Widget.hpp:127:14: note: in a call to non-static member function 'void rack::widget::Widget::addChild(rack::widget::Widget*)'
  127 |         void addChild(Widget *child);
      |              ^~~~~~~~
In member function 'virtual void PopupMenuParamWidget::onAction(const rack::event::Action&)',
    inlined from 'virtual void PopupMenuParamWidget::onButton(const rack::event::Button&)' at /Users/ericgao/Documents/Projects/Squinky/squinky/src/ctrl/PopupMenuParamWidget.h:145:17,
    inlined from 'virtual void PopupMenuParamWidget::onButton(const rack::event::Button&)' at /Users/ericgao/Documents/Projects/Squinky/squinky/src/ctrl/PopupMenuParamWidget.h:138:13:
/Users/ericgao/Documents/Projects/Squinky/squinky/src/ctrl/PopupMenuParamWidget.h:195:27: warning: 'this' pointer is null [-Wnonnull]
  195 |             menu->addChild(new PopupMenuItem(i, this));
      |             ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/widget/Widget.hpp: In member function 'virtual void PopupMenuParamWidget::onButton(const rack::event::Button&)':
/Users/ericgao/Documents/Projects/metamodule-plugin-sdk/metamodule-rack-interface/include/widget/Widget.hpp:127:14: note: in a call to non-static member function 'void rack::widget::Widget::addChild(rack::widget::Widget*)'
  127 |         void addChild(Widget *child);
      |              ^~~~~~~~
[935/940] Generating SquinkyLabs-debug.so
[936/940] Generating SquinkyLabs.so
copy from `SquinkyLabs-debug.so' [elf32-littlearm] to `/Users/ericgao/Documents/Projects/Squinky/build/SquinkyLabs.so' [elf32-littlearm]
   text    data     bss     dec     hex filename
 131954    6352     388  138694   21dc6 /Users/ericgao/Documents/Projects/Squinky/build/SquinkyLabs.so
[939/940] Running utility command for plugin
Checking if symbols in /Users/ericgao/Documents/Projects/Squinky/build/SquinkyLabs.so would be resolved
Symbol in plugin not found in api: _ZN9AudioMath30makeScalerWithBipolarAudioTrimEff
Symbol in plugin not found in api: _numLookupParams
Symbol in plugin not found in api: _ZN8SqHelper11COLOR_BLACKE
Symbol in plugin not found in api: _numBiquads
Symbol in plugin not found in api: _ZN11ObjectCacheIdE7getExp2Ev
Symbol in plugin not found in api: _ZN11ObjectCacheIdE8getTanh5Ev
Symbol in plugin not found in api: _ZN11ObjectCacheIfE13getAudioTaperEv
Symbol in plugin not found in api: _ZN11ObjectCacheIfE13get6PLPParamsEf
Symbol in plugin not found in api: _ZN14AsymWaveShaperC1Ev
Symbol in plugin not found in api: _ZN8SqHelper11COLOR_WHITEE
Creating plugin at /Users/ericgao/Documents/Projects/Squinky/metamodule-plugins/SquinkyLabs.mmplugin```

Good progress!

Yeah, that’s OK. I think the SurgeXT plugins should be done as native plugins, using the underlying libraries to generate MetaModule plugins – not by porting the VCV rack versions. The VCV rack versions make use of a button for each CV jack which changes the function of the knob above, and that’s going to be a confusing interface to use on the MetaModule.
I would suggest doing it like I did the Airwindows port, if you want to take a look at that in the metamodule-plugin-examples repo.

Right, you can’t load plugins into the simulator. The simulator “simulates” the GUI for loading plugins, but they aren’t actually loaded (the console output will tell you that when you load one). The reason is that the .mmplugin file is compiled for the MM’s processor, but your computer can’t run files compiled for another processor.

The way to run a plugin in the simulator is to use the ext_plugins.cmake file:

But… you will need to resolve those missing symbols first:

Each of these is a function or global variable that the plugin needs but wasn’t built when you built the plugin.
They probably are in a library or other source file that normally is built with SquinkyLabs.
Maybe there’s a source file or two that you forgot to include in the CMakeLists?
It’s easy to find out where these are: first you need to “demangle” the symbol names (I probably should have my script do the demangling for you). This is a site that makes it easy: https://demangler.com

Here’s what they are, demangled:

numLookupParams
SqHelper::COLOR_BLACK
numBiquads
ObjectCache<double>::getExp2()
ObjectCache<double>::getTanh5()
ObjectCache<float>::getAudioTaper()
ObjectCache<float>::get6PLPParams(float)
AsymWaveShaper::AsymWaveShaper()
SqHelper::COLOR_WHITE

Next step is to search for these in the source code. Obviously pay attention to namespaces and class scope, so search for getTanh5 not ObjectCache<double>::getTanh5(). Somewhere in the source code these are defined, and you need to add this file to the MM plugin cmake build. You’re looking for where the function bodies are, in a .cc or .cpp or .c file. Add those .cpp/cc/c file to the sources in the CMakeLists.txt and recompile.
numLookupParams and numBiquads might be global vars, not functions (I think that’s what the leading _ indicates ), so figure out where those are defined (not just declared with extern), and add that source file also.

Thank you, seriously learning a ton with this exercise. I’ve gotten yet again even farther - I’ve loaded it successfully in simulator and also have it showing up in the actual MetaModule - however, the MetaModule seems to be failing to load the faceplate whereas it loads fine in the simulator. Is there something that needs to be done slightly differently for the MetaModule assets folder?

And separately, I often see xmmintrin, emmintrin headers that don’t compile - I assume these are instructions that aren’t supported on the cortex, and I just replace the code with something more generic?

If you’re loading it as a built-in in the MM hardware then you need to do a firmware update with the new assets.uimg file. The fiwmare update files are generated when you build, so look in the build dir for one ending in -assets.zip, and unzip it and install it normally on your Metamodule.
But if you load it as a plugin then it should work without that.

Those are processor-specific intrinsics. Usually I see them surrounded by #ifdef to make sure they’re not called on an architecture that doesn’t support them. It’s really a bad idea to have code with bare intrinsics being called without checking first. But if you do run into that then you’ll need to get rid of it. Or better yet use a library such as simde (which is commonly used by lots of rack modules and is already available in the metamodule SDK). The simde library detects the architecture and uses the right intrinsics if available or otherwise uses basically a polyfill.

1 Like

I see, thanks. I’ll take a look at simde.

I believe I am just trying to load it as a plugin, i.e., taking the exported SquinkyLabs.mmplugin and putting it on the metamodule-plugins folder on the SD card, and loading it from the plugins menu. I unpacked it and can verify all the assets are there as well, so not sure if I am missing anything else, as it still complains that it cannot find the faceplate.

A link to my mmplugin in case: Dropbox

There’s a trailing space in the filename:
SquinkyLabs .mmplugin
should be:
SquinkyLabs.mmplugin

You might have that space in your cmakelists file, or maybe added it on accident manually?

Also, looking at the console logs, it’s failing to find this file:
squinkylabs-plug1/filter_panel.png

The problem is that the brand slug needs to match the mmplugin name. I’m not sure which is which, but it’s easiest to just use whatever VCV rack uses for the brand slug. (Notice slug is not always the same as name). So check what the brand slug for squinky is in the plugin.json and then use that in the cmake call to creaate_plugin.
Then also don’t manually change the name of the mmplugin, make sure you use the name that the script generates (you can add a version to it, like squinky-plug1-v2.0.0-dev-11.0.mmplugin)

Thanks, you’re a legend. I’ve gotten everything working end to end!

Unfortunately now the CPU spikes to 175% as soon as I plug in audio. This is described as a ladder filter module which seems pretty reasonably intensive for the Metamodule to run. Do you have a playbook or suggested literature on how to start profiling and figuring out how to reduce CPU usage?

Attaching my plugin below again. Dropbox

I don’t know of anything MM specific, but Squinky wrote lots of docs about working on VCV Rack plugins. One chapter is “Writing efficient plugins”. (As an aside, Squinky worked on the Voyetra-8 synth in the 80s, used by New Order amongst others)

Yeah… I actually decided to port this because I thought their plugins would be the most optimized. Very likely that I’ve just botched something on my end.

I just tried the plugin on hardware, the Stairway module loads great. With nothing patched, I get 10% load (which is really 2%, because in v2.0 there is a base load of 7-8%).
Then I get 28% (which would be 20%) with one channel patched, and 45% (37%) with two channels patched. No spikes, it’s a pretty steady load. This is at 48kHz, even down to 16 block size.

I didn’t try transferring a patch created in VCV. This was just creating a new patch on the MM and then adding the Stairway module and hitting Auto-map.

I haven’t looked at the code either… I would guess that a ladder filter could get down to a lot better efficiency (for example, see the Kocmoc LADR), but then again maybe it’s doing something complex that requires it. Or there could be some optimizations to be made (using 32-bit floats instead of 64-bit doubles, for example, or checking if it’s using any SIMD stuff that’s not supported on NEON, or making sure it’s not running polyphony since the MM doesn’t support that).

There are some ways to profile on hardware using a logic analyzer oscilloscope. Or, there’s the headless simulator project that lets you run a patch (on your computer) and reports back how long it took. The former is good for optimizing on the MM hardware only, while the latter is good for general over-all optimizations.

Wait, I’m very surprised - it completely dies on my Metamodule. I’m on 2.0-dev11 for what it’s worth. The Metamodule either outright crashes or it hits 175% CPU and makes a huge whining noise.

Strange. I installed a fresh v2.0-dev11 off the website. I only loaded the squinky plugin, no others. And, like I said, it’s a new patch with no other modules. Can you see if you do exactly all that if you get the same result?

Oh I found something. Patching into Resonance CV makes the cpu load spike. Must be some heavy calculation happening when it detects that jack as patched.

That could be it, but I’m also having trouble even patching in the left input jack to panel jack 1 without it suddenly spiking. I reflashed as well and installed the exact plugin in the above dropbox link

OK, I’m not sure what’s going on now – I’m getting the same behavior as you now, the cpu rides over 100% even if I patch one input jack. Which is strange – I feel like I’m crazy now, but I’m sure I was really playing with it for a while, at 48kHz, making the filter resonate and playing samples through it to hear its characteristics.

I’m just shooting in the dark here, but in the past when there’s issues that appear sometimes and not other times, it’s sometimes due to an initialized variable. Basically, the variable has a random value when it boots up. Maybe? I’m going to go through the code of the original module to see if I spot anything.
Did you make any modifications to the filter code that I should look out for?
Also if you can share the project in whatever state it’s in, that could be helpful to debug this.

diff --git a/dsp/third-party/src/SqMath.h b/dsp/third-party/src/SqMath.h
index c2904c7..b13ae6f 100644
--- a/dsp/third-party/src/SqMath.h
+++ b/dsp/third-party/src/SqMath.h
@@ -4,7 +4,6 @@
 #if ARCH_ARM64
 #include "arm_intrinsics_sub.h"
 #else
-#include <simde/x86/sse2.h>
 #endif
 #include <random>
 #if !defined(M_PI)
diff --git a/dsp/utils/LookupTable.h b/dsp/utils/LookupTable.h
index 71433b0..f82e582 100644
--- a/dsp/utils/LookupTable.h
+++ b/dsp/utils/LookupTable.h
@@ -187,15 +187,13 @@ inline void LookupTable<T>::initDiscrete(LookupTableParams<T>& params, int numEn
 template<>
 inline int LookupTable<double>::cvtt(double* input)
 {
-    auto x = _mm_load_sd(input);
-    return _mm_cvttsd_si32(x);
+    return static_cast<int>(*input);
 }
 
 template<>
 inline int LookupTable<float>::cvtt(float* input)
 {
-    auto x = _mm_load_ss(input);
-    return _mm_cvttss_si32(x);
+    return static_cast<int>(*input);
 }
 
 /***************************************************************************/
diff --git a/src/ColoredNoiseModule.cpp b/src/ColoredNoiseModule.cpp
index d7af452..21526df 100644
--- a/src/ColoredNoiseModule.cpp
+++ b/src/ColoredNoiseModule.cpp
@@ -159,7 +159,7 @@ struct ColorDisplay : TransparentWidget
     Label* _slopeLabel;
     Label* _signLabel;
    
-    void draw(NVGcontext *vg) override
+    void draw(NVGcontext *vg)
     {
         nvgGlobalTint(vg, color::WHITE);
         // First draw the solid fill

I did a few things which I’m not sure were correct when trying to use simde. In addition I commented out some throws since the docs said you couldn’t do exception handling yet.

https://www.dropbox.com/scl/fi/3xxbov77xzf7k2hub2b86/Squinky.zip?rlkey=8jnm3b2oaddhl40ci40srraa4&st=5w3jv09b&dl=0

This is my working directory atm