PluginManager.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. /*
  2. * PluginManager.cpp
  3. * -----------------
  4. * Purpose: Implementation of the plugin manager, which keeps a list of known plugins and instantiates them.
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #ifndef NO_PLUGINS
  11. #include "../../common/version.h"
  12. #include "PluginManager.h"
  13. #include "PlugInterface.h"
  14. #include "mpt/uuid/guid.hpp"
  15. #include "mpt/uuid/uuid.hpp"
  16. // Built-in plugins
  17. #include "DigiBoosterEcho.h"
  18. #include "LFOPlugin.h"
  19. #include "SymMODEcho.h"
  20. #include "dmo/DMOPlugin.h"
  21. #include "dmo/Chorus.h"
  22. #include "dmo/Compressor.h"
  23. #include "dmo/Distortion.h"
  24. #include "dmo/Echo.h"
  25. #include "dmo/Flanger.h"
  26. #include "dmo/Gargle.h"
  27. #include "dmo/I3DL2Reverb.h"
  28. #include "dmo/ParamEq.h"
  29. #include "dmo/WavesReverb.h"
  30. #ifdef MODPLUG_TRACKER
  31. #include "../../mptrack/plugins/MidiInOut.h"
  32. #endif // MODPLUG_TRACKER
  33. #include "../../common/mptStringBuffer.h"
  34. #include "../Sndfile.h"
  35. #include "../Loaders.h"
  36. #ifdef MPT_WITH_VST
  37. #include "../../mptrack/Vstplug.h"
  38. #include "../../pluginBridge/BridgeWrapper.h"
  39. #endif // MPT_WITH_VST
  40. #if defined(MPT_WITH_DMO)
  41. #include <winreg.h>
  42. #include <strmif.h>
  43. #include <tchar.h>
  44. #endif // MPT_WITH_DMO
  45. #ifdef MODPLUG_TRACKER
  46. #include "../../mptrack/Mptrack.h"
  47. #include "../../mptrack/TrackerSettings.h"
  48. #include "../../mptrack/AbstractVstEditor.h"
  49. #include "../../soundlib/AudioCriticalSection.h"
  50. #include "../mptrack/ExceptionHandler.h"
  51. #include "mpt/crc/crc.hpp"
  52. #endif // MODPLUG_TRACKER
  53. OPENMPT_NAMESPACE_BEGIN
  54. using namespace mpt::uuid_literals;
  55. #ifdef MPT_ALL_LOGGING
  56. #define VST_LOG
  57. #define DMO_LOG
  58. #endif
  59. #ifdef MODPLUG_TRACKER
  60. static constexpr const mpt::uchar *cacheSection = UL_("PluginCache");
  61. #endif // MODPLUG_TRACKER
  62. #ifdef MPT_WITH_VST
  63. uint8 VSTPluginLib::GetNativePluginArch()
  64. {
  65. uint8 result = 0;
  66. switch(mpt::OS::Windows::GetProcessArchitecture())
  67. {
  68. case mpt::OS::Windows::Architecture::x86:
  69. result = PluginArch_x86;
  70. break;
  71. case mpt::OS::Windows::Architecture::amd64:
  72. result = PluginArch_amd64;
  73. break;
  74. case mpt::OS::Windows::Architecture::arm:
  75. result = PluginArch_arm;
  76. break;
  77. case mpt::OS::Windows::Architecture::arm64:
  78. result = PluginArch_arm64;
  79. break;
  80. default:
  81. result = 0;
  82. break;
  83. }
  84. return result;
  85. }
  86. mpt::ustring VSTPluginLib::GetPluginArchName(uint8 arch)
  87. {
  88. mpt::ustring result;
  89. switch(arch)
  90. {
  91. case PluginArch_x86:
  92. result = U_("x86");
  93. break;
  94. case PluginArch_amd64:
  95. result = U_("amd64");
  96. break;
  97. case PluginArch_arm:
  98. result = U_("arm");
  99. break;
  100. case PluginArch_arm64:
  101. result = U_("arm64");
  102. break;
  103. default:
  104. result = U_("");
  105. break;
  106. }
  107. return result;
  108. }
  109. mpt::ustring VSTPluginLib::GetPluginArchNameUser(uint8 arch)
  110. {
  111. mpt::ustring result;
  112. #if defined(MPT_WITH_WINDOWS10)
  113. switch(arch)
  114. {
  115. case PluginArch_x86:
  116. result = U_("x86 (32bit)");
  117. break;
  118. case PluginArch_amd64:
  119. result = U_("amd64 (64bit)");
  120. break;
  121. case PluginArch_arm:
  122. result = U_("arm (32bit)");
  123. break;
  124. case PluginArch_arm64:
  125. result = U_("arm64 (64bit)");
  126. break;
  127. default:
  128. result = U_("");
  129. break;
  130. }
  131. #else // !MPT_WITH_WINDOWS10
  132. switch(arch)
  133. {
  134. case PluginArch_x86:
  135. result = U_("32-Bit");
  136. break;
  137. case PluginArch_amd64:
  138. result = U_("64-Bit");
  139. break;
  140. case PluginArch_arm:
  141. result = U_("32-Bit");
  142. break;
  143. case PluginArch_arm64:
  144. result = U_("64-Bit");
  145. break;
  146. default:
  147. result = U_("");
  148. break;
  149. }
  150. #endif // MPT_WITH_WINDOWS10
  151. return result;
  152. }
  153. uint8 VSTPluginLib::GetDllArch(bool fromCache) const
  154. {
  155. // Built-in plugins are always native.
  156. if(dllPath.empty())
  157. return GetNativePluginArch();
  158. #ifdef MPT_WITH_VST
  159. if(!dllArch || !fromCache)
  160. {
  161. dllArch = static_cast<uint8>(BridgeWrapper::GetPluginBinaryType(dllPath));
  162. }
  163. #else // !MPT_WITH_VST
  164. MPT_UNREFERENCED_PARAMETER(fromCache);
  165. #endif // MPT_WITH_VST
  166. return dllArch;
  167. }
  168. mpt::ustring VSTPluginLib::GetDllArchName(bool fromCache) const
  169. {
  170. return GetPluginArchName(GetDllArch(fromCache));
  171. }
  172. mpt::ustring VSTPluginLib::GetDllArchNameUser(bool fromCache) const
  173. {
  174. return GetPluginArchNameUser(GetDllArch(fromCache));
  175. }
  176. bool VSTPluginLib::IsNative(bool fromCache) const
  177. {
  178. return GetDllArch(fromCache) == GetNativePluginArch();
  179. }
  180. bool VSTPluginLib::IsNativeFromCache() const
  181. {
  182. return dllArch == GetNativePluginArch() || dllArch == 0;
  183. }
  184. #endif // MPT_WITH_VST
  185. // PluginCache format:
  186. // FullDllPath = <ID1><ID2><CRC32> (hex-encoded)
  187. // <ID1><ID2><CRC32>.Flags = Plugin Flags (see VSTPluginLib::DecodeCacheFlags).
  188. // <ID1><ID2><CRC32>.Vendor = Plugin Vendor String.
  189. #ifdef MODPLUG_TRACKER
  190. void VSTPluginLib::WriteToCache() const
  191. {
  192. SettingsContainer &cacheFile = theApp.GetPluginCache();
  193. const std::string crcName = dllPath.ToUTF8();
  194. const mpt::crc32 crc(crcName);
  195. const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc.result());
  196. mpt::PathString writePath = dllPath;
  197. if(theApp.IsPortableMode())
  198. {
  199. writePath = theApp.PathAbsoluteToInstallRelative(writePath);
  200. }
  201. cacheFile.Write<mpt::ustring>(cacheSection, writePath.ToUnicode(), IDs);
  202. cacheFile.Write<CString>(cacheSection, IDs + U_(".Vendor"), vendor);
  203. cacheFile.Write<int32>(cacheSection, IDs + U_(".Flags"), EncodeCacheFlags());
  204. }
  205. #endif // MODPLUG_TRACKER
  206. bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile)
  207. {
  208. #ifdef MODPLUG_TRACKER
  209. CVstPluginManager *that = theApp.GetPluginManager();
  210. if(that)
  211. {
  212. return that->CreateMixPlugin(mixPlugin, sndFile);
  213. }
  214. return false;
  215. #else
  216. if(!sndFile.m_PluginManager)
  217. {
  218. sndFile.m_PluginManager = std::make_unique<CVstPluginManager>();
  219. }
  220. return sndFile.m_PluginManager->CreateMixPlugin(mixPlugin, sndFile);
  221. #endif // MODPLUG_TRACKER
  222. }
  223. CVstPluginManager::CVstPluginManager()
  224. {
  225. #if defined(MPT_WITH_DMO)
  226. HRESULT COMinit = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  227. if(COMinit == S_OK || COMinit == S_FALSE)
  228. {
  229. MustUnInitilizeCOM = true;
  230. }
  231. #endif
  232. // Hard-coded "plugins"
  233. static constexpr struct
  234. {
  235. VSTPluginLib::CreateProc createProc;
  236. const char *filename, *name;
  237. uint32 pluginId1, pluginId2;
  238. VSTPluginLib::PluginCategory category;
  239. bool isInstrument, isOurs;
  240. } BuiltInPlugins[] =
  241. {
  242. // DirectX Media Objects Emulation
  243. { DMO::Chorus::Create, "{EFE6629C-81F7-4281-BD91-C9D604A95AF6}", "Chorus", kDmoMagic, 0xEFE6629C, VSTPluginLib::catDMO, false, false },
  244. { DMO::Compressor::Create, "{EF011F79-4000-406D-87AF-BFFB3FC39D57}", "Compressor", kDmoMagic, 0xEF011F79, VSTPluginLib::catDMO, false, false },
  245. { DMO::Distortion::Create, "{EF114C90-CD1D-484E-96E5-09CFAF912A21}", "Distortion", kDmoMagic, 0xEF114C90, VSTPluginLib::catDMO, false, false },
  246. { DMO::Echo::Create, "{EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D}", "Echo", kDmoMagic, 0xEF3E932C, VSTPluginLib::catDMO, false, false },
  247. { DMO::Flanger::Create, "{EFCA3D92-DFD8-4672-A603-7420894BAD98}", "Flanger", kDmoMagic, 0xEFCA3D92, VSTPluginLib::catDMO, false, false },
  248. { DMO::Gargle::Create, "{DAFD8210-5711-4B91-9FE3-F75B7AE279BF}", "Gargle", kDmoMagic, 0xDAFD8210, VSTPluginLib::catDMO, false, false },
  249. { DMO::I3DL2Reverb::Create, "{EF985E71-D5C7-42D4-BA4D-2D073E2E96F4}", "I3DL2Reverb", kDmoMagic, 0xEF985E71, VSTPluginLib::catDMO, false, false },
  250. { DMO::ParamEq::Create, "{120CED89-3BF4-4173-A132-3CB406CF3231}", "ParamEq", kDmoMagic, 0x120CED89, VSTPluginLib::catDMO, false, false },
  251. { DMO::WavesReverb::Create, "{87FC0268-9A55-4360-95AA-004A1D9DE26C}", "WavesReverb", kDmoMagic, 0x87FC0268, VSTPluginLib::catDMO, false, false },
  252. // First (inaccurate) Flanger implementation (will be chosen based on library name, shares ID1 and ID2 with regular Flanger)
  253. { DMO::Flanger::CreateLegacy, "{EFCA3D92-DFD8-4672-A603-7420894BAD98}", "Flanger (Legacy)", kDmoMagic, 0xEFCA3D92, VSTPluginLib::catHidden, false, false },
  254. // DigiBooster Pro Echo DSP
  255. { DigiBoosterEcho::Create, "", "DigiBooster Pro Echo", MagicLE("DBM0"), MagicLE("Echo"), VSTPluginLib::catRoomFx, false, true },
  256. // LFO
  257. { LFOPlugin::Create, "", "LFO", MagicLE("OMPT"), MagicLE("LFO "), VSTPluginLib::catGenerator, false, true },
  258. // SymMOD Echo
  259. { SymMODEcho::Create, "", "SymMOD Echo", MagicLE("SymM"), MagicLE("Echo"), VSTPluginLib::catRoomFx, false, true },
  260. #ifdef MODPLUG_TRACKER
  261. { MidiInOut::Create, "", "MIDI Input Output", PLUGMAGIC('V','s','t','P'), PLUGMAGIC('M','M','I','D'), VSTPluginLib::catSynth, true, true },
  262. #endif // MODPLUG_TRACKER
  263. };
  264. pluginList.reserve(std::size(BuiltInPlugins));
  265. for(const auto &plugin : BuiltInPlugins)
  266. {
  267. VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(plugin.createProc, true, mpt::PathString::FromUTF8(plugin.filename), mpt::PathString::FromUTF8(plugin.name));
  268. if(plug != nullptr)
  269. {
  270. pluginList.push_back(plug);
  271. plug->pluginId1 = plugin.pluginId1;
  272. plug->pluginId2 = plugin.pluginId2;
  273. plug->category = plugin.category;
  274. plug->isInstrument = plugin.isInstrument;
  275. #ifdef MODPLUG_TRACKER
  276. if(plugin.isOurs)
  277. plug->vendor = _T("OpenMPT Project");
  278. #endif // MODPLUG_TRACKER
  279. }
  280. }
  281. #ifdef MODPLUG_TRACKER
  282. // For security reasons, we do not load untrusted DMO plugins in libopenmpt.
  283. EnumerateDirectXDMOs();
  284. #endif
  285. }
  286. CVstPluginManager::~CVstPluginManager()
  287. {
  288. for(auto &plug : pluginList)
  289. {
  290. while(plug->pPluginsList != nullptr)
  291. {
  292. plug->pPluginsList->Release();
  293. }
  294. delete plug;
  295. }
  296. #if defined(MPT_WITH_DMO)
  297. if(MustUnInitilizeCOM)
  298. {
  299. CoUninitialize();
  300. MustUnInitilizeCOM = false;
  301. }
  302. #endif
  303. }
  304. bool CVstPluginManager::IsValidPlugin(const VSTPluginLib *pLib) const
  305. {
  306. return mpt::contains(pluginList, pLib);
  307. }
  308. void CVstPluginManager::EnumerateDirectXDMOs()
  309. {
  310. #if defined(MPT_WITH_DMO)
  311. static constexpr mpt::UUID knownDMOs[] =
  312. {
  313. "745057C7-F353-4F2D-A7EE-58434477730E"_uuid, // AEC (Acoustic echo cancellation, not usable)
  314. "EFE6629C-81F7-4281-BD91-C9D604A95AF6"_uuid, // Chorus
  315. "EF011F79-4000-406D-87AF-BFFB3FC39D57"_uuid, // Compressor
  316. "EF114C90-CD1D-484E-96E5-09CFAF912A21"_uuid, // Distortion
  317. "EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D"_uuid, // Echo
  318. "EFCA3D92-DFD8-4672-A603-7420894BAD98"_uuid, // Flanger
  319. "DAFD8210-5711-4B91-9FE3-F75B7AE279BF"_uuid, // Gargle
  320. "EF985E71-D5C7-42D4-BA4D-2D073E2E96F4"_uuid, // I3DL2Reverb
  321. "120CED89-3BF4-4173-A132-3CB406CF3231"_uuid, // ParamEq
  322. "87FC0268-9A55-4360-95AA-004A1D9DE26C"_uuid, // WavesReverb
  323. "F447B69E-1884-4A7E-8055-346F74D6EDB3"_uuid, // Resampler DMO (not usable)
  324. };
  325. HKEY hkEnum;
  326. TCHAR keyname[128];
  327. LONG cr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("software\\classes\\DirectShow\\MediaObjects\\Categories\\f3602b3f-0592-48df-a4cd-674721e7ebeb"), 0, KEY_READ, &hkEnum);
  328. DWORD index = 0;
  329. while (cr == ERROR_SUCCESS)
  330. {
  331. if ((cr = RegEnumKey(hkEnum, index, keyname, mpt::saturate_cast<DWORD>(std::size(keyname)))) == ERROR_SUCCESS)
  332. {
  333. CLSID clsid;
  334. mpt::winstring formattedKey = mpt::winstring(_T("{")) + mpt::winstring(keyname) + mpt::winstring(_T("}"));
  335. if(mpt::VerifyStringToCLSID(formattedKey, clsid))
  336. {
  337. if(!mpt::contains(knownDMOs, clsid))
  338. {
  339. HKEY hksub;
  340. formattedKey = mpt::winstring(_T("software\\classes\\DirectShow\\MediaObjects\\")) + mpt::winstring(keyname);
  341. if (RegOpenKey(HKEY_LOCAL_MACHINE, formattedKey.c_str(), &hksub) == ERROR_SUCCESS)
  342. {
  343. TCHAR name[64];
  344. DWORD datatype = REG_SZ;
  345. DWORD datasize = sizeof(name);
  346. if(ERROR_SUCCESS == RegQueryValueEx(hksub, nullptr, 0, &datatype, (LPBYTE)name, &datasize))
  347. {
  348. VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(DMOPlugin::Create, true, mpt::PathString::FromNative(mpt::GUIDToString(clsid)), mpt::PathString::FromNative(ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes<mpt::winstring>(name, datasize)));
  349. if(plug != nullptr)
  350. {
  351. try
  352. {
  353. pluginList.push_back(plug);
  354. plug->pluginId1 = kDmoMagic;
  355. plug->pluginId2 = clsid.Data1;
  356. plug->category = VSTPluginLib::catDMO;
  357. } catch(mpt::out_of_memory e)
  358. {
  359. mpt::delete_out_of_memory(e);
  360. delete plug;
  361. }
  362. #ifdef DMO_LOG
  363. MPT_LOG_GLOBAL(LogDebug, "DMO", MPT_UFORMAT("Found \"{}\" clsid={}\n")(plug->libraryName, plug->dllPath));
  364. #endif
  365. }
  366. }
  367. RegCloseKey(hksub);
  368. }
  369. }
  370. }
  371. }
  372. index++;
  373. }
  374. if (hkEnum) RegCloseKey(hkEnum);
  375. #endif // MPT_WITH_DMO
  376. }
  377. // Extract instrument and category information from plugin.
  378. #ifdef MPT_WITH_VST
  379. static void GetPluginInformation(bool maskCrashes, Vst::AEffect *effect, VSTPluginLib &library)
  380. {
  381. unsigned long exception = 0;
  382. library.category = static_cast<VSTPluginLib::PluginCategory>(CVstPlugin::DispatchSEH(maskCrashes, effect, Vst::effGetPlugCategory, 0, 0, nullptr, 0, exception));
  383. library.isInstrument = ((effect->flags & Vst::effFlagsIsSynth) || !effect->numInputs);
  384. if(library.isInstrument)
  385. {
  386. library.category = VSTPluginLib::catSynth;
  387. } else if(library.category >= VSTPluginLib::numCategories)
  388. {
  389. library.category = VSTPluginLib::catUnknown;
  390. }
  391. #ifdef MODPLUG_TRACKER
  392. std::vector<char> s(256, 0);
  393. CVstPlugin::DispatchSEH(maskCrashes, effect, Vst::effGetVendorString, 0, 0, s.data(), 0, exception);
  394. library.vendor = mpt::ToCString(mpt::Charset::Locale, s.data());
  395. #endif // MODPLUG_TRACKER
  396. }
  397. #endif // MPT_WITH_VST
  398. #ifdef MPT_WITH_VST
  399. static bool TryLoadPlugin(bool maskCrashes, VSTPluginLib *plug, HINSTANCE hLib, unsigned long &exception)
  400. {
  401. Vst::AEffect *pEffect = CVstPlugin::LoadPlugin(maskCrashes, *plug, hLib, CVstPlugin::BridgeMode::DetectRequiredBridgeMode);
  402. if(!pEffect || pEffect->magic != Vst::kEffectMagic || !pEffect->dispatcher)
  403. {
  404. return false;
  405. }
  406. CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effOpen, 0, 0, 0, 0, exception);
  407. plug->pluginId1 = pEffect->magic;
  408. plug->pluginId2 = pEffect->uniqueID;
  409. GetPluginInformation(maskCrashes, pEffect, *plug);
  410. #ifdef VST_LOG
  411. intptr_t nver = CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effGetVstVersion, 0,0, nullptr, 0, exception);
  412. if (!nver) nver = pEffect->version;
  413. MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("{}: v{}.0, {} in, {} out, {} programs, {} params, flags=0x{} realQ={} offQ={}")(
  414. plug->libraryName, nver,
  415. pEffect->numInputs, pEffect->numOutputs,
  416. mpt::ufmt::dec0<2>(pEffect->numPrograms), mpt::ufmt::dec0<2>(pEffect->numParams),
  417. mpt::ufmt::HEX0<4>(static_cast<int32>(pEffect->flags)), pEffect->realQualities, pEffect->offQualities));
  418. #endif // VST_LOG
  419. CVstPlugin::DispatchSEH(maskCrashes, pEffect, Vst::effClose, 0, 0, 0, 0, exception);
  420. return true;
  421. }
  422. #endif // !NO_NVST
  423. #ifdef MODPLUG_TRACKER
  424. // Add a plugin to the list of known plugins.
  425. VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, bool maskCrashes, const mpt::ustring &tags, bool fromCache, bool *fileFound)
  426. {
  427. const mpt::PathString fileName = dllPath.GetFileName();
  428. // Check if this is already a known plugin.
  429. for(const auto &dupePlug : pluginList)
  430. {
  431. if(!dllPath.CompareNoCase(dllPath, dupePlug->dllPath)) return dupePlug;
  432. }
  433. if(fileFound != nullptr)
  434. {
  435. *fileFound = dllPath.IsFile();
  436. }
  437. // Look if the plugin info is stored in the PluginCache
  438. if(fromCache)
  439. {
  440. SettingsContainer & cacheFile = theApp.GetPluginCache();
  441. // First try finding the full path
  442. mpt::ustring IDs = cacheFile.Read<mpt::ustring>(cacheSection, dllPath.ToUnicode(), U_(""));
  443. if(IDs.length() < 16)
  444. {
  445. // If that didn't work out, find relative path
  446. mpt::PathString relPath = theApp.PathAbsoluteToInstallRelative(dllPath);
  447. IDs = cacheFile.Read<mpt::ustring>(cacheSection, relPath.ToUnicode(), U_(""));
  448. }
  449. if(IDs.length() >= 16)
  450. {
  451. VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(nullptr, false, dllPath, fileName, tags);
  452. if(plug == nullptr)
  453. {
  454. return nullptr;
  455. }
  456. pluginList.push_back(plug);
  457. // Extract plugin IDs
  458. for (int i = 0; i < 16; i++)
  459. {
  460. int32 n = IDs[i] - '0';
  461. if (n > 9) n = IDs[i] + 10 - 'A';
  462. n &= 0x0f;
  463. if (i < 8)
  464. {
  465. plug->pluginId1 = (plug->pluginId1 << 4) | n;
  466. } else
  467. {
  468. plug->pluginId2 = (plug->pluginId2 << 4) | n;
  469. }
  470. }
  471. const mpt::ustring flagKey = IDs + U_(".Flags");
  472. plug->DecodeCacheFlags(cacheFile.Read<int32>(cacheSection, flagKey, 0));
  473. plug->vendor = cacheFile.Read<CString>(cacheSection, IDs + U_(".Vendor"), CString());
  474. #ifdef VST_LOG
  475. MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("Plugin \"{}\" found in PluginCache")(plug->libraryName));
  476. #endif // VST_LOG
  477. return plug;
  478. } else
  479. {
  480. #ifdef VST_LOG
  481. MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("Plugin mismatch in PluginCache: \"{}\" [{}]")(dllPath, IDs));
  482. #endif // VST_LOG
  483. }
  484. }
  485. // If this key contains a file name on program launch, a plugin previously crashed OpenMPT.
  486. theApp.GetSettings().Write<mpt::PathString>(U_("VST Plugins"), U_("FailedPlugin"), dllPath, SettingWriteThrough);
  487. bool validPlug = false;
  488. VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(nullptr, false, dllPath, fileName, tags);
  489. if(plug == nullptr)
  490. {
  491. return nullptr;
  492. }
  493. #ifdef MPT_WITH_VST
  494. unsigned long exception = 0;
  495. // Always scan plugins in a separate process
  496. HINSTANCE hLib = NULL;
  497. {
  498. #ifdef MODPLUG_TRACKER
  499. ExceptionHandler::Context ectx{ MPT_UFORMAT("VST Plugin: {}")(plug->dllPath.ToUnicode()) };
  500. ExceptionHandler::ContextSetter ectxguard{&ectx};
  501. #endif // MODPLUG_TRACKER
  502. validPlug = TryLoadPlugin(maskCrashes, plug, hLib, exception);
  503. }
  504. if(hLib)
  505. {
  506. FreeLibrary(hLib);
  507. }
  508. if(exception != 0)
  509. {
  510. CVstPluginManager::ReportPlugException(MPT_UFORMAT("Exception {} while trying to load plugin \"{}\"!\n")(mpt::ufmt::HEX0<8>(exception), plug->libraryName));
  511. }
  512. #endif // MPT_WITH_VST
  513. // Now it should be safe to assume that this plugin loaded properly. :)
  514. theApp.GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin"));
  515. // If OK, write the information in PluginCache
  516. if(validPlug)
  517. {
  518. pluginList.push_back(plug);
  519. plug->WriteToCache();
  520. } else
  521. {
  522. delete plug;
  523. }
  524. return (validPlug ? plug : nullptr);
  525. }
  526. // Remove a plugin from the list of known plugins and release any remaining instances of it.
  527. bool CVstPluginManager::RemovePlugin(VSTPluginLib *pFactory)
  528. {
  529. for(const_iterator p = begin(); p != end(); p++)
  530. {
  531. VSTPluginLib *plug = *p;
  532. if(plug == pFactory)
  533. {
  534. // Kill all instances of this plugin
  535. CriticalSection cs;
  536. while(plug->pPluginsList != nullptr)
  537. {
  538. plug->pPluginsList->Release();
  539. }
  540. pluginList.erase(p);
  541. delete plug;
  542. return true;
  543. }
  544. }
  545. return false;
  546. }
  547. #endif // MODPLUG_TRACKER
  548. // Create an instance of a plugin.
  549. bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile)
  550. {
  551. VSTPluginLib *pFound = nullptr;
  552. // Find plugin in library
  553. enum PlugMatchQuality
  554. {
  555. kNoMatch,
  556. kMatchName,
  557. kMatchId,
  558. kMatchNameAndId,
  559. };
  560. PlugMatchQuality match = kNoMatch; // "Match quality" of found plugin. Higher value = better match.
  561. #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
  562. const mpt::PathString libraryName = mpt::PathString::FromUnicode(mixPlugin.GetLibraryName());
  563. #else
  564. const std::string libraryName = mpt::ToCharset(mpt::Charset::UTF8, mixPlugin.GetLibraryName());
  565. #endif
  566. for(const auto &plug : pluginList)
  567. {
  568. const bool matchID = (plug->pluginId1 == mixPlugin.Info.dwPluginId1)
  569. && (plug->pluginId2 == mixPlugin.Info.dwPluginId2);
  570. #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
  571. const bool matchName = !mpt::PathString::CompareNoCase(plug->libraryName, libraryName);
  572. #else
  573. const bool matchName = !mpt::CompareNoCaseAscii(plug->libraryName.ToUTF8(), libraryName);
  574. #endif
  575. if(matchID && matchName)
  576. {
  577. pFound = plug;
  578. #ifdef MPT_WITH_VST
  579. if(plug->IsNative(false))
  580. {
  581. break;
  582. }
  583. #endif // MPT_WITH_VST
  584. // If the plugin isn't native, first check if a native version can be found.
  585. match = kMatchNameAndId;
  586. } else if(matchID && match < kMatchId)
  587. {
  588. pFound = plug;
  589. match = kMatchId;
  590. } else if(matchName && match < kMatchName)
  591. {
  592. pFound = plug;
  593. match = kMatchName;
  594. }
  595. }
  596. if(pFound != nullptr && pFound->Create != nullptr)
  597. {
  598. IMixPlugin *plugin = pFound->Create(*pFound, sndFile, &mixPlugin);
  599. return plugin != nullptr;
  600. }
  601. #ifdef MODPLUG_TRACKER
  602. bool maskCrashes = TrackerSettings::Instance().BrokenPluginsWorkaroundVSTMaskAllCrashes;
  603. if(!pFound && (mixPlugin.GetLibraryName() != U_("")))
  604. {
  605. // Try finding the plugin DLL in the plugin directory or plugin cache instead.
  606. mpt::PathString fullPath = TrackerSettings::Instance().PathPlugins.GetDefaultDir();
  607. if(fullPath.empty())
  608. {
  609. fullPath = theApp.GetInstallPath() + P_("Plugins\\");
  610. }
  611. fullPath += mpt::PathString::FromUnicode(mixPlugin.GetLibraryName()) + P_(".dll");
  612. pFound = AddPlugin(fullPath, maskCrashes);
  613. if(!pFound)
  614. {
  615. // Try plugin cache (search for library name)
  616. SettingsContainer &cacheFile = theApp.GetPluginCache();
  617. mpt::ustring IDs = cacheFile.Read<mpt::ustring>(cacheSection, mixPlugin.GetLibraryName(), U_(""));
  618. if(IDs.length() >= 16)
  619. {
  620. fullPath = cacheFile.Read<mpt::PathString>(cacheSection, IDs, P_(""));
  621. if(!fullPath.empty())
  622. {
  623. fullPath = theApp.PathInstallRelativeToAbsolute(fullPath);
  624. if(fullPath.IsFile())
  625. {
  626. pFound = AddPlugin(fullPath, maskCrashes);
  627. }
  628. }
  629. }
  630. }
  631. }
  632. #ifdef MPT_WITH_VST
  633. if(pFound && mixPlugin.Info.dwPluginId1 == Vst::kEffectMagic)
  634. {
  635. Vst::AEffect *pEffect = nullptr;
  636. HINSTANCE hLibrary = nullptr;
  637. bool validPlugin = false;
  638. pEffect = CVstPlugin::LoadPlugin(maskCrashes, *pFound, hLibrary, TrackerSettings::Instance().bridgeAllPlugins ? CVstPlugin::BridgeMode::ForceBridgeWithFallback : CVstPlugin::BridgeMode::Automatic);
  639. if(pEffect != nullptr && pEffect->dispatcher != nullptr && pEffect->magic == Vst::kEffectMagic)
  640. {
  641. validPlugin = true;
  642. GetPluginInformation(maskCrashes, pEffect, *pFound);
  643. // Update cached information
  644. pFound->WriteToCache();
  645. CVstPlugin *pVstPlug = new (std::nothrow) CVstPlugin(maskCrashes, hLibrary, *pFound, mixPlugin, *pEffect, sndFile);
  646. if(pVstPlug == nullptr)
  647. {
  648. validPlugin = false;
  649. }
  650. }
  651. if(!validPlugin)
  652. {
  653. FreeLibrary(hLibrary);
  654. CVstPluginManager::ReportPlugException(MPT_UFORMAT("Unable to create plugin \"{}\"!\n")(pFound->libraryName));
  655. }
  656. return validPlugin;
  657. } else
  658. {
  659. // "plug not found" notification code MOVED to CSoundFile::Create
  660. #ifdef VST_LOG
  661. MPT_LOG_GLOBAL(LogDebug, "VST", U_("Unknown plugin"));
  662. #endif
  663. }
  664. #endif // MPT_WITH_VST
  665. #endif // MODPLUG_TRACKER
  666. return false;
  667. }
  668. #ifdef MODPLUG_TRACKER
  669. void CVstPluginManager::OnIdle()
  670. {
  671. for(auto &factory : pluginList)
  672. {
  673. // Note: bridged plugins won't receive these messages and generate their own idle messages.
  674. IMixPlugin *p = factory->pPluginsList;
  675. while (p)
  676. {
  677. //rewbs. VSTCompliance: A specific plug has requested indefinite periodic processing time.
  678. p->Idle();
  679. //We need to update all open editors
  680. CAbstractVstEditor *editor = p->GetEditor();
  681. if (editor && editor->m_hWnd)
  682. {
  683. editor->UpdateParamDisplays();
  684. }
  685. //end rewbs. VSTCompliance:
  686. p = p->GetNextInstance();
  687. }
  688. }
  689. }
  690. void CVstPluginManager::ReportPlugException(const mpt::ustring &msg)
  691. {
  692. Reporting::Notification(msg);
  693. #ifdef VST_LOG
  694. MPT_LOG_GLOBAL(LogDebug, "VST", mpt::ToUnicode(msg));
  695. #endif
  696. }
  697. #endif // MODPLUG_TRACKER
  698. OPENMPT_NAMESPACE_END
  699. #endif // NO_PLUGINS