Generating a plugin as a static library

I’m working on my Graduation Work thesis for uni, which compares Unity and Unreal Engines native audio plugin functionality.

To do this, I’m trying to compile one of my JUCE plugins into a static library so I can use the PluginProcessor functionality in a JUCE-free wrapper that I can use for both game engines.

And yes I know that JUCE can generate Unity plugins, but that defeats part of the research so it’s sadly not an option.

The issue I’m running into (apart from my lack of knowledge on libraries in general) is that I cannot find a way to obscure the wrapper to not use any JUCE code at all.

Since I’m linking with the .lib of the plugin, I need to use the PluginProcessor.h header, which includes the JuceHeader and everything else I wrote of course. So I thought that I’ll just make another header for the wrapper that doesn’t contain the plugin (and therefore no JUCE code) which I’d use for the unity project for example, and then another header which does contain the plugin, and is used by the wrapper.cpp file.

This approach creates a wrapper.lib file without any issues, but when I link it with the unity plugin (using the header that has no JUCE code), the linker complains about undefined symbols which all have to do with JUCE code.


I know the structure of the project is quite unclear so let me try to clarify:

I have 3 separate projects: juce-plugin - wrapper - unity-plugin

juce-plugin

juce-plugin is the actual JUCE plugin that uses CMake and generates a VST3 file, and along with it (by default) a static library.

wrapper

wrapper is my custom wrapper which only needs the PluginProcessor functionality (mainly prepareToPlay and processBlock for example). Wrapper links with the static library generated by JUCE and includes all the headers from juce-plugin and native juce modules.

wrapper is currently split in 3 files, wrapper.cpp - wrapper.h and public-wrapper.h
wrapper.cpp has all the functionality of wrapper.h, which includes the plugin’s header file.
public-wrapper.h is exactly the same as wrapper.h but doesn’t include the plugin processor header, and also removed the processor class from the private field of the wrapper class. So I’m basically just not showing the plugin to whatever links with the wrapper.

So wrapper.cpp and wrapper.h are used to create wrapper.lib which generates fine.

unity-plugin / unreal-plugin

unity-plugin links with wrapper.lib (with a bunch of undefined symbol errors), but uses public-wrapper.h so in theory it doesn’t know about any JUCE code.

These are the errors:

simpleplugin.lib(plugin.obj) : error LNK2019: unresolved external symbol "public: __cdecl juce::this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode::this_will_fail_to_link_if_some_of_your_compile_un
its_are_built_in_release_mode(void)" (??0this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode@juce@@QEAA@XZ) referenced in function "void __cdecl juce::`dynamic initializer for 'compileUnitMismatchSe 
ntinel''(void)" (??__EcompileUnitMismatchSentinel@juce@@YAXXZ) [C:\Dev\C++\engineaudioplugin-simple\bin\unity\audioplugin-simpleplugin.vcxproj]
simpleplugin.lib(plugin.obj) : error LNK2019: unresolved external symbol "public: __cdecl juce::Colour::Colour(unsigned int)" (??0Colour@juce@@QEAA@I@Z) referenced in function "void __cdecl juce::Colours::`dynamic initializer 
 for 'aliceblue''(void)" (??__Ealiceblue@Colours@juce@@YAXXZ) [C:\Dev\C++\engineaudioplugin-simple\bin\unity\audioplugin-simpleplugin.vcxproj]
simpleplugin.lib(plugin.obj) : error LNK2019: unresolved external symbol "public: __cdecl SpectralResonatorAudioProcessor::SpectralResonatorAudioProcessor(void)" (??0SpectralResonatorAudioProcessor@@QEAA@XZ) referenced in fun 
ction "public: __cdecl source::Plugin::Plugin(void)" (??0Plugin@source@@QEAA@XZ) [C:\Dev\C++\engineaudioplugin-simple\bin\unity\audioplugin-simpleplugin.vcxproj]
simpleplugin.lib(plugin.obj) : error LNK2019: unresolved external symbol "public: virtual __cdecl SpectralResonatorAudioProcessor::~SpectralResonatorAudioProcessor(void)" (??1SpectralResonatorAudioProcessor@@UEAA@XZ) referenc 
ed in function "public: __cdecl source::Plugin::~Plugin(void)" (??1Plugin@source@@QEAA@XZ) [C:\Dev\C++\engineaudioplugin-simple\bin\unity\audioplugin-simpleplugin.vcxproj]
C:\Dev\C++\engineaudioplugin-simple\bin\unity\Release\audioplugin-simpleplugin.dll : fatal error LNK1120: 4 unresolved externals [C:\Dev\C++\engineaudioplugin-simple\bin\unity\audioplugin-simpleplugin.vcxproj]

I saw the first one and tried rebuilding everything in debug mode but it has the same result, only for debug instead.


I’m honestly at a loss at the moment and can’t really wrap my head around how everything works.

I think that maybe the plugin’s generated .lib file doesn’t contain any JUCE related functionality but that wouldn’t make sense to me, since a VST3 should also contain all JUCE functionality.
Or maybe the header without JUCE code is not compatible with the lib that does contain it?
Or is my approach the problem? I might need a dll instead which contains everything and then link to that with only the plugin’s processor code exposed?

So to summarize the question: how can I use my wrapper while only exposing the necessary functions for Unity and Unreal Engine to use?

Linking is often defered until a binary is created, in this case the binary is the plugin itself. This is why you’re seeing the linking errors at the end stage.

Your wrapper.lib will contain references to JUCE code because it interfaces with the AudioProcessor class.

You could create a DLL with a few exported methods:

bool initPlugin (double sampleRate, int blockSize);
void shutdown();
void getNextAudioBlock (const float** inputs, float** outputs, int numChannels, int numSamples);

These internally would create an instance of your ‘AudioProcessor’ and call ‘prepareToPlay’, ‘process’ etc. You wouldn’t need any JUCE headers in your engine facing code as we dont reference any JUCE types here.

This would allow you to keep the JUCE code contained in the DLL and out of the game-engine facing interface.

Passing parameters between your plugin and the engine gets more complicated but this should at least allow you to get audio in/out of the plugin.

Have fun :slight_smile:

1 Like

It does seem that creating a DLL was the way to go. This works and links fine (only with Clang which is a bit weird), but all DSP process methods crash the program :sweat:
I even tried with the chorus DSP module from JUCE but it still crashes.

But I guess that’s another problem I’ll have to debug somehow…

Thanks for the help!

How should I be creating the library?

Right now I’m just using the auto generated lib for VST3, and also the SharedCode lib.

Can I somehow create the plugin with add_library() in CMake?

You shouldn’t need a plugin format at all.

You could do something like this:
dllexample.zip (2.6 KB)

1 Like

Thank you so much for taking the time to make that example. It’s fully working now (apart from some hiccups in my plugin haha).

Now I can continue writing paper! Can I add your user name, or better yet, legal name in my paper as special thanks? I was stuck on this for a while so you really helped a lot.

1 Like