VST3 loading, but only for a second

I am new to JUCE, but I love it so far.

I have a VST3 loading button, and it loads the VST, but only for a moment, then it disappears and the UI freaks out. Admittedly, I am still learning about C++, but any help appreciated with this.

I’ve tracked down the issue to “instance->createEditorIfNeeded()”, but not sure why it only is showing for a moment.

//==============================================================================
void NewProjectAudioProcessorEditor::paint (juce::Graphics& g)
{
    // (Our component is opaque, so we must completely fill the background with a solid colour)
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

    // make button to load VST3
    auto newButton = new juce::TextButton{ "Load VST3" };
    newButton->setBounds(getWidth() / 2 - 50, getHeight() / 2 - 25, 100, 50);
    addAndMakeVisible(newButton);

    // button on click
    newButton->onClick = [this] {

        // path to plugin
        juce::File pluginFile(PluginPath);
        pluginFormatManager.addDefaultFormats();

        // add plugin to array
        for (int i = 0; i < pluginFormatManager.getNumFormats(); ++i) {
            plist.scanAndAddFile(juce::String(pluginFile.getFullPathName()), true, pluginDescriptions,
                *pluginFormatManager.getFormat(i));
        }

        if (auto instance = pluginFormatManager.createPluginInstance(*pluginDescriptions[0], 44100, 1764, ErrorMessage)) {

            // create window
            auto basicWindow = new BasicWindow("Information", juce::Colours::grey, juce::DocumentWindow::allButtons);
            basicWindow->setUsingNativeTitleBar(true);
            basicWindow->centreWithSize(basicWindow->getWidth(), basicWindow->getHeight());

            // add editor to window
            AudioProcessorEditor* instanceEditor = instance->createEditorIfNeeded();
            basicWindow->setContentOwned(instanceEditor, true);

            // make window visible
            basicWindow->setVisible(true);
        }
        else {

            // show error
            auto newButtonError = new juce::TextButton{ ErrorMessage };
            addAndMakeVisible(newButtonError);
            newButtonError->setBounds(10, 10, 333, 333);
        }

    };


}

Every frame you are making a new button. Depending on what auto type is (I’m guessing it would be a raw pointer), you might be causing massive memory leaks here.

Move the creation of the button and associated logic out of the paint routine. Make it a member of the class that has that paint function.

2 Likes

In addition to Fanduss explanations, don’t use createEditorIfNeeded(). In a well designed project, this editor is already owned somewhere else.
Instead use createEditor() and make sure to move that pointer to a proper owned place, like you did with the subsequent setContentOwned()

1 Like

Another thing to mention is using new in modern C++ is almost never needed. It’s almost always better to be using std::unique_ptr (or in some cases std::shared_ptr, but rarely) so that you don’t need to be worrying about matching up a delete call for every new object you create.

As mentioned, juce::TextButton loadButton { "Load VST3" }; should be a member of NewProjectAudioProcessorEditor and the addAndMakeVisible and onClick setup should be in its constructor.

1 Like

I made it a void in the NewProjectAudioProcessorEditor class, and I am calling it from what is pasted first below. should I maybe return the button object? It’s still encountering the same error. Is it possible this has something to do with an audio stream not being initialized?

//==============================================================================
NewProjectAudioProcessorEditor::NewProjectAudioProcessorEditor (NewProjectAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    // editor's size to whatever you need it to be.
    setSize (400, 300);
    NewProjectAudioProcessorEditor::buttonMake();

}

Here’s the class member.

// class member for the button void
void NewProjectAudioProcessorEditor::buttonMake() {

    //using AudioGraphIOProcessor = juce::AudioProcessorGraph::AudioGraphIOProcessor;
    //using Node = juce::AudioProcessorGraph::Node;

    // make button to load VST3
    auto newButton = new juce::TextButton{ "Load VST3" };
    newButton->setBounds(getWidth() / 2 - 50, getHeight() / 2 - 25, 100, 50);
    addAndMakeVisible(newButton);

    // button on click
    newButton->onClick = [this] {

        // path to plugin
        juce::File pluginFile(PluginPath);
        pluginFormatManager.addDefaultFormats();

        // add plugin to array
        for (int i = 0; i < pluginFormatManager.getNumFormats(); ++i) {
            plist.scanAndAddFile(juce::String(pluginFile.getFullPathName()), true, pluginDescriptions,
                *pluginFormatManager.getFormat(i));
        }

        if (auto instance = pluginFormatManager.createPluginInstance(*pluginDescriptions[0], 44100, 1764, ErrorMessage)) {

            // create window
            auto basicWindow = new BasicWindow("Information", juce::Colours::grey, juce::DocumentWindow::allButtons);
            basicWindow->setUsingNativeTitleBar(true);
            basicWindow->centreWithSize(basicWindow->getWidth(), basicWindow->getHeight());

            // add editor to window
            AudioProcessorEditor* instanceEditor = instance->createEditor();
            basicWindow->setContentOwned(instanceEditor, true);

            // make window visible
            basicWindow->setVisible(true);
        }
        else {

            // show error
            auto newButtonError = new juce::TextButton{ ErrorMessage };
            addAndMakeVisible(newButtonError);
            newButtonError->setBounds(10, 10, 333, 333);
        }

    };


};

I’ll work towards this, thanks

You have some pretty significant gaps in your c++ basics here, that’s the first thing you should put your time into.

By the end of this function call, you have no way to access your button, there’s also no clean up here, so every time you create an editor, you are creating a button on the heap and leaving it there. You probably don’t need to use ‘new’ at all for the button, just make it a member of NewProjectAudioProcessorEditor directly. Then assign the instructions in the constructor where setSize currently is.

All this auto x = new thing(); In functions is bad practice. You are allocating memory and then throwing away the location, leaving it in memory. Break that habit now and save yourself many hours/weeks later.

2 Likes

My terrible noob C++ programming skills aside, the issue has to do with the audio. I created a VST without audio connections, and the crash doesn’t happen.

Thanks to everyone for the great tips on programming. I am going to study and try to apply what was mentioned!

I’ve been reading up on “new”, versus alternatives, are you a fan of smart pointers?

Hi Digivibe,
for UI components, it’s usually a good idea to have them as std::unique_ptr members in your component class. I’d recommend studying the JUCE examples (and the JUCE code in general) to adopt the concepts and paradigms in their usage of C++.
When it comes to learning C++ in general, I’d recommend focusing on all things related to memory. It’s very important to have a solid understanding of stack and heap, and the differences between values, pointers, and references. The RAII technique for managing object lifecycle is something you should know. C++ also has a complex type system that you need to learn in order to decrypt all kinds of compiler errors you’ll encounter at some point.
Specific to audio programming, the vast topic of concurrency, multithreading and thread synchronisation is pretty important as well. Not so C++ specific, but general programming know-how are concepts related to object oriented programming and algorithmic complexity.
There are many good courses on youtube.
Have fun on the journey!

1 Like

Smart pointers are basically the default way to do things today, using raw pointers isn’t very common anymore and tends to only happen in specific circumstances. The nature of a raw pointer is that its lifetime is kinda vague, and ownership of the underlying data is also debatable. Using raw pointers is one of the easiest ways to shoot yourself in the foot.

The comment above is full of great advice. I highly recommend the Cherno on YouTube, and matkat music has a great fundamentals course (paid) if you want to speed run yourself to competency (that’s what I did in 2019/2020).

1 Like

I also have the same issue, In my case the plugin Interface does not show at all, just a dark grey box corresponding to the bounds of the vst3 plugin loaded inside.
VST3 HOSTING METHOD:

void MainComponent::hostVST3(juce::File &file)
{
    formatManager.addDefaultFormats();

    OwnedArray<PluginDescription> typesFound;

    AudioPluginFormat * format = formatManager.getFormat(0);

    KnownPluginList pluginList;
    pluginList.scanAndAddFile(vst3Description.fileOrIdentifier, true, typesFound, *format);

    infoLabel.setText(typesFound[0]->name, juce::dontSendNotification);

    juce::String errorMessage;

    std::unique_ptr<AudioPluginInstance> vst3Instance = formatManager.createPluginInstance(*typesFound[0], 44100.0, 512, errorMessage);

    if(vst3Instance != nullptr)
    {
        createEditor(*vst3Instance);
    }
    else
    {
        infoLabel.setText("VST3 NOT LOADED", juce::dontSendNotification);
    }
}

SHOWING PLUGIN GUI METHOD

void MainComponent::createEditor(AudioPluginInstance& pluginInstance)
{
    juce::AudioProcessorEditor * vstEditor = pluginInstance.createEditor();

    if (vstEditor != nullptr)
    {

        addAndMakeVisible(vstEditor);
        vstEditor->setBounds(70, 20, 300, 200);
     }
    else
    {
        infoLabel.setText("Failed to obtain AudioProcessor", juce::dontSendNotification);
    }
}

Wondering If I am making any mistake here.

Let me give more context:
The Button Callback

void MainComponent::buttonClicked(juce::Button *button){
    if (button == &loadButton)
    {
        loadButton.setButtonText("LOADING...");
        loadFile();
        loadButton.setButtonText("LOAD VST3 PLUGIN");
    }
}

VST3 File LOADING Method:

void MainComponent::loadFile()
{
    fileChooser = std::make_unique<FileChooser>("Select a VST3 Plugin", File::getSpecialLocation(File::userDesktopDirectory), "*.vst3");

    auto folderChooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles;

    fileChooser->launchAsync(folderChooserFlags, [this](const FileChooser& chooser) mutable
    {
        auto result = chooser.getResult();
        if (result.exists())
        {
            retrieveVST3data(result);
            hostVST3(result);

        }
    });
}

Maybe now it’s better. so after loading the file successfully, then the HostVst3 method and the createEditor method are triggered.

The plugin instance will only live while a unique_ptr owning it remains in scope, but it looks like vst3Instance is dropped at the end of the function. You could try making the vst3Instance a data member of your component, and assigning to that data member, so that the plugin remains alive after hostVST3 returns.

Will try now and get back to you. Thanks for replying

@reuk It finally worked. Thank you so much!