[FR] VST3PluginFormat::loadFromVstPresetFile()

VSTPluginFormat has some static methods to save and recall FXB and FXP files… I see in JuceVST3Component there are methods to read VST3 .vstpreset files but there aren’t any static methods where we can pass an AudioPluginInstance pointer to set or recall a VST3’s state… unless I’m missing them…

(I’d also be looking for a similar static method to load an AU aupreset file).

Cheers,

Rail

1 Like

if you fancy writing it yourself I have some code I did for an unreleased “IPlug2” that I wrote for VirtualCZ which might help you implement this - allows you to write .vstpreset/aupreset/tfx - although you’ll need to rewrite it for JUCE

I’m not exactly sure what you mean. JUCE will automatically read the .vstpreset file provided by the VST3 host’s setState method, decode it for you and pass the contents to your plug-in’s AudioProcessor::setStateInformation.

What about from the hosting side? Can we just supply the binary data to setStateInformation in the same way that we can with aupreset files?

For example, I have

bool loadPresetFromFile (const File& file)
{
    MemoryBlock content;
    file.loadFileAsData (content);
    ap->setStateInformation (content.getData(), (int) content.getSize());
}

and I’d like to extend this to vstpreset files.

This is for hosting plug-ins… so I get an AudioProcessor pointer and I can access certain of the plug-in’s methods… setState() isn’t one of them

bool ExtEffectToolbarComponent::loadPreset (File& fPath)
{
    if (! fPath.existsAsFile())
        return false;

    MemoryBlock mb;

    if (fPath.getFileExtension().equalsIgnoreCase (".FXPST"))   // ACCENT PRESET
        {
        // Open Accent custom preset here

        :
    
        m_pPluginProcessor->setStateInformation (mb.getData(), (int) mb.getSize());
    
        return true;
        }
    else if (fPath.getFileExtension().equalsIgnoreCase (".FXP") && m_szPluginFormatName == "VST")    // VST2
        {
        fPath.loadFileAsData (mb);
    
        if (AudioPluginInstance* const plugin = dynamic_cast<AudioPluginInstance*> (m_pPluginProcessor))
            {
            VSTPluginFormat::loadFromFXBFile (plugin, mb.getData(), mb.getSize());
        
            return true;
            }
        }
    else if (fPath.getFileExtension().equalsIgnoreCase (".VSTPRESET") && m_szPluginFormatName == "VST3")  // VST3
       {
        fPath.loadFileAsData (mb);
    
        m_pPluginProcessor->setStateInformation (mb.getData(), (int) mb.getSize()); // This needs to call JuceVST3Component::setState()
        }
    else if (fPath.getFileExtension().equalsIgnoreCase (".AUPRESET") && m_szPluginFormatName == "AudioUnit")  // Audio Unit
        {
        fPath.loadFileAsData (mb);
    
        m_pPluginProcessor->setStateInformation (mb.getData(), (int) mb.getSize()); // TODO: Need to test
        }

    return false;
}

So as you can see from the code segment above I need to either dynamic_cast the AudioProcessor* to a VST3 or AudioUnit plug-in object or I need a static method like VSTPluginFormat::loadFromFXBFile to open the .vstplugin or .aupreset file… and I can’t see a way to dynamic_cast… so I need the latter.

It looks like the VST3PluginInstance struct would need to be moved into a header file so I can include it in my class so I can use:

else if (fPath.getFileExtension().equalsIgnoreCase (".VSTPRESET") && m_szPluginFormatName == "VST3")  // VST3
    {
    fPath.loadFileAsData (mb);
    
    if (VST3PluginInstance* const plugin = dynamic_cast<VST3PluginInstance*> (m_pPluginProcessor))
        plugin->setStateInformation (mb.getData(), (int) mb.getSize());
    }

Cheers,

Rail

Hi Oli,

aupreset and vstpreset is already in the JUCE code… it’s just a means to access it from an AudioProcessor* I’m looking for.

Thanks!

Rail

JUCE’s AudioProcessor interface just doesn’t really fit very well to the underlying state management of VST3. However, by using JUCE’s AudioProcessor::getPlatformSpecificData you can invoke the VST3’s setState method directly:

#include <pluginterfaces/vst/ivstcomponent.h>
#include <public.sdk/source/common/memorystream.h>

void setVST3PluginStateDirect (AudioPluginInstance* instance, const MemoryBlock& rawData)
{
    auto funknown = static_cast<Steinberg::FUnknown*> (instance->getPlatformSpecificData());
    Steinberg::Vst::IComponent* vstcomponent = nullptr;
    
    if (funknown->queryInterface (Steinberg::Vst::IComponent_iid, (void**) &vstcomponent) == 0
        && vstcomponent != nullptr)
    {
        
        auto* memoryStream = new Steinberg::MemoryStream (rawData.getData(), (int) rawData.getSize());
        vstcomponent->setState (memoryStream);
        memoryStream->release();
        vstcomponent->release();
    }
}

Would that work for you?

1 Like

That works great!! Off to see if I need a similar function for AUPRESET

(EDIT)… And aupresets work fine!

Thanks,

Rail

Hi Rail, I’m trying to add this to Waveform but the call to setVST3PluginStateDirect doesn’t seem to have any effect. I’m testing this with Steinberg’s Padshop at the moment. Can I ask what plugins you’ve verified the above method works with?

It seems quite difficult to find plugins with a free trial that don’t require an eLicenser and fully support vstpreset files…

I tested it locally with FabFilter, McDSP and Waves and a bunch of others… (no Synths though).

The only qualm about this fix is there are some warnings you have to live with from inside the VST3 SDK.

/Users/username/SDKs/VST3 SDK/pluginterfaces/base/funknown.h:342:7: ‘Steinberg::FUnknown’ has virtual functions but non-virtual destructor

McDSP ships with vstpresets and aupresets

Rail

Yeah, I’ve got this lovely list of ignores around the includes which I copied from the JUCE VST3 files:

    #if _MSC_VER
     #pragma warning (disable: 4505)
     #pragma warning (push, 0)
     #pragma warning (disable: 4702)
    #elif __clang__
     #pragma clang diagnostic push
     #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
     #pragma clang diagnostic ignored "-Wreorder"
     #pragma clang diagnostic ignored "-Wunsequenced"
     #pragma clang diagnostic ignored "-Wint-to-pointer-cast"
     #pragma clang diagnostic ignored "-Wunused-parameter"
     #pragma clang diagnostic ignored "-Wconversion"
     #pragma clang diagnostic ignored "-Woverloaded-virtual"
     #pragma clang diagnostic ignored "-Wshadow"
     #pragma clang diagnostic ignored "-Wdeprecated-register"
     #pragma clang diagnostic ignored "-Wunused-function"
     #pragma clang diagnostic ignored "-Wsign-conversion"
     #pragma clang diagnostic ignored "-Wsign-compare"
     #pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor"
     #pragma clang diagnostic ignored "-Wdeprecated-declarations"
     #pragma clang diagnostic ignored "-Wextra-semi"
     #if __has_warning("-Wcomma")
      #pragma clang diagnostic ignored "-Wcomma"
     #endif
    #endif

    #include <pluginterfaces/vst/ivstcomponent.h>
    #include <public.sdk/source/common/memorystream.h>

    #if _MSC_VER
     #pragma warning (pop)
    #elif __clang__
     #pragma clang diagnostic pop
    #endif

I’ve now go this working with McDSP plugins but can’t seem to load Padshop presets for some reason. I’ve dug around a bit and it looks like the FUID is wrong so I’ve no idea what the reason for that is.

I think what would be nice is for JUCE to have a wrapper around the Steinberg::Vst::PresetFile class as that’s got some static methods for reading and writing presets.

Thanks for the pointers!

Hi, I’m using this setVST3PluginStateDirect with Halion 6.vst3. When I use Halion as a standalone executable I’m able to save .vstpreset files. The menu button in HALion is “Export program as VST3 Preset…”

Now I’m hosting HALion in JUCE. After I do setVST3PluginStateDirect(), none of the parameters appear to have changed according to myPlugin.get()->getParameter(i). Also, in my code that would normally generate sound with some other VST, I get silence. What might be happening? Is there a more formal check on the content of a VST3 file? I can pass a path to some junk txt file, but it doesn’t raise a warning, which is problematic. I think a quirk about HALion is that if you haven’t loaded a preset, you’ll just get silence, whereas most synths have a default state that can make noise.