Browse Source

VST3 Host: Add support for loading PluginDescriptions from moduleinfo.json

v7.0.9
reuk 2 years ago
parent
commit
cf9fc84669
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
3 changed files with 136 additions and 78 deletions
  1. +11
    -0
      modules/juce_audio_processors/format_types/juce_VST3Headers.h
  2. +124
    -78
      modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  3. +1
    -0
      modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h

+ 11
- 0
modules/juce_audio_processors/format_types/juce_VST3Headers.h View File

@@ -112,6 +112,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings",
#include <pluginterfaces/vst/ivstmidicontrollers.h>
#include <pluginterfaces/vst/ivstchannelcontextinfo.h>
#include <public.sdk/source/common/memorystream.h>
#include <public.sdk/source/vst/utility/uid.h>
#include <public.sdk/source/vst/vsteditcontroller.h>
#include <public.sdk/source/vst/vstpresetfile.h>
@@ -143,6 +144,11 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings",
#include <base/source/flock.cpp>
#endif
#pragma push_macro ("True")
#undef True
#pragma push_macro ("False")
#undef False
#include <base/source/updatehandler.cpp>
#include <pluginterfaces/base/conststringtable.cpp>
#include <pluginterfaces/base/funknown.cpp>
@@ -155,7 +161,9 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings",
#include <public.sdk/source/common/memorystream.cpp>
#include <public.sdk/source/common/pluginview.cpp>
#include <public.sdk/source/vst/hosting/hostclasses.cpp>
#include <public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp>
#include <public.sdk/source/vst/utility/stringconvert.cpp>
#include <public.sdk/source/vst/utility/uid.h>
#include <public.sdk/source/vst/vstbus.cpp>
#include <public.sdk/source/vst/vstcomponent.cpp>
#include <public.sdk/source/vst/vstcomponentbase.cpp>
@@ -164,6 +172,9 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings",
#include <public.sdk/source/vst/vstparameters.cpp>
#include <public.sdk/source/vst/vstpresetfile.cpp>
#pragma pop_macro ("True")
#pragma pop_macro ("False")
#if VST_VERSION >= 0x03060c // 3.6.12
#include <public.sdk/source/vst/hosting/pluginterfacesupport.cpp>
#endif


+ 124
- 78
modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -188,6 +188,47 @@ static void fillDescriptionWith (PluginDescription& description, ObjectType& obj
description.manufacturerName = toString (object.vendor).trim();
}
static std::vector<PluginDescription> createPluginDescriptions (const File& pluginFile, const Steinberg::ModuleInfo& info)
{
std::vector<PluginDescription> result;
for (const auto& c : info.classes)
{
if (c.category != kVstAudioEffectClass)
continue;
PluginDescription description;
description.fileOrIdentifier = pluginFile.getFullPathName();
description.lastFileModTime = pluginFile.getLastModificationTime();
description.lastInfoUpdateTime = Time::getCurrentTime();
description.manufacturerName = CharPointer_UTF8 (info.factoryInfo.vendor.c_str());
description.name = CharPointer_UTF8 (info.name.c_str());
description.descriptiveName = CharPointer_UTF8 (info.name.c_str());
description.pluginFormatName = "VST3";
description.numInputChannels = 0;
description.numOutputChannels = 0;
const auto uid = VST3::UID::fromString (c.cid);
if (! uid)
continue;
description.deprecatedUid = getHashForRange (uid->data());
description.uniqueId = getHashForRange (getNormalisedTUID (uid->data()));
description.category = CharPointer_UTF8 (c.category.c_str());
description.isInstrument = std::any_of (c.subCategories.begin(),
c.subCategories.end(),
[] (const auto& subcategory) { return subcategory == "Instrument"; });
result.push_back (description);
}
return result;
}
static void createPluginDescription (PluginDescription& description,
const File& pluginFile, const String& company, const String& name,
const PClassInfo& info, PClassInfo2* info2, PClassInfoW* infoW,
@@ -813,26 +854,41 @@ private:
};
//==============================================================================
struct DescriptionFactory
struct DescriptionLister
{
DescriptionFactory (VST3HostContext* host, IPluginFactory* pluginFactory)
: vst3HostContext (host), factory (pluginFactory)
static std::vector<PluginDescription> findDescriptionsFast (const File& file)
{
jassert (pluginFactory != nullptr);
}
const auto moduleinfo = file.getChildFile ("Contents").getChildFile ("moduleinfo.json");
if (! moduleinfo.existsAsFile())
return {};
MemoryBlock mb;
virtual ~DescriptionFactory() {}
if (! moduleinfo.loadFileAsData (mb))
return {};
const std::string_view blockAsStringView (static_cast<const char*> (mb.getData()), mb.getSize());
const auto parsed = Steinberg::ModuleInfoLib::parseJson (blockAsStringView, nullptr);
if (! parsed)
return {};
return createPluginDescriptions (file, *parsed);
}
Result findDescriptionsAndPerform (const File& file)
static std::vector<PluginDescription> findDescriptionsSlow (VST3HostContext& host,
IPluginFactory& factory,
const File& file)
{
std::vector<PluginDescription> result;
StringArray foundNames;
PFactoryInfo factoryInfo;
factory->getFactoryInfo (&factoryInfo);
factory.getFactoryInfo (&factoryInfo);
auto companyName = toString (factoryInfo.vendor).trim();
Result result (Result::ok());
auto numClasses = factory->countClasses();
auto numClasses = factory.countClasses();
// Every ARA::IMainFactory must have a matching Steinberg::IComponent.
// The match is determined by the two classes having the same name.
@@ -842,7 +898,7 @@ struct DescriptionFactory
for (Steinberg::int32 i = 0; i < numClasses; ++i)
{
PClassInfo info;
factory->getClassInfo (i, &info);
factory.getClassInfo (i, &info);
if (std::strcmp (info.category, kARAMainFactoryClass) == 0)
araMainFactoryClassNames.insert (info.name);
}
@@ -851,7 +907,7 @@ struct DescriptionFactory
for (Steinberg::int32 i = 0; i < numClasses; ++i)
{
PClassInfo info;
factory->getClassInfo (i, &info);
factory.getClassInfo (i, &info);
if (std::strcmp (info.category, kVstAudioEffectClass) != 0)
continue;
@@ -868,13 +924,13 @@ struct DescriptionFactory
VSTComSmartPtr<IPluginFactory2> pf2;
VSTComSmartPtr<IPluginFactory3> pf3;
if (pf2.loadFrom (factory))
if (pf2.loadFrom (&factory))
{
info2.reset (new PClassInfo2());
pf2->getClassInfo2 (i, info2.get());
}
if (pf3.loadFrom (factory))
if (pf3.loadFrom (&factory))
{
infoW.reset (new PClassInfoW());
pf3->getClassInfoUnicode (i, infoW.get());
@@ -888,9 +944,9 @@ struct DescriptionFactory
{
VSTComSmartPtr<Vst::IComponent> component;
if (component.loadFrom (factory, info.cid))
if (component.loadFrom (&factory, info.cid))
{
if (component->initialize (vst3HostContext->getFUnknown()) == kResultOk)
if (component->initialize (host.getFUnknown()) == kResultOk)
{
auto numInputs = getNumSingleDirectionChannelsFor (component, Direction::input);
auto numOutputs = getNumSingleDirectionChannelsFor (component, Direction::output);
@@ -915,41 +971,11 @@ struct DescriptionFactory
desc.hasARAExtension = true;
if (desc.uniqueId != 0)
result = performOnDescription (desc);
if (result.failed())
break;
result.push_back (desc);
}
return result;
}
virtual Result performOnDescription (PluginDescription&) = 0;
private:
VSTComSmartPtr<VST3HostContext> vst3HostContext;
VSTComSmartPtr<IPluginFactory> factory;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionFactory)
};
struct DescriptionLister : public DescriptionFactory
{
DescriptionLister (VST3HostContext* host, IPluginFactory* pluginFactory)
: DescriptionFactory (host, pluginFactory)
{
}
Result performOnDescription (PluginDescription& desc)
{
list.add (new PluginDescription (desc));
return Result::ok();
}
OwnedArray<PluginDescription> list;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionLister)
};
//==============================================================================
@@ -1356,9 +1382,11 @@ private:
if (std::strcmp (info.category, kVstAudioEffectClass) != 0)
continue;
const auto uniqueId = getHashForRange (getNormalisedTUID (info.cid));
const auto deprecatedUid = getHashForRange (info.cid);
if (toString (info.name).trim() == description.name
&& (getHashForRange (getNormalisedTUID (info.cid)) == description.uniqueId
|| getHashForRange (info.cid) == description.deprecatedUid))
&& (uniqueId == description.uniqueId || deprecatedUid == description.deprecatedUid))
{
name = description.name;
return true;
@@ -3791,7 +3819,18 @@ bool VST3PluginFormat::setStateFromVSTPresetFile (AudioPluginInstance* api, cons
void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results, const String& fileOrIdentifier)
{
if (fileMightContainThisPluginType (fileOrIdentifier))
if (! fileMightContainThisPluginType (fileOrIdentifier))
return;
if (const auto fast = DescriptionLister::findDescriptionsFast (File (fileOrIdentifier)); ! fast.empty())
{
for (const auto& d : fast)
results.add (new PluginDescription (d));
return;
}
for (const auto& file : getLibraryPaths (fileOrIdentifier))
{
/**
Since there is no apparent indication if a VST3 plugin is a shell or not,
@@ -3799,21 +3838,16 @@ void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& resul
for every housed plugin.
*/
VSTComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (fileOrIdentifier)
.getPluginFactory());
VSTComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (file)
.getPluginFactory());
if (pluginFactory != nullptr)
{
VSTComSmartPtr<VST3HostContext> host (new VST3HostContext());
DescriptionLister lister (host, pluginFactory);
lister.findDescriptionsAndPerform (File (fileOrIdentifier));
if (pluginFactory == nullptr)
continue;
results.addCopiesOf (lister.list);
}
else
{
jassertfalse;
}
VSTComSmartPtr<VST3HostContext> host (new VST3HostContext());
for (const auto& d : DescriptionLister::findDescriptionsSlow (*host, *pluginFactory, File (file)))
results.add (new PluginDescription (d));
}
}
@@ -3834,13 +3868,12 @@ void VST3PluginFormat::createARAFactoryAsync (const PluginDescription& descripti
}
static std::unique_ptr<AudioPluginInstance> createVST3Instance (VST3PluginFormat& format,
const PluginDescription& description)
const PluginDescription& description,
const File& file)
{
if (! format.fileMightContainThisPluginType (description.fileOrIdentifier))
return nullptr;
const File file { description.fileOrIdentifier };
struct ScopedWorkingDirectory
{
~ScopedWorkingDirectory() { previousWorkingDirectory.setAsCurrentWorkingDirectory(); }
@@ -3868,15 +3901,33 @@ static std::unique_ptr<AudioPluginInstance> createVST3Instance (VST3PluginFormat
return instance;
}
StringArray VST3PluginFormat::getLibraryPaths (const String& fileOrIdentifier)
{
#if JUCE_WINDOWS
if (! File (fileOrIdentifier).existsAsFile())
{
StringArray files;
recursiveFileSearch (files, fileOrIdentifier, true);
return files;
}
#endif
return { fileOrIdentifier };
}
void VST3PluginFormat::createPluginInstance (const PluginDescription& description,
double, int, PluginCreationCallback callback)
{
auto result = createVST3Instance (*this, description);
const auto errorMsg = result == nullptr ? TRANS ("Unable to load XXX plug-in file").replace ("XXX", "VST-3")
: String();
for (const auto& file : getLibraryPaths (description.fileOrIdentifier))
{
if (auto result = createVST3Instance (*this, description, file))
{
callback (std::move (result), {});
return;
}
}
callback (std::move (result), errorMsg);
callback (nullptr, TRANS ("Unable to load XXX plug-in file").replace ("XXX", "VST-3"));
}
bool VST3PluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const
@@ -3888,12 +3939,7 @@ bool VST3PluginFormat::fileMightContainThisPluginType (const String& fileOrIdent
{
auto f = File::createFileWithoutCheckingPath (fileOrIdentifier);
return f.hasFileExtension (".vst3")
#if JUCE_MAC || JUCE_LINUX || JUCE_BSD
&& f.exists();
#else
&& f.existsAsFile();
#endif
return f.hasFileExtension (".vst3") && f.exists();
}
String VST3PluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)


+ 1
- 0
modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h View File

@@ -76,6 +76,7 @@ private:
int initialBufferSize, PluginCreationCallback) override;
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const override;
void recursiveFileSearch (StringArray&, const File&, bool recursive);
StringArray getLibraryPaths (const String&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat)
};


Loading…
Cancel
Save