From 1ad5ad7c543e54843da32fafd99f723c87e5cc97 Mon Sep 17 00:00:00 2001 From: Dmitry Makarenko Date: Fri, 3 Apr 2026 18:24:21 +0300 Subject: [PATCH] allow loading multi-component vst3 effects --- .../internal/registeraudiopluginsscenario.cpp | 16 +++++--- .../vst/internal/vstplugininstance.cpp | 10 +++-- .../vst/internal/vstpluginmetareader.cpp | 41 +++++++++++++++---- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/framework/audioplugins/internal/registeraudiopluginsscenario.cpp b/src/framework/audioplugins/internal/registeraudiopluginsscenario.cpp index 1a0d8a659d01a..29ee35beff84e 100644 --- a/src/framework/audioplugins/internal/registeraudiopluginsscenario.cpp +++ b/src/framework/audioplugins/internal/registeraudiopluginsscenario.cpp @@ -56,23 +56,27 @@ PluginScanResult RegisterAudioPluginsScenario::scanPlugins() const PluginScanResult result; - std::map registered; + //! NOTE: A single plugin path can map to multiple resource IDs (multi-component bundles), + //! so we track all IDs per path. + std::map > pathToIds; for (const auto& info : knownPluginsRegister()->pluginInfoList()) { - registered[info.path] = info.meta.id; + pathToIds[info.path].push_back(info.meta.id); } for (const auto& scanner : scannerRegister()->scanners()) { for (const auto& path : scanner->scanPlugins()) { - if (auto it = registered.find(path); it != registered.end()) { - registered.erase(it); + if (auto it = pathToIds.find(path); it != pathToIds.end()) { + pathToIds.erase(it); } else { result.newPluginPaths.push_back(path); } } } - for (const auto& [path, id] : registered) { - result.missingPluginIds.push_back(id); + for (const auto& [path, ids] : pathToIds) { + for (const auto& id : ids) { + result.missingPluginIds.push_back(id); + } } return result; diff --git a/src/framework/vst/internal/vstplugininstance.cpp b/src/framework/vst/internal/vstplugininstance.cpp index e23c42ea70923..4ba6937c70585 100644 --- a/src/framework/vst/internal/vstplugininstance.cpp +++ b/src/framework/vst/internal/vstplugininstance.cpp @@ -109,14 +109,18 @@ void VstPluginInstance::load() const auto& factory = m_module->getFactory(); + //! NOTE: For multi-component bundles, the resource ID is the component name or UID. + //! Match by name or UID against the factory's audio effect components. for (const ClassInfo& classInfo : factory.classInfos()) { if (classInfo.category() != kVstAudioEffectClass) { continue; } - m_pluginProvider = std::make_unique(factory, classInfo); - m_classInfo = classInfo; - break; + if (classInfo.name() == m_resourceId || classInfo.ID().toString() == m_resourceId) { + m_pluginProvider = std::make_unique(factory, classInfo); + m_classInfo = classInfo; + break; + } } if (!m_pluginProvider) { diff --git a/src/framework/vst/internal/vstpluginmetareader.cpp b/src/framework/vst/internal/vstpluginmetareader.cpp index 7a14a50b41cdd..d188b9405fcbd 100644 --- a/src/framework/vst/internal/vstpluginmetareader.cpp +++ b/src/framework/vst/internal/vstpluginmetareader.cpp @@ -49,26 +49,49 @@ RetVal VstPluginMetaReader::readMeta(const io::path_t& pl } const auto& factory = module->getFactory(); - AudioResourceMetaList result; + std::vector audioEffects; for (const ClassInfo& classInfo : factory.classInfos()) { - if (classInfo.category() != kVstAudioEffectClass) { - continue; + if (classInfo.category() == kVstAudioEffectClass) { + audioEffects.push_back(classInfo); + } + } + + if (audioEffects.empty()) { + return make_ret(Err::NoAudioEffect); + } + + std::string baseName = io::completeBasename(pluginPath).toStdString(); + bool multiComponent = audioEffects.size() > 1; + + AudioResourceMetaList result; + std::set usedIds; + + for (size_t i = 0; i < audioEffects.size(); ++i) { + const ClassInfo& classInfo = audioEffects[i]; + + std::string id; + if (multiComponent) { + id = classInfo.name(); + if (id.empty()) { + id = baseName + " " + std::to_string(i + 1); + } + if (muse::contains(usedIds, id)) { + id = classInfo.ID().toString(); + } + usedIds.insert(id); + } else { + id = baseName; } muse::audio::AudioResourceMeta meta; - meta.id = io::completeBasename(pluginPath).toStdString(); + meta.id = std::move(id); meta.type = muse::audio::AudioResourceType::VstPlugin; meta.attributes.emplace(muse::audio::CATEGORIES_ATTRIBUTE, String::fromStdString(classInfo.subCategoriesString())); meta.vendor = classInfo.vendor(); meta.hasNativeEditorSupport = true; result.emplace_back(std::move(meta)); - break; - } - - if (result.empty()) { - return make_ret(Err::NoAudioEffect); } return RetVal::make_ok(result);