From: <sv...@op...> - 2024-08-04 21:58:18
|
Author: sagamusix Date: Sun Aug 4 23:58:03 2024 New Revision: 21326 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=21326 Log: [New] Can now load shell VST plugins (https://bugs.openmpt.org/view.php?id=1551). [Mod] OpenMPT: Version is now 1.32.00.22 Modified: trunk/OpenMPT/common/versionNumber.h trunk/OpenMPT/mptrack/ModDocTemplate.cpp trunk/OpenMPT/mptrack/Mptrack.cpp trunk/OpenMPT/mptrack/SelectPluginDialog.cpp trunk/OpenMPT/mptrack/SelectPluginDialog.h trunk/OpenMPT/mptrack/TrackerSettings.cpp trunk/OpenMPT/mptrack/TrackerSettings.h trunk/OpenMPT/mptrack/Vstplug.cpp trunk/OpenMPT/mptrack/Vstplug.h trunk/OpenMPT/mptrack/mod2midi.cpp trunk/OpenMPT/pluginBridge/Bridge.cpp trunk/OpenMPT/pluginBridge/Bridge.h trunk/OpenMPT/pluginBridge/BridgeOpCodes.h trunk/OpenMPT/soundlib/plugins/PluginManager.cpp trunk/OpenMPT/soundlib/plugins/PluginManager.h trunk/OpenMPT/soundlib/plugins/PluginStructs.h Modified: trunk/OpenMPT/common/versionNumber.h ============================================================================== --- trunk/OpenMPT/common/versionNumber.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/common/versionNumber.h Sun Aug 4 23:58:03 2024 (r21326) @@ -18,6 +18,6 @@ #define VER_MAJORMAJOR 1 #define VER_MAJOR 32 #define VER_MINOR 00 -#define VER_MINORMINOR 21 +#define VER_MINORMINOR 22 OPENMPT_NAMESPACE_END Modified: trunk/OpenMPT/mptrack/ModDocTemplate.cpp ============================================================================== --- trunk/OpenMPT/mptrack/ModDocTemplate.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/ModDocTemplate.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -134,14 +134,15 @@ { if(auto plugManager = theApp.GetPluginManager(); plugManager != nullptr) { - if(auto plugLib = plugManager->AddPlugin(filename, TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes); plugLib != nullptr) + bool isPlugin = false; + for(VSTPluginLib *plugLib : plugManager->AddPlugin(filename, TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes)) { + isPlugin = true; if(!CSelectPluginDlg::VerifyPlugin(plugLib, nullptr)) - { plugManager->RemovePlugin(plugLib); - } - return nullptr; } + if(isPlugin) + return nullptr; } } Modified: trunk/OpenMPT/mptrack/Mptrack.cpp ============================================================================== --- trunk/OpenMPT/mptrack/Mptrack.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/Mptrack.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -2287,9 +2287,15 @@ /////////////////////////////////////////////////////////////////////////////////// // -// DirectX Plugins +// Restore / save plugin list // +static const auto PLUGFORMAT_FILENAME = MPT_UFORMAT("Plugin{}"); +static const auto PLUGFORMAT_TAGS = MPT_UFORMAT("Plugin{}.Tags"); +static const auto PLUGFORMAT_TAGS_BUILTIN = MPT_UFORMAT("Plugin{}{}.Tags"); +static const auto PLUGFORMAT_LIBNAME = MPT_UFORMAT("Plugin{}.LibraryName"); +static const auto PLUGFORMAT_SHELLID = MPT_UFORMAT("Plugin{}.ShellPluginID"); + void CTrackApp::InitializeDXPlugins() { m_pPluginManager = new CVstPluginManager; @@ -2298,17 +2304,17 @@ bool maskCrashes = TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes; std::vector<VSTPluginLib *> nonFoundPlugs; - const mpt::PathString failedPlugin = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), U_("FailedPlugin"), P_("")); + const mpt::PathString failedPlugin = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), U_("FailedPlugin")); + ConfirmAnswer skipFailed = cnfCancel; CDialog pluginScanDlg; CWnd *textWnd = nullptr; DWORD64 scanStart = Util::GetTickCount64(); // Read tags for built-in plugins - for(auto plug : *m_pPluginManager) + for(auto &plug : *m_pPluginManager) { - mpt::ustring key = MPT_UFORMAT("Plugin{}{}.Tags")(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2)); - plug->tags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), key, mpt::ustring()); + plug->tags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), PLUGFORMAT_TAGS_BUILTIN(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2))); } // Restructured plugin cache @@ -2319,58 +2325,68 @@ } m_pPluginManager->reserve(numPlugins); - auto plugIDFormat = MPT_UFORMAT("Plugin{}"); auto scanFormat = MPT_CFORMAT("Scanning Plugin {} / {}...\n{}"); - auto tagFormat = MPT_UFORMAT("Plugin{}.Tags"); for(size_t plug = 0; plug < numPlugins; plug++) { - mpt::PathString plugPath = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), plugIDFormat(plug), mpt::PathString()); - if(!plugPath.empty()) - { - plugPath = PathInstallRelativeToAbsolute(plugPath); + const mpt::PathString plugPath = PathInstallRelativeToAbsolute(GetSettings().Read<mpt::PathString>(U_("VST Plugins"), PLUGFORMAT_FILENAME(plug))); + if(plugPath.empty()) + continue; - if(!pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 2000) + if(!pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 2000) + { + // If this is taking too long, show the user what they're waiting for. + pluginScanDlg.Create(IDD_SCANPLUGINS, gpSplashScreen); + pluginScanDlg.ShowWindow(SW_SHOW); + pluginScanDlg.CenterWindow(gpSplashScreen); + textWnd = pluginScanDlg.GetDlgItem(IDC_SCANTEXT); + } else if(pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 30) + { + textWnd->SetWindowText(scanFormat(plug + 1, numPlugins + 1, plugPath)); + MSG msg; + while(::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - // If this is taking too long, show the user what they're waiting for. - pluginScanDlg.Create(IDD_SCANPLUGINS, gpSplashScreen); - pluginScanDlg.ShowWindow(SW_SHOW); - pluginScanDlg.CenterWindow(gpSplashScreen); - textWnd = pluginScanDlg.GetDlgItem(IDC_SCANTEXT); - } else if(pluginScanDlg.m_hWnd && Util::GetTickCount64() >= scanStart + 30) - { - textWnd->SetWindowText(scanFormat(plug + 1, numPlugins + 1, plugPath)); - MSG msg; - while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - scanStart = Util::GetTickCount64(); + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); } + scanStart = Util::GetTickCount64(); + } - if(plugPath == failedPlugin) + if(plugPath == failedPlugin) + { + GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin")); + if(skipFailed == cnfCancel) { - GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin")); - const CString text = _T("The following plugin has previously crashed OpenMPT during initialisation:\n\n") + failedPlugin.ToCString() + _T("\n\nDo you still want to load it?"); - if(Reporting::Confirm(text, false, true, &pluginScanDlg) == cnfNo) - { - continue; - } + const CString text = MPT_CFORMAT("The following plugin has previously crashed OpenMPT during initialisation:\n\n{}\n\nDo you still want to load it?") + (failedPlugin.ToCString()); + skipFailed = Reporting::Confirm(text, false, true, &pluginScanDlg); } - mpt::ustring plugTags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), tagFormat(plug), mpt::ustring()); + if(skipFailed == cnfNo) + continue; + } - bool plugFound = true; - VSTPluginLib *lib = m_pPluginManager->AddPlugin(plugPath, maskCrashes, plugTags, true, &plugFound); - if(!plugFound && lib != nullptr) - { - nonFoundPlugs.push_back(lib); - } - if(lib != nullptr && lib->libraryName == P_("MIDI Input Output") && lib->pluginId1 == PLUGMAGIC('V','s','t','P') && lib->pluginId2 == PLUGMAGIC('M','M','I','D')) + const uint32 shellPluginID = mpt::parse_hex<uint32>(GetSettings().Read<mpt::ustring>(U_("VST Plugins"), PLUGFORMAT_SHELLID(plug))); + bool plugFound = true; + for(VSTPluginLib *lib : m_pPluginManager->AddPlugin(plugPath, maskCrashes, true, &plugFound, shellPluginID)) + { + if(lib->libraryName == P_("MIDI Input Output") && lib->pluginId1 == PLUGMAGIC('V', 's', 't', 'P') && lib->pluginId2 == PLUGMAGIC('M', 'M', 'I', 'D') && !lib->shellPluginID) { // This appears to be an old version of our MIDI I/O plugin, which is now built right into the main executable. m_pPluginManager->RemovePlugin(lib); + continue; + } + if(!plugFound) + nonFoundPlugs.push_back(lib); + if(shellPluginID && lib->shellPluginID != shellPluginID) + continue; + + lib->tags = GetSettings().Read<mpt::ustring>(U_("VST Plugins"), PLUGFORMAT_TAGS(plug)); + if(shellPluginID != 0) + { + if(mpt::PathString libName = GetSettings().Read<mpt::PathString>(U_("VST Plugins"), PLUGFORMAT_LIBNAME(plug)); !libName.empty()) + lib->libraryName = std::move(libName); } + } } GetPluginCache().Flush(); @@ -2392,24 +2408,34 @@ #ifndef NO_PLUGINS size_t plugIndex = 0; - for(auto plug : *m_pPluginManager) + for(auto &plug : *m_pPluginManager) { if(!plug->isBuiltIn) { - mpt::PathString plugPath = plug->dllPath; + mpt::PathString plugPath; if(theApp.IsPortableMode()) + plugPath = PathAbsoluteToInstallRelative(plug->dllPath); + else + plugPath = plug->dllPath; + + const auto libName = PLUGFORMAT_LIBNAME(plugIndex), shellID = PLUGFORMAT_SHELLID(plugIndex); + if(plug->shellPluginID != 0) + { + theApp.GetSettings().Write(U_("VST Plugins"), libName, plug->libraryName); + theApp.GetSettings().Write(U_("VST Plugins"), shellID, mpt::ufmt::HEX0<8>(plug->shellPluginID)); + } else { - plugPath = PathAbsoluteToInstallRelative(plugPath); + theApp.GetSettings().Remove(U_("VST Plugins"), libName); + theApp.GetSettings().Remove(U_("VST Plugins"), shellID); } - theApp.GetSettings().Write<mpt::PathString>(U_("VST Plugins"), MPT_UFORMAT("Plugin{}")(plugIndex), plugPath); - theApp.GetSettings().Write(U_("VST Plugins"), MPT_UFORMAT("Plugin{}.Tags")(plugIndex), plug->tags); + theApp.GetSettings().Write<mpt::PathString>(U_("VST Plugins"), PLUGFORMAT_FILENAME(plugIndex), plugPath); + theApp.GetSettings().Write(U_("VST Plugins"), PLUGFORMAT_TAGS(plugIndex), plug->tags); plugIndex++; } else { - mpt::ustring key = MPT_UFORMAT("Plugin{}{}.Tags")(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2)); - theApp.GetSettings().Write(U_("VST Plugins"), key, plug->tags); + theApp.GetSettings().Write(U_("VST Plugins"), PLUGFORMAT_TAGS_BUILTIN(mpt::ufmt::HEX0<8>(plug->pluginId1), mpt::ufmt::HEX0<8>(plug->pluginId2)), plug->tags); } } theApp.GetSettings().Write(U_("VST Plugins"), U_("NumPlugins"), static_cast<uint32>(plugIndex)); Modified: trunk/OpenMPT/mptrack/SelectPluginDialog.cpp ============================================================================== --- trunk/OpenMPT/mptrack/SelectPluginDialog.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/SelectPluginDialog.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -156,6 +156,7 @@ m_pPlugin->SetOutputPlugin(oldOutput); m_pPlugin->Info.dwPluginId1 = pFactory->pluginId1; m_pPlugin->Info.dwPluginId2 = pFactory->pluginId2; + m_pPlugin->Info.shellPluginID = pFactory->shellPluginID; m_pPlugin->editorX = m_pPlugin->editorY = int32_min; m_pPlugin->SetAutoSuspend(TrackerSettings::Instance().enableAutoSuspend); @@ -216,7 +217,7 @@ if(changed) { if(m_pPlugin->Info.dwPluginId2) - TrackerSettings::Instance().gnPlugWindowLast = m_pPlugin->Info.dwPluginId2; + TrackerSettings::Instance().lastSelectedPlugin = {static_cast<uint32>(m_pPlugin->Info.dwPluginId1), static_cast<uint32>(m_pPlugin->Info.dwPluginId2), m_pPlugin->Info.shellPluginID}; if(m_pModDoc) { m_pModDoc->UpdateAllViews(nullptr, PluginHint(static_cast<PLUGINDEX>(m_nPlugSlot + 1)).Info().Names()); @@ -349,7 +350,7 @@ }; PlugMatchQuality foundPlugin = kNoMatch; - const int32 lastPluginID = TrackerSettings::Instance().gnPlugWindowLast; + const auto lastPluginID = TrackerSettings::Instance().lastSelectedPlugin.Get(); const bool nameFilterActive = !m_nameFilter.empty(); const auto currentTags = mpt::split(m_nameFilter, U_(" ")); @@ -357,11 +358,11 @@ { bool first = true; - for(auto p : *pManager) + for(auto &p : *pManager) { MPT_ASSERT(p); const VSTPluginLib &plug = *p; - if(plug.category == PluginCategory::Hidden && (m_pPlugin == nullptr || m_pPlugin->pMixPlugin == nullptr || &m_pPlugin->pMixPlugin->GetPluginFactory() != p)) + if(plug.category == PluginCategory::Hidden && (m_pPlugin == nullptr || m_pPlugin->pMixPlugin == nullptr || &m_pPlugin->pMixPlugin->GetPluginFactory() != p.get())) continue; if(nameFilterActive) @@ -445,12 +446,16 @@ { // Plugin with matching ID to current slot's plug if(plug.pluginId1 == m_pPlugin->Info.dwPluginId1 - && plug.pluginId2 == m_pPlugin->Info.dwPluginId2) + && plug.pluginId2 == m_pPlugin->Info.dwPluginId2 + && plug.shellPluginID == m_pPlugin->Info.shellPluginID) { currentPlug = h; foundPlugin = kSameIdAsCurrent; } - } else if(plug.pluginId2 == lastPluginID && foundPlugin < kSameIdAsLastWithPlatformMatch) + } else if(static_cast<uint32>(plug.pluginId1) == lastPluginID.pluginID1 + && static_cast<uint32>(plug.pluginId2) == lastPluginID.pluginID2 + && plug.shellPluginID == lastPluginID.shellPluginID + && foundPlugin < kSameIdAsLastWithPlatformMatch) { // Previously selected plugin #ifdef MPT_WITH_VST @@ -689,8 +694,7 @@ for(const auto &file : dlg.GetFilenames()) { - VSTPluginLib *lib = plugManager->AddPlugin(file, TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes, mpt::ustring(), false); - if(lib != nullptr) + for(VSTPluginLib *lib : plugManager->AddPlugin(file, TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes, false)) { update = true; if(!VerifyPlugin(lib, this)) @@ -701,7 +705,7 @@ plugLib = lib; // If this plugin was missing anywhere, try loading it - ReloadMissingPlugins(lib); + ReloadMissingPlugins(*lib); } } } @@ -726,9 +730,9 @@ UpdatePluginsList(plugLib); // If any of the plugins was missing anywhere, try loading it - for(auto p : *theApp.GetPluginManager()) + for(auto &p : *theApp.GetPluginManager()) { - ReloadMissingPlugins(p); + ReloadMissingPlugins(*p); } } @@ -763,8 +767,7 @@ ::DispatchMessage(&msg); } - VSTPluginLib *lib = pManager->AddPlugin(fileName, maskCrashes, mpt::ustring(), false); - if(lib) + for(VSTPluginLib *lib : pManager->AddPlugin(fileName, maskCrashes, false)) { update = true; if(!VerifyPlugin(lib, parent)) @@ -793,7 +796,7 @@ // After adding new plugins, check if they were missing in any open songs. -void CSelectPluginDlg::ReloadMissingPlugins(const VSTPluginLib *lib) const +void CSelectPluginDlg::ReloadMissingPlugins(const VSTPluginLib &lib) const { CVstPluginManager *plugManager = theApp.GetPluginManager(); auto docs = theApp.GetOpenDocuments(); @@ -804,8 +807,9 @@ for(auto &plugin : sndFile.m_MixPlugins) { if(plugin.pMixPlugin == nullptr - && plugin.Info.dwPluginId1 == lib->pluginId1 - && plugin.Info.dwPluginId2 == lib->pluginId2) + && plugin.Info.dwPluginId1 == lib.pluginId1 + && plugin.Info.dwPluginId2 == lib.pluginId2 + && plugin.Info.shellPluginID == lib.shellPluginID) { updateDoc = true; plugManager->CreateMixPlugin(plugin, sndFile); Modified: trunk/OpenMPT/mptrack/SelectPluginDialog.h ============================================================================== --- trunk/OpenMPT/mptrack/SelectPluginDialog.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/SelectPluginDialog.h Sun Aug 4 23:58:03 2024 (r21326) @@ -69,7 +69,7 @@ VSTPluginLib *GetSelectedPlugin(); void SaveWindowPos() const; - void ReloadMissingPlugins(const VSTPluginLib *lib) const; + void ReloadMissingPlugins(const VSTPluginLib &lib) const; void UpdatePluginsList(const VSTPluginLib *forceSelect = nullptr); Modified: trunk/OpenMPT/mptrack/TrackerSettings.cpp ============================================================================== --- trunk/OpenMPT/mptrack/TrackerSettings.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/TrackerSettings.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -187,7 +187,7 @@ , gnPlugWindowY(conf, U_("Display"), U_("PlugSelectWindowY"), 273) , gnPlugWindowWidth(conf, U_("Display"), U_("PlugSelectWindowWidth"), 450) , gnPlugWindowHeight(conf, U_("Display"), U_("PlugSelectWindowHeight"), 540) - , gnPlugWindowLast(conf, U_("Display"), U_("PlugSelectWindowLast"), 0) + , lastSelectedPlugin(conf, U_("Display"), U_("PlugSelectWindowLast"), {}) , gnMsgBoxVisiblityFlags(conf, U_("Display"), U_("MsgBoxVisibilityFlags"), uint32_max) , GUIUpdateInterval(conf, U_("Display"), U_("GUIUpdateInterval"), 0) , FSUpdateInterval(conf, U_("Display"), U_("FSUpdateInterval"), 500) Modified: trunk/OpenMPT/mptrack/TrackerSettings.h ============================================================================== --- trunk/OpenMPT/mptrack/TrackerSettings.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/TrackerSettings.h Sun Aug 4 23:58:03 2024 (r21326) @@ -178,7 +178,7 @@ template<> inline EQPreset FromSettingValue(const SettingValue &val) { ASSERT(val.GetTypeTag() == "EQPreset"); - EQPresetPacked valpacked = DecodeBinarySetting<EQPresetPacked>(val.as<std::vector<std::byte> >()); + EQPresetPacked valpacked = DecodeBinarySetting<EQPresetPacked>(val.as<std::vector<std::byte>>()); EQPreset valresult; std::memcpy(valresult.szName, valpacked.szName, std::size(valresult.szName)); std::copy(valpacked.Gains, valpacked.Gains + MAX_EQ_BANDS, valresult.Gains); @@ -209,7 +209,7 @@ std::array<NoteType, notesPerChord - 1> notes; // Additional chord notes }; -using MPTChords = std::array<MPTChord, 60>; // Size == kcCommandSetNumNotes +using MPTChords = std::array<MPTChord, 60>; // Size == kcCommandSetNumNotes + 1 // MIDI recording enum RecordAftertouchOptions @@ -278,7 +278,7 @@ public: enum { - defaultSize = 10, // In percent + defaultSize = 10, // In percent }; SampleUndoBufferSize(int32 percent = defaultSize) : sizePercent(percent) { CalculateSize(); } @@ -292,6 +292,15 @@ template<> inline SampleUndoBufferSize FromSettingValue(const SettingValue &val) { return SampleUndoBufferSize(val.as<int32>()); } +struct LastPluginID +{ + uint32 pluginID1, pluginID2, shellPluginID; +}; + +template<> inline SettingValue ToSettingValue(const LastPluginID &val) { return EncodeBinarySetting(val); } +template<> inline LastPluginID FromSettingValue(const SettingValue &val) { return DecodeBinarySetting<LastPluginID>(val.as<std::vector<std::byte>>()); } + + mpt::ustring IgnoredCCsToString(const std::bitset<128> &midiIgnoreCCs); std::bitset<128> StringToIgnoredCCs(const mpt::ustring &in); @@ -673,7 +682,7 @@ Setting<int32> gnPlugWindowY; Setting<int32> gnPlugWindowWidth; Setting<int32> gnPlugWindowHeight; - Setting<int32> gnPlugWindowLast; // Last selected plugin ID + Setting<LastPluginID> lastSelectedPlugin; Setting<uint32> gnMsgBoxVisiblityFlags; Setting<uint32> GUIUpdateInterval; Modified: trunk/OpenMPT/mptrack/Vstplug.cpp ============================================================================== --- trunk/OpenMPT/mptrack/Vstplug.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/Vstplug.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -42,6 +42,7 @@ OPENMPT_NAMESPACE_BEGIN static VstTimeInfo g_timeInfoFallback = {}; +static int32 g_shellPluginToLoad = 0; #ifdef MPT_ALL_LOGGING #define VST_LOG @@ -98,7 +99,19 @@ } -AEffect *CVstPlugin::LoadPlugin(bool maskCrashes, VSTPluginLib &plugin, HMODULE &library, BridgeMode bridgeMode) +static bool IsValidPlugin(const AEffect *effect) +{ + return effect && effect->magic == Vst::kEffectMagic && effect->dispatcher != nullptr; +} + + +static bool IsBridged(const AEffect &effect) +{ + return !memcmp(&effect.reservedForHost2, "OMPT", 4); +} + + +std::pair<Vst::AEffect *, Vst::MainProc> CVstPlugin::LoadPluginInternal(bool maskCrashes, VSTPluginLib &plugin, HMODULE &library, BridgeMode bridgeMode) { const mpt::PathString &pluginPath = plugin.dllPath; @@ -117,7 +130,7 @@ effect = BridgeWrapper::Create(plugin, false); if(effect != nullptr) { - return effect; + return {effect, nullptr}; } } catch(BridgeWrapper::BridgeNotFoundException &) { @@ -134,7 +147,7 @@ effect = BridgeWrapper::Create(plugin, bridgeMode == BridgeMode::DetectRequiredBridgeMode); if(effect != nullptr) { - return effect; + return {effect, nullptr}; } } catch(BridgeWrapper::BridgeNotFoundException &) { @@ -142,7 +155,7 @@ if(!isNative) { Reporting::Error("Could not locate the plugin bridge executable, which is required for running non-native plugins.", "OpenMPT Plugin Bridge"); - return nullptr; + return {nullptr, nullptr}; } } catch(BridgeWrapper::BridgeException &e) { @@ -154,12 +167,12 @@ (plugin.dllPath, mpt::get_exception_text<mpt::ustring>(e)); if(Reporting::Confirm(msg, _T("OpenMPT Plugin Bridge")) == cnfNo) { - return nullptr; + return {nullptr, nullptr}; } } else { Reporting::Error(mpt::get_exception_text<mpt::ustring>(e), "OpenMPT Plugin Bridge"); - return nullptr; + return {nullptr, nullptr}; } } // If plugin was marked to use the plugin bridge but this somehow doesn't work (e.g. because the bridge is missing), @@ -177,7 +190,7 @@ if(exception) { CVstPluginManager::ReportPlugException(MPT_UFORMAT("Exception caught while loading {}")(pluginPath)); - return nullptr; + return {nullptr, nullptr}; } } if(library == nullptr) @@ -185,7 +198,7 @@ DWORD error = GetLastError(); if(error == ERROR_MOD_NOT_FOUND) { - return nullptr; + return {nullptr, nullptr}; } else if(error == ERROR_DLL_INIT_FAILED) { // A likely reason for this error is that Fiber Local Storage slots are exhausted, e.g. because too many plugins ship with a statically linked runtime. @@ -206,11 +219,9 @@ if(library != nullptr && library != INVALID_HANDLE_VALUE) { - auto pMainProc = (Vst::MainProc)GetProcAddress(library, "VSTPluginMain"); + auto pMainProc = reinterpret_cast<Vst::MainProc>(GetProcAddress(library, "VSTPluginMain")); if(pMainProc == nullptr) - { - pMainProc = (Vst::MainProc)GetProcAddress(library, "main"); - } + pMainProc = reinterpret_cast<Vst::MainProc>(GetProcAddress(library, "main")); if(pMainProc != nullptr) { @@ -218,23 +229,107 @@ ExceptionHandler::Context ectx{ MPT_UFORMAT("VST Plugin: {}")(plugin.dllPath.ToUnicode()) }; ExceptionHandler::ContextSetter ectxguard{&ectx}; #endif // MODPLUG_TRACKER - DWORD exception = SETryOrError(maskCrashes, [&](){ effect = pMainProc(CVstPlugin::MasterCallBack); }); - if(exception) - { - return nullptr; - } + if(!SETryOrError(maskCrashes, [&]() { effect = pMainProc(CVstPlugin::MasterCallBack); })) + return {effect, pMainProc}; } else { #ifdef VST_LOG MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("Entry point not found! (handle={})")(mpt::ufmt::PTR(library))); #endif // VST_LOG - return nullptr; } } + return {nullptr, nullptr}; +} + - return effect; +CVstPlugin::LoadResult CVstPlugin::LoadPlugin(bool maskCrashes, VSTPluginLib &plugin, BridgeMode bridgeMode, unsigned long &exception) +{ + LoadResult result{}; + auto [effectPtr, mainProc] = LoadPluginInternal(maskCrashes, plugin, result.library, bridgeMode); + result.effect = effectPtr; + result.mainProc = mainProc; + if(!IsValidPlugin(result.effect)) + return result; + + Vst::AEffect *effect = result.effect; + result.magic = effect->magic; + result.uniqueID = effect->uniqueID; + + size_t shellPlugIndex = 0; + if(static_cast<PluginCategory>(DispatchSEH(maskCrashes, *effect, Vst::effGetPlugCategory, 0, 0, nullptr, 0, exception)) == PluginCategory::Shell) + { + std::vector<char> name(256, 0); // 64 chars officially supported, but our plugin bridge assumes 256 chars max for all strings + intptr_t childID; + while((childID = CVstPlugin::DispatchSEH(maskCrashes, *effect, Vst::effShellGetNextPlugin, 0, 0, name.data(), 0.0f, exception)) != 0) + { + name[63] = 0; + result.shellPlugins.emplace_back(name.data(), static_cast<uint32>(childID)); + + if(static_cast<uint32>(childID) == plugin.shellPluginID) + shellPlugIndex = result.shellPlugins.size(); + } + } + + if(plugin.shellPluginID) + { + if(!shellPlugIndex) + { + result.effect = nullptr; + Reporting::Error(MPT_UFORMAT("The shell plugin \"{}\" does not contain \"{}\".") + (plugin.dllPath.ToUnicode(), plugin.libraryName)); + } else + { + SelectShellPlugin(maskCrashes, result, plugin); + effect = result.effect; + } + } + + return result; +} + + +bool CVstPlugin::SelectShellPlugin(bool maskCrashes, LoadResult &loadResult, const VSTPluginLib &plugin) +{ + if(!loadResult.effect) + return false; + + g_shellPluginToLoad = plugin.shellPluginID; + if(IsBridged(*loadResult.effect)) + { + loadResult.effect->dispatcher(loadResult.effect, Vst::effVendorSpecific, kVendorOpenMPT, kCallVSTPluginMain, nullptr, 0.0f); + } else + { + unsigned long exception = 0; + if(loadResult.effect) + CVstPlugin::DispatchSEH(maskCrashes, *loadResult.effect, Vst::effClose, 0, 0, nullptr, 0.0f, exception); + exception = SETryOrError(maskCrashes, [&]() { loadResult.effect = loadResult.mainProc(CVstPlugin::MasterCallBack); }); + } + g_shellPluginToLoad = 0; + + if(!IsValidPlugin(loadResult.effect)) + loadResult.effect = nullptr; + return loadResult.effect != nullptr; } + +void CVstPlugin::GetPluginMetadata(bool maskCrashes, LoadResult &loadResult, VSTPluginLib &plugin) +{ + std::vector<char> vendor(256, 0); + unsigned long exception = 0; + CVstPlugin::DispatchSEH(maskCrashes, *loadResult.effect, Vst::effGetVendorString, 0, 0, vendor.data(), 0.0f, exception); + vendor.back() = '\0'; + plugin.vendor = mpt::ToCString(mpt::Charset::Locale, vendor.data()); + + if(CVstPlugin::IsInstrument(*loadResult.effect)) + plugin.category = PluginCategory::Synth; + else + plugin.category = static_cast<PluginCategory>(DispatchSEH(maskCrashes, *loadResult.effect, Vst::effGetPlugCategory, 0, 0, nullptr, 0.0f, exception)); + + if(plugin.category >= PluginCategory::NumCategories) + plugin.category = PluginCategory::Unknown; +} + + static void operator|= (Vst::VstTimeInfoFlags &lhs, Vst::VstTimeInfoFlags rhs) { lhs = (lhs | rhs).as_enum(); @@ -253,9 +348,9 @@ enum { - HostDoNotKnow = 0, - HostCanDo = 1, - HostCanNotDo = -1 + HostDoNotKnow = 0, + HostCanDo = 1, + HostCanNotDo = -1 }; CVstPlugin *pVstPlugin = nullptr; @@ -283,9 +378,11 @@ return kVstVersion; // Returns the unique id of a plugin that's currently loading - // We don't support shell plugins currently, so we only support one effect ID as well. case audioMasterCurrentId: - return (effect != nullptr) ? effect->uniqueID : 0; + if(pVstPlugin && pVstPlugin->m_pMixStruct) + return pVstPlugin->m_pMixStruct->Info.shellPluginID ? pVstPlugin->m_pMixStruct->Info.shellPluginID : effect->uniqueID; + else + return g_shellPluginToLoad; // Call application idle routine (this will call effEditIdle for all open editors too) case audioMasterIdle: @@ -594,7 +691,8 @@ || !strcmp((char *)ptr, HostCanDo::openFileSelector) || !strcmp((char *)ptr, HostCanDo::closeFileSelector) || !strcmp((char *)ptr, HostCanDo::acceptIOChanges) - || !strcmp((char *)ptr, HostCanDo::reportConnectionChanges)) + || !strcmp((char *)ptr, HostCanDo::reportConnectionChanges) + || !strcmp((char *)ptr, HostCanDo::shellCategory)) { return HostCanDo; } else @@ -881,7 +979,7 @@ , m_isInitialized(false) , m_needIdle(false) , timeInfo{} - , isBridged(!memcmp(&effect.reservedForHost2, "OMPT", 4)) + , isBridged(IsBridged(effect)) { // Open plugin and initialize data structures Initialize(); @@ -892,18 +990,13 @@ void CVstPlugin::Initialize() { - m_Ectx = { MPT_UFORMAT("VST Plugin: {}")(m_Factory.dllPath.ToUnicode()) }; - // If filename matched during load but plugin ID didn't, make sure it's updated. - m_pMixStruct->Info.dwPluginId1 = m_Factory.pluginId1 = m_Effect.magic; - m_pMixStruct->Info.dwPluginId2 = m_Factory.pluginId2 = m_Effect.uniqueID; - // Store a pointer so we can get the CVstPlugin object from the basic VST effect object. m_Effect.reservedForHost1 = this; m_nSampleRate = m_SndFile.GetSampleRate(); - // First try to let the plugin know the render parameters. + // First try to let the plugin know the render parameters. (added in r6457, but I have no recollection of which plugins needed this!) Dispatch(effSetSampleRate, 0, 0, nullptr, static_cast<float>(m_nSampleRate)); Dispatch(effSetBlockSize, 0, MIXBUFFERSIZE, nullptr, 0.0f); @@ -1085,12 +1178,12 @@ // Wrapper for VST dispatch call with structured exception handling. -intptr_t CVstPlugin::DispatchSEH(bool maskCrashes, AEffect *effect, VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt, unsigned long &exception) +intptr_t CVstPlugin::DispatchSEH(bool maskCrashes, AEffect &effect, VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt, unsigned long &exception) { - if(effect->dispatcher != nullptr) + if(effect.dispatcher != nullptr) { intptr_t result = 0; - DWORD e = SETryOrError(maskCrashes, [&](){ result = effect->dispatcher(effect, opCode, index, value, ptr, opt); }); + DWORD e = SETryOrError(maskCrashes, [&](){ result = effect.dispatcher(&effect, opCode, index, value, ptr, opt); }); if(e) { exception = e; @@ -1704,9 +1797,15 @@ } +bool CVstPlugin::IsInstrument(Vst::AEffect &effect) +{ + return (effect.flags & effFlagsIsSynth) || !effect.numInputs; +} + + bool CVstPlugin::IsInstrument() const { - return ((m_Effect.flags & effFlagsIsSynth) || (!m_Effect.numInputs)); + return IsInstrument(m_Effect); } Modified: trunk/OpenMPT/mptrack/Vstplug.h ============================================================================== --- trunk/OpenMPT/mptrack/Vstplug.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/Vstplug.h Sun Aug 4 23:58:03 2024 (r21326) @@ -30,6 +30,7 @@ class CSoundFile; struct SNDMIXPLUGIN; struct VSTPluginLib; +enum class PluginCategory : uint8; class CVstPlugin final : public IMidiPlugin @@ -84,9 +85,28 @@ DetectRequiredBridgeMode, }; - static Vst::AEffect *LoadPlugin(bool maskCrashes, VSTPluginLib &plugin, HMODULE &library, BridgeMode bridgeMode); + struct LoadResult + { + struct ShellPlugin + { + std::string name; + uint32 shellPluginID = 0; + }; + + Vst::AEffect *effect = nullptr; + Vst::MainProc mainProc = nullptr; + HMODULE library = nullptr; + int32 magic = 0, uniqueID = 0; + std::vector<ShellPlugin> shellPlugins; + }; + + static LoadResult LoadPlugin(bool maskCrashes, VSTPluginLib &plugin, BridgeMode bridgeMode, unsigned long &exception); + static bool SelectShellPlugin(bool maskCrashes, LoadResult &loadResult, const VSTPluginLib &plugin); + static void GetPluginMetadata(bool maskCrashes, LoadResult &loadResult, VSTPluginLib &plugin); protected: + static std::pair<Vst::AEffect *, Vst::MainProc> LoadPluginInternal(bool maskCrashes, VSTPluginLib &plugin, HMODULE &library, BridgeMode bridgeMode); + void Initialize(); public: @@ -99,8 +119,6 @@ bool ProgramsAreChunks() const override { return (m_Effect.flags & Vst::effFlagsProgramChunks) != 0; } ChunkData GetChunk(bool isBank) override; void SetChunk(const ChunkData &chunk, bool isBank) override; - // If true, the plugin produces an output even if silence is being fed into it. - //bool ShouldProcessSilence() { return IsInstrument() || ((m_Effect.flags & effFlagsNoSoundInStop) == 0 && Dispatch(effGetTailSize, 0, 0, nullptr, 0.0f) != 1) override; } // Old JUCE versions set effFlagsNoSoundInStop even when the shouldn't (see various ValhallaDSP reverb plugins). While the user cannot change the plugin bypass setting manually yet, play safe with VST plugins and do not optimize. bool ShouldProcessSilence() override { return true; } @@ -120,7 +138,7 @@ CString GetParamLabel(PlugParamIndex param) override { return GetParamPropertyString(param, Vst::effGetParamLabel); }; CString GetParamDisplay(PlugParamIndex param) override { return GetParamPropertyString(param, Vst::effGetParamDisplay); }; - static intptr_t DispatchSEH(bool maskCrashes, Vst::AEffect *effect, Vst::VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt, unsigned long &exception); + static intptr_t DispatchSEH(bool maskCrashes, Vst::AEffect &effect, Vst::VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt, unsigned long &exception); intptr_t Dispatch(Vst::VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt); bool HasEditor() const override { return (m_Effect.flags & Vst::effFlagsHasEditor) != 0; } @@ -129,6 +147,7 @@ void Bypass(bool bypass = true) override; + static bool IsInstrument(Vst::AEffect &effect); bool IsInstrument() const override; bool CanRecieveMidiEvents() override; Modified: trunk/OpenMPT/mptrack/mod2midi.cpp ============================================================================== --- trunk/OpenMPT/mptrack/mod2midi.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/mptrack/mod2midi.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -440,7 +440,7 @@ public: Conversion(CSoundFile &sndFile, const InstrMap &instrMap, std::ostream &file, bool overlappingInstruments, const GetLengthType &songLength) : m_oldInstruments(sndFile.GetNumInstruments()) - , m_plugFactory(nullptr, true, {}, {}, {}) + , m_plugFactory(nullptr, true, {}, {}) , m_sndFile(sndFile) , m_file(file) , m_songLength(songLength) Modified: trunk/OpenMPT/pluginBridge/Bridge.cpp ============================================================================== --- trunk/OpenMPT/pluginBridge/Bridge.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/pluginBridge/Bridge.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -368,17 +368,15 @@ return; } - auto mainProc = (Vst::MainProc)GetProcAddress(m_library, "VSTPluginMain"); - if(mainProc == nullptr) - { - mainProc = (Vst::MainProc)GetProcAddress(m_library, "main"); - } + m_mainProc = reinterpret_cast<Vst::MainProc>(GetProcAddress(m_library, "VSTPluginMain")); + if(m_mainProc == nullptr) + m_mainProc = reinterpret_cast<Vst::MainProc>(GetProcAddress(m_library, "main")); - if(mainProc != nullptr) + if(m_mainProc != nullptr) { __try { - m_nativeEffect = mainProc(MasterCallback); + m_nativeEffect = m_mainProc(MasterCallback); } __except(EXCEPTION_EXECUTE_HANDLER) { m_nativeEffect = nullptr; @@ -624,6 +622,13 @@ } } break; + case kCallVSTPluginMain: + if(m_mainProc) + { + Dispatch(effClose, 0, 0, nullptr, 0.0f); + m_nativeEffect = m_mainProc(MasterCallback); + UpdateEffectStruct(); + } default: msg.result = 0; } Modified: trunk/OpenMPT/pluginBridge/Bridge.h ============================================================================== --- trunk/OpenMPT/pluginBridge/Bridge.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/pluginBridge/Bridge.h Sun Aug 4 23:58:03 2024 (r21326) @@ -34,6 +34,7 @@ // Plugin Vst::AEffect *m_nativeEffect = nullptr; + Vst::MainProc m_mainProc = nullptr; HMODULE m_library = nullptr; HWND m_window = nullptr; int m_windowWidth = 0, m_windowHeight = 0; Modified: trunk/OpenMPT/pluginBridge/BridgeOpCodes.h ============================================================================== --- trunk/OpenMPT/pluginBridge/BridgeOpCodes.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/pluginBridge/BridgeOpCodes.h Sun Aug 4 23:58:03 2024 (r21326) @@ -35,6 +35,8 @@ kBeginGetProgram, // Stop using parameter value cache kEndGetProgram, + // Call the VSTPluginMain entry point again (for shell plugins) + kCallVSTPluginMain, // Constant for identifying our vendor-specific opcodes kVendorOpenMPT = Vst::FourCC("OMPT"), Modified: trunk/OpenMPT/soundlib/plugins/PluginManager.cpp ============================================================================== --- trunk/OpenMPT/soundlib/plugins/PluginManager.cpp Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/soundlib/plugins/PluginManager.cpp Sun Aug 4 23:58:03 2024 (r21326) @@ -242,7 +242,10 @@ pathStr = theApp.PathAbsoluteToInstallRelative(dllPath).ToUnicode(); else pathStr = dllPath.ToUnicode(); - + + if(shellPluginID) + pathStr += U_("|") + mpt::ufmt::HEX0<8>(shellPluginID); + // CRC is used to distinguish plugins sharing the same IDs const std::string crcName = mpt::ToCharset(mpt::Charset::UTF8, pathStr); const mpt::crc32 crc(crcName); @@ -350,19 +353,15 @@ pluginList.reserve(std::size(BuiltInPlugins)); for(const auto &plugin : BuiltInPlugins) { - VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(plugin.createProc, true, mpt::PathString::FromUTF8(plugin.filename), mpt::PathString::FromUTF8(plugin.name)); - if(plug != nullptr) - { - pluginList.push_back(plug); - plug->pluginId1 = plugin.pluginId1; - plug->pluginId2 = plugin.pluginId2; - plug->category = plugin.category; - plug->isInstrument = plugin.isInstrument; + auto &plug = pluginList.emplace_back(std::make_unique<VSTPluginLib>(plugin.createProc, true, mpt::PathString::FromUTF8(plugin.filename), mpt::PathString::FromUTF8(plugin.name))); + plug->pluginId1 = plugin.pluginId1; + plug->pluginId2 = plugin.pluginId2; + plug->category = plugin.category; + plug->isInstrument = plugin.isInstrument; #ifdef MODPLUG_TRACKER - if(plugin.isOurs) - plug->vendor = _T("OpenMPT Project"); + if(plugin.isOurs) + plug->vendor = _T("OpenMPT Project"); #endif // MODPLUG_TRACKER - } } #ifdef MODPLUG_TRACKER @@ -382,7 +381,6 @@ plug->RemovePluginInstanceFromList(*pluginInstance); pluginInstance->Release(); } - delete plug; } #if defined(MPT_WITH_DMO) if(MustUnInitilizeCOM) @@ -396,7 +394,10 @@ bool CVstPluginManager::IsValidPlugin(const VSTPluginLib *pLib) const { - return mpt::contains(pluginList, pLib); + return std::find_if(pluginList.begin(), pluginList.end(), [pLib](const std::unique_ptr<VSTPluginLib> &value) + { + return value.get() == pLib; + }) != pluginList.end(); } @@ -444,24 +445,13 @@ if(ERROR_SUCCESS == RegQueryValueEx(hksub, nullptr, 0, &datatype, (LPBYTE)name, &datasize)) { - VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(DMOPlugin::Create, true, mpt::PathString::FromNative(mpt::GUIDToString(clsid)), mpt::PathString::FromNative(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<mpt::winstring>(name, datasize))); - if(plug != nullptr) - { - try - { - pluginList.push_back(plug); - plug->pluginId1 = kDmoMagic; - plug->pluginId2 = clsid.Data1; - plug->category = PluginCategory::DMO; - } catch(mpt::out_of_memory e) - { - mpt::delete_out_of_memory(e); - delete plug; - } + auto &plug = pluginList.emplace_back(std::make_unique<VSTPluginLib>(DMOPlugin::Create, true, mpt::PathString::FromNative(mpt::GUIDToString(clsid)), mpt::PathString::FromNative(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<mpt::winstring>(name, datasize)))); + plug->pluginId1 = kDmoMagic; + plug->pluginId2 = clsid.Data1; + plug->category = PluginCategory::DMO; #ifdef DMO_LOG - MPT_LOG_GLOBAL(LogDebug, "DMO", MPT_UFORMAT("Found \"{}\" clsid={}\n")(plug->libraryName, plug->dllPath)); + MPT_LOG_GLOBAL(LogDebug, "DMO", MPT_UFORMAT("Found \"{}\" clsid={}\n")(plug->libraryName, plug->dllPath)); #endif - } } RegCloseKey(hksub); } @@ -475,74 +465,50 @@ } -// Extract instrument and category information from plugin. #ifdef MPT_WITH_VST -static void GetPluginInformation(bool maskCrashes, Vst::AEffect *effect, VSTPluginLib &library) +// Convert CVstPlugin::LoadResult into a collection of VSTPluginLibs. +static std::vector<VSTPluginLib> GetPluginInformation(VSTPluginLib plug, const CVstPlugin::LoadResult &loadResult) { - unsigned long exception = 0; - library.category = static_cast<PluginCategory>(CVstPlugin::DispatchSEH(maskCrashes, effect, Vst::effGetPlugCategory, 0, 0, nullptr, 0, exception)); - library.isInstrument = ((effect->flags & Vst::effFlagsIsSynth) || !effect->numInputs); + plug.pluginId1 = loadResult.magic; + plug.pluginId2 = loadResult.uniqueID; + plug.isInstrument = CVstPlugin::IsInstrument(*loadResult.effect); - if(library.isInstrument) + std::vector<VSTPluginLib> containedPlugins; + if(loadResult.shellPlugins.empty()) { - library.category = PluginCategory::Synth; - } else if(library.category >= PluginCategory::NumCategories) - { - library.category = PluginCategory::Unknown; - } - #ifdef MODPLUG_TRACKER - std::vector<char> s(256, 0); - CVstPlugin::DispatchSEH(maskCrashes, effect, Vst::effGetVendorString, 0, 0, s.data(), 0, exception); - library.vendor = mpt::ToCString(mpt::Charset::Locale, s.data()); -#endif // MODPLUG_TRACKER -} -#endif // MPT_WITH_VST - - -#ifdef MPT_WITH_VST -static bool TryLoadPlugin(bool maskCrashes, VSTPluginLib *plug, HINSTANCE hLib, unsigned long &exception) -{ - Vst::AEffect *pEffect = CVstPlugin::LoadPlugin(maskCrashes, *plug, hLib, CVstPlugin::BridgeMode::DetectRequiredBridgeMode); - if(!pEffect || pEffect->magic != Vst::kEffectMagic || !pEffect->dispatcher) + plug.WriteToCache(); +#endif // MODPLUG_TRACKER + containedPlugins.push_back(std::move(plug)); + } else { - return false; + for(auto &shellPlug : loadResult.shellPlugins) + { + plug.shellPluginID = shellPlug.shellPluginID; + plug.libraryName = mpt::PathString::FromLocale(shellPlug.name); +#ifdef MODPLUG_TRACKER + plug.WriteToCache(); +#endif // MODPLUG_TRACKER + containedPlugins.push_back(plug); + } } - CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effOpen, 0, 0, 0, 0, exception); - - plug->pluginId1 = pEffect->magic; - plug->pluginId2 = pEffect->uniqueID; - - GetPluginInformation(maskCrashes, pEffect, *plug); - -#ifdef VST_LOG - intptr_t nver = CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effGetVstVersion, 0,0, nullptr, 0, exception); - if (!nver) nver = pEffect->version; - MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("{}: v{}.0, {} in, {} out, {} programs, {} params, flags=0x{} realQ={} offQ={}")( - plug->libraryName, nver, - pEffect->numInputs, pEffect->numOutputs, - mpt::ufmt::dec0<2>(pEffect->numPrograms), mpt::ufmt::dec0<2>(pEffect->numParams), - mpt::ufmt::HEX0<4>(static_cast<int32>(pEffect->flags)), pEffect->realQualities, pEffect->offQualities)); -#endif // VST_LOG - - CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effClose, 0, 0, 0, 0, exception); - - return true; + return containedPlugins; } -#endif // !NO_NVST +#endif // !NO_VST #ifdef MODPLUG_TRACKER // Add a plugin to the list of known plugins. -VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, bool maskCrashes, const mpt::ustring &tags, bool fromCache, bool *fileFound) +std::vector<VSTPluginLib *> CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, bool maskCrashes, bool fromCache, bool *fileFound, uint32 shellPluginID) { const mpt::PathString fileName = dllPath.GetFilenameBase(); - // Check if this is already a known plugin. + // Check if this is already a known plugin for(const auto &dupePlug : pluginList) { - if(!mpt::PathCompareNoCase(dllPath, dupePlug->dllPath)) return dupePlug; + if(shellPluginID == dupePlug->shellPluginID && !mpt::PathCompareNoCase(dllPath, dupePlug->dllPath)) + return {dupePlug.get()}; } if(fileFound != nullptr) @@ -553,24 +519,23 @@ // Look if the plugin info is stored in the PluginCache if(fromCache) { - SettingsContainer & cacheFile = theApp.GetPluginCache(); + mpt::ustring shellStr; + if(shellPluginID) + shellStr = UL_("|") + mpt::ufmt::HEX0<8>(shellPluginID); + + SettingsContainer &cacheFile = theApp.GetPluginCache(); // First try finding the full path - mpt::ustring IDs = cacheFile.Read<mpt::ustring>(cacheSection, dllPath.ToUnicode(), U_("")); + mpt::ustring IDs = cacheFile.Read<mpt::ustring>(cacheSection, dllPath.ToUnicode() + shellStr, U_("")); if(IDs.length() < 16) { // If that didn't work out, find relative path mpt::PathString relPath = theApp.PathAbsoluteToInstallRelative(dllPath); - IDs = cacheFile.Read<mpt::ustring>(cacheSection, relPath.ToUnicode(), U_("")); + IDs = cacheFile.Read<mpt::ustring>(cacheSection, relPath.ToUnicode() + shellStr, U_("")); } if(IDs.length() >= 16) { - VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(nullptr, false, dllPath, fileName, tags); - if(plug == nullptr) - { - return nullptr; - } - pluginList.push_back(plug); + auto &plug = pluginList.emplace_back(std::make_unique<VSTPluginLib>(nullptr, false, dllPath, fileName)); // Extract plugin IDs uint32 id1 = 0, id2 = 0; @@ -591,11 +556,12 @@ const mpt::ustring flagKey = IDs + U_(".Flags"); plug->DecodeCacheFlags(cacheFile.Read<int32>(cacheSection, flagKey, 0)); plug->vendor = cacheFile.Read<CString>(cacheSection, IDs + U_(".Vendor"), CString()); + plug->shellPluginID = shellPluginID; #ifdef VST_LOG MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("Plugin \"{}\" found in PluginCache")(plug->libraryName)); #endif // VST_LOG - return plug; + return {plug.get()}; } else { #ifdef VST_LOG @@ -607,33 +573,41 @@ // If this key contains a file name on program launch, a plugin previously crashed OpenMPT. theApp.GetSettings().Write<mpt::PathString>(U_("VST Plugins"), U_("FailedPlugin"), dllPath, SettingWriteThrough); - bool validPlug = false; - - VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(nullptr, false, dllPath, fileName, tags); - if(plug == nullptr) - { - return nullptr; - } + std::vector<VSTPluginLib *> foundPlugins; #ifdef MPT_WITH_VST unsigned long exception = 0; // Always scan plugins in a separate process - HINSTANCE hLib = NULL; { -#ifdef MODPLUG_TRACKER - ExceptionHandler::Context ectx{ MPT_UFORMAT("VST Plugin: {}")(plug->dllPath.ToUnicode()) }; + ExceptionHandler::Context ectx{ MPT_UFORMAT("VST Plugin: {}")(dllPath.ToUnicode()) }; ExceptionHandler::ContextSetter ectxguard{&ectx}; -#endif // MODPLUG_TRACKER - validPlug = TryLoadPlugin(maskCrashes, plug, hLib, exception); - } - if(hLib) - { - FreeLibrary(hLib); + VSTPluginLib plug{nullptr, false, dllPath, fileName}; + auto loadResult = CVstPlugin::LoadPlugin(maskCrashes, plug, CVstPlugin::BridgeMode::DetectRequiredBridgeMode, exception); + Vst::AEffect *pEffect = loadResult.effect; + if(pEffect) + { + foundPlugins = AddPluginsToList(GetPluginInformation(std::move(plug), loadResult), + [&](VSTPluginLib &library, bool updateExisting) + { + if(updateExisting) + return; + if(library.shellPluginID) + { + if(!CVstPlugin::SelectShellPlugin(maskCrashes, loadResult, library)) + return; + } + CVstPlugin::GetPluginMetadata(maskCrashes, loadResult, library); + }); + } + if(loadResult.library) + { + FreeLibrary(loadResult.library); + } } if(exception != 0) { - CVstPluginManager::ReportPlugException(MPT_UFORMAT("Exception {} while trying to load plugin \"{}\"!\n")(mpt::ufmt::HEX0<8>(exception), plug->libraryName)); + CVstPluginManager::ReportPlugException(MPT_UFORMAT("Exception {} while trying to load plugin \"{}\"!\n")(mpt::ufmt::HEX0<8>(exception), fileName)); } #endif // MPT_WITH_VST @@ -641,17 +615,7 @@ // Now it should be safe to assume that this plugin loaded properly. :) theApp.GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin")); - // If OK, write the information in PluginCache - if(validPlug) - { - pluginList.push_back(plug); - plug->WriteToCache(); - } else - { - delete plug; - } - - return (validPlug ? plug : nullptr); + return foundPlugins; } @@ -660,7 +624,7 @@ { for(const_iterator p = begin(); p != end(); p++) { - VSTPluginLib *plug = *p; + VSTPluginLib *plug = p->get(); if(plug == pFactory) { // Kill all instances of this plugin @@ -673,7 +637,6 @@ pluginInstance->Release(); } pluginList.erase(p); - delete plug; return true; } } @@ -696,7 +659,7 @@ kMatchNameAndId, }; - PlugMatchQuality match = kNoMatch; // "Match quality" of found plugin. Higher value = better match. + PlugMatchQuality match = kNoMatch; // "Match quality" of found plugin. Higher value = better match. #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT const mpt::PathString libraryName = mpt::PathString::FromUnicode(mixPlugin.GetLibraryName()); #else @@ -705,7 +668,8 @@ for(const auto &plug : pluginList) { const bool matchID = (plug->pluginId1 == mixPlugin.Info.dwPluginId1) - && (plug->pluginId2 == mixPlugin.Info.dwPluginId2); + && (plug->pluginId2 == mixPlugin.Info.dwPluginId2) + && (plug->shellPluginID == mixPlugin.Info.shellPluginID); #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT const bool matchName = !mpt::PathCompareNoCase(plug->libraryName, libraryName); #else @@ -714,7 +678,7 @@ if(matchID && matchName) { - pFound = plug; + pFound = plug.get(); #ifdef MPT_WITH_VST if(plug->IsNative(false)) { @@ -725,11 +689,11 @@ match = kMatchNameAndId; } else if(matchID && match < kMatchId) { - pFound = plug; + pFound = plug.get(); match = kMatchId; } else if(matchName && match < kMatchName) { - pFound = plug; + pFound = plug.get(); match = kMatchName; } } @@ -756,20 +720,19 @@ if(pFound) { bool maskCrashes = TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes; - Vst::AEffect *pEffect = nullptr; - HINSTANCE hLibrary = nullptr; bool validPlugin = false; - pEffect = CVstPlugin::LoadPlugin(maskCrashes, *pFound, hLibrary, TrackerSettings::Instance().bridgeAllPlugins ? CVstPlugin::BridgeMode::ForceBridgeWithFallback : CVstPlugin::BridgeMode::Automatic); + unsigned long exception = 0; + auto loadResult = CVstPlugin::LoadPlugin(maskCrashes, *pFound, TrackerSettings::Instance().bridgeAllPlugins ? CVstPlugin::BridgeMode::ForceBridgeWithFallback : CVstPlugin::BridgeMode::Automatic, exception); + Vst::AEffect *pEffect = loadResult.effect; - if(pEffect != nullptr && pEffect->dispatcher != nullptr && pEffect->magic == Vst::kEffectMagic) + if(pEffect != nullptr) { - GetPluginInformation(maskCrashes, pEffect, *pFound); + // If filename matched during load but plugin ID didn't, make sure it's updated. + mixPlugin.Info.dwPluginId1 = pFound->pluginId1 = loadResult.magic; + mixPlugin.Info.dwPluginId2 = pFound->pluginId2 = loadResult.uniqueID; - // Update cached information - pFound->WriteToCache(); - - CVstPlugin *pVstPlug = new (std::nothrow) CVstPlugin(maskCrashes, hLibrary, *pFound, mixPlugin, *pEffect, sndFile); + CVstPlugin *pVstPlug = new (std::nothrow) CVstPlugin(maskCrashes, loadResult.library, *pFound, mixPlugin, *pEffect, sndFile); if(pVstPlug) { pFound->InsertPluginInstanceIntoList(*pVstPlug); @@ -777,11 +740,20 @@ validPlugin = (pVstPlug != nullptr); CriticalSection cs; mixPlugin.pMixPlugin = pVstPlug; + +#ifdef MODPLUG_TRACKER + AddPluginsToList(GetPluginInformation(*pFound, loadResult), + [&](VSTPluginLib &library, bool updateExisting) + { + if(&library == pFound && updateExisting) + CVstPlugin::GetPluginMetadata(maskCrashes, loadResult, library); + }); +#endif } - if(!validPlugin) + if(!validPlugin && loadResult.library) { - FreeLibrary(hLibrary); + FreeLibrary(loadResult.library); CVstPluginManager::ReportPlugException(MPT_UFORMAT("Unable to create plugin \"{}\"!\n")(pFound->libraryName)); } return validPlugin; @@ -798,6 +770,84 @@ } +#ifdef MPT_WITH_VST +std::vector<VSTPluginLib *> CVstPluginManager::AddPluginsToList(std::vector<VSTPluginLib> containedPlugins, std::function<void(VSTPluginLib &, bool)> updateFunc) +{ + std::vector<VSTPluginLib *> newPlugins; + if(containedPlugins.empty()) + return newPlugins; + + // Find existing shell plugins belonging to the same file first, so that we don't have to iterate through the whole plugin list again and again + std::map<uint32, size_t> existingCandidates; + for(size_t i = 0; i < pluginList.size(); i++) + { + const auto &plug = pluginList[i]; + if(plug->pluginId1 == containedPlugins.front().pluginId1 && plug->pluginId2 == containedPlugins.front().pluginId2) + { + if(!mpt::PathCompareNoCase(plug->dllPath, containedPlugins.front().dllPath)) + existingCandidates[plug->shellPluginID] = i; + } + } + + // Add found plugins to list or update them if they already exist + std::set<uint32> containedIDs; + VSTPluginLib *first = nullptr; + for(auto &containedPlug : containedPlugins) + { + containedIDs.insert(containedPlug.shellPluginID); + VSTPluginLib *found = nullptr; + if(auto it = existingCandidates.find(containedPlug.shellPluginID); it != existingCandidates.end()) + { + auto &plug = pluginList[it->second]; + MPT_ASSERT(plug->pluginId1 == containedPlug.pluginId1 && plug->pluginId2 == containedPlug.pluginId2); + if(plug->shellPluginID == containedPlug.shellPluginID) + found = plug.get(); + } + const bool updateExisting = found != nullptr; + if(updateExisting) + { + found->libraryName = containedPlug.libraryName; + } else + { + auto &plug = pluginList.emplace_back(std::make_unique<VSTPluginLib>(std::move(containedPlug))); + found = plug.get(); + newPlugins.push_back(found); + } + updateFunc(*found, updateExisting); + + if(found) + { + if(!first) + first = found; + +#ifdef MODPLUG_TRACKER + found->WriteToCache(); +#endif // MODPLUG_TRACKER + } + } + + // Are there any shell plugins in our list that are no longer part of the shell plugin? + if(containedPlugins.size() > 1) + { + size_t deleted = 0; + for(const auto &[id, i] : existingCandidates) + { + if(!containedIDs.contains(id) && !pluginList[i - deleted]->pPluginsList) + { + MPT_ASSERT(pluginList[i - deleted]->shellPluginID == id); + pluginList.erase(pluginList.begin() + i - deleted); + deleted++; + } + } + } + + if(newPlugins.empty() && first) + newPlugins.push_back(first); + return newPlugins; +} +#endif // MPT_WITH_VST + + #ifdef MODPLUG_TRACKER void CVstPluginManager::OnIdle() { Modified: trunk/OpenMPT/soundlib/plugins/PluginManager.h ============================================================================== --- trunk/OpenMPT/soundlib/plugins/PluginManager.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/soundlib/plugins/PluginManager.h Sun Aug 4 23:58:03 2024 (r21326) @@ -12,6 +12,8 @@ #include "openmpt/all/BuildSettings.hpp" +#include <functional> + OPENMPT_NAMESPACE_BEGIN constexpr int32 PLUGMAGIC(char a, char b, char c, char d) noexcept @@ -67,6 +69,7 @@ #endif // MODPLUG_TRACKER int32 pluginId1 = 0; // Plugin type (kEffectMagic, kDmoMagic, ...) int32 pluginId2 = 0; // Plugin unique ID + uint32 shellPluginID = 0; // ID of shell child plugin PluginCategory category = PluginCategory::Unknown; const bool isBuiltIn : 1; bool isInstrument : 1; @@ -75,22 +78,28 @@ mutable uint8 dllArch = 0; public: - VSTPluginLib(CreateProc factoryProc, bool isBuiltIn, const mpt::PathString &dllPath, const mpt::PathString &libraryName -#ifdef MODPLUG_TRACKER - , const mpt::ustring &tags = mpt::ustring(), const CString &vendor = CString() -#endif // MODPLUG_TRACKER - ) + VSTPluginLib(CreateProc factoryProc, bool isBuiltIn, mpt::PathString dllPath, mpt::PathString libraryName) : Create(factoryProc) - , libraryName(libraryName), dllPath(dllPath) -#ifdef MODPLUG_TRACKER - , tags(tags) - , vendor(vendor) -#endif // MODPLUG_TRACKER + , libraryName(std::move(libraryName)), dllPath(std::move(dllPath)) , category(PluginCategory::Unknown) , isBuiltIn(isBuiltIn), isInstrument(false) , useBridge(false), shareBridgeInstance(true), modernBridge(true) { } + VSTPluginLib(VSTPluginLib &&) = default; + VSTPluginLib(const VSTPluginLib &other) + : Create(other.Create) + , libraryName(other.libraryName), dllPath(other.dllPath) +#ifdef MODPLUG_TRACKER + , tags(other.tags), vendor(other.vendor) +#endif // MODPLUG_TRACKER + , pluginId1(other.pluginId1), pluginId2(other.pluginId2), shellPluginID(other.shellPluginID) + , category(other.category) + , isBuiltIn(other.isBuiltIn), isInstrument(other.isInstrument) + , useBridge(other.useBridge), shareBridgeInstance(other.shareBridgeInstance), modernBridge(other.modernBridge) + , dllArch(other.dllArch) + { + } #ifdef MPT_WITH_VST @@ -151,14 +160,14 @@ #if defined(MPT_WITH_DMO) bool MustUnInitilizeCOM = false; #endif - std::vector<VSTPluginLib *> pluginList; + std::vector<std::unique_ptr<VSTPluginLib>> pluginList; public: CVstPluginManager(); ~CVstPluginManager(); - using iterator = std::vector<VSTPluginLib *>::iterator; - using const_iterator = std::vector<VSTPluginLib *>::const_iterator; + using iterator = decltype(pluginList)::iterator; + using const_iterator = decltype(pluginList)::const_iterator; iterator begin() { return pluginList.begin(); } const_iterator begin() const { return pluginList.begin(); } @@ -168,7 +177,7 @@ size_t size() const { return pluginList.size(); } bool IsValidPlugin(const VSTPluginLib *pLib) const; - VSTPluginLib *AddPlugin(const mpt::PathString &dllPath, bool maskCrashes, const mpt::ustring &tags = mpt::ustring(), bool fromCache = true, bool *fileFound = nullptr); + std::vector<VSTPluginLib *> AddPlugin(const mpt::PathString &dllPath, bool maskCrashes, bool fromCache = true, bool *fileFound = nullptr, uint32 shellPluginID = 0); bool RemovePlugin(VSTPluginLib *); bool CreateMixPlugin(SNDMIXPLUGIN &, CSoundFile &); void OnIdle(); @@ -177,6 +186,8 @@ protected: void EnumerateDirectXDMOs(); + std::vector<VSTPluginLib *> AddPluginsToList(std::vector<VSTPluginLib> containedPlugins, std::function<void(VSTPluginLib &, bool)> updateFunc); + #else // NO_PLUGINS public: const VSTPluginLib **begin() const { return nullptr; } Modified: trunk/OpenMPT/soundlib/plugins/PluginStructs.h ============================================================================== --- trunk/OpenMPT/soundlib/plugins/PluginStructs.h Sun Aug 4 23:52:56 2024 (r21325) +++ trunk/OpenMPT/soundlib/plugins/PluginStructs.h Sun Aug 4 23:58:03 2024 (r21326) @@ -55,16 +55,17 @@ irAutoSuspend = 0x10, // Plugin will automatically suspend on silence }; - int32le dwPluginId1; // Plugin type (kEffectMagic, kDmoMagic, kBuzzMagic) + int32le dwPluginId1; // Plugin type (kEffectMagic, kDmoMagic or custom for built-in plugins) int32le dwPluginId2; // Plugin unique ID uint8le routingFlags; // See RoutingFlags uint8le mixMode; uint8le gain; // Divide by 10 to get real gain uint8le reserved; uint32le dwOutputRouting; // 0 = send to master 0x80 + x = send to plugin x - uint32le dwReserved[4]; // Reserved for routing info + uint32le shellPluginID; // For shell plugins: The child plugin to load + uint32le dwReserved[3]; // Reserved for routing info mpt::modecharbuf<32, mpt::String::nullTerminated> szName; // User-chosen plugin display name - this is locale ANSI! - mpt::modecharbuf<64, mpt::String::nullTerminated> szLibraryName; // original DLL name - this is UTF-8! + mpt::modecharbuf<64, mpt::String::nullTerminated> szLibraryName; // original DLL name (shell plugins: child plugin name) - this is UTF-8! // Should only be called from SNDMIXPLUGIN::SetBypass() and IMixPlugin::Bypass() void SetBypass(bool bypass = true) { if(bypass) routingFlags |= irBypass; else routingFlags &= uint8(~irBypass); } |