Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,75 @@
if(SMTG_ENABLE_VST3_HOSTING_EXAMPLES AND TARGET vstgui AND TARGET vstgui_standalone)
##########################################################################################
set(target VST3Inspector)
##########################################################################################
set(${target}_sources
"${SDK_ROOT}/pluginterfaces/base/coreiids.cpp"
"${SDK_ROOT}/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp"
"${SDK_ROOT}/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp"
"source/app.cpp"
"source/window.cpp"
"source/window.h"
)
set(${target}_resources
"resource/window.uidesc"
)
##########################################################################################
if(SMTG_MAC)
set(${target}_sources
${${target}_sources}
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm"
)
set_source_files_properties(
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm" PROPERTIES
COMPILE_FLAGS "-fobjc-arc"
)
elseif(SMTG_WIN)
set(${target}_sources
${${target}_sources}
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_win32.cpp"
)
elseif(SMTG_LINUX)
set(${target}_sources
${${target}_sources}
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_linux.cpp"
)
endif(SMTG_MAC)
##########################################################################################
# 'vstgui_add_executable' links both targets 'vstgui' and 'vstgui_standalone' to 'VST3Inspector'
vstgui_add_executable(${target} "${${target}_sources}")
vstgui_set_target_bundle_id(${target} "com.steinberg.vstsdk.vst3inspector")
target_link_libraries(${target}
PRIVATE
sdk_hosting
)
if(NOT SMTG_MAC)
set_target_properties(${target}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<$<CONFIG:Debug>:bin/Debug>$<$<CONFIG:Release>:bin/Release>$<$<CONFIG:ReleaseLTO>:bin/ReleaseLTO>/VST3Inspector
)
endif(NOT SMTG_MAC)
vstgui_add_resources(${target} "${${target}_resources}")
vstgui_set_target_infoplist(${target} "resource/Info.plist")
target_include_directories(${target}
PRIVATE
"${SMTG_VSTGUI_SOURCE_DIR}/"
"${SDK_ROOT}"
)
if(SDK_IDE_HOSTING_EXAMPLES_FOLDER)
set_target_properties(${target}
PROPERTIES
${SDK_IDE_HOSTING_EXAMPLES_FOLDER}
)
endif(SDK_IDE_HOSTING_EXAMPLES_FOLDER)
endif()
@@ -0,0 +1,18 @@
# VST 3 Inspector Application
## Introduction
As Cross-platform source code: Simple cross-platform (Win/macOS/Linux) host application, built with [VSTGUI](https://steinbergmedia.github.io/vst3_dev_portal/pages/What+is+the+VST+3+SDK/VST3Inspector.html), which scans the **VST 3** Folder, collects information from the factory about each **VST 3** plug-in and display it in its UI.
> See also: [Online Documentation](https://steinbergmedia.github.io/vst3_dev_portal/pages/What+is+the+VST+3+SDK/AudioHost.html#audiohost-application).
## Getting Started
This application is part of the VST 3 SDK package. It is created with the VST 3 SDK root project.
> See the top-level README of the VST 3 SDK: https://github.com/steinbergmedia/vst3sdk.git
## Getting Help
* Read through the SDK documentation on the **[VST 3 Developer Portal](https://steinbergmedia.github.io/vst3_dev_portal/pages/index.html)**
* Ask some real people in the official **[VST 3 Developer Forum](https://forums.steinberg.net/c/developer/103)**
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE}</string>
<key>CFBundleIdentifier</key>
<string>com.steinberg.vstsdk.vst3inspector</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>VST3Inspector</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2021 Steinberg Media Technologies. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : VST3Inspector
// Filename : public.sdk/samples/vst-hosting/inspectorapp/app.cpp
// Created by : Steinberg, 01/2021
// Description : VST3 Inspector Application
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "vstgui/standalone/include/helpers/appdelegate.h"
#include "vstgui/standalone/include/helpers/windowlistener.h"
#include "vstgui/standalone/include/iapplication.h"
#include "window.h"
//------------------------------------------------------------------------
namespace VST3Inspector {
using namespace VSTGUI;
using namespace VSTGUI::Standalone;
//------------------------------------------------------------------------
struct App : Application::DelegateAdapter, WindowListenerAdapter
{
App () : Application::DelegateAdapter ({"VST3Inspector", "1.0.0", VSTGUI_STANDALONE_APP_URI}) {}
void finishLaunching () override
{
if (auto window = makeWindow ())
{
window->registerWindowListener (this);
window->show ();
}
}
void onClosed (const IWindow& window) override { IApplication::instance ().quit (); }
};
//------------------------------------------------------------------------
static Application::Init gAppDelegate (std::make_unique<App> ());
//------------------------------------------------------------------------
} // VST3Inspector
@@ -0,0 +1,500 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : VST3Inspector
// Filename : public.sdk/samples/vst-hosting/inspectorapp/window.cpp
// Created by : Steinberg, 01/2021
// Description : VST3 Inspector Application
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "window.h"
#include "vstgui/lib/cbitmap.h"
#include "vstgui/lib/platform/platformfactory.h"
#include "vstgui/standalone/include/helpers/menubuilder.h"
#include "vstgui/standalone/include/helpers/uidesc/customization.h"
#include "vstgui/standalone/include/helpers/value.h"
#include "vstgui/standalone/include/helpers/valuelistener.h"
#include "vstgui/standalone/include/helpers/windowcontroller.h"
#include "vstgui/standalone/include/ialertbox.h"
#include "vstgui/standalone/include/iapplication.h"
#include "vstgui/standalone/include/iasync.h"
#include "vstgui/standalone/include/iuidescwindow.h"
#include "vstgui/uidescription/cstream.h"
#include "vstgui/uidescription/delegationcontroller.h"
#include "vstgui/uidescription/iuidescription.h"
#include "vstgui/uidescription/uiattributes.h"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/moduleinfo/moduleinfo.h"
#include "public.sdk/source/vst/moduleinfo/moduleinfocreator.h"
#include "public.sdk/source/vst/moduleinfo/moduleinfoparser.h"
#include <cassert>
#include <fstream>
#include <unordered_map>
//------------------------------------------------------------------------
namespace VST3Inspector {
namespace {
//------------------------------------------------------------------------
std::string loadFile (const std::string& path)
{
std::ifstream file (path, std::ios_base::in | std::ios_base::binary);
if (!file.is_open ())
return {};
auto size = file.seekg (0, std::ios_base::end).tellg ();
file.seekg (0, std::ios_base::beg);
std::string data;
data.resize (size);
file.read (data.data (), data.size ());
if (file.bad ())
return {};
return data;
}
//------------------------------------------------------------------------
} // anonymous
using namespace VSTGUI;
using namespace VSTGUI::Standalone;
using namespace Steinberg;
//------------------------------------------------------------------------
class DummyFactory : public IPluginFactory
{
public:
static DummyFactory* instance ()
{
static DummyFactory gInstance;
return &gInstance;
}
private:
tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) override
{
*info = {};
return kResultTrue;
}
int32 PLUGIN_API countClasses () override { return 0; }
tresult PLUGIN_API getClassInfo (int32 /*index*/, PClassInfo* /*info*/) override
{
return kResultFalse;
}
tresult PLUGIN_API createInstance (FIDString /*cid*/, FIDString /*_iid*/,
void** /*obj*/) override
{
return kNoInterface;
}
tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override
{
QUERY_INTERFACE (_iid, obj, IPluginFactory::iid, IPluginFactory)
QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown)
return kNoInterface;
}
uint32 PLUGIN_API addRef () override { return 1000; }
uint32 PLUGIN_API release () override { return 1000; }
};
//------------------------------------------------------------------------
class InvalidModule : public VST3::Hosting::Module
{
public:
InvalidModule () { factory = VST3::Hosting::PluginFactory (DummyFactory::instance ()); }
private:
bool load (const std::string& /*path*/, std::string& /*errorDescription*/) override
{
return true;
};
};
//------------------------------------------------------------------------
struct ValueMap
{
ValuePtr addValue (const ValuePtr& value)
{
auto index = valueList.size ();
valueList.push_back (value);
valueMap.insert ({value->getID ().getString (), index});
return value;
}
ValuePtr get (const std::string& ID)
{
auto it = valueMap.find (ID);
if (it != valueMap.end ())
return valueList.at (it->second);
return nullptr;
}
template <typename T>
auto get (const std::string& ID)
{
if (auto value = get (ID))
{
return std::dynamic_pointer_cast<T> (value);
}
return std::shared_ptr<T> (nullptr);
}
const UIDesc::IModelBinding::ValueList& getValues () const { return valueList; }
private:
UIDesc::IModelBinding::ValueList valueList;
std::unordered_map<std::string, size_t> valueMap;
};
//------------------------------------------------------------------------
static constexpr auto ModulePathListID = "ModulePathList";
static constexpr auto ModulePathID = "Module::Path";
static constexpr auto FactoryVendorID = "Factory::Vendor";
static constexpr auto FactoryUrlID = "Factory::URL";
static constexpr auto FactoryEMailID = "Factory::EMail";
static constexpr auto FactoryFlagsID = "Factory::Flags";
static constexpr auto ClassInfoListID = "ClassInfoList";
static constexpr auto ClassInfoClassID = "ClassInfo::ClassID";
static constexpr auto ClassInfoCategoryID = "ClassInfo::Category";
static constexpr auto ClassInfoNameID = "ClassInfo::Name";
static constexpr auto ClassInfoVendorID = "ClassInfo::Vendor";
static constexpr auto ClassInfoVersionID = "ClassInfo::Version";
static constexpr auto ClassInfoSDKVersionID = "ClassInfo::SDKVersion";
static constexpr auto ClassInfoSubCategoriesID = "ClassInfo::SubCategories";
static constexpr auto ClassInfoCardinalityID = "ClassInfo::Cardinality";
static constexpr auto ClassInfoClassFlagsID = "ClassInfo::ClassFlags";
using namespace VST3::Hosting;
//------------------------------------------------------------------------
struct SnapshotController : DelegationController, NonAtomicReferenceCounted
{
SnapshotController (IController* parent) : DelegationController (parent) {}
~SnapshotController () noexcept override {}
CView* createView (const UIAttributes& attributes, const IUIDescription* description) override
{
if (auto viewName = attributes.getAttributeValue (IUIDescription::kCustomViewName))
{
if (*viewName == "ImageView")
{
imageView = VSTGUI::shared (new CView (CRect ()));
return imageView;
}
}
return controller->createView (attributes, description);
}
void setSnapshot (const std::string& moduleBasePath,
const ModuleInfo::SnapshotList* newSnapshot)
{
snapshot = newSnapshot ? *newSnapshot : ModuleInfo::SnapshotList ();
if (snapshot.empty ())
{
imageView->setBackground (nullptr);
imageView->setViewSize ({});
}
else
{
SharedPointer<CBitmap> bitmap;
const auto& factory = getPlatformFactory ();
for (const auto& imageDesc : snapshot)
{
if (auto pb = factory.createBitmapFromPath (
(moduleBasePath + "/" + imageDesc.path).data ()))
{
pb->setScaleFactor (imageDesc.scaleFactor);
if (bitmap)
bitmap->addBitmap (pb);
else
bitmap = VSTGUI::owned (new CBitmap (pb));
}
}
imageView->setBackground (bitmap);
if (bitmap)
{
auto viewSize = imageView->getViewSize ();
viewSize.setSize (bitmap->getSize ());
imageView->setViewSize (viewSize);
}
}
}
SharedPointer<CView> imageView;
ModuleInfo::SnapshotList snapshot;
};
//------------------------------------------------------------------------
struct WindowController : WindowControllerAdapter,
UIDesc::CustomizationAdapter,
UIDesc::IModelBinding,
MenuBuilderAdapter,
ValueListenerAdapter
{
WindowController ()
{
values.addValue (Value::makeStringListValue (ModulePathListID, {"", ""}))
->registerListener (this);
// Module Values
values.addValue (Value::makeStringValue (ModulePathID, ""));
// Factory Values
values.addValue (Value::makeStringValue (FactoryVendorID, ""));
values.addValue (Value::makeStringValue (FactoryUrlID, ""));
values.addValue (Value::makeStringValue (FactoryEMailID, ""));
values.addValue (Value::makeStringValue (FactoryFlagsID, ""));
values.addValue (Value::makeStringListValue (ClassInfoListID, {"", ""}))
->registerListener (this);
// Class Info Values
values.addValue (Value::makeStringValue (ClassInfoClassID, ""));
values.addValue (Value::makeStringValue (ClassInfoCategoryID, ""));
values.addValue (Value::makeStringValue (ClassInfoNameID, ""));
values.addValue (Value::makeStringValue (ClassInfoVendorID, ""));
values.addValue (Value::makeStringValue (ClassInfoVersionID, ""));
values.addValue (Value::makeStringValue (ClassInfoSDKVersionID, ""));
values.addValue (Value::makeStringValue (ClassInfoSubCategoriesID, ""));
values.addValue (Value::makeStringValue (ClassInfoCardinalityID, ""));
values.addValue (Value::makeStringValue (ClassInfoClassFlagsID, ""));
Async::schedule (Async::backgroundQueue (), [this] () {
auto modulePaths = Module::getModulePaths ();
Async::schedule (Async::mainQueue (),
[this, paths = std::move (modulePaths)] () mutable {
setModulePaths (std::move (paths));
});
});
}
static Optional<std::string> lastPathComponent (const std::string& path)
{
#if SMTG_WINDOWS
static constexpr auto pathSeparator = '\\';
#else
static constexpr auto pathSeparator = '/';
#endif
size_t sepPos = path.find_last_of (pathSeparator);
if (sepPos == std::string::npos)
return {};
return {path.substr (sepPos + 1)};
}
void setModulePaths (Module::PathList&& pathList)
{
modulePathList = std::move (pathList);
std::sort (modulePathList.begin (), modulePathList.end (),
[] (const auto& lhs, const auto& rhs) {
auto lhsName = lastPathComponent (lhs);
auto rhsName = lastPathComponent (rhs);
if (lhsName && rhsName)
{
std::for_each (lhsName->begin (), lhsName->end (),
[] (auto& c) { c = std::tolower (c); });
std::for_each (rhsName->begin (), rhsName->end (),
[] (auto& c) { c = std::tolower (c); });
return *lhsName < *rhsName;
}
return lhs < rhs;
});
moduleInfos.clear ();
moduleInfos.resize (modulePathList.size ());
IStringListValue::StringList nameList;
for (const auto& path : modulePathList)
{
if (auto name = lastPathComponent (path))
nameList.emplace_back (*name);
else
nameList.emplace_back (path);
}
if (auto value = values.get<IStringListValue> (ModulePathListID))
value->updateStringList (nameList);
if (auto value = values.get (ModulePathListID))
Value::performSingleEdit (*value, 0.);
}
void onEndEdit (IValue& value) override
{
if (value.getID () == ModulePathListID)
{
onModuleSelection (static_cast<uint32_t> (
value.getConverter ().normalizedToPlain (value.getValue ())));
}
if (value.getID () == ClassInfoListID)
{
onClassInfoSelection (static_cast<uint32_t> (
value.getConverter ().normalizedToPlain (value.getValue ())));
}
}
void setStringValue (const std::string& valueID, const std::string& string)
{
auto value = values.get (valueID);
if (value)
Value::performStringValueEdit (*value, UTF8String (string));
}
std::string createFlagsString (int32_t flags) const
{
std::string flagsString = "0b";
for (int32_t i = 31; i >= 0; --i)
{
if (flags & (1 << i))
flagsString += "1";
else
flagsString += "0";
}
return flagsString;
}
std::string createSubCategoryString (const ModuleInfo::ClassInfo& info) const
{
std::string result;
for (const auto& cat : info.subCategories)
{
if (result.empty () == false)
result += "|";
result += cat;
}
return result;
}
void onClassInfoSelection (uint32_t index)
{
if (index >= moduleInfos[currentSelectedModule].classes.size ())
{
setStringValue (ClassInfoClassID, "");
setStringValue (ClassInfoCategoryID, "");
setStringValue (ClassInfoNameID, "");
setStringValue (ClassInfoVendorID, "");
setStringValue (ClassInfoVersionID, "");
setStringValue (ClassInfoSDKVersionID, "");
setStringValue (ClassInfoSubCategoriesID, "");
setStringValue (ClassInfoCardinalityID, "");
setStringValue (ClassInfoClassFlagsID, "");
if (snapShotController)
snapShotController->setSnapshot ("", nullptr);
return;
}
const auto& cl = moduleInfos[currentSelectedModule].classes[index];
setStringValue (ClassInfoClassID, cl.cid);
setStringValue (ClassInfoCategoryID, cl.category);
setStringValue (ClassInfoNameID, cl.name);
setStringValue (ClassInfoVendorID, cl.vendor);
setStringValue (ClassInfoVersionID, cl.version);
setStringValue (ClassInfoSDKVersionID, cl.sdkVersion);
setStringValue (ClassInfoSubCategoriesID, createSubCategoryString (cl));
if (cl.cardinality == Steinberg::PClassInfo::ClassCardinality::kManyInstances)
setStringValue (ClassInfoCardinalityID, "");
else
setStringValue (ClassInfoCardinalityID, std::to_string (cl.cardinality));
setStringValue (ClassInfoClassFlagsID, createFlagsString (cl.flags));
if (snapShotController)
snapShotController->setSnapshot (modulePathList[currentSelectedModule], &cl.snapshots);
}
void updateModuleInfo (uint32_t index)
{
const auto& modulePath = modulePathList[index];
if (auto moduleInfoPath = Module::getModuleInfoPath (modulePath))
{
auto moduleJson = loadFile (*moduleInfoPath);
if (auto moduleInfo = ModuleInfoLib::parseJson (moduleJson, nullptr))
{
moduleInfos[index] = *moduleInfo;
}
}
else
{
std::string errorDesc;
if (auto module = Module::create (modulePath, errorDesc))
{
moduleInfos[index] = ModuleInfoLib::createModuleInfo (*module.get (), true);
}
}
}
void onModuleSelection (uint32_t index)
{
if (index >= modulePathList.size ())
return;
currentSelectedModule = index;
if (moduleInfos[index].name.empty ())
{
updateModuleInfo (index);
}
const auto& moduleInfo = moduleInfos[index];
const auto& modulePath = modulePathList[index];
setStringValue (ModulePathID, modulePath);
setStringValue (FactoryVendorID, moduleInfo.factoryInfo.vendor);
setStringValue (FactoryUrlID, moduleInfo.factoryInfo.url);
setStringValue (FactoryEMailID, moduleInfo.factoryInfo.email);
setStringValue (FactoryFlagsID, createFlagsString (moduleInfo.factoryInfo.flags));
IStringListValue::StringList classInfoList;
for (const auto& classInfo : moduleInfo.classes)
{
classInfoList.emplace_back (classInfo.name);
}
if (auto value = values.get<IStringListValue> (ClassInfoListID))
{
value->updateStringList (classInfoList);
}
// select first class info
if (auto value = values.get (ClassInfoListID))
Value::performSingleEdit (*value, 0.);
}
const ValueList& getValues () const override { return values.getValues (); }
IController* createController (const UTF8StringView& name, IController* parent,
const IUIDescription* /*uiDesc*/) override
{
if (name == "SnapshotViewController")
{
snapShotController = VSTGUI::shared (new SnapshotController (parent));
return snapShotController;
}
return nullptr;
}
bool showCommandInMenu (const Interface& /*context*/, const Command& /*cmd*/) const override
{
return false;
}
private:
ValueMap values;
ValuePtr modulePathListValue;
SharedPointer<SnapshotController> snapShotController;
using ModuleInfoList = std::vector<ModuleInfo>;
Module::PathList modulePathList;
ModuleInfoList moduleInfos;
uint32_t currentSelectedModule {0};
};
//------------------------------------------------------------------------
WindowPtr makeWindow ()
{
auto controller = std::make_shared<WindowController> ();
UIDesc::Config config;
config.uiDescFileName = "window.uidesc";
config.viewName = "MainWindow";
config.windowConfig.autoSaveFrameName = "MainWindow";
config.windowConfig.type = WindowType::Document;
config.windowConfig.style = WindowStyle ().border ().close ().centered ().size ();
config.windowConfig.title = "VST3 Inspector";
config.customization = controller;
config.modelBinding = controller;
return UIDesc::makeWindow (config);
}
//------------------------------------------------------------------------
} // VST3Inspector
@@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : VST3Inspector
// Filename : public.sdk/samples/vst-hosting/inspectorapp/window.h
// Created by : Steinberg, 01/2021
// Description : VST3 Inspector Application
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "vstgui/standalone/include/iwindow.h"
//------------------------------------------------------------------------
namespace VST3Inspector {
//------------------------------------------------------------------------
VSTGUI::Standalone::WindowPtr makeWindow ();
//------------------------------------------------------------------------
} // VST3Inspector