GUI-less plugin


I wonder if anyone has ever successfully created an Audio Plugin (VST for instance) with no GUI (I mean, no editor window)?

Is it enough to return 0 in the createEditor() method? Or should there be anything more done?


Ok I was able to do it successfully using the Demo Plugin and returning 0 in the createEditor() method.
Then, when opening the plugin in the Plugin Host demo, it appears to be correct and replaces the fancy GUI by a default window with a list of parameter.

My problem is that when I try to open the very same test plugin in my application I hit an assertion in

#1 0x1ab6292f in juce::DeletedAtShutdown::deleteAll () at juce_DeletedAtShutdown.cpp:80 80 jassert (getObjects().size() == 0);
Does it mean that the destructor of one of the objects registered to be deleted at shutdown has created another object to be deleted at shutdown?
Actually I don’t quite understand why shutdownJuce_GUI gets called in JuceVSTWrapper::~JuceVSTWrapper, wouldn’t it cause problems to cleanup juce’s statics if the host application also needs them? Apparently it’s not, because it works in Plugin Host demo but I would like to know why.

Has anyone ever reached the same problem?


The host doesn’t share any statics with the plugin, so they effectively have their own juce system running, which gets shut down separately.

This is a real weird problem because actually, after watching in the debugger all the methods of DeletedAtShutdown class, it seems it’s not that one destructor creates another DeletedAtShutdown object, but rather one of the 2 objects in the list to be deleted that fails to be removed from the Array<DeletedAtShutdown*>…

The 2 objects are a Desktop and an InternalTimerThread, and it’s when destructing the InternalTimerThread that Array::removeValue() fails (the Desktop is successfully deleted):

[code]void removeValue (ParameterType valueToRemove)
const ScopedLockType lock (getLock());
ElementType* e = data.elements;

    for (int i = numUsed; --i >= 0;)
        if (valueToRemove == *e)
            remove (static_cast <int> (e - data.elements.getData()));


valueToRemove never matches *e … :frowning:

What I do is quite simple, it’s just the following in a Google Test unit-test:

TEST_F(MyTestCase, ScanPlugins) { juce::AudioPluginFormatManager::getInstance()->addDefaultFormats(); int aTotalFormats = juce::AudioPluginFormatManager::getInstance()->getNumFormats(); juce::KnownPluginList aPluginList; juce::FileSearchPath aSearchPath; juce::File aFile("~/testpath"); aSearchPath.add(aFile); for (int i = 0; i < aTotalFormats; ++i) { juce::AudioPluginFormat* paCurrentFormat = juce::AudioPluginFormatManager::getInstance()->getFormat(i); ASSERT_TRUE(paCurrentFormat != 0); juce::PluginDirectoryScanner aScanner(aPluginList, *paCurrentFormat, aSearchPath, true, juce::File::nonexistent); for (;;) { DBG( "Testing:" + aScanner.getNextPluginFileThatWillBeScanned() ); if (! aScanner.scanNextFile(true)) break; } } }
then the main.cpp is just:

[code]int main(int argc, char **argv) {

::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();

return ret;
Any idea of what could be wrong?

If some of your plugins are juce plugins done with debug builds and the host is a debug version, then these modules will all have the same symbols exposed, and they could get their statics cross-linked. If you strip the symbols in the host it might help.

I was comparing build options of the Plugin Host program and my test program one by one when your answer confirmed my suspicions…
Building my dummy plugin in release mode solved the problem for good at least.
Why do problems that seem to come from outer space always come from the linker in the end? :roll:

Thanks Jules. At least this problem made me read the code of your Array class, and I think it’s the first time I see a placement new operator and explicit calls to destructors for real! :slight_smile:

I think that class is the only place I’ve ever used those techniques! It’d be nice if it wasn’t necessary, but is a really efficient way to do it.