VST3 shell (Waves) plug-ins instantiation very slow

#1

Hi
Anyone using Waves plug-ins (let’s say the mercury bundle featuring more than 400 plug-ins) can easily notice that the instantiation of one of these vst3 plug-in can be very slow. Actually it can take more than one minute, for some of them (easy to reproduce with the AudioPluginHost sample Application). When comparing the duration of the same plug-in insertion on Reaper you quickly come to the conclusion that something can be enhanced on the Juce vst3 hosting side.

More precisely, the instantiation of the first enumerated plug-in (e.g. Waves API-2500) is almost instant, while the instantiation of bottom listed plug-ins (e.g. Waves Q10) can take more than a minute.
What happens is that during each first plug-in instance creation, every single vst3 component of the vst3 module is created and destroyed, only to check if the name of the AudioProcessor description can be found. This is clearly overkill, because this potentially causes load and unload of other DLLs and license verification for most plug-ins.

So I submitted a code update for juce_VST3PluginFormat.cpp on the Juce Git Master here:
https://github.com/WeAreROLI/JUCE/pull/530

Can you please consider this pull request? This makes a huge difference for Waves users

Cheers

4 Likes
#2

Thanks, we’d had reports of this… but you beat us to the solution!

Also Waves related:

We modified our local copy to fix that.

Cheers,

Rail

1 Like
#3

Hi Rail
Glad it helps someone else…

Up for Juce admins!

Fred

1 Like
#4

At the moment we’re checking more than just PluginDescription::name. We also check fileOrIdentifier and uid, so the patch is not quite equivalent.

#5

That bit of code is also over 5 years old, and hasn’t been touched since it was introduced. What other plug-ins have you tested with?

#6

If this doesn’t work we need to figure out a cache approach to ID Waves shell plugins for loading… We’ve had a few complaints about this issue by Waves bundle owners.

I have this “fix” out for testing to see how it works in the field.

Alternatives is while scanning add a boolean value to the settings if it’s a “WaveShell” plugin… and when loading use that knowledge to use this method vs. the old MatchingDescriptionFinder::findDescriptionsAndPerform() method. That way if two manufacturers use the same plugin name it’ll work… and we rely on the fact that Waves hopefully won’t have duplicate names in the WaveShell. (Currently isShell is set to zero for VST3 for WaveShell plugins).

As part of my local JUCE copy in juce_VST3PluginFormat.cpp I check if it’s a Waves shell with:

 bool bIsWavesShell = (companyName == "Waves");

for my dontRescan code for WaveShell plugins.

That could be used later:

    if (component->initialize (vst3HostContext->getFUnknown()) == kResultOk)
    {
        auto numInputs  = getNumSingleDirectionChannelsFor (component, true, true);
        auto numOutputs = getNumSingleDirectionChannelsFor (component, false, true);

        createPluginDescription (desc, file, companyName, name,
                                 info, info2, infoW, numInputs, numOutputs);
    
        desc.hasSharedContainer = bIsWavesShell;

        component->terminate();
    }

If you get the flag set correctly in the scanner… then in the open() code you can use:

    bool open (const File& f, const PluginDescription& description)
    {
        dllHandle.reset (new DLLHandle (f.getFullPathName()));

        ComSmartPtr<IPluginFactory> pluginFactory (dllHandle->getPluginFactory());

        if (pluginFactory != nullptr)
        {
            ComSmartPtr<VST3HostContext> host (new VST3HostContext());
        
            if (description.hasSharedContainer)
            {
                auto result = findName (pluginFactory, description);    // Use suggested method searching by name only
                
                if (result.wasOk())
                {
                    name = description.name;
                    return true;
                }
            }
            else
            {
                MatchingDescriptionFinder finder (host, pluginFactory, description);
        
                auto result = finder.findDescriptionsAndPerform (f);    // Use original search method
        
                if (result.getErrorMessage() == MatchingDescriptionFinder::getSuccessString())
                {
                    name = description.name;
                    return true;
                }
            }
        }

        return false;
    }

Rail

#7

I’ve also had many complaints from customers that Waves VST3 plugins take a long time to scan. I’ve seen it disappear into that method for up to 30 seconds at a time in testing.

#8

I think my change above should satisfy ROLI and give us a fix. Users will have to rescan their plugins to get the benefit (their settings flag updated).

I just rebuilt my scanner and plugin and this tests fine.

Rail

#9

Hi Tom

At the moment we’re checking more than just PluginDescription::name . We also check fileOrIdentifier and uid , so the patch is not quite equivalent.

Well, true, the uid is not checked. what about replacing the findName(IPluginFactory* factory, const PluginDescription& description) by:

    Result findMatch(IPluginFactory* factory, const PluginDescription& description)
    {
        if (factory == nullptr)
            return Result::fail(L"Invalid Parameter");

        Result result(Result::fail(L"not found"));
        auto numClasses = factory->countClasses();

        for (Steinberg::int32 i = 0; i < numClasses; ++i)
        {
            PClassInfo info;
            factory->getClassInfo(i, &info);

            if (std::strcmp(info.category, kVstAudioEffectClass) != 0)
                continue;

            const String infoName(toString(info.name).trim());

            if (infoName == description.name && getHashForTUID(info.cid) == description.uid)
                return Result::ok();
        }
        return result;
    }

The Unique identifier of a vst3 component, like for COM CLSID is specific to it, to this only should make sure no other plug-in component could use the same.
Well what I’m not really happy with in the Juce PluginDescription wrapping of VST3 is the hashing of this UID into a 32 bit value. I think it would be preferable to keep the VST3 UID as-is in a descriptor (that’s what I do on my side).

I added the change above as a 3rd commit to my pull request.
https://github.com/WeAreROLI/JUCE/pull/530

FYI, I tested with Waves, and MDA plug-ins for shell based vst3.
Otherwise, quickly checked the FabFilter, Voxengo and iZotope and this works the same as before.

Cheers

#10

Hi Rail
Waves is not the only plug-in manufacturer using a shell or shared container.
For example, the free set of VST3 examples provided in the SDK, the mda suite of GUI less plugins also uses a single container for all plug-ins.
Another manufacturer, (I don’t know if they released the VST3 version yet) B<>Com, also deliver their plug-ins (ambisonic/HOA suite of plug-ins) as several shells.

Cheers

Fred

#11

Waves are the only shell with enough content that this matters.

Of course you can add the check for “mda” as well if you wanted to.

 bool bIsMdaShell = (companyName == "mda" && file.getFileName() == "mda-vst3.vst3");

and

 desc.hasSharedContainer = bIsWavesShell || bIsMdaShell;

However, as I mentioned it’s not worth the effort and mda will load using the original method if the isShell flag is zero.

Rail

#12

Thank you all for your input.

1 Like
#13

Thank you for the merge.

While I still have your attention about VST3 pull requests, may I get an second chance about my old code change request for VST3 channel types mapping?
https://github.com/WeAreROLI/JUCE/pull/404
I always need to remember that I have this change in my code repository whenever updating Juce, because otherwise, the 3rd party vst3 plug-ins using large multi-channel IO setup end-up with wrong channel mapping.

Thanks
Fred