1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

VST3: Allow manifest helper to run independently

This commit is contained in:
Anthony Nicholls 2025-05-14 10:27:35 +01:00 committed by Anthony Nicholls
parent 80116d60da
commit f3d7c74ea1
46 changed files with 1299 additions and 1519 deletions

View file

@ -32,6 +32,28 @@
==============================================================================
*/
//==============================================================================
// This suppresses a warning caused by some of the Steinberg source code
#ifndef _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#endif
#include <array>
#include <atomic>
#include <cassert>
#include <cctype>
#include <cstddef>
#include <cstring>
#include <functional>
#include <iomanip>
#include <ios>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
//==============================================================================
// This suppresses a warning in juce_TargetPlatform.h
#ifndef JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1
@ -40,6 +62,7 @@
#include <juce_core/system/juce_CompilerWarnings.h>
#include <juce_core/system/juce_CompilerSupport.h>
//==============================================================================
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wc++98-compat-extra-semi",
"-Wdeprecated-declarations",
"-Wexpansion-to-defined",
@ -56,36 +79,12 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wc++98-compat-extra-semi",
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387 6031)
// As of at least 3.7.12 there is a bug in fplatform.h that leads to SMTG_CPP20
// having the wrong value when the /Zc:__cplusplus is not enabled. This work
// around prevents needing to provide that flag
#include <juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fplatform.h>
#ifdef SMTG_CPP20
#undef SMTG_CPP20
#define SMTG_CPP20 JUCE_CXX20_IS_AVAILABLE
#endif
#ifndef _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#endif
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#if JUCE_MAC
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm>
#elif JUCE_WINDOWS
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp>
#elif JUCE_LINUX
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp>
#define NOMINMAX
#endif
#include <juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/coreiids.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/commonstringconvert.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.cpp>
@ -97,3 +96,93 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387 6031)
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
//==============================================================================
#if JucePlugin_Enable_ARA
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wpragma-pack")
#include <ARA_API/ARAVST3.h>
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#endif // JucePlugin_Enable_ARA
//==============================================================================
#define jassert(x) assert ((x))
#define jassertfalse assert (false)
#define DBG(x)
#define JUCE_DECLARE_NON_COPYABLE(x)
#define JUCE_API
#if __has_include ("JucePluginDefines.h")
#include "JucePluginDefines.h"
#endif
#include <juce_audio_processors/format_types/juce_VST3Utilities.h>
#include <juce_audio_processors/utilities/juce_VST3Interface.h>
#include "juce_VST3ModuleInfo.h"
//==============================================================================
class JucePluginModule : public VST3::Hosting::Module
{
public:
JucePluginModule()
{
using namespace Steinberg;
using namespace VST3::Hosting;
PluginFactory tmp { owned (new juce::JucePluginFactoryBase()) };
factory = std::move (tmp);
}
private:
bool load (const std::string&, std::string&) final { return {}; }
};
std::optional<Steinberg::ModuleInfo::CompatibilityList> loadCompatibilityFromModule (const VST3::Hosting::Module& pluginModule)
{
const auto& factory = pluginModule.getFactory();
const auto& infos = factory.classInfos();
const auto iter = std::find_if (infos.begin(), infos.end(), [&] (const auto& info)
{
return info.category() == kPluginCompatibilityClass;
});
if (iter == infos.end())
return {};
const auto compatibility = factory.createInstance<Steinberg::IPluginCompatibility> (iter->ID());
if (compatibility == nullptr)
return {};
Steinberg::MemoryStream stream;
if (compatibility->getCompatibilityJSON (&stream) != Steinberg::kResultOk)
return {};
const std::string_view streamView (stream.getData(), stream.getSize());
return Steinberg::ModuleInfoLib::parseCompatibilityJson (streamView, nullptr);
}
//==============================================================================
int main()
{
const JucePluginModule pluginModule;
auto moduleInfo = Steinberg::ModuleInfoLib::createModuleInfo (pluginModule, false);
if (auto compatibility = loadCompatibilityFromModule (pluginModule))
moduleInfo.compatibility = *compatibility;
std::stringstream output;
Steinberg::ModuleInfoLib::outputJson (moduleInfo, output);
std::cout << output.str() << std::endl;
return 0;
}
//==============================================================================
namespace VST3::Hosting
{
Module::SnapshotList Module::getSnapshots (const std::string&) { return {}; }
Optional<std::string> Module::getModuleInfoPath (const std::string&) { return {}; }
Module::Ptr Module::create (const std::string&, std::string&) { return {}; }
} // namespace VST3::Hosting

View file

@ -0,0 +1,386 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
#ifndef DOXYGEN
namespace juce
{
[[maybe_unused]] static Steinberg::FUID toSteinbergUID (const VST3Interface::Id& uid)
{
return Steinberg::FUID::fromTUID ((const char*) (uid.data()));
}
[[maybe_unused]] static VST3Interface::Id toVST3InterfaceId (const Steinberg::TUID uid)
{
VST3Interface::Id iid;
std::memcpy (iid.data(), uid, iid.size());
return iid;
}
[[maybe_unused]] static VST3Interface::Id getVST3InterfaceId (VST3Interface::Type interfaceType)
{
#if JUCE_VST3_CAN_REPLACE_VST2
if (interfaceType == VST3Interface::Type::controller || interfaceType == VST3Interface::Type::component)
return VST3Interface::vst2PluginId (JucePlugin_VSTUniqueID, JucePlugin_Name, interfaceType);
#endif
return VST3Interface::jucePluginId (JucePlugin_ManufacturerCode, JucePlugin_PluginCode, interfaceType);
}
[[maybe_unused]] static std::vector<VST3Interface::Id> getAllVST3CompatibleClasses()
{
return
{
#if JUCE_VST3_CAN_REPLACE_VST2
getVST3InterfaceId (VST3Interface::Type::component),
#endif
#ifdef JUCE_VST3_COMPATIBLE_CLASSES
JUCE_VST3_COMPATIBLE_CLASSES
#endif
};
}
//==============================================================================
// See https://steinbergmedia.github.io/vst3_dev_portal/pages/FAQ/Compatibility+with+VST+2.x+or+VST+1.html
#ifdef JUCE_VST3_COMPATIBLE_CLASSES
class JucePluginCompatibility final : public Steinberg::IPluginCompatibility
{
public:
virtual ~JucePluginCompatibility() = default;
JUCE_DECLARE_VST3_COM_REF_METHODS
Steinberg::tresult PLUGIN_API getCompatibilityJSON (Steinberg::IBStream* stream) override
{
// We must avoid relying on any dependencies here including anything in juce_core
std::string json = std::invoke ([]
{
static const auto newId = toSteinbergUID (getVST3InterfaceId (VST3Interface::Type::component));
static const auto oldIds = getAllVST3CompatibleClasses();
std::stringstream str;
str << "[{"
<< "\"New\": \"" << VST3::UID { newId }.toString() << "\", "
<< "\"Old\": [";
for (int i = 0; i < oldIds.size(); ++i)
{
str << "\""
<< std::hex
<< std::setw (2)
<< std::setfill ('0')
<< std::uppercase;
for (auto byte : oldIds[i])
str << (int) byte;
str.clear();
str << "\"";
if (i < oldIds.size() - 1)
str << ", ";
}
str << "]}]";
return str.str();
});
return stream->write (json.data(), (Steinberg::int32) json.size());
}
Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID targetIID,
void** obj) override
{
const auto result = testForMultiple (*this,
targetIID,
UniqueBase<IPluginCompatibility>{},
UniqueBase<FUnknown>{});
if (result.isOk())
return result.extract (obj);
jassertfalse; // Something new?
*obj = nullptr;
return Steinberg::kNotImplemented;
}
inline static const Steinberg::FUID iid = toSteinbergUID (getVST3InterfaceId (VST3Interface::Type::compatibility));
private:
std::atomic<int> refCount { 1 };
};
#endif // JUCE_VST3_COMPATIBLE_CLASSES
//==============================================================================
class JucePluginFactoryBase : public Steinberg::IPluginFactory3
{
public:
JucePluginFactoryBase() = default;
virtual ~JucePluginFactoryBase() = default;
//==============================================================================
JUCE_DECLARE_VST3_COM_REF_METHODS
Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID targetIID, void** obj) final
{
const auto result = testForMultiple (*this,
targetIID,
UniqueBase<IPluginFactory3>{},
UniqueBase<IPluginFactory2>{},
UniqueBase<IPluginFactory>{},
UniqueBase<FUnknown>{});
if (result.isOk())
return result.extract (obj);
jassertfalse; // Something new?
*obj = nullptr;
return Steinberg::kNotImplemented;
}
//==============================================================================
Steinberg::int32 PLUGIN_API countClasses() final
{
return (Steinberg::int32) getClassEntries().size;
}
Steinberg::tresult PLUGIN_API getFactoryInfo (Steinberg::PFactoryInfo* info) final
{
if (info == nullptr)
return Steinberg::kInvalidArgument;
std::memcpy (info, &factoryInfo, sizeof (factoryInfo));
return Steinberg::kResultOk;
}
Steinberg::tresult PLUGIN_API getClassInfo (Steinberg::int32 index,
Steinberg::PClassInfo* info) final
{
return getPClassInfo (index, info, &ClassEntry::info2);
}
Steinberg::tresult PLUGIN_API getClassInfo2 (Steinberg::int32 index,
Steinberg::PClassInfo2* info) final
{
return getPClassInfo (index, info, &ClassEntry::info2);
}
Steinberg::tresult PLUGIN_API getClassInfoUnicode (Steinberg::int32 index,
Steinberg::PClassInfoW* info) final
{
return getPClassInfo (index, info, &ClassEntry::infoW);
}
Steinberg::tresult PLUGIN_API setHostContext (Steinberg::FUnknown*) override
{
jassertfalse;
return Steinberg::kNotImplemented;
}
Steinberg::tresult PLUGIN_API createInstance (Steinberg::FIDString cid,
Steinberg::FIDString sourceIid,
void** obj) override
{
*obj = nullptr;
Steinberg::TUID tuid;
std::memcpy (tuid, sourceIid, sizeof (tuid));
#if VST_VERSION >= 0x030608
auto sourceFuid = Steinberg::FUID::fromTUID (tuid);
#else
FUID sourceFuid;
sourceFuid = tuid;
#endif
if (cid == nullptr || sourceIid == nullptr || ! sourceFuid.isValid())
{
jassertfalse; // The host you're running in has severe implementation issues!
return Steinberg::kInvalidArgument;
}
Steinberg::TUID iidToQuery;
sourceFuid.toTUID (iidToQuery);
for (auto& entry : getClassEntries())
{
if (doUIDsMatch (entry.infoW.cid, cid))
{
if (auto instance = becomeVSTComSmartPtrOwner (createInstance (entry)))
{
if (instance->queryInterface (iidToQuery, obj) == Steinberg::kResultOk)
return Steinberg::kResultOk;
}
break;
}
}
return Steinberg::kNoInterface;
}
protected:
//==============================================================================
struct ClassEntry
{
#ifndef JucePlugin_Vst3ComponentFlags
#if JucePlugin_IsSynth
#define JucePlugin_Vst3ComponentFlags Steinberg::Vst::kSimpleModeSupported
#else
#define JucePlugin_Vst3ComponentFlags 0
#endif
#endif
#ifndef JucePlugin_Vst3Category
#if JucePlugin_IsSynth
#define JucePlugin_Vst3Category Steinberg::Vst::PlugType::kInstrumentSynth
#else
#define JucePlugin_Vst3Category Steinberg::Vst::PlugType::kFx
#endif
#endif
ClassEntry (VST3Interface::Type interfaceType,
const char* name,
bool includeFlagsAndCategory) noexcept
: ClassEntry (getVST3InterfaceId (interfaceType), name, includeFlagsAndCategory)
{}
ClassEntry (VST3Interface::Id interfaceId,
const char* name,
bool includeFlagsAndCategory) noexcept
: info2 ((const char*) interfaceId.data(),
Steinberg::PClassInfo::kManyInstances,
name,
JucePlugin_Name,
includeFlagsAndCategory ? JucePlugin_Vst3ComponentFlags : 0,
includeFlagsAndCategory ? JucePlugin_Vst3Category : "",
JucePlugin_Manufacturer,
JucePlugin_VersionString,
kVstVersionString)
{
infoW.fromAscii (info2);
}
Steinberg::PClassInfo2 info2;
Steinberg::PClassInfoW infoW;
private:
JUCE_DECLARE_NON_COPYABLE (ClassEntry)
};
struct ClassEntrySpan
{
const ClassEntry* data{};
size_t size{};
const ClassEntry* begin() const { return data; }
const ClassEntry* end() const { return data + size; }
};
static ClassEntrySpan getClassEntries()
{
static const ClassEntry classEntries[]
{
#ifdef JUCE_VST3_COMPATIBLE_CLASSES
{ VST3Interface::Type::compatibility, kPluginCompatibilityClass, false },
#endif
{ VST3Interface::Type::component, kVstAudioEffectClass, true },
{ VST3Interface::Type::controller, kVstComponentControllerClass, true },
#if JucePlugin_Enable_ARA
{ VST3Interface::Type::ara, kARAMainFactoryClass, true }
#endif
};
return { classEntries, std::size (classEntries) };
}
virtual Steinberg::FUnknown* createInstance (const ClassEntry& entry)
{
#ifdef JUCE_VST3_COMPATIBLE_CLASSES
if (doUIDsMatch (entry.info2.cid, JucePluginCompatibility::iid.toTUID()))
return new JucePluginCompatibility();
#else
(void) entry;
#endif
jassertfalse;
return {};
}
private:
const ClassEntry& getClassEntry (Steinberg::int32 index) const
{
return getClassEntries().data[index];
}
//==============================================================================
template <typename PClassInfoTargetType, typename PClassInfoSourceType>
Steinberg::tresult PLUGIN_API getPClassInfo (Steinberg::int32 index,
PClassInfoTargetType* info,
PClassInfoSourceType ClassEntry::*source)
{
if (info == nullptr)
{
jassertfalse;
return Steinberg::kInvalidArgument;
}
std::memcpy (info, &(getClassEntry (index).*source), sizeof (*info));
return Steinberg::kResultOk;
}
//==============================================================================
std::atomic<int> refCount { 1 };
const Steinberg::PFactoryInfo factoryInfo
{
JucePlugin_Manufacturer,
JucePlugin_ManufacturerWebsite,
JucePlugin_ManufacturerEmail,
Steinberg::Vst::kDefaultFactoryFlags
};
//==============================================================================
// no leak detector here to prevent it firing on shutdown when running in hosts that
// don't release the factory object correctly...
JUCE_DECLARE_NON_COPYABLE (JucePluginFactoryBase)
};
} // namespace juce
#endif // DOXYGEN

View file

@ -68,12 +68,13 @@
Enable this if you want your VST3 plug-in to load and save VST2 compatible
state. This allows hosts to replace VST2 plug-ins with VST3 plug-ins. If
you change this option then your VST3 plug-in will, by default, be incompatible
with previous versions.
you change this option then your VST3 plug-in will, by default, be
incompatible with previous versions.
If you've already released a VST2 and VST3 with this flag set to 0, you can still enable
migration from VST2 to VST3 on newer hosts. See VST3ClientExtensions::getCompatibleClasses()
and VST3ClientExtensions::getCompatibleParameterIds() for more details.
If you've already released a VST2 and VST3 with this flag set to 0, you can
still enable migration from VST2 to VST3 on newer hosts by defining the
JUCE_VST3_COMPATIBLE_CLASSES preprocessor and implementing the
VST3ClientExtensions::getCompatibleParameterIds() method.
*/
#ifndef JUCE_VST3_CAN_REPLACE_VST2
#define JUCE_VST3_CAN_REPLACE_VST2 1
@ -81,10 +82,12 @@
/** Config: JUCE_FORCE_USE_LEGACY_PARAM_IDS
Enable this if you want to force JUCE to use a continuous parameter
index to identify a parameter in a DAW (this was the default in old
versions of JUCE). This is index is usually used by the DAW to save
automation data and enabling this may mess up user's DAW projects.
Enable this if you want to force JUCE to use a continuous parameter index to
identify a parameter in a DAW (this was the default in old versions of JUCE,
and is always the default for VST2 plugins). This index is usually used by
the DAW to save automation data. Changing this setting may mess up user's
DAW projects, see VST3ClientExtensions::getCompatibleParameterIds() for a
way to overcome this issue on newer VST3 hosts.
*/
#ifndef JUCE_FORCE_USE_LEGACY_PARAM_IDS
#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 0

View file

@ -1814,9 +1814,9 @@ private:
if (args.ptr == nullptr)
return 0;
const auto uid = VST3ClientExtensions::convertVST2PluginId (JucePlugin_VSTUniqueID,
JucePlugin_Name,
VST3ClientExtensions::InterfaceType::component);
const auto uid = VST3Interface::vst2PluginId (JucePlugin_VSTUniqueID,
JucePlugin_Name,
VST3Interface::Type::component);
std::copy (uid.begin(), uid.end(), reinterpret_cast<std::byte*> (args.ptr));
return 1;
#endif

View file

@ -60,9 +60,12 @@ JUCE_BEGIN_NO_SANITIZE ("vptr")
#include <juce_audio_plugin_client/detail/juce_VSTWindowUtilities.h>
#include <juce_gui_basics/native/juce_WindowsHooks_windows.h>
//==============================================================================
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
#include <juce_audio_processors/utilities/juce_FlagCache.h>
#include <juce_audio_processors/format_types/juce_VST3Utilities.h>
#include <juce_audio_processors/format_types/juce_VST3Common.h>
#include <juce_audio_plugin_client/VST3/juce_VST3ModuleInfo.h>
#if JUCE_VST3_CAN_REPLACE_VST2 && ! JUCE_FORCE_USE_LEGACY_PARAM_IDS && ! JUCE_IGNORE_VST3_MISMATCHED_PARAMETER_ID_WARNING
@ -111,51 +114,11 @@ JUCE_BEGIN_NO_SANITIZE ("vptr")
#include <juce_core/native/juce_CFHelpers_mac.h>
#endif
//==============================================================================
#if JucePlugin_Enable_ARA
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wpragma-pack")
#include <ARA_API/ARAVST3.h>
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#if ARA_SUPPORT_VERSION_1
#error "Unsupported ARA version - only ARA version 2 and onward are supported by the current implementation"
#endif
DEF_CLASS_IID (ARA::IPlugInEntryPoint)
DEF_CLASS_IID (ARA::IPlugInEntryPoint2)
DEF_CLASS_IID (ARA::IMainFactory)
#endif
namespace juce
{
using VST3InterfaceType = VST3ClientExtensions::InterfaceType;
using VST3InterfaceId = VST3ClientExtensions::InterfaceId;
using namespace Steinberg;
static FUID toSteinbergUID (const VST3InterfaceId& uid)
{
return FUID::fromTUID ((const char*) (uid.data()));
}
static VST3InterfaceId toVST3InterfaceId (const TUID uid)
{
VST3InterfaceId iid;
std::memcpy (iid.data(), uid, iid.size());
return iid;
}
static VST3InterfaceId getInterfaceId (VST3InterfaceType interfaceType)
{
#if JUCE_VST3_CAN_REPLACE_VST2
if (interfaceType == VST3InterfaceType::controller || interfaceType == VST3InterfaceType::component)
return VST3ClientExtensions::convertVST2PluginId (JucePlugin_VSTUniqueID, JucePlugin_Name, interfaceType);
#endif
return VST3ClientExtensions::convertJucePluginId (JucePlugin_ManufacturerCode, JucePlugin_PluginCode, interfaceType);
}
//==============================================================================
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
double getScaleFactorForWindow (HWND);
@ -623,7 +586,7 @@ public:
bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); }
std::map<Vst::ParamID, AudioProcessorParameter*> getParameterMap (const VST3InterfaceId& pluginId) const
std::map<Vst::ParamID, AudioProcessorParameter*> getParameterMap (const VST3Interface::Id& pluginId) const
{
const auto iter = compatibleParameterIdMap.find (pluginId);
return iter != compatibleParameterIdMap.end() ? iter->second
@ -638,7 +601,7 @@ public:
void updateParameterMapping()
{
static const auto currentPluginId = getInterfaceId (VST3InterfaceType::component);
static const auto currentPluginId = getVST3InterfaceId (VST3Interface::Type::component);
compatibleParameterIdMap = {};
compatibleParameterIdMap[currentPluginId] = paramMap;
@ -649,7 +612,7 @@ public:
if (ext == nullptr)
return;
for (const auto& compatibleClass : getAllCompatibleClasses (*audioProcessor))
for (const auto& compatibleClass : getAllVST3CompatibleClasses())
{
auto& parameterIdMap = compatibleParameterIdMap[compatibleClass];
for (auto [oldParamId, newParamId] : ext->getCompatibleParameterIds (compatibleClass))
@ -678,28 +641,8 @@ public:
}
}
/* Includes all compatible classes declared in the client extensions, along with
our own ID in the case that JUCE_VST3_CAN_REPLACE_VST2 is set.
*/
static std::vector<VST3ClientExtensions::InterfaceId> getAllCompatibleClasses (AudioProcessor& processor)
{
auto result = std::invoke ([&]
{
if (auto* ext = processor.getVST3ClientExtensions())
return ext->getCompatibleClasses();
return std::vector<VST3ClientExtensions::InterfaceId>{};
});
#if JUCE_VST3_CAN_REPLACE_VST2
result.push_back (getInterfaceId (VST3InterfaceType::component));
#endif
return result;
}
//==============================================================================
inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::processor));
inline static const FUID iid = toSteinbergUID (getVST3InterfaceId (VST3Interface::Type::processor));
private:
//==============================================================================
@ -810,7 +753,7 @@ private:
CachedParamValues cachedParamValues;
Vst::ParamID bypassParamID = 0, programParamID = static_cast<Vst::ParamID> (paramPreset);
bool bypassIsRegularParameter = false;
std::map<VST3InterfaceId, std::map<Vst::ParamID, AudioProcessorParameter*>> compatibleParameterIdMap;
std::map<VST3Interface::Id, std::map<Vst::ParamID, AudioProcessorParameter*>> compatibleParameterIdMap;
std::map<String, AudioProcessorParameter*> juceIdParameterMap;
//==============================================================================
@ -917,7 +860,7 @@ public:
}
//==============================================================================
inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::controller));
inline static const FUID iid = toSteinbergUID (getVST3InterfaceId (VST3Interface::Type::controller));
//==============================================================================
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Winconsistent-missing-override")
@ -1273,7 +1216,7 @@ public:
setParamNormalized (vstParamId, paramValue);
}
if (! JuceAudioProcessor::getAllCompatibleClasses (*pluginInstance).empty())
if (! getAllVST3CompatibleClasses().empty())
{
restartFlags |= Vst::kParamIDMappingChanged;
audioProcessor->updateParameterMapping();
@ -2688,7 +2631,7 @@ private:
return createARAFactory();
}
inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::ara));
inline static const FUID iid = toSteinbergUID (getVST3InterfaceId (VST3Interface::Type::ara));
private:
//==============================================================================
@ -2761,7 +2704,7 @@ public:
AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; }
//==============================================================================
inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::component));
inline static const FUID iid = toSteinbergUID (getVST3InterfaceId (VST3Interface::Type::component));
JUCE_DECLARE_VST3_COM_REF_METHODS
@ -2985,7 +2928,7 @@ public:
//==============================================================================
bool shouldTryToLoadVst2State()
{
return ! JuceAudioProcessor::getAllCompatibleClasses (*pluginInstance).empty();
return ! getAllVST3CompatibleClasses().empty();
}
bool shouldWriteStateWithVst2Compatibility()
@ -4138,182 +4081,21 @@ bool shutdownModule()
}
#endif
// See https://steinbergmedia.github.io/vst3_dev_portal/pages/FAQ/Compatibility+with+VST+2.x+or+VST+1.html
class JucePluginCompatibility final : public IPluginCompatibility
//==============================================================================
class JucePluginFactory final : public JucePluginFactoryBase
{
public:
virtual ~JucePluginCompatibility() = default;
JucePluginFactory() = default;
JUCE_DECLARE_VST3_COM_REF_METHODS
tresult PLUGIN_API getCompatibilityJSON (IBStream* stream) override
{
const ScopedJuceInitialiser_GUI libraryInitialiser;
auto filter = createPluginFilterOfType (AudioProcessor::WrapperType::wrapperType_VST3);
const auto compatibilityObjects = std::invoke ([&]
{
const auto compatibleClasses = JuceAudioProcessor::getAllCompatibleClasses (*filter);
if (compatibleClasses.empty())
return Array<var>();
DynamicObject::Ptr object { new DynamicObject };
// New iid is the ID of our Audio Effect class
object->setProperty ("New", String (VST3::UID (JuceVST3Component::iid).toString()));
object->setProperty ("Old", std::invoke ([&]
{
Array<var> oldArray;
for (const auto& uid : compatibleClasses)
oldArray.add (String::toHexString (uid.data(), (int) uid.size(), 0));
return oldArray;
}));
return Array<var> { object.get() };
});
MemoryOutputStream memory;
JSON::writeToStream (memory, var { compatibilityObjects });
return stream->write (memory.getMemoryBlock().getData(), (Steinberg::int32) memory.getDataSize());
}
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
{
const auto result = testForMultiple (*this,
targetIID,
UniqueBase<IPluginCompatibility>{},
UniqueBase<FUnknown>{});
if (result.isOk())
return result.extract (obj);
jassertfalse; // Something new?
*obj = nullptr;
return kNotImplemented;
}
inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::compatibility));
private:
std::atomic<int> refCount { 1 };
};
//==============================================================================
using CreateFunction = FUnknown* (*) (const VSTComSmartPtr<Vst::IHostApplication>&,
const RunLoop&);
//==============================================================================
struct JucePluginFactory final : public IPluginFactory3
{
JucePluginFactory()
: factoryInfo (JucePlugin_Manufacturer, JucePlugin_ManufacturerWebsite,
JucePlugin_ManufacturerEmail, Vst::kDefaultFactoryFlags) {}
virtual ~JucePluginFactory() = default;
//==============================================================================
JUCE_DECLARE_VST3_COM_REF_METHODS
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
{
const auto result = testForMultiple (*this,
targetIID,
UniqueBase<IPluginFactory3>{},
UniqueBase<IPluginFactory2>{},
UniqueBase<IPluginFactory>{},
UniqueBase<FUnknown>{});
if (result.isOk())
return result.extract (obj);
jassertfalse; // Something new?
*obj = nullptr;
return kNotImplemented;
}
//==============================================================================
Steinberg::int32 PLUGIN_API countClasses() override
{
return (Steinberg::int32) getClassEntries().size();
}
tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) override
{
if (info == nullptr)
return kInvalidArgument;
memcpy (info, &factoryInfo, sizeof (PFactoryInfo));
return kResultOk;
}
tresult PLUGIN_API getClassInfo (Steinberg::int32 index, PClassInfo* info) override
{
return getPClassInfo<PClassInfo> (index, info);
}
tresult PLUGIN_API getClassInfo2 (Steinberg::int32 index, PClassInfo2* info) override
{
return getPClassInfo<PClassInfo2> (index, info);
}
tresult PLUGIN_API getClassInfoUnicode (Steinberg::int32 index, PClassInfoW* info) override
{
if (info != nullptr)
{
memcpy (info, &getClassEntries()[static_cast<size_t> (index)].infoW, sizeof (PClassInfoW));
return kResultOk;
}
return kInvalidArgument;
}
tresult PLUGIN_API createInstance (FIDString cid, FIDString sourceIid, void** obj) override
Steinberg::tresult PLUGIN_API createInstance (Steinberg::FIDString cid,
Steinberg::FIDString sourceIid,
void** obj) override
{
const ScopedRunLoop scope { runLoop };
*obj = nullptr;
TUID tuid;
memcpy (tuid, sourceIid, sizeof (TUID));
#if VST_VERSION >= 0x030608
auto sourceFuid = FUID::fromTUID (tuid);
#else
FUID sourceFuid;
sourceFuid = tuid;
#endif
if (cid == nullptr || sourceIid == nullptr || ! sourceFuid.isValid())
{
jassertfalse; // The host you're running in has severe implementation issues!
return kInvalidArgument;
}
TUID iidToQuery;
sourceFuid.toTUID (iidToQuery);
for (auto& entry : getClassEntries())
{
if (doUIDsMatch (entry.infoW.cid, cid))
{
if (auto instance = becomeVSTComSmartPtrOwner (entry.createFunction (host, runLoop)))
{
if (instance->queryInterface (iidToQuery, obj) == kResultOk)
return kResultOk;
}
break;
}
}
return kNoInterface;
return JucePluginFactoryBase::createInstance (cid, sourceIid, obj);
}
tresult PLUGIN_API setHostContext (FUnknown* context) override
Steinberg::tresult PLUGIN_API setHostContext (Steinberg::FUnknown* context) override
{
runLoop.loadFrom (context);
host.loadFrom (context);
@ -4330,136 +4112,24 @@ struct JucePluginFactory final : public IPluginFactory3
}
private:
//==============================================================================
std::atomic<int> refCount { 1 };
const PFactoryInfo factoryInfo;
VSTComSmartPtr<Vst::IHostApplication> host;
RunLoop runLoop;
//==============================================================================
struct ClassEntry
FUnknown* createInstance (const ClassEntry& entry) final
{
ClassEntry (const PClassInfo2& info, CreateFunction fn) noexcept
: info2 (info), createFunction (fn)
{
infoW.fromAscii (info);
}
if (doUIDsMatch (entry.info2.cid, JuceVST3Component::iid))
return static_cast<Vst::IAudioProcessor*> (new JuceVST3Component (host, runLoop));
PClassInfo2 info2;
PClassInfoW infoW;
CreateFunction createFunction = {};
if (doUIDsMatch (entry.info2.cid, JuceVST3EditController::iid))
return static_cast<Vst::IEditController*> (new JuceVST3EditController (host, runLoop));
private:
JUCE_DECLARE_NON_COPYABLE (ClassEntry)
};
static Span<const ClassEntry> getClassEntries()
{
#ifndef JucePlugin_Vst3ComponentFlags
#if JucePlugin_IsSynth
#define JucePlugin_Vst3ComponentFlags Vst::kSimpleModeSupported
#else
#define JucePlugin_Vst3ComponentFlags 0
#endif
#endif
#ifndef JucePlugin_Vst3Category
#if JucePlugin_IsSynth
#define JucePlugin_Vst3Category Vst::PlugType::kInstrumentSynth
#else
#define JucePlugin_Vst3Category Vst::PlugType::kFx
#endif
#endif
static const PClassInfo2 compatibilityClass { JucePluginCompatibility::iid,
PClassInfo::kManyInstances,
kPluginCompatibilityClass,
JucePlugin_Name,
0,
"",
JucePlugin_Manufacturer,
JucePlugin_VersionString,
kVstVersionString };
static const PClassInfo2 componentClass { JuceVST3Component::iid,
PClassInfo::kManyInstances,
kVstAudioEffectClass,
JucePlugin_Name,
JucePlugin_Vst3ComponentFlags,
JucePlugin_Vst3Category,
JucePlugin_Manufacturer,
JucePlugin_VersionString,
kVstVersionString };
static const PClassInfo2 controllerClass { JuceVST3EditController::iid,
PClassInfo::kManyInstances,
kVstComponentControllerClass,
JucePlugin_Name,
JucePlugin_Vst3ComponentFlags,
JucePlugin_Vst3Category,
JucePlugin_Manufacturer,
JucePlugin_VersionString,
kVstVersionString };
#if JucePlugin_Enable_ARA
static const PClassInfo2 araFactoryClass { JuceARAFactory::iid,
PClassInfo::kManyInstances,
kARAMainFactoryClass,
JucePlugin_Name,
JucePlugin_Vst3ComponentFlags,
JucePlugin_Vst3Category,
JucePlugin_Manufacturer,
JucePlugin_VersionString,
kVstVersionString };
if (doUIDsMatch (entry.info2.cid, JuceARAFactory::iid))
return static_cast<ARA::IMainFactory*> (new JuceARAFactory());
#endif
static const ClassEntry classEntries[]
{
ClassEntry { componentClass, [] (const VSTComSmartPtr<Vst::IHostApplication>& h,
const RunLoop& l) -> FUnknown*
{
return static_cast<Vst::IAudioProcessor*> (new JuceVST3Component (h, l));
} },
ClassEntry { controllerClass, [] (const VSTComSmartPtr<Vst::IHostApplication>& h,
const RunLoop& l) -> FUnknown*
{
return static_cast<Vst::IEditController*> (new JuceVST3EditController (h, l));
} },
ClassEntry { compatibilityClass, [] (const VSTComSmartPtr<Vst::IHostApplication>&,
const RunLoop&) -> FUnknown*
{
return new JucePluginCompatibility;
} },
#if JucePlugin_Enable_ARA
ClassEntry { araFactoryClass, [] (const VSTComSmartPtr<Vst::IHostApplication>&,
const RunLoop&) -> FUnknown*
{
return static_cast<ARA::IMainFactory*> (new JuceARAFactory);
} },
#endif
};
return Span { classEntries };
return JucePluginFactoryBase::createInstance (entry);
}
//==============================================================================
template <class PClassInfoType>
tresult PLUGIN_API getPClassInfo (Steinberg::int32 index, PClassInfoType* info)
{
if (info != nullptr)
{
zerostruct (*info);
memcpy (info, (PClassInfoType*) &getClassEntries()[static_cast<size_t> (index)].info2, sizeof (PClassInfoType));
return kResultOk;
}
jassertfalse;
return kInvalidArgument;
}
//==============================================================================
// no leak detector here to prevent it firing on shutdown when running in hosts that
// don't release the factory object correctly...
JUCE_DECLARE_NON_COPYABLE (JucePluginFactory)
RunLoop runLoop;
VSTComSmartPtr<Vst::IHostApplication> host;
};
} // namespace juce