1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +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

@ -1,441 +0,0 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfotool
// Filename : public.sdk/samples/vst-utilities/moduleinfotool/main.cpp
// Created by : Steinberg, 12/2021
// Description : main entry point
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2024, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------
#include "pluginterfaces/base/iplugincompatibility.h"
#include "public.sdk/source/common/memorystream.h"
#include "public.sdk/source/common/readfile.h"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/moduleinfo/moduleinfocreator.h"
#include "public.sdk/source/vst/moduleinfo/moduleinfoparser.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "base/source/fcommandline.h"
#include "pluginterfaces/base/fplatform.h"
#include "pluginterfaces/vst/vsttypes.h"
#include <cstdio>
#include <fstream>
#include <iostream>
//------------------------------------------------------------------------
namespace Steinberg {
namespace ModuleInfoTool {
namespace {
//------------------------------------------------------------------------
constexpr auto BUILD_INFO = "moduleinfotool 1.0.0 [Built " __DATE__ "]";
//------------------------------------------------------------------------
//-- Options
constexpr auto optHelp = "help";
constexpr auto optCreate = "create";
constexpr auto optValidate = "validate";
constexpr auto optModuleVersion = "version";
constexpr auto optModulePath = "path";
constexpr auto optInfoPath = "infopath";
constexpr auto optModuleCompatPath = "compat";
constexpr auto optOutputPath = "output";
//------------------------------------------------------------------------
void printUsage (std::ostream& s)
{
s << "Usage:\n";
s << " moduleinfotool -create -version VERSION -path MODULE_PATH [-compat PATH -output PATH]\n";
s << " moduleinfotool -validate -path MODULE_PATH [-infopath PATH]\n";
}
//------------------------------------------------------------------------
std::optional<ModuleInfo::CompatibilityList> openAndParseCompatJSON (const std::string& path)
{
auto data = readFile (path);
if (data.empty ())
{
std::cout << "Can not read '" << path << "'\n";
printUsage (std::cout);
return {};
}
std::stringstream error;
auto result = ModuleInfoLib::parseCompatibilityJson (data, &error);
if (!result)
{
std::cout << "Can not parse '" << path << "'\n";
std::cout << error.str ();
printUsage (std::cout);
return {};
}
return result;
}
//------------------------------------------------------------------------
std::optional<ModuleInfo::CompatibilityList> loadCompatibilityFromModule (const VST3::Hosting::Module& module)
{
const auto& factory = module.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) != kResultOk)
return {};
const std::string_view streamView (stream.getData(), stream.getSize());
return ModuleInfoLib::parseCompatibilityJson (streamView, nullptr);
}
//------------------------------------------------------------------------
int createJSON (const std::optional<ModuleInfo::CompatibilityList>& compat,
const std::string& modulePath, const std::string& moduleVersion,
std::ostream& outStream)
{
std::string errorStr;
auto module = VST3::Hosting::Module::create (modulePath, errorStr);
if (!module)
{
std::cout << errorStr;
return 1;
}
auto moduleInfo = ModuleInfoLib::createModuleInfo (*module, false);
if (compat)
moduleInfo.compatibility = *compat;
else if (auto loaded = loadCompatibilityFromModule (*module))
moduleInfo.compatibility = *loaded;
moduleInfo.version = moduleVersion;
std::stringstream output;
ModuleInfoLib::outputJson (moduleInfo, output);
auto str = output.str ();
outStream << str;
return 0;
}
//------------------------------------------------------------------------
struct validate_error : std::exception
{
validate_error (const std::string& str) : str (str) {}
const char* what () const noexcept override { return str.data (); }
private:
std::string str;
};
//------------------------------------------------------------------------
void validate (const ModuleInfo& moduleInfo, const VST3::Hosting::Module& module)
{
auto factory = module.getFactory ();
auto factoryInfo = factory.info ();
auto classInfoList = factory.classInfos ();
auto snapshotList = module.getSnapshots (module.getPath ());
if (factoryInfo.vendor () != moduleInfo.factoryInfo.vendor)
throw validate_error ("factoryInfo.vendor different: " + moduleInfo.factoryInfo.vendor);
if (factoryInfo.url () != moduleInfo.factoryInfo.url)
throw validate_error ("factoryInfo.url different: " + moduleInfo.factoryInfo.url);
if (factoryInfo.email () != moduleInfo.factoryInfo.email)
throw validate_error ("factoryInfo.email different: " + moduleInfo.factoryInfo.email);
if (factoryInfo.flags () != moduleInfo.factoryInfo.flags)
throw validate_error ("factoryInfo.flags different: " +
std::to_string (moduleInfo.factoryInfo.flags));
for (const auto& ci : moduleInfo.classes)
{
auto cid = VST3::UID::fromString (ci.cid);
if (!cid)
throw validate_error ("could not parse class UID: " + ci.cid);
auto it = std::find_if (classInfoList.begin (), classInfoList.end (),
[&] (const auto& el) { return el.ID () == *cid; });
if (it == classInfoList.end ())
throw validate_error ("cannot find CID in class list: " + ci.cid);
if (it->name () != ci.name)
throw validate_error ("class name different: " + ci.name);
if (it->category () != ci.category)
throw validate_error ("class category different: " + ci.category);
if (it->vendor () != ci.vendor)
throw validate_error ("class vendor different: " + ci.vendor);
if (it->version () != ci.version)
throw validate_error ("class version different: " + ci.version);
if (it->sdkVersion () != ci.sdkVersion)
throw validate_error ("class sdkVersion different: " + ci.sdkVersion);
if (it->subCategories () != ci.subCategories)
throw validate_error ("class subCategories different: " /* + ci.subCategories*/);
if (it->cardinality () != ci.cardinality)
throw validate_error ("class cardinality different: " +
std::to_string (ci.cardinality));
if (it->classFlags () != ci.flags)
throw validate_error ("class flags different: " + std::to_string (ci.flags));
classInfoList.erase (it);
auto snapshotListIt = std::find_if (snapshotList.begin (), snapshotList.end (),
[&] (const auto& el) { return el.uid == *cid; });
if (snapshotListIt == snapshotList.end () && !ci.snapshots.empty ())
throw validate_error ("cannot find snapshots for: " + ci.cid);
for (const auto& snapshot : ci.snapshots)
{
auto snapshotIt = std::find_if (
snapshotListIt->images.begin (), snapshotListIt->images.end (),
[&] (const auto& el) { return el.scaleFactor == snapshot.scaleFactor; });
if (snapshotIt == snapshotListIt->images.end ())
throw validate_error ("cannot find snapshots for scale factor: " +
std::to_string (snapshot.scaleFactor));
std::string_view path (snapshotIt->path);
if (path.find (module.getPath ()) == 0)
path.remove_prefix (module.getPath ().size () + 1);
if (path != snapshot.path)
throw validate_error ("cannot find snapshots with path: " + snapshot.path);
snapshotListIt->images.erase (snapshotIt);
}
if (snapshotListIt != snapshotList.end () && !snapshotListIt->images.empty ())
{
std::string errorStr = "Missing Snapshots in moduleinfo:\n";
for (const auto& s : snapshotListIt->images)
{
errorStr += s.path + '\n';
}
throw validate_error (errorStr);
}
if (snapshotListIt != snapshotList.end ())
snapshotList.erase (snapshotListIt);
}
if (!classInfoList.empty ())
throw validate_error ("Missing classes in moduleinfo");
if (!snapshotList.empty ())
throw validate_error ("Missing snapshots in moduleinfo");
}
//------------------------------------------------------------------------
int validate (const std::string& modulePath, std::string infoJsonPath)
{
if (infoJsonPath.empty ())
{
auto path = VST3::Hosting::Module::getModuleInfoPath (modulePath);
if (!path)
{
std::cerr << "Module does not contain a moduleinfo.json: '" << modulePath << "'"
<< '\n';
return 1;
}
infoJsonPath = *path;
}
auto data = readFile (infoJsonPath);
if (data.empty ())
{
std::cerr << "Empty or non existing file: '" << infoJsonPath << "'" << '\n';
printUsage (std::cout);
return 1;
}
auto moduleInfo = ModuleInfoLib::parseJson (data, &std::cerr);
if (moduleInfo)
{
std::string errorStr;
auto module = VST3::Hosting::Module::create (modulePath, errorStr);
if (!module)
{
std::cerr << errorStr;
printUsage (std::cout);
return 1;
}
try
{
validate (*moduleInfo, *module);
}
catch (const std::exception& exc)
{
std::cerr << "Error:\n" << exc.what () << '\n';
printUsage (std::cout);
return 1;
}
return 0;
}
printUsage (std::cout);
return 1;
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
int run (int argc, char* argv[])
{
// parse command line
CommandLine::Descriptions desc;
CommandLine::VariablesMap valueMap;
CommandLine::FilesVector files;
using Description = CommandLine::Description;
desc.addOptions (
BUILD_INFO,
{
{optCreate, "Create moduleinfo", Description::kBool},
{optValidate, "Validate moduleinfo", Description::kBool},
{optModuleVersion, "Module version", Description::kString},
{optModulePath, "Path to module", Description::kString},
{optInfoPath, "Path to moduleinfo.json", Description::kString},
{optModuleCompatPath, "Path to compatibility.json", Description::kString},
{optOutputPath, "Write json to file instead of stdout", Description::kString},
{optHelp, "Print help", Description::kBool},
});
CommandLine::parse (argc, argv, desc, valueMap, &files);
bool isCreate = valueMap.count (optCreate) != 0 && valueMap.count (optModuleVersion) != 0 &&
valueMap.count (optModulePath) != 0;
bool isValidate = valueMap.count (optValidate) && valueMap.count (optModulePath) != 0;
if (valueMap.hasError () || valueMap.count (optHelp) || !(isCreate || isValidate))
{
std::cout << '\n' << desc << '\n';
printUsage (std::cout);
return 1;
}
int result = 1;
const auto& modulePath = valueMap[optModulePath];
if (isCreate)
{
auto* outputStream = &std::cout;
std::optional<ModuleInfo::CompatibilityList> compat;
if (valueMap.count (optModuleCompatPath) != 0)
{
const auto& compatPath = valueMap[optModuleCompatPath];
compat = openAndParseCompatJSON (compatPath);
if (!compat)
return 1;
}
bool writeToFile = false;
if (valueMap.count (optOutputPath) != 0)
{
writeToFile = true;
#if SMTG_OS_WINDOWS
auto tmp = Vst::StringConvert::convert (valueMap[optOutputPath]);
auto outputFile = reinterpret_cast<const wchar_t*> (tmp.data ());
#else
auto outputFile = valueMap[optOutputPath];
#endif
auto ostream = new std::ofstream (outputFile);
if (ostream->is_open ())
outputStream = ostream;
else
{
std::cout << "Cannot create output file: " << valueMap[optOutputPath] << '\n';
return result;
}
}
const auto& moduleVersion = valueMap[optModuleVersion];
result = createJSON (compat, modulePath, moduleVersion, *outputStream);
if (writeToFile)
delete outputStream;
}
else if (isValidate)
{
std::string moduleInfoJsonPath;
if (valueMap.count (optInfoPath) != 0)
moduleInfoJsonPath = valueMap[optInfoPath];
result = validate (modulePath, moduleInfoJsonPath);
}
return result;
}
//------------------------------------------------------------------------
} // ModuleInfoTool
} // Steinberg
//------------------------------------------------------------------------
#if SMTG_OS_WINDOWS
//------------------------------------------------------------------------
using Utf8String = std::string;
//------------------------------------------------------------------------
using Utf8Args = std::vector<Utf8String>;
Utf8Args toUtf8Args (int argc, wchar_t* wargv[])
{
Utf8Args utf8Args;
for (int i = 0; i < argc; i++)
{
auto str = reinterpret_cast<const Steinberg::Vst::TChar*> (wargv[i]);
utf8Args.push_back (Steinberg::Vst::StringConvert::convert (str));
}
return utf8Args;
}
//------------------------------------------------------------------------
using Utf8ArgPtrs = std::vector<char*>;
Utf8ArgPtrs toUtf8ArgPtrs (Utf8Args& utf8Args)
{
Utf8ArgPtrs utf8ArgPtrs;
for (auto& el : utf8Args)
{
utf8ArgPtrs.push_back (el.data ());
}
return utf8ArgPtrs;
}
//------------------------------------------------------------------------
int wmain (int argc, wchar_t* wargv[])
{
Utf8Args utf8Args = toUtf8Args (argc, wargv);
Utf8ArgPtrs utf8ArgPtrs = toUtf8ArgPtrs (utf8Args);
char** argv = &(utf8ArgPtrs.at (0));
return Steinberg::ModuleInfoTool::run (argc, argv);
}
#else
//------------------------------------------------------------------------
int main (int argc, char* argv[])
{
return Steinberg::ModuleInfoTool::run (argc, argv);
}
//------------------------------------------------------------------------
#endif // SMTG_OS_WINDOWS

View file

@ -41,129 +41,6 @@ namespace juce
JUCE_BEGIN_NO_SANITIZE ("vptr")
//==============================================================================
#define JUCE_DECLARE_VST3_COM_REF_METHODS \
Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \
Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; }
#define JUCE_DECLARE_VST3_COM_QUERY_METHODS \
Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \
{ \
jassertfalse; \
*obj = nullptr; \
return Steinberg::kNotImplemented; \
}
inline bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexcept
{
return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0;
}
/* Holds a tresult and a pointer to an object.
Useful for holding intermediate results of calls to queryInterface.
*/
class QueryInterfaceResult
{
public:
QueryInterfaceResult() = default;
QueryInterfaceResult (Steinberg::tresult resultIn, void* ptrIn)
: result (resultIn), ptr (ptrIn) {}
bool isOk() const noexcept { return result == Steinberg::kResultOk; }
Steinberg::tresult extract (void** obj) const
{
*obj = result == Steinberg::kResultOk ? ptr : nullptr;
return result;
}
private:
Steinberg::tresult result = Steinberg::kResultFalse;
void* ptr = nullptr;
};
/* Holds a tresult and a pointer to an object.
Calling InterfaceResultWithDeferredAddRef::extract() will also call addRef
on the pointed-to object. It is expected that users will use
InterfaceResultWithDeferredAddRef to hold intermediate results of a queryInterface
call. When a suitable interface is found, the function can be exited with
`return suitableInterface.extract (obj)`, which will set the obj pointer,
add a reference to the interface, and return the appropriate result code.
*/
class InterfaceResultWithDeferredAddRef
{
public:
InterfaceResultWithDeferredAddRef() = default;
template <typename Ptr>
InterfaceResultWithDeferredAddRef (Steinberg::tresult resultIn, Ptr* ptrIn)
: result (resultIn, ptrIn),
addRefFn (doAddRef<Ptr>) {}
bool isOk() const noexcept { return result.isOk(); }
Steinberg::tresult extract (void** obj) const
{
const auto toReturn = result.extract (obj);
if (result.isOk() && *obj != nullptr)
NullCheckedInvocation::invoke (addRefFn, *obj);
return toReturn;
}
private:
template <typename Ptr>
static void doAddRef (void* obj) { static_cast<Ptr*> (obj)->addRef(); }
QueryInterfaceResult result;
void (*addRefFn) (void*) = nullptr;
};
template <typename ClassType> struct UniqueBase {};
template <typename CommonClassType, typename SourceClassType> struct SharedBase {};
template <typename ToTest, typename CommonClassType, typename SourceClassType>
InterfaceResultWithDeferredAddRef testFor (ToTest& toTest,
const Steinberg::TUID targetIID,
SharedBase<CommonClassType, SourceClassType>)
{
if (! doUIDsMatch (targetIID, CommonClassType::iid))
return {};
return { Steinberg::kResultOk, static_cast<CommonClassType*> (static_cast<SourceClassType*> (std::addressof (toTest))) };
}
template <typename ToTest, typename ClassType>
InterfaceResultWithDeferredAddRef testFor (ToTest& toTest,
const Steinberg::TUID targetIID,
UniqueBase<ClassType>)
{
return testFor (toTest, targetIID, SharedBase<ClassType, ClassType>{});
}
template <typename ToTest>
InterfaceResultWithDeferredAddRef testForMultiple (ToTest&, const Steinberg::TUID) { return {}; }
template <typename ToTest, typename Head, typename... Tail>
InterfaceResultWithDeferredAddRef testForMultiple (ToTest& toTest, const Steinberg::TUID targetIID, Head head, Tail... tail)
{
const auto result = testFor (toTest, targetIID, head);
if (result.isOk())
return result;
return testForMultiple (toTest, targetIID, tail...);
}
//==============================================================================
#if VST_VERSION < 0x030608
#define kAmbi1stOrderACN kBFormat
#endif
//==============================================================================
inline juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (juce::CharPointer_UTF8 ((juce::CharPointer_UTF8::CharType*) string)); }
inline juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); }
@ -184,6 +61,11 @@ inline void toString128 (Steinberg::Vst::String128 result, const juce::String& s
Steinberg::UString (result, 128).assign (toString (source));
}
//==============================================================================
#if VST_VERSION < 0x030608
#define kAmbi1stOrderACN kBFormat
#endif
#if JUCE_WINDOWS
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND;
#elif JUCE_MAC
@ -192,7 +74,6 @@ inline void toString128 (Steinberg::Vst::String128 result, const juce::String& s
static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeX11EmbedWindowID;
#endif
//==============================================================================
static inline Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor,
bool isInput, int busIndex)
@ -1192,83 +1073,6 @@ private:
std::vector<ChannelMapping> mappings;
};
//==============================================================================
// We have to trust that Steinberg won't double-delete
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDelete)
template <class ObjectType>
class VSTComSmartPtr
{
public:
VSTComSmartPtr() = default;
VSTComSmartPtr (const VSTComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~VSTComSmartPtr() { if (source != nullptr) source->release(); }
explicit operator bool() const noexcept { return operator!= (nullptr); }
ObjectType* get() const noexcept { return source; }
ObjectType& operator*() const noexcept { return *source; }
ObjectType* operator->() const noexcept { return source; }
VSTComSmartPtr& operator= (const VSTComSmartPtr& other)
{
auto p = other;
std::swap (p.source, source);
return *this;
}
VSTComSmartPtr& operator= (std::nullptr_t)
{
return operator= (VSTComSmartPtr{});
}
bool operator== (std::nullptr_t) const noexcept { return source == nullptr; }
bool operator!= (std::nullptr_t) const noexcept { return source != nullptr; }
bool loadFrom (Steinberg::FUnknown* o)
{
*this = nullptr;
return o != nullptr && o->queryInterface (ObjectType::iid, (void**) &source) == Steinberg::kResultOk;
}
bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid)
{
jassert (factory != nullptr);
*this = nullptr;
return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk;
}
/** Increments refcount. */
static auto addOwner (ObjectType* t)
{
return VSTComSmartPtr (t, true);
}
/** Does not initially increment refcount; assumes t has a positive refcount. */
static auto becomeOwner (ObjectType* t)
{
return VSTComSmartPtr (t, false);
}
private:
explicit VSTComSmartPtr (ObjectType* object, bool autoAddRef) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); }
ObjectType* source = nullptr;
};
/** Increments refcount. */
template <class ObjectType>
auto addVSTComSmartPtrOwner (ObjectType* t)
{
return VSTComSmartPtr<ObjectType>::addOwner (t);
}
/** Does not initially increment refcount; assumes t has a positive refcount. */
template <class ObjectType>
auto becomeVSTComSmartPtrOwner (ObjectType* t)
{
return VSTComSmartPtr<ObjectType>::becomeOwner (t);
}
// NOLINTEND(clang-analyzer-cplusplus.NewDelete)
//==============================================================================
/* This class stores a plugin's preferred MIDI mappings.

View file

@ -245,6 +245,22 @@ namespace Presonus
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
#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 // JucePlugin_Enable_ARA
//==============================================================================
#if JUCE_WINDOWS
#include <windows.h>
#endif

View file

@ -35,20 +35,10 @@
#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD)
#include "juce_VST3Headers.h"
#include "juce_VST3Utilities.h"
#include "juce_VST3Common.h"
#include "juce_ARACommon.h"
#if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)
#include <ARA_API/ARAVST3.h>
namespace ARA
{
DEF_CLASS_IID (IMainFactory)
DEF_CLASS_IID (IPlugInEntryPoint)
DEF_CLASS_IID (IPlugInEntryPoint2)
}
#endif
namespace juce
{

View file

@ -33,6 +33,7 @@
*/
#include "juce_VST3Headers.h"
#include "juce_VST3Utilities.h"
#include "juce_VST3Common.h"
namespace juce

View file

@ -0,0 +1,243 @@
/*
==============================================================================
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
{
JUCE_BEGIN_NO_SANITIZE ("vptr")
//==============================================================================
#define JUCE_DECLARE_VST3_COM_REF_METHODS \
Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \
Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; }
#define JUCE_DECLARE_VST3_COM_QUERY_METHODS \
Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \
{ \
jassertfalse; \
*obj = nullptr; \
return Steinberg::kNotImplemented; \
}
inline bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexcept
{
return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0;
}
/* Holds a tresult and a pointer to an object.
Useful for holding intermediate results of calls to queryInterface.
*/
class QueryInterfaceResult
{
public:
QueryInterfaceResult() = default;
QueryInterfaceResult (Steinberg::tresult resultIn, void* ptrIn)
: result (resultIn), ptr (ptrIn) {}
bool isOk() const noexcept { return result == Steinberg::kResultOk; }
Steinberg::tresult extract (void** obj) const
{
*obj = result == Steinberg::kResultOk ? ptr : nullptr;
return result;
}
private:
Steinberg::tresult result = Steinberg::kResultFalse;
void* ptr = nullptr;
};
/* Holds a tresult and a pointer to an object.
Calling InterfaceResultWithDeferredAddRef::extract() will also call addRef
on the pointed-to object. It is expected that users will use
InterfaceResultWithDeferredAddRef to hold intermediate results of a queryInterface
call. When a suitable interface is found, the function can be exited with
`return suitableInterface.extract (obj)`, which will set the obj pointer,
add a reference to the interface, and return the appropriate result code.
*/
class InterfaceResultWithDeferredAddRef
{
public:
InterfaceResultWithDeferredAddRef() = default;
template <typename Ptr>
InterfaceResultWithDeferredAddRef (Steinberg::tresult resultIn, Ptr* ptrIn)
: result (resultIn, ptrIn),
addRefFn (doAddRef<Ptr>) {}
bool isOk() const noexcept { return result.isOk(); }
Steinberg::tresult extract (void** obj) const
{
const auto toReturn = result.extract (obj);
if (result.isOk() && *obj != nullptr && addRefFn != nullptr)
addRefFn (*obj);
return toReturn;
}
private:
template <typename Ptr>
static void doAddRef (void* obj) { static_cast<Ptr*> (obj)->addRef(); }
QueryInterfaceResult result;
void (*addRefFn) (void*) = nullptr;
};
template <typename ClassType> struct UniqueBase {};
template <typename CommonClassType, typename SourceClassType> struct SharedBase {};
template <typename ToTest, typename CommonClassType, typename SourceClassType>
InterfaceResultWithDeferredAddRef testFor (ToTest& toTest,
const Steinberg::TUID targetIID,
SharedBase<CommonClassType, SourceClassType>)
{
if (! doUIDsMatch (targetIID, CommonClassType::iid))
return {};
return { Steinberg::kResultOk, static_cast<CommonClassType*> (static_cast<SourceClassType*> (std::addressof (toTest))) };
}
template <typename ToTest, typename ClassType>
InterfaceResultWithDeferredAddRef testFor (ToTest& toTest,
const Steinberg::TUID targetIID,
UniqueBase<ClassType>)
{
return testFor (toTest, targetIID, SharedBase<ClassType, ClassType>{});
}
template <typename ToTest>
InterfaceResultWithDeferredAddRef testForMultiple (ToTest&, const Steinberg::TUID) { return {}; }
template <typename ToTest, typename Head, typename... Tail>
InterfaceResultWithDeferredAddRef testForMultiple (ToTest& toTest, const Steinberg::TUID targetIID, Head head, Tail... tail)
{
const auto result = testFor (toTest, targetIID, head);
if (result.isOk())
return result;
return testForMultiple (toTest, targetIID, tail...);
}
//==============================================================================
// We have to trust that Steinberg won't double-delete
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDelete)
template <class ObjectType>
class VSTComSmartPtr
{
public:
VSTComSmartPtr() = default;
VSTComSmartPtr (const VSTComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~VSTComSmartPtr() { if (source != nullptr) source->release(); }
explicit operator bool() const noexcept { return operator!= (nullptr); }
ObjectType* get() const noexcept { return source; }
ObjectType& operator*() const noexcept { return *source; }
ObjectType* operator->() const noexcept { return source; }
VSTComSmartPtr& operator= (const VSTComSmartPtr& other)
{
auto p = other;
std::swap (p.source, source);
return *this;
}
VSTComSmartPtr& operator= (std::nullptr_t)
{
return operator= (VSTComSmartPtr{});
}
bool operator== (std::nullptr_t) const noexcept { return source == nullptr; }
bool operator!= (std::nullptr_t) const noexcept { return source != nullptr; }
bool loadFrom (Steinberg::FUnknown* o)
{
*this = nullptr;
return o != nullptr && o->queryInterface (ObjectType::iid, (void**) &source) == Steinberg::kResultOk;
}
bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid)
{
jassert (factory != nullptr);
*this = nullptr;
return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk;
}
/** Increments refcount. */
static auto addOwner (ObjectType* t)
{
return VSTComSmartPtr (t, true);
}
/** Does not initially increment refcount; assumes t has a positive refcount. */
static auto becomeOwner (ObjectType* t)
{
return VSTComSmartPtr (t, false);
}
private:
explicit VSTComSmartPtr (ObjectType* object, bool autoAddRef) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); }
ObjectType* source = nullptr;
};
/** Increments refcount. */
template <class ObjectType>
auto addVSTComSmartPtrOwner (ObjectType* t)
{
return VSTComSmartPtr<ObjectType>::addOwner (t);
}
/** Does not initially increment refcount; assumes t has a positive refcount. */
template <class ObjectType>
auto becomeVSTComSmartPtrOwner (ObjectType* t)
{
return VSTComSmartPtr<ObjectType>::becomeOwner (t);
}
// NOLINTEND(clang-analyzer-cplusplus.NewDelete)
JUCE_END_NO_SANITIZE
} // namespace juce
#endif // DOXYGEN

View file

@ -142,6 +142,7 @@
//==============================================================================
#include "utilities/juce_AAXClientExtensions.h"
#include "utilities/juce_VST2ClientExtensions.h"
#include "utilities/juce_VST3Interface.h"
#include "utilities/juce_VST3ClientExtensions.h"
#include "format_types/juce_ARACommon.h"
#include "utilities/juce_ExtensionsVisitor.h"

View file

@ -1155,8 +1155,8 @@ public:
See also the helper function getXmlFromBinary() for loading settings as XML.
In the case that this AudioProcessor is implementing a VST3 that has declared compatible
plugins via VST3ClientExtensions::getCompatibleClasses(), the state passed to this
function may have been created by one of these compatible plugins.
plugins via JUCE_VST3_COMPATIBLE_CLASSES, the state passed to this function may have
been created by one of these compatible plugins.
If the parameter IDs of the current plugin differ from the IDs of the plugin whose state
was passed to this function, you can use information from the plugin state

View file

@ -36,8 +36,8 @@
#include <juce_core/system/juce_PlatformDefs.h>
#ifndef JUCE_API
#define JUCE_API
#if ! (defined (JUCE_API) || defined (DOXYGEN))
#define JUCE_API
#endif
#if (JucePlugin_Enable_ARA || (JUCE_PLUGINHOST_ARA && (JUCE_PLUGINHOST_VST3 || JUCE_PLUGINHOST_AU))) && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)

View file

@ -35,128 +35,11 @@
namespace juce
{
std::map<uint32_t, String> VST3ClientExtensions::getCompatibleParameterIds (const InterfaceId&) const
std::map<uint32_t, String> VST3ClientExtensions::getCompatibleParameterIds (const VST3Interface::Id&) const
{
return {};
}
VST3ClientExtensions::InterfaceId VST3ClientExtensions::convertJucePluginId (uint32_t manufacturerCode,
uint32_t pluginCode,
InterfaceType interfaceType)
{
const auto word0 = std::invoke ([&]() -> uint32_t
{
switch (interfaceType)
{
case InterfaceType::ara: [[fallthrough]];
case InterfaceType::controller: [[fallthrough]];
case InterfaceType::compatibility: [[fallthrough]];
case InterfaceType::component: return 0xABCDEF01;
case InterfaceType::processor: return 0x0101ABAB;
}
jassertfalse;
return 0;
});
const auto word1 = std::invoke ([&]() -> uint32_t
{
switch (interfaceType)
{
case InterfaceType::ara: return 0xA1B2C3D4;
case InterfaceType::controller: return 0x1234ABCD;
case InterfaceType::compatibility: return 0xC0DEF00D;
case InterfaceType::component: return 0x9182FAEB;
case InterfaceType::processor: return 0xABCDEF01;
}
jassertfalse;
return 0;
});
constexpr auto getByteFromLSB = [] (uint32_t word, int byteIndex)
{
jassert (isPositiveAndNotGreaterThan (byteIndex, 3));
return (std::byte) ((word >> (byteIndex * 8)) & 0xff);
};
#if JUCE_WINDOWS
constexpr auto isWindows = true;
#else
constexpr auto isWindows = false;
#endif
return {
getByteFromLSB (word0, isWindows ? 0 : 3),
getByteFromLSB (word0, isWindows ? 1 : 2),
getByteFromLSB (word0, isWindows ? 2 : 1),
getByteFromLSB (word0, isWindows ? 3 : 0),
getByteFromLSB (word1, isWindows ? 2 : 3),
getByteFromLSB (word1, isWindows ? 3 : 2),
getByteFromLSB (word1, isWindows ? 0 : 1),
getByteFromLSB (word1, isWindows ? 1 : 0),
getByteFromLSB (manufacturerCode, 3),
getByteFromLSB (manufacturerCode, 2),
getByteFromLSB (manufacturerCode, 1),
getByteFromLSB (manufacturerCode, 0),
getByteFromLSB (pluginCode, 3),
getByteFromLSB (pluginCode, 2),
getByteFromLSB (pluginCode, 1),
getByteFromLSB (pluginCode, 0)
};
}
VST3ClientExtensions::InterfaceId VST3ClientExtensions::convertVST2PluginId (uint32_t pluginCode,
const String& pluginName,
InterfaceType interfaceType)
{
VST3ClientExtensions::InterfaceId iid{};
iid[0] = (std::byte) 'V';
iid[1] = (std::byte) 'S';
iid[2] = (std::byte) std::invoke ([&]
{
switch (interfaceType)
{
case InterfaceType::controller: return 'E';
case InterfaceType::component: return 'T';
case InterfaceType::ara: [[fallthrough]];
case InterfaceType::compatibility: [[fallthrough]];
case InterfaceType::processor: break;
}
// A VST2 plugin only has two interfaces
// - component (the audio effect)
// - controller (the editor/UI)
jassertfalse;
return '\0';
});
iid[3] = (std::byte) (pluginCode >> 24);
iid[4] = (std::byte) (pluginCode >> 16);
iid[5] = (std::byte) (pluginCode >> 8);
iid[6] = (std::byte) pluginCode;
for (const auto [index, character] : enumerate (pluginName, (size_t) 7))
{
if (index >= iid.size())
break;
iid[index] = (std::byte) CharacterFunctions::toLowerCase (character);
}
#if JUCE_WINDOWS
std::swap (iid[0], iid[3]);
std::swap (iid[1], iid[2]);
std::swap (iid[4], iid[5]);
std::swap (iid[6], iid[7]);
#endif
return iid;
}
uint32_t VST3ClientExtensions::convertJuceParameterId (const String& parameterId, bool studioOneCompatible)
{
auto hash = (uint32_t) (parameterId.hashCode());
@ -167,27 +50,4 @@ uint32_t VST3ClientExtensions::convertJuceParameterId (const String& parameterId
return hash;
}
VST3ClientExtensions::InterfaceId VST3ClientExtensions::toInterfaceId (const String& interfaceIdString)
{
jassert (interfaceIdString.length() == 32);
jassert (interfaceIdString.containsOnly ("0123456789abcdefABCDEF"));
return { (std::byte) interfaceIdString.substring ( 0, 2).getHexValue32(),
(std::byte) interfaceIdString.substring ( 2, 4).getHexValue32(),
(std::byte) interfaceIdString.substring ( 4, 6).getHexValue32(),
(std::byte) interfaceIdString.substring ( 6, 8).getHexValue32(),
(std::byte) interfaceIdString.substring ( 8, 10).getHexValue32(),
(std::byte) interfaceIdString.substring (10, 12).getHexValue32(),
(std::byte) interfaceIdString.substring (12, 14).getHexValue32(),
(std::byte) interfaceIdString.substring (14, 16).getHexValue32(),
(std::byte) interfaceIdString.substring (16, 18).getHexValue32(),
(std::byte) interfaceIdString.substring (18, 20).getHexValue32(),
(std::byte) interfaceIdString.substring (20, 22).getHexValue32(),
(std::byte) interfaceIdString.substring (22, 24).getHexValue32(),
(std::byte) interfaceIdString.substring (24, 26).getHexValue32(),
(std::byte) interfaceIdString.substring (26, 28).getHexValue32(),
(std::byte) interfaceIdString.substring (28, 30).getHexValue32(),
(std::byte) interfaceIdString.substring (30, 32).getHexValue32() };
}
} // namespace juce

View file

@ -41,7 +41,7 @@ namespace Steinberg
using TUID = char[16];
} // namespace Steinberg
#endif
#endif // DOXYGEN
namespace juce
{
@ -104,36 +104,6 @@ struct VST3ClientExtensions
*/
virtual bool getPluginHasMainInput() const { return true; }
using InterfaceId = std::array<std::byte, 16>;
/** This function should return the UIDs of any compatible VST2 or VST3
plug-ins.
This information will be used to implement the IPluginCompatibility
interface. Hosts can use this interface to determine whether this VST3
is capable of replacing a given VST2.
Each compatible class is a 16-byte array that corresponds to the VST3
interface ID for the class implementing the IComponent interface.
For VST2 or JUCE plugins these IDs can be determined in the following
ways:
- Use convertVST2PluginId() for VST2 plugins or JUCE VST3 plugins with
JUCE_VST3_CAN_REPLACE_VST3 enabled
- Use convertJucePluginId() for any other JUCE VST3 plugins
If JUCE_VST3_CAN_REPLACE_VST2 is enabled the VST3 plugin will have the
same identifier as the VST2 plugin and therefore there will be no need
to implement this function.
If the parameter IDs between compatible versions differ
getCompatibleParameterIds() should also be overridden. However, unlike
getCompatibleParameterIds() this function should remain constant and
always return the same IDs.
@see getCompatibleParameterIds()
*/
virtual std::vector<InterfaceId> getCompatibleClasses() const { return {}; }
/** This function should return a map of VST3 parameter IDs and the JUCE
parameters they map to.
@ -195,74 +165,35 @@ struct VST3ClientExtensions
@endcode
@param compatibleClass A plugin identifier, either for the current
plugin or one listed in getCompatibleClasses().
plugin or one listed in JUCE_VST3_COMPATIBLE_CLASSES.
This parameter allows the implementation to
return a different parameter map for each
compatible class. Use convertJucePluginId() and
convertVST2PluginId() to determine the class IDs
used by JUCE plugins.
When JUCE_VST3_CAN_REPLACE_VST2 is set, the
InterfaceId denoting the VST2 version of the
plugin will match the InterfaceId of the
VST3 that replaces it. In this case, you should
only include the VST2 mappings in the returned
map, assuming there are no collisions between
the VST2 parameter indices and the VST3 ParamIDs.
In the unlikely event of a collision between
the VST2 and VST3 parameter IDs, you should
inspect the state that was most recently
passed to setStateInformation() to determine
whether the host is loading a VST2 state that
requires parameter remapping. If you determine
that no remapping is necessary, you can indicate
this by returning an empty map.
compatible class. Use VST3Interface::jucePluginId()
and VST3Interface::vst2PluginId() to determine
the class IDs used by JUCE plugins. When
JUCE_VST3_CAN_REPLACE_VST2 is set, the ID
denoting the VST2 version of the plugin will
match the ID of the VST3 that replaces it. In
this case, assuming there are no collisions
between the VST2 parameter indices and the VST3
ParamIDs you should only include the VST2
mappings in the returned map. In the unlikely
event of a collision you should inspect the
state that was most recently passed to
setStateInformation() to determine whether the
host is loading a VST2 state that requires
parameter remapping. If you determine that no
remapping is necessary, you can indicate this by
returning an empty map.
@returns A map where each key is a VST3 parameter ID in the compatible
plugin, and the value is the unique JUCE parameter ID in the
current plugin that it should be mapped to.
@see getCompatibleClasses, convertJucePluginId, convertVST2PluginId, convertJuceParameterId
@see JUCE_VST3_COMPATIBLE_CLASSES, VST3Interface::jucePluginId, VST3Interface::vst2PluginId, VST3Interface::hexStringToId
*/
virtual std::map<uint32_t, String> getCompatibleParameterIds (const InterfaceId& compatibleClass) const;
virtual std::map<uint32_t, String> getCompatibleParameterIds (const VST3Interface::Id& compatibleClass) const;
/** An enum indicating the various VST3 interface types.
In most cases users shouldn't need to concern themselves with any
interfaces other than the component, which is used to report the actual
audio effect.
*/
enum class InterfaceType
{
ara,
controller,
compatibility,
component,
processor
};
/** Returns a 16-byte array indicating the VST3 interface ID used for a
given JUCE VST3 plugin.
Internally this is what JUCE will use to assign an ID to each VST3
interface, unless JUCE_VST3_CAN_REPLACE_VST2 is enabled.
@see convertVST2PluginId, getCompatibleClasses, getCompatibleParameterIds
*/
static InterfaceId convertJucePluginId (uint32_t manufacturerCode,
uint32_t pluginCode,
InterfaceType interfaceType = InterfaceType::component);
/** Returns a 16-byte array indicating the VST3 interface ID used for a
given VST2 plugin.
Internally JUCE will use this method to assign an ID for the component
and controller interfaces when JUCE_VST3_CAN_REPLACE_VST2 is enabled.
@see convertJucePluginId, getCompatibleClasses, getCompatibleParameterIds
*/
static InterfaceId convertVST2PluginId (uint32_t pluginCode,
const String& pluginName,
InterfaceType interfaceType = InterfaceType::component);
/** Returns the VST3 compatible parameter ID reported for a given JUCE
parameter.
@ -276,8 +207,67 @@ struct VST3ClientExtensions
static uint32_t convertJuceParameterId (const String& parameterId,
bool studioOneCompatible = true);
/** Converts a 32-character hex notation string to a VST3 interface ID. */
static InterfaceId toInterfaceId (const String& interfaceIdString);
private:
/** Instead of overriding this function you should define the preprocessor
definition JUCE_VST3_COMPATIBLE_CLASSES as described in the docs.
@see JUCE_VST3_COMPATIBLE_CLASSES
*/
virtual std::vector<VST3Interface::Id> getCompatibleClasses() const final
{
return {};
}
};
#if DOXYGEN
/** An optional user defined preprocessor definition for declaring a comma
separated list of VST2 and VST3 plugin identifiers that this VST3 plugin
can replace in a DAW session.
The definition of this preprocessor must be defined at the project
level, normally in your CMake or Projucer project files.
This information will be used to implement the IPluginCompatibility
interface.
If JUCE_VST3_CAN_REPLACE_VST2 is enabled, the VST3 plugin will have the
same identifier as the VST2 plugin and therefore you don't need to
implement this preprocessor definition.
This preprocessor definition can contain code that depends on any class
or function defined as part of the VST3Interface struct but should avoid
any other dependencies!
Each compatible class is a 16-byte array that corresponds to the VST3
interface identifier as reported by a plugins IComponent interface.
For VST2 or JUCE plugins these identifiers can be determined in the
following ways:
- Use VST3Interface::vst2PluginId() for any VST2 plugins or JUCE VST3
plugin with JUCE_VST3_CAN_REPLACE_VST3 enabled
- Use VST3Interface::jucePluginId() for any other JUCE VST3 plugins
Examples
@code
// Defines a VST2 plugin this VST3 can replace
JUCE_VST3_COMPATIBLE_CLASSES=VST3Interface::vst2PluginId ('Plug', "Plugin Name")
// Defines a VST3 plugin this VST3 can replace
JUCE_VST3_COMPATIBLE_CLASSES=VST3Interface::jucePluginId ('Manu', 'Plug')
// Defines both a VST2 and a VST3 plugin this VST3 can replace
JUCE_VST3_COMPATIBLE_CLASSES=VST3Interface::vst2PluginId ('Plug', "Plugin Name"), VST3Interface::jucePluginId ('Manu', 'Plug')
// Defines a non-JUCE VST3 plugin this VST3 can replace
JUCE_VST3_COMPATIBLE_CLASSES=VST3Interface::hexStringToId ("0F1E2D3C4B5A69788796A5B4C3D2E1F0")
@endcode
If the parameter IDs between compatible versions differ
VST3ClientExtensions::getCompatibleParameterIds() should also be overridden.
@see VST3Interface VST3ClientExtensions::getCompatibleParameterIds()
*/
#define JUCE_VST3_COMPATIBLE_CLASSES
#endif // DOXYGEN
} // namespace juce

View file

@ -0,0 +1,248 @@
/*
==============================================================================
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
namespace juce
{
/** Useful functions and classes for defining VST3 Interface Ids.
The classes and functions in this struct are intentionally lightweight,
requiring almost no JUCE or Steinberg VST3 SDK dependencies.
@tags{Audio}
*/
struct VST3Interface
{
/** An enum indicating the various VST3 interface types.
In most cases users shouldn't need to concern themselves with any interfaces
other than the component, which is used to report the actual audio effect.
*/
enum class Type
{
ara,
controller,
compatibility,
component,
processor
};
/** A type storing the byte values for a unique VST3 interface identifier. */
using Id = std::array<std::byte, 16>;
/** Returns a 16-byte array indicating the VST3 interface ID used for a given
VST2 plugin.
Internally JUCE will use this method to assign an ID for the component and
controller interfaces when JUCE_VST3_CAN_REPLACE_VST2 is enabled.
@see jucePluginId, hexStringToId
*/
static Id vst2PluginId (uint32_t pluginCode,
const char* pluginName,
Type interfaceType = Type::component)
{
Id iid{};
iid[0] = (std::byte) 'V';
iid[1] = (std::byte) 'S';
iid[2] = (std::byte) std::invoke ([&]
{
switch (interfaceType)
{
case Type::controller: return 'E';
case Type::component: return 'T';
case Type::ara: [[fallthrough]];
case Type::compatibility: [[fallthrough]];
case Type::processor: break;
}
// A VST2 plugin only has two possible interfaces
// - component (the audio effect)
// - controller (the editor/UI)
jassertfalse;
return '\0';
});
iid[3] = (std::byte) (pluginCode >> 24);
iid[4] = (std::byte) (pluginCode >> 16);
iid[5] = (std::byte) (pluginCode >> 8);
iid[6] = (std::byte) pluginCode;
for (size_t index = 7; index < iid.size() && *pluginName != 0; ++index)
{
iid[index] = (std::byte) std::tolower (*pluginName);
++pluginName;
}
#if JUCE_WINDOWS
std::swap (iid[0], iid[3]);
std::swap (iid[1], iid[2]);
std::swap (iid[4], iid[5]);
std::swap (iid[6], iid[7]);
#endif
return iid;
}
/** Returns a 16-byte array indicating the VST3 interface ID used for a given
JUCE VST3 plugin.
Internally this is what JUCE will use to assign an ID to each VST3 interface,
unless JUCE_VST3_CAN_REPLACE_VST2 is enabled.
@see vst2PluginId, hexStringToId
*/
static inline Id jucePluginId (uint32_t manufacturerCode,
uint32_t pluginCode,
Type interfaceType = Type::component)
{
const auto word0 = std::invoke ([&]() -> uint32_t
{
switch (interfaceType)
{
case Type::ara: [[fallthrough]];
case Type::controller: [[fallthrough]];
case Type::compatibility: [[fallthrough]];
case Type::component: return 0xABCDEF01;
case Type::processor: return 0x0101ABAB;
}
jassertfalse;
return 0;
});
const auto word1 = std::invoke ([&]() -> uint32_t
{
switch (interfaceType)
{
case Type::ara: return 0xA1B2C3D4;
case Type::controller: return 0x1234ABCD;
case Type::compatibility: return 0xC0DEF00D;
case Type::component: return 0x9182FAEB;
case Type::processor: return 0xABCDEF01;
}
jassertfalse;
return 0;
});
constexpr auto getByteFromLSB = [] (uint32_t word, int byteIndex)
{
jassert (0 <= byteIndex && byteIndex <= 3);
return (std::byte) ((word >> (byteIndex * 8)) & 0xff);
};
#if JUCE_WINDOWS
constexpr auto isWindows = true;
#else
constexpr auto isWindows = false;
#endif
return {
getByteFromLSB (word0, isWindows ? 0 : 3),
getByteFromLSB (word0, isWindows ? 1 : 2),
getByteFromLSB (word0, isWindows ? 2 : 1),
getByteFromLSB (word0, isWindows ? 3 : 0),
getByteFromLSB (word1, isWindows ? 2 : 3),
getByteFromLSB (word1, isWindows ? 3 : 2),
getByteFromLSB (word1, isWindows ? 0 : 1),
getByteFromLSB (word1, isWindows ? 1 : 0),
getByteFromLSB (manufacturerCode, 3),
getByteFromLSB (manufacturerCode, 2),
getByteFromLSB (manufacturerCode, 1),
getByteFromLSB (manufacturerCode, 0),
getByteFromLSB (pluginCode, 3),
getByteFromLSB (pluginCode, 2),
getByteFromLSB (pluginCode, 1),
getByteFromLSB (pluginCode, 0)
};
}
/** Converts a 32-character hex notation string to a VST3 interface ID.
@see jucePluginId, vst2PluginId
*/
static inline Id hexStringToId (const char* hex)
{
jassert (std::strlen (hex) == 32);
const auto getByteValue = [](const char* str)
{
const auto getCharacterValue = [](const char c)
{
if (c >= '0' && c <= '9')
return (std::byte) (c - '0');
if (c >= 'A' && c <= 'F')
return (std::byte) (c - 'A' + 10);
if (c >= 'a' && c <= 'f')
return (std::byte) (c - 'a' + 10);
// Invalid hex character!
jassertfalse;
return std::byte{};
};
return getCharacterValue (str[0]) << 4
| getCharacterValue (str[1]);
};
return { getByteValue (hex),
getByteValue (hex + 2),
getByteValue (hex + 4),
getByteValue (hex + 6),
getByteValue (hex + 8),
getByteValue (hex + 10),
getByteValue (hex + 12),
getByteValue (hex + 14),
getByteValue (hex + 16),
getByteValue (hex + 18),
getByteValue (hex + 20),
getByteValue (hex + 22),
getByteValue (hex + 24),
getByteValue (hex + 26),
getByteValue (hex + 28),
getByteValue (hex + 30) };
}
VST3Interface() = delete;
};
} // namespace juce