Vstplug.cpp 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762
  1. /*
  2. * Vstplug.cpp
  3. * -----------
  4. * Purpose: VST Plugin handling / processing
  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. #ifdef MPT_WITH_VST
  11. #include "Vstplug.h"
  12. #ifdef MODPLUG_TRACKER
  13. #include "Moddoc.h"
  14. #include "Mainfrm.h"
  15. #include "AbstractVstEditor.h"
  16. #include "VSTEditor.h"
  17. #include "DefaultVstEditor.h"
  18. #include "ExceptionHandler.h"
  19. #endif // MODPLUG_TRACKER
  20. #include "../soundlib/Sndfile.h"
  21. #include "../soundlib/MIDIEvents.h"
  22. #include "MIDIMappingDialog.h"
  23. #include "../common/mptStringBuffer.h"
  24. #include "FileDialog.h"
  25. #include "../pluginBridge/BridgeWrapper.h"
  26. #include "../pluginBridge/BridgeOpCodes.h"
  27. #include "../soundlib/plugins/OpCodes.h"
  28. #include "../soundlib/plugins/PluginManager.h"
  29. #include "../misc/mptOSException.h"
  30. using namespace Vst;
  31. DECLARE_FLAGSET(Vst::VstTimeInfoFlags)
  32. OPENMPT_NAMESPACE_BEGIN
  33. static VstTimeInfo g_timeInfoFallback = { 0 };
  34. #ifdef MPT_ALL_LOGGING
  35. #define VST_LOG
  36. #endif
  37. using VstCrash = Windows::SEH::Code;
  38. bool CVstPlugin::MaskCrashes() noexcept
  39. {
  40. return m_maskCrashes;
  41. }
  42. template <typename Tfn>
  43. DWORD CVstPlugin::SETryOrError(bool maskCrashes, Tfn fn)
  44. {
  45. DWORD exception = 0;
  46. if(maskCrashes)
  47. {
  48. exception = Windows::SEH::TryOrError(fn);
  49. if(exception)
  50. {
  51. ExceptionHandler::TaintProcess(ExceptionHandler::TaintReason::Plugin);
  52. }
  53. } else
  54. {
  55. fn();
  56. }
  57. return exception;
  58. }
  59. template <typename Tfn>
  60. DWORD CVstPlugin::SETryOrError(Tfn fn)
  61. {
  62. DWORD exception = 0;
  63. if(MaskCrashes())
  64. {
  65. exception = Windows::SEH::TryOrError(fn);
  66. if(exception)
  67. {
  68. ExceptionHandler::TaintProcess(ExceptionHandler::TaintReason::Plugin);
  69. }
  70. } else
  71. {
  72. #ifdef MODPLUG_TRACKER
  73. ExceptionHandler::ContextSetter ectxguard{&m_Ectx};
  74. #endif // MODPLUG_TRACKER
  75. fn();
  76. }
  77. return exception;
  78. }
  79. AEffect *CVstPlugin::LoadPlugin(bool maskCrashes, VSTPluginLib &plugin, HMODULE &library, BridgeMode bridgeMode)
  80. {
  81. const mpt::PathString &pluginPath = plugin.dllPath;
  82. AEffect *effect = nullptr;
  83. library = nullptr;
  84. const bool isNative = plugin.IsNative(false);
  85. if(bridgeMode != BridgeMode::Automatic || plugin.useBridge || !isNative)
  86. {
  87. if(bridgeMode == BridgeMode::DetectRequiredBridgeMode)
  88. {
  89. // First try modern bridge, then legacy bridge
  90. plugin.modernBridge = true;
  91. try
  92. {
  93. effect = BridgeWrapper::Create(plugin, false);
  94. if(effect != nullptr)
  95. {
  96. return effect;
  97. }
  98. } catch(BridgeWrapper::BridgeNotFoundException &)
  99. {
  100. } catch(BridgeWrapper::BridgeException &)
  101. {
  102. }
  103. // Retry with legacy bridge
  104. plugin.useBridge = true;
  105. plugin.modernBridge = false;
  106. }
  107. try
  108. {
  109. effect = BridgeWrapper::Create(plugin, bridgeMode == BridgeMode::DetectRequiredBridgeMode);
  110. if(effect != nullptr)
  111. {
  112. return effect;
  113. }
  114. } catch(BridgeWrapper::BridgeNotFoundException &)
  115. {
  116. // Try normal loading
  117. if(!isNative)
  118. {
  119. Reporting::Error("Could not locate the plugin bridge executable, which is required for running non-native plugins.", "OpenMPT Plugin Bridge");
  120. return nullptr;
  121. }
  122. } catch(BridgeWrapper::BridgeException &e)
  123. {
  124. // If there was some error, don't try normal loading as well... unless the user really wants it.
  125. if(isNative)
  126. {
  127. const CString msg =
  128. MPT_CFORMAT("The following error occurred while trying to load\n{}\n\n{}\n\nDo you want to try to load the plugin natively?")
  129. (plugin.dllPath, mpt::get_exception_text<mpt::ustring>(e));
  130. if(Reporting::Confirm(msg, _T("OpenMPT Plugin Bridge")) == cnfNo)
  131. {
  132. return nullptr;
  133. }
  134. } else
  135. {
  136. Reporting::Error(mpt::get_exception_text<mpt::ustring>(e), "OpenMPT Plugin Bridge");
  137. return nullptr;
  138. }
  139. }
  140. // If plugin was marked to use the plugin bridge but this somehow doesn't work (e.g. because the bridge is missing),
  141. // disable the plugin bridge for this plugin.
  142. plugin.useBridge = false;
  143. plugin.modernBridge = true;
  144. }
  145. {
  146. #ifdef MODPLUG_TRACKER
  147. ExceptionHandler::Context ectx{ MPT_UFORMAT("VST Plugin: {}")(plugin.dllPath.ToUnicode()) };
  148. ExceptionHandler::ContextSetter ectxguard{&ectx};
  149. #endif // MODPLUG_TRACKER
  150. DWORD exception = SETryOrError(maskCrashes, [&](){ library = LoadLibrary(pluginPath.AsNative().c_str()); });
  151. if(exception)
  152. {
  153. CVstPluginManager::ReportPlugException(MPT_UFORMAT("Exception caught while loading {}")(pluginPath));
  154. return nullptr;
  155. }
  156. }
  157. if(library == nullptr)
  158. {
  159. DWORD error = GetLastError();
  160. if(error == ERROR_MOD_NOT_FOUND)
  161. {
  162. return nullptr;
  163. } else if(error == ERROR_DLL_INIT_FAILED)
  164. {
  165. // 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.
  166. // Before Windows 10 1903, there was a limit of 128 FLS slots per process, and the VS2017 runtime uses two FLS slots, so this could cause a worst-case limit
  167. // of 62 different plugins per process (assuming they all use a statically-linked runtime).
  168. // In Windows 10 1903, the FLS limit was finally raised, so this message is mostly relevant for older systems.
  169. CVstPluginManager::ReportPlugException(U_("Plugin initialization failed. This may be caused by loading too many plugins.\nTry activating the Plugin Bridge for this plugin."));
  170. }
  171. #ifdef _DEBUG
  172. mpt::ustring buf = MPT_UFORMAT("Warning: encountered problem when loading plugin dll. Error {}: {}")
  173. ( mpt::ufmt::hex(error)
  174. , mpt::ToUnicode(mpt::windows::GetErrorMessage(error))
  175. );
  176. Reporting::Error(buf, "DEBUG: Error when loading plugin dll");
  177. #endif //_DEBUG
  178. }
  179. if(library != nullptr && library != INVALID_HANDLE_VALUE)
  180. {
  181. auto pMainProc = (Vst::MainProc)GetProcAddress(library, "VSTPluginMain");
  182. if(pMainProc == nullptr)
  183. {
  184. pMainProc = (Vst::MainProc)GetProcAddress(library, "main");
  185. }
  186. if(pMainProc != nullptr)
  187. {
  188. #ifdef MODPLUG_TRACKER
  189. ExceptionHandler::Context ectx{ MPT_UFORMAT("VST Plugin: {}")(plugin.dllPath.ToUnicode()) };
  190. ExceptionHandler::ContextSetter ectxguard{&ectx};
  191. #endif // MODPLUG_TRACKER
  192. DWORD exception = SETryOrError(maskCrashes, [&](){ effect = pMainProc(CVstPlugin::MasterCallBack); });
  193. if(exception)
  194. {
  195. return nullptr;
  196. }
  197. } else
  198. {
  199. #ifdef VST_LOG
  200. MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("Entry point not found! (handle={})")(mpt::ufmt::PTR(library)));
  201. #endif // VST_LOG
  202. return nullptr;
  203. }
  204. }
  205. return effect;
  206. }
  207. static void operator|= (Vst::VstTimeInfoFlags &lhs, Vst::VstTimeInfoFlags rhs)
  208. {
  209. lhs = (lhs | rhs).as_enum();
  210. }
  211. intptr_t VSTCALLBACK CVstPlugin::MasterCallBack(AEffect *effect, VstOpcodeToHost opcode, int32 index, intptr_t value, void *ptr, float opt)
  212. {
  213. #ifdef VST_LOG
  214. MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("VST plugin to host: Eff: {}, Opcode = {}, Index = {}, Value = {}, PTR = {}, OPT = {}\n")(
  215. mpt::ufmt::PTR(effect), mpt::ufmt::val(opcode),
  216. mpt::ufmt::val(index), mpt::ufmt::PTR(value), mpt::ufmt::PTR(ptr), mpt::ufmt::flt(opt, 3)));
  217. MPT_TRACE();
  218. #else
  219. MPT_UNREFERENCED_PARAMETER(opt);
  220. #endif
  221. enum
  222. {
  223. HostDoNotKnow = 0,
  224. HostCanDo = 1,
  225. HostCanNotDo = -1
  226. };
  227. CVstPlugin *pVstPlugin = nullptr;
  228. if(effect != nullptr)
  229. {
  230. pVstPlugin = static_cast<CVstPlugin *>(effect->reservedForHost1);
  231. }
  232. switch(opcode)
  233. {
  234. // Called when plugin param is changed via gui
  235. case audioMasterAutomate:
  236. // Strum Acoustic GS-1 and Strum Electric GS-1 send audioMasterAutomate during effOpen (WTF #1),
  237. // but when sending back effCanBeAutomated, they just crash (WTF #2).
  238. // As a consequence, just generally forbid this action while the plugin is not fully initialized yet.
  239. if(pVstPlugin != nullptr && pVstPlugin->m_isInitialized && pVstPlugin->CanAutomateParameter(index))
  240. {
  241. // This parameter can be automated. Ugo Motion constantly sends automation callback events for parameters that cannot be automated...
  242. pVstPlugin->AutomateParameter((PlugParamIndex)index);
  243. }
  244. return 0;
  245. // Called when plugin asks for VST version supported by host
  246. case audioMasterVersion:
  247. return kVstVersion;
  248. // Returns the unique id of a plugin that's currently loading
  249. // We don't support shell plugins currently, so we only support one effect ID as well.
  250. case audioMasterCurrentId:
  251. return (effect != nullptr) ? effect->uniqueID : 0;
  252. // Call application idle routine (this will call effEditIdle for all open editors too)
  253. case audioMasterIdle:
  254. theApp.GetPluginManager()->OnIdle();
  255. return 0;
  256. // Inquire if an input or output is beeing connected; index enumerates input or output counting from zero,
  257. // value is 0 for input and != 0 otherwise. note: the return value is 0 for <true> such that older versions
  258. // will always return true.
  259. case audioMasterPinConnected:
  260. if (value) //input:
  261. return (index < 2) ? 0 : 1; //we only support up to 2 inputs. Remember: 0 means yes.
  262. else //output:
  263. return (index < 2) ? 0 : 1; //2 outputs max too
  264. //---from here VST 2.0 extension opcodes------------------------------------------------------
  265. // <value> is a filter which is currently ignored - DEPRECATED in VST 2.4
  266. case audioMasterWantMidi:
  267. return 1;
  268. // returns const VstTimeInfo* (or 0 if not supported)
  269. // <value> should contain a mask indicating which fields are required
  270. case audioMasterGetTime:
  271. if(pVstPlugin)
  272. {
  273. VstTimeInfo &timeInfo = pVstPlugin->timeInfo;
  274. MemsetZero(timeInfo);
  275. timeInfo.sampleRate = pVstPlugin->m_nSampleRate;
  276. CSoundFile &sndFile = pVstPlugin->GetSoundFile();
  277. if(pVstPlugin->IsSongPlaying())
  278. {
  279. timeInfo.flags |= kVstTransportPlaying;
  280. if(pVstPlugin->GetSoundFile().m_SongFlags[SONG_PATTERNLOOP]) timeInfo.flags |= kVstTransportCycleActive;
  281. timeInfo.samplePos = sndFile.GetTotalSampleCount();
  282. if(pVstPlugin->m_positionChanged)
  283. {
  284. timeInfo.flags |= kVstTransportChanged;
  285. pVstPlugin->lastBarStartPos = -1.0;
  286. }
  287. } else
  288. {
  289. timeInfo.flags |= kVstTransportChanged; //just stopped.
  290. timeInfo.samplePos = 0;
  291. pVstPlugin->lastBarStartPos = -1.0;
  292. }
  293. if((value & kVstNanosValid))
  294. {
  295. timeInfo.flags |= kVstNanosValid;
  296. timeInfo.nanoSeconds = static_cast<double>(Util::mul32to64_unsigned(timeGetTime(), 1000000));
  297. }
  298. if((value & kVstPpqPosValid))
  299. {
  300. timeInfo.flags |= kVstPpqPosValid;
  301. if (timeInfo.flags & kVstTransportPlaying)
  302. {
  303. timeInfo.ppqPos = (timeInfo.samplePos / timeInfo.sampleRate) * (sndFile.GetCurrentBPM() / 60.0);
  304. } else
  305. {
  306. timeInfo.ppqPos = 0;
  307. }
  308. ROWINDEX rpm = pVstPlugin->GetSoundFile().m_PlayState.m_nCurrentRowsPerMeasure;
  309. if(!rpm)
  310. rpm = 4;
  311. if((pVstPlugin->GetSoundFile().m_PlayState.m_nRow % rpm) == 0)
  312. {
  313. pVstPlugin->lastBarStartPos = std::floor(timeInfo.ppqPos);
  314. }
  315. if(pVstPlugin->lastBarStartPos >= 0)
  316. {
  317. timeInfo.barStartPos = pVstPlugin->lastBarStartPos;
  318. timeInfo.flags |= kVstBarsValid;
  319. }
  320. }
  321. if((value & kVstTempoValid))
  322. {
  323. timeInfo.tempo = sndFile.GetCurrentBPM();
  324. if (timeInfo.tempo)
  325. {
  326. timeInfo.flags |= kVstTempoValid;
  327. }
  328. }
  329. if((value & kVstTimeSigValid))
  330. {
  331. timeInfo.flags |= kVstTimeSigValid;
  332. // Time signature. numerator = rows per beats / rows pear measure (should sound somewhat logical to you).
  333. // the denominator is a bit more tricky, since it cannot be set explicitely. so we just assume quarters for now.
  334. ROWINDEX rpb = std::max(sndFile.m_PlayState.m_nCurrentRowsPerBeat, ROWINDEX(1));
  335. timeInfo.timeSigNumerator = std::max(sndFile.m_PlayState.m_nCurrentRowsPerMeasure, rpb) / rpb;
  336. timeInfo.timeSigDenominator = 4; //std::gcd(pSndFile->m_nCurrentRowsPerMeasure, pSndFile->m_nCurrentRowsPerBeat);
  337. }
  338. return ToIntPtr(&timeInfo);
  339. } else
  340. {
  341. MemsetZero(g_timeInfoFallback);
  342. return ToIntPtr(&g_timeInfoFallback);
  343. }
  344. // Receive MIDI events from plugin
  345. case audioMasterProcessEvents:
  346. if(pVstPlugin != nullptr && ptr != nullptr)
  347. {
  348. pVstPlugin->ReceiveVSTEvents(static_cast<VstEvents *>(ptr));
  349. return 1;
  350. }
  351. break;
  352. // DEPRECATED in VST 2.4
  353. case audioMasterSetTime:
  354. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Set Time"));
  355. break;
  356. // returns tempo (in bpm * 10000) at sample frame location passed in <value> - DEPRECATED in VST 2.4
  357. case audioMasterTempoAt:
  358. // Screw it! Let's just return the tempo at this point in time (might be a bit wrong).
  359. if (pVstPlugin != nullptr)
  360. {
  361. return mpt::saturate_round<int32>(pVstPlugin->GetSoundFile().GetCurrentBPM() * 10000);
  362. }
  363. return (125 * 10000);
  364. // parameters - DEPRECATED in VST 2.4
  365. case audioMasterGetNumAutomatableParameters:
  366. //MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Get Num Automatable Parameters"));
  367. if(pVstPlugin != nullptr)
  368. {
  369. return pVstPlugin->GetNumParameters();
  370. }
  371. break;
  372. // Apparently, this one is broken in VST SDK anyway. - DEPRECATED in VST 2.4
  373. case audioMasterGetParameterQuantization:
  374. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Audio Master Get Parameter Quantization"));
  375. break;
  376. // numInputs and/or numOutputs has changed
  377. case audioMasterIOChanged:
  378. if(pVstPlugin != nullptr)
  379. {
  380. CriticalSection cs;
  381. return pVstPlugin->InitializeIOBuffers() ? 1 : 0;
  382. }
  383. break;
  384. // Plugin needs idle calls (outside its editor window) - DEPRECATED in VST 2.4
  385. case audioMasterNeedIdle:
  386. if(pVstPlugin != nullptr)
  387. {
  388. pVstPlugin->m_needIdle = true;
  389. }
  390. return 1;
  391. // index: width, value: height
  392. case audioMasterSizeWindow:
  393. if(pVstPlugin != nullptr)
  394. {
  395. CAbstractVstEditor *pVstEditor = pVstPlugin->GetEditor();
  396. if (pVstEditor && pVstEditor->IsResizable())
  397. {
  398. pVstEditor->SetSize(index, static_cast<int>(value));
  399. }
  400. }
  401. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Size Window"));
  402. return 1;
  403. case audioMasterGetSampleRate:
  404. if(pVstPlugin)
  405. {
  406. return pVstPlugin->m_nSampleRate;
  407. } else
  408. {
  409. // HERCs Abakos queries the sample rate while the plugin is being created and then never again...
  410. return TrackerSettings::Instance().GetMixerSettings().gdwMixingFreq;
  411. }
  412. case audioMasterGetBlockSize:
  413. return MIXBUFFERSIZE;
  414. case audioMasterGetInputLatency:
  415. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Get Input Latency"));
  416. break;
  417. case audioMasterGetOutputLatency:
  418. if(pVstPlugin)
  419. {
  420. return mpt::saturate_round<intptr_t>(pVstPlugin->GetOutputLatency() * pVstPlugin->GetSoundFile().GetSampleRate());
  421. }
  422. break;
  423. // input pin in <value> (-1: first to come), returns cEffect* - DEPRECATED in VST 2.4
  424. case audioMasterGetPreviousPlug:
  425. if(pVstPlugin != nullptr)
  426. {
  427. std::vector<IMixPlugin *> list;
  428. if(pVstPlugin->GetInputPlugList(list) != 0)
  429. {
  430. // We don't assign plugins to pins...
  431. CVstPlugin *plugin = dynamic_cast<CVstPlugin *>(list[0]);
  432. if(plugin != nullptr)
  433. {
  434. return ToIntPtr(&plugin->m_Effect);
  435. }
  436. }
  437. }
  438. break;
  439. // output pin in <value> (-1: first to come), returns cEffect* - DEPRECATED in VST 2.4
  440. case audioMasterGetNextPlug:
  441. if(pVstPlugin != nullptr)
  442. {
  443. std::vector<IMixPlugin *> list;
  444. if(pVstPlugin->GetOutputPlugList(list) != 0)
  445. {
  446. // We don't assign plugins to pins...
  447. CVstPlugin *plugin = dynamic_cast<CVstPlugin *>(list[0]);
  448. if(plugin != nullptr)
  449. {
  450. return ToIntPtr(&plugin->m_Effect);
  451. }
  452. }
  453. }
  454. break;
  455. // realtime info
  456. // returns: 0: not supported, 1: replace, 2: accumulate - DEPRECATED in VST 2.4 (replace is default)
  457. case audioMasterWillReplaceOrAccumulate:
  458. return 1; //we replace.
  459. case audioMasterGetCurrentProcessLevel:
  460. if(pVstPlugin != nullptr && pVstPlugin->GetSoundFile().IsRenderingToDisc())
  461. return kVstProcessLevelOffline;
  462. else
  463. return kVstProcessLevelRealtime;
  464. break;
  465. // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
  466. case audioMasterGetAutomationState:
  467. // Not entirely sure what this means. We can write automation TO the plug.
  468. // Is that "read" in this context?
  469. //MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Get Automation State"));
  470. return kVstAutomationReadWrite;
  471. case audioMasterOfflineStart:
  472. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Offlinestart"));
  473. break;
  474. case audioMasterOfflineRead:
  475. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Offlineread"));
  476. break;
  477. case audioMasterOfflineWrite:
  478. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Offlinewrite"));
  479. break;
  480. case audioMasterOfflineGetCurrentPass:
  481. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: OfflineGetcurrentpass"));
  482. break;
  483. case audioMasterOfflineGetCurrentMetaPass:
  484. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: OfflineGetCurrentMetapass"));
  485. break;
  486. // for variable i/o, sample rate in <opt> - DEPRECATED in VST 2.4
  487. case audioMasterSetOutputSampleRate:
  488. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Set Output Sample Rate"));
  489. break;
  490. // result in ret - DEPRECATED in VST 2.4
  491. case audioMasterGetOutputSpeakerArrangement:
  492. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Get Output Speaker Arrangement"));
  493. break;
  494. case audioMasterGetVendorString:
  495. strcpy((char *) ptr, TrackerSettings::Instance().vstHostVendorString.Get().c_str());
  496. return 1;
  497. case audioMasterGetProductString:
  498. strcpy((char *) ptr, TrackerSettings::Instance().vstHostProductString.Get().c_str());
  499. return 1;
  500. case audioMasterGetVendorVersion:
  501. return TrackerSettings::Instance().vstHostVendorVersion;
  502. case audioMasterVendorSpecific:
  503. return 0;
  504. // void* in <ptr>, format not defined yet - DEPRECATED in VST 2.4
  505. case audioMasterSetIcon:
  506. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Set Icon"));
  507. break;
  508. // string in ptr, see below
  509. case audioMasterCanDo:
  510. //Other possible Can Do strings are:
  511. if(!strcmp((char*)ptr, HostCanDo::sendVstEvents)
  512. || !strcmp((char *)ptr, HostCanDo::sendVstMidiEvent)
  513. || !strcmp((char *)ptr, HostCanDo::sendVstTimeInfo)
  514. || !strcmp((char *)ptr, HostCanDo::receiveVstEvents)
  515. || !strcmp((char *)ptr, HostCanDo::receiveVstMidiEvent)
  516. || !strcmp((char *)ptr, HostCanDo::supplyIdle)
  517. || !strcmp((char *)ptr, HostCanDo::sizeWindow)
  518. || !strcmp((char *)ptr, HostCanDo::openFileSelector)
  519. || !strcmp((char *)ptr, HostCanDo::closeFileSelector)
  520. || !strcmp((char *)ptr, HostCanDo::acceptIOChanges)
  521. || !strcmp((char *)ptr, HostCanDo::reportConnectionChanges))
  522. {
  523. return HostCanDo;
  524. } else
  525. {
  526. return HostCanNotDo;
  527. }
  528. case audioMasterGetLanguage:
  529. return kVstLangEnglish;
  530. // returns platform specific ptr - DEPRECATED in VST 2.4
  531. case audioMasterOpenWindow:
  532. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Open Window"));
  533. break;
  534. // close window, platform specific handle in <ptr> - DEPRECATED in VST 2.4
  535. case audioMasterCloseWindow:
  536. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Close Window"));
  537. break;
  538. // get plugin directory, FSSpec on MAC, else char*
  539. case audioMasterGetDirectory:
  540. //MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Get Directory"));
  541. // Need to allocate space for path only, but I guess noone relies on this anyway.
  542. //return ToVstPtr(pVstPlugin->GetPluginFactory().dllPath.GetPath().ToLocale());
  543. //return ToVstPtr(TrackerSettings::Instance().PathPlugins.GetDefaultDir());
  544. break;
  545. // something has changed, update 'multi-fx' display
  546. case audioMasterUpdateDisplay:
  547. if(pVstPlugin != nullptr)
  548. {
  549. // Note to self for testing: Electri-Q sends opcode. Korg M1 sends this when switching between Combi and Multi mode to update the preset names.
  550. CAbstractVstEditor *pVstEditor = pVstPlugin->GetEditor();
  551. if(pVstEditor && ::IsWindow(pVstEditor->m_hWnd))
  552. {
  553. pVstEditor->UpdateDisplay();
  554. }
  555. }
  556. return 0;
  557. //---from here VST 2.1 extension opcodes------------------------------------------------------
  558. // begin of automation session (when mouse down), parameter index in <index>
  559. case audioMasterBeginEdit:
  560. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Begin Edit"));
  561. break;
  562. // end of automation session (when mouse up), parameter index in <index>
  563. case audioMasterEndEdit:
  564. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: End Edit"));
  565. break;
  566. // open a fileselector window with VstFileSelect* in <ptr>
  567. case audioMasterOpenFileSelector:
  568. //---from here VST 2.2 extension opcodes------------------------------------------------------
  569. // close a fileselector operation with VstFileSelect* in <ptr>: Must be always called after an open !
  570. case audioMasterCloseFileSelector:
  571. if(pVstPlugin != nullptr && ptr != nullptr)
  572. {
  573. return pVstPlugin->VstFileSelector(opcode == audioMasterCloseFileSelector, *static_cast<VstFileSelect *>(ptr));
  574. }
  575. // open an editor for audio (defined by XML text in ptr) - DEPRECATED in VST 2.4
  576. case audioMasterEditFile:
  577. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Edit File"));
  578. break;
  579. // get the native path of currently loading bank or project
  580. // (called from writeChunk) void* in <ptr> (char[2048], or sizeof(FSSpec)) - DEPRECATED in VST 2.4
  581. // Note: The shortcircuit VSTi actually uses this feature.
  582. case audioMasterGetChunkFile:
  583. #ifdef MODPLUG_TRACKER
  584. if(pVstPlugin && pVstPlugin->GetModDoc())
  585. {
  586. mpt::ustring pathStr = TrackerSettings::Instance().pluginProjectPath;
  587. if(pathStr.empty())
  588. {
  589. pathStr = U_("%1");
  590. }
  591. const mpt::PathString projectPath = pVstPlugin->GetModDoc()->GetPathNameMpt().GetPath();
  592. const mpt::PathString projectFile = pVstPlugin->GetModDoc()->GetPathNameMpt().GetFullFileName();
  593. pathStr = mpt::String::Replace(pathStr, U_("%1"), U_("?1?"));
  594. pathStr = mpt::String::Replace(pathStr, U_("%2"), U_("?2?"));
  595. pathStr = mpt::String::Replace(pathStr, U_("?1?"), projectPath.ToUnicode());
  596. pathStr = mpt::String::Replace(pathStr, U_("?2?"), projectFile.ToUnicode());
  597. mpt::PathString path = mpt::PathString::FromUnicode(pathStr);
  598. if(path.empty())
  599. {
  600. return 0;
  601. }
  602. path.EnsureTrailingSlash();
  603. ::SHCreateDirectoryEx(NULL, path.AsNative().c_str(), nullptr);
  604. path += projectFile;
  605. strcpy(static_cast<char*>(ptr), path.ToLocale().c_str());
  606. return 1;
  607. }
  608. #endif
  609. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Get Chunk File"));
  610. break;
  611. //---from here VST 2.3 extension opcodes------------------------------------------------------
  612. // result a VstSpeakerArrangement in ret - DEPRECATED in VST 2.4
  613. case audioMasterGetInputSpeakerArrangement:
  614. MPT_LOG_GLOBAL(LogDebug, "VST", U_("VST plugin to host: Get Input Speaker Arrangement"));
  615. break;
  616. }
  617. // Unknown codes:
  618. return 0;
  619. }
  620. // Helper function for file selection dialog stuff.
  621. intptr_t CVstPlugin::VstFileSelector(bool destructor, VstFileSelect &fileSel)
  622. {
  623. if(!destructor)
  624. {
  625. fileSel.returnMultiplePaths = nullptr;
  626. fileSel.numReturnPaths = 0;
  627. fileSel.reserved = 0;
  628. std::string returnPath;
  629. if(fileSel.command != kVstDirectorySelect)
  630. {
  631. // Plugin wants to load or save a file.
  632. std::string extensions, workingDir;
  633. for(int32 i = 0; i < fileSel.numFileTypes; i++)
  634. {
  635. const VstFileType &type = fileSel.fileTypes[i];
  636. extensions += type.name;
  637. extensions += "|";
  638. #if MPT_OS_WINDOWS
  639. extensions += "*.";
  640. extensions += type.dosType;
  641. #elif MPT_OS_MACOSX_OR_IOS
  642. extensions += "*";
  643. extensions += type.macType;
  644. #elif MPT_OS_GENERIC_UNIX
  645. extensions += "*.";
  646. extensions += type.unixType;
  647. #else
  648. #error Platform-specific code missing
  649. #endif
  650. extensions += "|";
  651. }
  652. extensions += "|";
  653. if(fileSel.initialPath != nullptr)
  654. {
  655. workingDir = fileSel.initialPath;
  656. } else
  657. {
  658. // Plugins are probably looking for presets...?
  659. //workingDir = TrackerSettings::Instance().PathPluginPresets.GetWorkingDir();
  660. }
  661. FileDialog dlg = OpenFileDialog();
  662. if(fileSel.command == kVstFileSave)
  663. {
  664. dlg = SaveFileDialog();
  665. } else if(fileSel.command == kVstMultipleFilesLoad)
  666. {
  667. dlg = OpenFileDialog().AllowMultiSelect();
  668. }
  669. dlg.ExtensionFilter(extensions)
  670. .WorkingDirectory(mpt::PathString::FromLocale(workingDir))
  671. .AddPlace(GetPluginFactory().dllPath.GetPath());
  672. if(!dlg.Show(GetEditor()))
  673. return 0;
  674. if(fileSel.command == kVstMultipleFilesLoad)
  675. {
  676. // Multiple paths
  677. const auto &files = dlg.GetFilenames();
  678. fileSel.numReturnPaths = mpt::saturate_cast<int32>(files.size());
  679. fileSel.returnMultiplePaths = new (std::nothrow) char *[fileSel.numReturnPaths];
  680. if(!fileSel.returnMultiplePaths)
  681. return 0;
  682. for(int32 i = 0; i < fileSel.numReturnPaths; i++)
  683. {
  684. const std::string fname_ = files[i].ToLocale();
  685. char *fname = new (std::nothrow) char[fname_.length() + 1];
  686. if(fname)
  687. strcpy(fname, fname_.c_str());
  688. fileSel.returnMultiplePaths[i] = fname;
  689. }
  690. return 1;
  691. } else
  692. {
  693. // Single path
  694. // VOPM doesn't initialize required information properly (it doesn't memset the struct to 0)...
  695. if(FourCC("VOPM") == GetUID())
  696. {
  697. fileSel.sizeReturnPath = _MAX_PATH;
  698. }
  699. returnPath = dlg.GetFirstFile().ToLocale();
  700. }
  701. } else
  702. {
  703. // Plugin wants a directory
  704. BrowseForFolder dlg(mpt::PathString::FromLocale(fileSel.initialPath != nullptr ? fileSel.initialPath : ""), mpt::ToCString(mpt::Charset::Locale, fileSel.title != nullptr ? fileSel.title : ""));
  705. if(!dlg.Show(GetEditor()))
  706. return 0;
  707. returnPath = dlg.GetDirectory().ToLocale();
  708. if(FourCC("VSTr") == GetUID() && fileSel.returnPath != nullptr && fileSel.sizeReturnPath == 0)
  709. {
  710. // Old versions of reViSiT (which still relied on the host's file selector) seem to be dodgy.
  711. // They report a path size of 0, but when using an own buffer, they will crash.
  712. // So we'll just assume that reViSiT can handle long enough (_MAX_PATH) paths here.
  713. fileSel.sizeReturnPath = mpt::saturate_cast<int32>(returnPath.length() + 1);
  714. }
  715. }
  716. // Return single path (file or directory)
  717. if(fileSel.returnPath == nullptr || fileSel.sizeReturnPath == 0)
  718. {
  719. // Provide some memory for the return path.
  720. fileSel.sizeReturnPath = mpt::saturate_cast<int32>(returnPath.length() + 1);
  721. fileSel.returnPath = new(std::nothrow) char[fileSel.sizeReturnPath];
  722. if(fileSel.returnPath == nullptr)
  723. {
  724. return 0;
  725. }
  726. fileSel.reserved = 1;
  727. } else
  728. {
  729. fileSel.reserved = 0;
  730. }
  731. const auto len = std::min(returnPath.size(), static_cast<size_t>(fileSel.sizeReturnPath - 1));
  732. strncpy(fileSel.returnPath, returnPath.data(), len);
  733. fileSel.returnPath[len] = '\0';
  734. fileSel.numReturnPaths = 1;
  735. fileSel.returnMultiplePaths = nullptr;
  736. return 1;
  737. } else
  738. {
  739. // Close file selector - delete allocated strings.
  740. if(fileSel.command == kVstMultipleFilesLoad && fileSel.returnMultiplePaths != nullptr)
  741. {
  742. for(int32 i = 0; i < fileSel.numReturnPaths; i++)
  743. {
  744. if(fileSel.returnMultiplePaths[i] != nullptr)
  745. {
  746. delete[] fileSel.returnMultiplePaths[i];
  747. }
  748. }
  749. delete[] fileSel.returnMultiplePaths;
  750. fileSel.returnMultiplePaths = nullptr;
  751. } else
  752. {
  753. if(fileSel.reserved == 1 && fileSel.returnPath != nullptr)
  754. {
  755. delete[] fileSel.returnPath;
  756. fileSel.returnPath = nullptr;
  757. }
  758. }
  759. return 1;
  760. }
  761. }
  762. //////////////////////////////////////////////////////////////////////////////
  763. //
  764. // CVstPlugin
  765. //
  766. CVstPlugin::CVstPlugin(bool maskCrashes, HMODULE hLibrary, VSTPluginLib &factory, SNDMIXPLUGIN &mixStruct, AEffect &effect, CSoundFile &sndFile)
  767. : IMidiPlugin(factory, sndFile, &mixStruct)
  768. , m_maskCrashes(maskCrashes)
  769. , m_Effect(effect)
  770. , timeInfo{}
  771. , isBridged(!memcmp(&effect.reservedForHost2, "OMPT", 4))
  772. , m_hLibrary(hLibrary)
  773. , m_nSampleRate(sndFile.GetSampleRate())
  774. , m_isInitialized(false)
  775. , m_needIdle(false)
  776. {
  777. // Open plugin and initialize data structures
  778. Initialize();
  779. InsertIntoFactoryList();
  780. m_isInitialized = true;
  781. }
  782. void CVstPlugin::Initialize()
  783. {
  784. m_Ectx = { MPT_UFORMAT("VST Plugin: {}")(m_Factory.dllPath.ToUnicode()) };
  785. // If filename matched during load but plugin ID didn't, make sure it's updated.
  786. m_pMixStruct->Info.dwPluginId1 = m_Factory.pluginId1 = m_Effect.magic;
  787. m_pMixStruct->Info.dwPluginId2 = m_Factory.pluginId2 = m_Effect.uniqueID;
  788. // Store a pointer so we can get the CVstPlugin object from the basic VST effect object.
  789. m_Effect.reservedForHost1 = this;
  790. m_nSampleRate = m_SndFile.GetSampleRate();
  791. // First try to let the plugin know the render parameters.
  792. Dispatch(effSetSampleRate, 0, 0, nullptr, static_cast<float>(m_nSampleRate));
  793. Dispatch(effSetBlockSize, 0, MIXBUFFERSIZE, nullptr, 0.0f);
  794. Dispatch(effOpen, 0, 0, nullptr, 0.0f);
  795. // VST 2.0 plugins return 2 here, VST 2.4 plugins return 2400... Great!
  796. m_isVst2 = Dispatch(effGetVstVersion, 0,0, nullptr, 0.0f) >= 2;
  797. if(m_isVst2)
  798. {
  799. // Set VST speaker in/out setup to Stereo. Required for some plugins (e.g. Voxengo SPAN 2)
  800. // All this might get more interesting when adding sidechaining support...
  801. VstSpeakerArrangement sa{};
  802. sa.numChannels = 2;
  803. sa.type = kSpeakerArrStereo;
  804. for(std::size_t i = 0; i < std::size(sa.speakers); i++)
  805. {
  806. // For now, only left and right speaker are used.
  807. switch(i)
  808. {
  809. case 0:
  810. sa.speakers[i].type = kSpeakerL;
  811. mpt::String::WriteAutoBuf(sa.speakers[i].name) = "Left";
  812. break;
  813. case 1:
  814. sa.speakers[i].type = kSpeakerR;
  815. mpt::String::WriteAutoBuf(sa.speakers[i].name) = "Right";
  816. break;
  817. default:
  818. sa.speakers[i].type = kSpeakerUndefined;
  819. break;
  820. }
  821. }
  822. // For some reason, this call crashes in a call to free() in AdmiralQuality NaiveLPF / SCAMP 1.2 (newer versions are fine).
  823. // This does not happen when running the plugin in pretty much any host, or when running in OpenMPT 1.22 and older
  824. // (EXCEPT when recompiling those old versions with VS2010), so it sounds like an ASLR issue to me.
  825. // AdmiralQuality also doesn't know what to do.
  826. if(GetUID() != FourCC("CSI4"))
  827. {
  828. // For now, input setup = output setup.
  829. Dispatch(effSetSpeakerArrangement, 0, ToIntPtr(&sa), &sa, 0.0f);
  830. }
  831. // Dummy pin properties collection.
  832. // We don't use them but some plugs might do inits in here.
  833. VstPinProperties tempPinProperties;
  834. Dispatch(effGetInputProperties, 0, 0, &tempPinProperties, 0);
  835. Dispatch(effGetOutputProperties, 0, 0, &tempPinProperties, 0);
  836. Dispatch(effConnectInput, 0, 1, nullptr, 0.0f);
  837. if (m_Effect.numInputs > 1) Dispatch(effConnectInput, 1, 1, nullptr, 0.0f);
  838. Dispatch(effConnectOutput, 0, 1, nullptr, 0.0f);
  839. if (m_Effect.numOutputs > 1) Dispatch(effConnectOutput, 1, 1, nullptr, 0.0f);
  840. // Disable all inputs and outputs beyond stereo left and right:
  841. for(int32 i = 2; i < m_Effect.numInputs; i++)
  842. Dispatch(effConnectInput, i, 0, nullptr, 0.0f);
  843. for(int32 i = 2; i < m_Effect.numOutputs; i++)
  844. Dispatch(effConnectOutput, i, 0, nullptr, 0.0f);
  845. }
  846. // Second try to let the plugin know the render parameters.
  847. Dispatch(effSetSampleRate, 0, 0, nullptr, static_cast<float>(m_nSampleRate));
  848. Dispatch(effSetBlockSize, 0, MIXBUFFERSIZE, nullptr, 0.0f);
  849. if(m_Effect.numPrograms > 0)
  850. {
  851. BeginSetProgram(0);
  852. EndSetProgram();
  853. }
  854. InitializeIOBuffers();
  855. Dispatch(effSetProcessPrecision, 0, kVstProcessPrecision32, nullptr, 0.0f);
  856. m_isInstrument = IsInstrument();
  857. RecalculateGain();
  858. m_pProcessFP = (m_Effect.flags & effFlagsCanReplacing) ? m_Effect.processReplacing : m_Effect.process;
  859. // Issue samplerate again here, cos some plugs like it before the block size, other like it right at the end.
  860. Dispatch(effSetSampleRate, 0, 0, nullptr, static_cast<float>(m_nSampleRate));
  861. // Korg Wavestation GUI won't work until plugin was resumed at least once.
  862. // On the other hand, some other plugins (notably Synthedit plugins like Superwave P8 2.3 or Rez 3.0) don't like this
  863. // and won't load their stored plugin data instantly, so only do this for the troublesome plugins...
  864. // Also apply this fix for Korg's M1 plugin, as this will fixes older versions of said plugin, newer versions don't require the fix.
  865. // EZDrummer / Superior Drummer won't load their samples until playback has started.
  866. if(GetUID() == FourCC("KLWV") // Wavestation
  867. || GetUID() == FourCC("KLM1") // M1
  868. || GetUID() == FourCC("dfhe") // EZDrummer
  869. || GetUID() == FourCC("dfh2")) // Superior Drummer
  870. {
  871. Resume();
  872. Suspend();
  873. }
  874. }
  875. bool CVstPlugin::InitializeIOBuffers()
  876. {
  877. // Input pointer array size must be >= 2 for now - the input buffer assignment might write to non allocated mem. otherwise
  878. // In case of a bridged plugin, the AEffect struct has been updated before calling this opcode, so we don't have to worry about it being up-to-date.
  879. return m_mixBuffer.Initialize(std::max(m_Effect.numInputs, int32(2)), m_Effect.numOutputs);
  880. }
  881. CVstPlugin::~CVstPlugin()
  882. {
  883. CriticalSection cs;
  884. CloseEditor();
  885. if (m_isVst2)
  886. {
  887. Dispatch(effConnectInput, 0, 0, nullptr, 0);
  888. if (m_Effect.numInputs > 1) Dispatch(effConnectInput, 1, 0, nullptr, 0);
  889. Dispatch(effConnectOutput, 0, 0, nullptr, 0);
  890. if (m_Effect.numOutputs > 1) Dispatch(effConnectOutput, 1, 0, nullptr, 0);
  891. }
  892. CVstPlugin::Suspend();
  893. m_isInitialized = false;
  894. Dispatch(effClose, 0, 0, nullptr, 0);
  895. if(TrackerSettings::Instance().BrokenPluginsWorkaroundVSTNeverUnloadAnyPlugin)
  896. {
  897. // Buggy SynthEdit 1.4 plugins: Showing a SynthEdit 1.4 plugin's editor, fully unloading the plugin,
  898. // then loading another (unrelated) SynthEdit 1.4 plugin and showing its editor causes a crash.
  899. } else
  900. {
  901. if(m_hLibrary)
  902. {
  903. FreeLibrary(m_hLibrary);
  904. }
  905. }
  906. }
  907. void CVstPlugin::Release()
  908. {
  909. delete this;
  910. }
  911. void CVstPlugin::Idle()
  912. {
  913. if(m_needIdle)
  914. {
  915. if(!(Dispatch(effIdle, 0, 0, nullptr, 0.0f)))
  916. m_needIdle = false;
  917. }
  918. if (m_pEditor && m_pEditor->m_hWnd)
  919. {
  920. Dispatch(effEditIdle, 0, 0, nullptr, 0.0f);
  921. }
  922. }
  923. int32 CVstPlugin::GetNumPrograms() const
  924. {
  925. return std::max(m_Effect.numPrograms, int32(0));
  926. }
  927. PlugParamIndex CVstPlugin::GetNumParameters() const
  928. {
  929. return m_Effect.numParams;
  930. }
  931. // Check whether a VST parameter can be automated
  932. bool CVstPlugin::CanAutomateParameter(PlugParamIndex index)
  933. {
  934. return (Dispatch(effCanBeAutomated, index, 0, nullptr, 0.0f) != 0);
  935. }
  936. int32 CVstPlugin::GetUID() const
  937. {
  938. return m_Effect.uniqueID;
  939. }
  940. int32 CVstPlugin::GetVersion() const
  941. {
  942. return m_Effect.version;
  943. }
  944. // Wrapper for VST dispatch call with structured exception handling.
  945. intptr_t CVstPlugin::DispatchSEH(bool maskCrashes, AEffect *effect, VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt, unsigned long &exception)
  946. {
  947. if(effect->dispatcher != nullptr)
  948. {
  949. intptr_t result = 0;
  950. DWORD e = SETryOrError(maskCrashes, [&](){ result = effect->dispatcher(effect, opCode, index, value, ptr, opt); });
  951. if(e)
  952. {
  953. exception = e;
  954. }
  955. return result;
  956. }
  957. return 0;
  958. }
  959. intptr_t CVstPlugin::Dispatch(VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt)
  960. {
  961. #ifdef VST_LOG
  962. {
  963. mpt::ustring codeStr;
  964. if(opCode >= 0 && static_cast<std::size_t>(opCode) < std::size(VstOpCodes))
  965. {
  966. codeStr = mpt::ToUnicode(mpt::Charset::ASCII, VstOpCodes[opCode]);
  967. } else
  968. {
  969. codeStr = mpt::ufmt::val(opCode);
  970. }
  971. MPT_LOG_GLOBAL(LogDebug, "VST", MPT_UFORMAT("About to Dispatch({}) (Plugin=\"{}\"), index: {}, value: {}, ptr: {}, opt: {}!\n")(codeStr, m_Factory.libraryName, index, mpt::ufmt::PTR(value), mpt::ufmt::PTR(ptr), mpt::ufmt::flt(opt, 3)));
  972. }
  973. #endif
  974. if(!m_Effect.dispatcher)
  975. {
  976. return 0;
  977. }
  978. intptr_t result = 0;
  979. {
  980. DWORD exception = SETryOrError([&](){ result = m_Effect.dispatcher(&m_Effect, opCode, index, value, ptr, opt); });
  981. if(exception)
  982. {
  983. mpt::ustring codeStr;
  984. if(opCode < mpt::saturate_cast<int32>(std::size(VstOpCodes)))
  985. {
  986. codeStr = mpt::ToUnicode(mpt::Charset::ASCII, VstOpCodes[opCode]);
  987. } else
  988. {
  989. codeStr = mpt::ufmt::val(opCode);
  990. }
  991. ReportPlugException(MPT_UFORMAT("Exception {} in Dispatch({})")(mpt::ufmt::HEX0<8>(exception), codeStr));
  992. }
  993. }
  994. return result;
  995. }
  996. int32 CVstPlugin::GetCurrentProgram()
  997. {
  998. if(m_Effect.numPrograms > 0)
  999. {
  1000. return static_cast<int32>(Dispatch(effGetProgram, 0, 0, nullptr, 0));
  1001. }
  1002. return 0;
  1003. }
  1004. CString CVstPlugin::GetCurrentProgramName()
  1005. {
  1006. std::vector<char> s(256, 0);
  1007. // kVstMaxProgNameLen is 24... too short for some plugins, so use at least 256 bytes.
  1008. Dispatch(effGetProgramName, 0, 0, s.data(), 0);
  1009. return mpt::ToCString(mpt::Charset::Locale, s.data());
  1010. }
  1011. void CVstPlugin::SetCurrentProgramName(const CString &name)
  1012. {
  1013. Dispatch(effSetProgramName, 0, 0, const_cast<char *>(mpt::ToCharset(mpt::Charset::Locale, name.Left(kVstMaxProgNameLen)).c_str()), 0.0f);
  1014. }
  1015. CString CVstPlugin::GetProgramName(int32 program)
  1016. {
  1017. // kVstMaxProgNameLen is 24... too short for some plugins, so use at least 256 bytes.
  1018. std::vector<char> rawname(256, 0);
  1019. if(program < m_Effect.numPrograms)
  1020. {
  1021. if(Dispatch(effGetProgramNameIndexed, program, -1 /*category*/, rawname.data(), 0) != 1)
  1022. {
  1023. // Fallback: Try to get current program name.
  1024. rawname.assign(256, 0);
  1025. int32 curProg = GetCurrentProgram();
  1026. if(program != curProg)
  1027. {
  1028. SetCurrentProgram(program);
  1029. }
  1030. Dispatch(effGetProgramName, 0, 0, rawname.data(), 0);
  1031. if(program != curProg)
  1032. {
  1033. SetCurrentProgram(curProg);
  1034. }
  1035. }
  1036. }
  1037. return mpt::ToCString(mpt::Charset::Locale, rawname.data());
  1038. }
  1039. void CVstPlugin::SetCurrentProgram(int32 nIndex)
  1040. {
  1041. if(m_Effect.numPrograms > 0)
  1042. {
  1043. if(nIndex < m_Effect.numPrograms)
  1044. {
  1045. BeginSetProgram(nIndex);
  1046. EndSetProgram();
  1047. }
  1048. }
  1049. }
  1050. void CVstPlugin::BeginSetProgram(int32 program)
  1051. {
  1052. Dispatch(effBeginSetProgram, 0, 0, nullptr, 0);
  1053. if(program != -1)
  1054. Dispatch(effSetProgram, 0, program, nullptr, 0);
  1055. }
  1056. void CVstPlugin::EndSetProgram()
  1057. {
  1058. Dispatch(effEndSetProgram, 0, 0, nullptr, 0);
  1059. }
  1060. void CVstPlugin::BeginGetProgram(int32 program)
  1061. {
  1062. if(program != -1)
  1063. Dispatch(effSetProgram, 0, program, nullptr, 0);
  1064. if(isBridged)
  1065. Dispatch(effVendorSpecific, kVendorOpenMPT, kBeginGetProgram, nullptr, 0);
  1066. }
  1067. void CVstPlugin::EndGetProgram()
  1068. {
  1069. if(isBridged)
  1070. Dispatch(effVendorSpecific, kVendorOpenMPT, kEndGetProgram, nullptr, 0);
  1071. }
  1072. PlugParamValue CVstPlugin::GetParameter(PlugParamIndex nIndex)
  1073. {
  1074. float fResult = 0;
  1075. if(nIndex < m_Effect.numParams && m_Effect.getParameter != nullptr)
  1076. {
  1077. DWORD exception = SETryOrError([&](){ fResult = m_Effect.getParameter(&m_Effect, nIndex); });
  1078. if(exception)
  1079. {
  1080. //ReportPlugException(U_("Exception in getParameter (Plugin=\"{}\")!\n"), m_Factory.szLibraryName);
  1081. }
  1082. }
  1083. return fResult;
  1084. }
  1085. void CVstPlugin::SetParameter(PlugParamIndex nIndex, PlugParamValue fValue)
  1086. {
  1087. DWORD exception = 0;
  1088. if(nIndex < m_Effect.numParams && m_Effect.setParameter)
  1089. {
  1090. exception = SETryOrError([&](){ m_Effect.setParameter(&m_Effect, nIndex, fValue); });
  1091. }
  1092. ResetSilence();
  1093. if(exception)
  1094. {
  1095. //ReportPlugException(mpt::format(U_("Exception in SetParameter({}, {})!"))(nIndex, fValue));
  1096. }
  1097. }
  1098. // Helper function for retreiving parameter name / label / display
  1099. CString CVstPlugin::GetParamPropertyString(PlugParamIndex param, Vst::VstOpcodeToPlugin opcode)
  1100. {
  1101. if(m_Effect.numParams > 0 && param < m_Effect.numParams)
  1102. {
  1103. // Increased to 256 bytes since SynthMaster 2.8 writes more than 64 bytes of 0-padding. Kind of ridiculous if you consider that kVstMaxParamStrLen = 8...
  1104. std::vector<char> s(256, 0);
  1105. Dispatch(opcode, param, 0, s.data(), 0);
  1106. return mpt::ToCString(mpt::Charset::Locale, s.data());
  1107. }
  1108. return CString();
  1109. }
  1110. CString CVstPlugin::GetParamName(PlugParamIndex param)
  1111. {
  1112. VstParameterProperties properties{};
  1113. if(param < m_Effect.numParams && Dispatch(effGetParameterProperties, param, 0, &properties, 0.0f) == 1)
  1114. {
  1115. mpt::String::SetNullTerminator(properties.label);
  1116. return mpt::ToCString(mpt::Charset::Locale, properties.label);
  1117. } else
  1118. {
  1119. return GetParamPropertyString(param, effGetParamName);
  1120. }
  1121. }
  1122. CString CVstPlugin::GetDefaultEffectName()
  1123. {
  1124. if(m_isVst2)
  1125. {
  1126. std::vector<char> s(256, 0);
  1127. Dispatch(effGetEffectName, 0, 0, s.data(), 0);
  1128. return mpt::ToCString(mpt::Charset::Locale, s.data());
  1129. }
  1130. return CString();
  1131. }
  1132. void CVstPlugin::Resume()
  1133. {
  1134. const uint32 sampleRate = m_SndFile.GetSampleRate();
  1135. //reset some stuff
  1136. m_MixState.nVolDecayL = 0;
  1137. m_MixState.nVolDecayR = 0;
  1138. if(m_isResumed)
  1139. {
  1140. Dispatch(effStopProcess, 0, 0, nullptr, 0.0f);
  1141. Dispatch(effMainsChanged, 0, 0, nullptr, 0.0f); // calls plugin's suspend
  1142. }
  1143. if (sampleRate != m_nSampleRate)
  1144. {
  1145. m_nSampleRate = sampleRate;
  1146. Dispatch(effSetSampleRate, 0, 0, nullptr, static_cast<float>(m_nSampleRate));
  1147. }
  1148. Dispatch(effSetBlockSize, 0, MIXBUFFERSIZE, nullptr, 0.0f);
  1149. //start off some stuff
  1150. Dispatch(effMainsChanged, 0, 1, nullptr, 0.0f); // calls plugin's resume
  1151. Dispatch(effStartProcess, 0, 0, nullptr, 0.0f);
  1152. m_isResumed = true;
  1153. }
  1154. void CVstPlugin::Suspend()
  1155. {
  1156. if(m_isResumed)
  1157. {
  1158. Dispatch(effStopProcess, 0, 0, nullptr, 0.0f);
  1159. Dispatch(effMainsChanged, 0, 0, nullptr, 0.0f); // calls plugin's suspend (theoretically, plugins should clean their buffers here, but oh well, the number of plugins which don't do this is surprisingly high.)
  1160. m_isResumed = false;
  1161. }
  1162. }
  1163. // Send events to plugin. Returns true if there are events left to be processed.
  1164. void CVstPlugin::ProcessVSTEvents()
  1165. {
  1166. // Process VST events
  1167. if(m_Effect.dispatcher != nullptr && vstEvents.Finalise() > 0)
  1168. {
  1169. DWORD exception = SETryOrError([&](){ m_Effect.dispatcher(&m_Effect, effProcessEvents, 0, 0, &vstEvents, 0); });
  1170. ResetSilence();
  1171. if(exception)
  1172. {
  1173. ReportPlugException(MPT_UFORMAT("Exception {} in ProcessVSTEvents(numEvents:{})!")(
  1174. mpt::ufmt::HEX0<8>(exception),
  1175. vstEvents.size()));
  1176. }
  1177. }
  1178. }
  1179. // Receive events from plugin and send them to the next plugin in the chain.
  1180. void CVstPlugin::ReceiveVSTEvents(const VstEvents *events)
  1181. {
  1182. if(m_pMixStruct == nullptr)
  1183. {
  1184. return;
  1185. }
  1186. ResetSilence();
  1187. // I think we should only route events to plugins that are explicitely specified as output plugins of the current plugin.
  1188. // This should probably use GetOutputPlugList here if we ever get to support multiple output plugins.
  1189. PLUGINDEX receiver = m_pMixStruct->GetOutputPlugin();
  1190. if(receiver != PLUGINDEX_INVALID)
  1191. {
  1192. IMixPlugin *plugin = m_SndFile.m_MixPlugins[receiver].pMixPlugin;
  1193. CVstPlugin *vstPlugin = dynamic_cast<CVstPlugin *>(plugin);
  1194. // Add all events to the plugin's queue.
  1195. for(const auto &ev : *events)
  1196. {
  1197. if(vstPlugin != nullptr)
  1198. {
  1199. // Directly enqueue the message and preserve as much of the event data as possible (e.g. delta frames, which are currently not used by OpenMPT but might be by plugins)
  1200. vstPlugin->vstEvents.Enqueue(ev);
  1201. } else if(plugin != nullptr)
  1202. {
  1203. if(ev->type == kVstMidiType)
  1204. {
  1205. plugin->MidiSend(static_cast<const VstMidiEvent *>(ev)->midiData);
  1206. } else if(ev->type == kVstSysExType)
  1207. {
  1208. auto event = static_cast<const VstMidiSysexEvent *>(ev);
  1209. plugin->MidiSysexSend(mpt::as_span(mpt::byte_cast<const std::byte *>(event->sysexDump), event->dumpBytes));
  1210. }
  1211. }
  1212. }
  1213. }
  1214. #ifdef MODPLUG_TRACKER
  1215. if(m_recordMIDIOut)
  1216. {
  1217. // Spam MIDI data to all views
  1218. for(const auto &ev : *events)
  1219. {
  1220. if(ev->type == kVstMidiType)
  1221. {
  1222. VstMidiEvent *event = static_cast<VstMidiEvent *>(ev);
  1223. ::SendNotifyMessage(CMainFrame::GetMainFrame()->GetMidiRecordWnd(), WM_MOD_MIDIMSG, event->midiData, reinterpret_cast<LPARAM>(this));
  1224. }
  1225. }
  1226. }
  1227. #endif // MODPLUG_TRACKER
  1228. }
  1229. void CVstPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames)
  1230. {
  1231. ProcessVSTEvents();
  1232. // If the plugin is found & ok, continue
  1233. if(m_pProcessFP != nullptr && m_mixBuffer.Ok())
  1234. {
  1235. int32 numInputs = m_Effect.numInputs, numOutputs = m_Effect.numOutputs;
  1236. //RecalculateGain();
  1237. // Merge stereo input before sending to the plugin if it can only handle one input.
  1238. if (numInputs == 1)
  1239. {
  1240. float *plugInputL = m_mixBuffer.GetInputBuffer(0);
  1241. float *plugInputR = m_mixBuffer.GetInputBuffer(1);
  1242. for (uint32 i = 0; i < numFrames; i++)
  1243. {
  1244. plugInputL[i] = 0.5f * (plugInputL[i] + plugInputR[i]);
  1245. }
  1246. }
  1247. float **outputBuffers = m_mixBuffer.GetOutputBufferArray();
  1248. if(!isBridged)
  1249. {
  1250. m_mixBuffer.ClearOutputBuffers(numFrames);
  1251. }
  1252. // Do the VST processing magic
  1253. MPT_ASSERT(numFrames <= MIXBUFFERSIZE);
  1254. {
  1255. DWORD exception = SETryOrError([&](){ m_pProcessFP(&m_Effect, m_mixBuffer.GetInputBufferArray(), outputBuffers, numFrames); });
  1256. if(exception)
  1257. {
  1258. Bypass();
  1259. mpt::ustring processMethod = (m_Effect.flags & effFlagsCanReplacing) ? U_("processReplacing") : U_("process");
  1260. ReportPlugException(MPT_UFORMAT("The plugin threw an exception ({}) in {}. It has automatically been set to \"Bypass\".")(mpt::ufmt::HEX0<8>(exception), processMethod));
  1261. }
  1262. }
  1263. // Mix outputs of multi-output VSTs:
  1264. if(numOutputs > 2)
  1265. {
  1266. MPT_ASSERT(outputBuffers != nullptr);
  1267. // first, mix extra outputs on a stereo basis
  1268. int32 outs = numOutputs;
  1269. // so if nOuts is not even, let process the last output later
  1270. if((outs % 2u) == 1) outs--;
  1271. // mix extra stereo outputs
  1272. for(int32 iOut = 2; iOut < outs; iOut++)
  1273. {
  1274. for(uint32 i = 0; i < numFrames; i++)
  1275. {
  1276. outputBuffers[iOut % 2u][i] += outputBuffers[iOut][i]; // assumed stereo.
  1277. }
  1278. }
  1279. // if m_Effect.numOutputs is odd, mix half the signal of last output to each channel
  1280. if(outs != numOutputs)
  1281. {
  1282. // trick : if we are here, numOutputs = m_Effect.numOutputs - 1 !!!
  1283. for(uint32 i = 0; i < numFrames; i++)
  1284. {
  1285. float v = 0.5f * outputBuffers[outs][i];
  1286. outputBuffers[0][i] += v;
  1287. outputBuffers[1][i] += v;
  1288. }
  1289. }
  1290. }
  1291. if(numOutputs != 0)
  1292. {
  1293. MPT_ASSERT(outputBuffers != nullptr);
  1294. ProcessMixOps(pOutL, pOutR, outputBuffers[0], outputBuffers[numOutputs > 1 ? 1 : 0], numFrames);
  1295. }
  1296. // If the I/O format of the bridge changed in the meanwhile, update it now.
  1297. if(isBridged && Dispatch(effVendorSpecific, kVendorOpenMPT, kCloseOldProcessingMemory, nullptr, 0.0f) != 0)
  1298. {
  1299. InitializeIOBuffers();
  1300. }
  1301. }
  1302. vstEvents.Clear();
  1303. m_positionChanged = false;
  1304. }
  1305. bool CVstPlugin::MidiSend(uint32 dwMidiCode)
  1306. {
  1307. // Note-Offs go at the start of the queue (since OpenMPT 1.17). Needed for situations like this:
  1308. // ... ..|C-5 01
  1309. // C-5 01|=== ..
  1310. // TODO: Should not be used with real-time notes! Letting the key go too quickly
  1311. // (e.g. while output device is being initalized) will cause the note to be stuck!
  1312. bool insertAtFront = (MIDIEvents::GetTypeFromEvent(dwMidiCode) == MIDIEvents::evNoteOff);
  1313. VstMidiEvent event{};
  1314. event.type = kVstMidiType;
  1315. event.byteSize = sizeof(event);
  1316. event.midiData = dwMidiCode;
  1317. ResetSilence();
  1318. return vstEvents.Enqueue(&event, insertAtFront);
  1319. }
  1320. bool CVstPlugin::MidiSysexSend(mpt::const_byte_span sysex)
  1321. {
  1322. VstMidiSysexEvent event{};
  1323. event.type = kVstSysExType;
  1324. event.byteSize = sizeof(event);
  1325. event.dumpBytes = mpt::saturate_cast<int32>(sysex.size());
  1326. event.sysexDump = sysex.data(); // We will make our own copy in VstEventQueue::Enqueue
  1327. ResetSilence();
  1328. return vstEvents.Enqueue(&event);
  1329. }
  1330. void CVstPlugin::HardAllNotesOff()
  1331. {
  1332. constexpr uint32 SCRATCH_BUFFER_SIZE = 64;
  1333. float out[2][SCRATCH_BUFFER_SIZE]; // scratch buffers
  1334. // The JUCE framework doesn't like processing while being suspended.
  1335. const bool wasSuspended = !IsResumed();
  1336. if(wasSuspended)
  1337. {
  1338. Resume();
  1339. }
  1340. const bool isWavestation = GetUID() == FourCC("KLWV");
  1341. const bool isSawer = GetUID() == FourCC("SaWR");
  1342. for(uint8 mc = 0; mc < m_MidiCh.size(); mc++)
  1343. {
  1344. PlugInstrChannel &channel = m_MidiCh[mc];
  1345. channel.ResetProgram();
  1346. SendMidiPitchBend(mc, EncodePitchBendParam(MIDIEvents::pitchBendCentre)); // centre pitch bend
  1347. if(!isWavestation && !isSawer)
  1348. {
  1349. // Korg Wavestation doesn't seem to like this CC, it can introduce ghost notes or
  1350. // prevent new notes from being played.
  1351. // Image-Line Sawer does not like it either and resets some parameters so that the plugin is all
  1352. // distorted afterwards.
  1353. MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllControllersOff, mc, 0));
  1354. }
  1355. if(!isSawer)
  1356. {
  1357. // Image-Line Sawer takes ages to execute this CC.
  1358. MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllNotesOff, mc, 0));
  1359. }
  1360. MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllSoundOff, mc, 0));
  1361. for(std::size_t i = 0; i < std::size(channel.noteOnMap); i++) //all notes
  1362. {
  1363. for(auto &c : channel.noteOnMap[i])
  1364. {
  1365. while(c != 0)
  1366. {
  1367. MidiSend(MIDIEvents::NoteOff(mc, static_cast<uint8>(i), 0));
  1368. c--;
  1369. }
  1370. }
  1371. }
  1372. }
  1373. // Let plugin process events
  1374. while(vstEvents.GetNumQueuedEvents() > 0)
  1375. {
  1376. Process(out[0], out[1], SCRATCH_BUFFER_SIZE);
  1377. }
  1378. if(wasSuspended)
  1379. {
  1380. Suspend();
  1381. }
  1382. }
  1383. void CVstPlugin::SaveAllParameters()
  1384. {
  1385. if(m_pMixStruct == nullptr)
  1386. {
  1387. return;
  1388. }
  1389. m_pMixStruct->defaultProgram = -1;
  1390. if(ProgramsAreChunks())
  1391. {
  1392. void *p = nullptr;
  1393. // Try to get whole bank
  1394. intptr_t byteSize = Dispatch(effGetChunk, 0, 0, &p, 0);
  1395. if (!p)
  1396. {
  1397. // Getting bank failed, try to get just preset
  1398. byteSize = Dispatch(effGetChunk, 1, 0, &p, 0);
  1399. } else
  1400. {
  1401. // We managed to get the bank, now we need to remember which program we're on.
  1402. m_pMixStruct->defaultProgram = GetCurrentProgram();
  1403. }
  1404. if (p != nullptr)
  1405. {
  1406. LimitMax(byteSize, Util::MaxValueOfType(byteSize) - 4);
  1407. try
  1408. {
  1409. m_pMixStruct->pluginData.resize(byteSize + 4);
  1410. auto data = m_pMixStruct->pluginData.data();
  1411. memcpy(data, "fEvN", 4); // 'NvEf', return value of deprecated effIdentify call
  1412. memcpy(data + 4, p, byteSize);
  1413. return;
  1414. } catch(mpt::out_of_memory e)
  1415. {
  1416. mpt::delete_out_of_memory(e);
  1417. }
  1418. }
  1419. }
  1420. // This plugin doesn't support chunks: save parameters
  1421. IMixPlugin::SaveAllParameters();
  1422. }
  1423. void CVstPlugin::RestoreAllParameters(int32 program)
  1424. {
  1425. if(m_pMixStruct != nullptr && m_pMixStruct->pluginData.size() >= 4)
  1426. {
  1427. auto data = m_pMixStruct->pluginData.data();
  1428. if (!memcmp(data, "fEvN", 4)) // 'NvEf', return value of deprecated effIdentify call
  1429. {
  1430. if ((program>=0) && (program < m_Effect.numPrograms))
  1431. {
  1432. // Bank
  1433. Dispatch(effSetChunk, 0, m_pMixStruct->pluginData.size() - 4, data + 4, 0);
  1434. SetCurrentProgram(program);
  1435. } else
  1436. {
  1437. // Program
  1438. BeginSetProgram(-1);
  1439. Dispatch(effSetChunk, 1, m_pMixStruct->pluginData.size() - 4, data + 4, 0);
  1440. EndSetProgram();
  1441. }
  1442. } else
  1443. {
  1444. IMixPlugin::RestoreAllParameters(program);
  1445. }
  1446. }
  1447. }
  1448. CAbstractVstEditor *CVstPlugin::OpenEditor()
  1449. {
  1450. try
  1451. {
  1452. if(HasEditor())
  1453. return new COwnerVstEditor(*this);
  1454. else
  1455. return new CDefaultVstEditor(*this);
  1456. } catch(mpt::out_of_memory e)
  1457. {
  1458. mpt::delete_out_of_memory(e);
  1459. ReportPlugException(U_("Exception in OpenEditor()"));
  1460. return nullptr;
  1461. }
  1462. }
  1463. void CVstPlugin::Bypass(bool bypass)
  1464. {
  1465. Dispatch(effSetBypass, bypass ? 1 : 0, 0, nullptr, 0.0f);
  1466. IMixPlugin::Bypass(bypass);
  1467. }
  1468. void CVstPlugin::NotifySongPlaying(bool playing)
  1469. {
  1470. m_isSongPlaying = playing;
  1471. }
  1472. bool CVstPlugin::IsInstrument() const
  1473. {
  1474. return ((m_Effect.flags & effFlagsIsSynth) || (!m_Effect.numInputs));
  1475. }
  1476. bool CVstPlugin::CanRecieveMidiEvents()
  1477. {
  1478. return Dispatch(effCanDo, 0, 0, const_cast<char *>(PluginCanDo::receiveVstMidiEvent), 0.0f) != 0;
  1479. }
  1480. void CVstPlugin::ReportPlugException(const mpt::ustring &text) const
  1481. {
  1482. CVstPluginManager::ReportPlugException(MPT_UFORMAT("{} (Plugin: {})")(text, m_Factory.libraryName));
  1483. }
  1484. // Cache program names for plugin bridge
  1485. void CVstPlugin::CacheProgramNames(int32 firstProg, int32 lastProg)
  1486. {
  1487. if(isBridged)
  1488. {
  1489. int32 offsets[2] = { firstProg, lastProg };
  1490. Dispatch(effVendorSpecific, kVendorOpenMPT, kCacheProgramNames, offsets, 0.0f);
  1491. }
  1492. }
  1493. // Cache parameter names for plugin bridge
  1494. void CVstPlugin::CacheParameterNames(int32 firstParam, int32 lastParam)
  1495. {
  1496. if(isBridged)
  1497. {
  1498. int32 offsets[2] = { firstParam, lastParam };
  1499. Dispatch(effVendorSpecific, kVendorOpenMPT, kCacheParameterInfo, offsets, 0.0f);
  1500. }
  1501. }
  1502. IMixPlugin::ChunkData CVstPlugin::GetChunk(bool isBank)
  1503. {
  1504. std::byte *chunk = nullptr;
  1505. auto size = Dispatch(effGetChunk, isBank ? 0 : 1, 0, &chunk, 0);
  1506. if(chunk == nullptr)
  1507. {
  1508. size = 0;
  1509. }
  1510. return ChunkData(chunk, size);
  1511. }
  1512. void CVstPlugin::SetChunk(const ChunkData &chunk, bool isBank)
  1513. {
  1514. Dispatch(effSetChunk, isBank ? 0 : 1, chunk.size(), const_cast<std::byte *>(chunk.data()), 0);
  1515. }
  1516. OPENMPT_NAMESPACE_END
  1517. #endif // MPT_WITH_VST