1
0

PlugInterface.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. /*
  2. * PlugInterface.cpp
  3. * -----------------
  4. * Purpose: Default plugin interface implementation
  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. #include "../Sndfile.h"
  11. #include "PlugInterface.h"
  12. #include "PluginManager.h"
  13. #include "../../common/FileReader.h"
  14. #ifdef MODPLUG_TRACKER
  15. #include "../../mptrack/Moddoc.h"
  16. #include "../../mptrack/Mainfrm.h"
  17. #include "../../mptrack/InputHandler.h"
  18. #include "../../mptrack/AbstractVstEditor.h"
  19. #include "../../mptrack/DefaultVstEditor.h"
  20. // LoadProgram/SaveProgram
  21. #include "../../mptrack/FileDialog.h"
  22. #include "../../mptrack/VstPresets.h"
  23. #include "../../common/mptFileIO.h"
  24. #include "../mod_specifications.h"
  25. #endif // MODPLUG_TRACKER
  26. #include "mpt/base/aligned_array.hpp"
  27. #include "mpt/io/base.hpp"
  28. #include "mpt/io/io.hpp"
  29. #include "mpt/io/io_span.hpp"
  30. #include <cmath>
  31. #ifndef NO_PLUGINS
  32. OPENMPT_NAMESPACE_BEGIN
  33. #ifdef MODPLUG_TRACKER
  34. CModDoc *IMixPlugin::GetModDoc() { return m_SndFile.GetpModDoc(); }
  35. const CModDoc *IMixPlugin::GetModDoc() const { return m_SndFile.GetpModDoc(); }
  36. #endif // MODPLUG_TRACKER
  37. IMixPlugin::IMixPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  38. : m_Factory(factory)
  39. , m_SndFile(sndFile)
  40. , m_pMixStruct(mixStruct)
  41. {
  42. m_SndFile.m_loadedPlugins++;
  43. m_MixState.pMixBuffer = mpt::align_bytes<8, MIXBUFFERSIZE * 2>(m_MixBuffer);
  44. while(m_pMixStruct != &(m_SndFile.m_MixPlugins[m_nSlot]) && m_nSlot < MAX_MIXPLUGINS - 1)
  45. {
  46. m_nSlot++;
  47. }
  48. }
  49. IMixPlugin::~IMixPlugin()
  50. {
  51. #ifdef MODPLUG_TRACKER
  52. CloseEditor();
  53. CriticalSection cs;
  54. #endif // MODPLUG_TRACKER
  55. // First thing to do, if we don't want to hang in a loop
  56. if (m_Factory.pPluginsList == this) m_Factory.pPluginsList = m_pNext;
  57. if (m_pMixStruct)
  58. {
  59. m_pMixStruct->pMixPlugin = nullptr;
  60. m_pMixStruct = nullptr;
  61. }
  62. if (m_pNext) m_pNext->m_pPrev = m_pPrev;
  63. if (m_pPrev) m_pPrev->m_pNext = m_pNext;
  64. m_pPrev = nullptr;
  65. m_pNext = nullptr;
  66. m_SndFile.m_loadedPlugins--;
  67. }
  68. void IMixPlugin::InsertIntoFactoryList()
  69. {
  70. m_pMixStruct->pMixPlugin = this;
  71. m_pNext = m_Factory.pPluginsList;
  72. if(m_Factory.pPluginsList)
  73. {
  74. m_Factory.pPluginsList->m_pPrev = this;
  75. }
  76. m_Factory.pPluginsList = this;
  77. }
  78. #ifdef MODPLUG_TRACKER
  79. void IMixPlugin::SetSlot(PLUGINDEX slot)
  80. {
  81. m_nSlot = slot;
  82. m_pMixStruct = &m_SndFile.m_MixPlugins[slot];
  83. }
  84. PlugParamValue IMixPlugin::GetScaledUIParam(PlugParamIndex param)
  85. {
  86. const auto [paramMin, paramMax] = GetParamUIRange(param);
  87. return (std::clamp(GetParameter(param), paramMin, paramMax) - paramMin) / (paramMax - paramMin);
  88. }
  89. void IMixPlugin::SetScaledUIParam(PlugParamIndex param, PlugParamValue value)
  90. {
  91. const auto [paramMin, paramMax] = GetParamUIRange(param);
  92. const auto scaledVal = paramMin + std::clamp(value, 0.0f, 1.0f) * (paramMax - paramMin);
  93. SetParameter(param, scaledVal);
  94. }
  95. CString IMixPlugin::GetFormattedParamName(PlugParamIndex param)
  96. {
  97. CString paramName = GetParamName(param);
  98. CString name;
  99. if(paramName.IsEmpty())
  100. {
  101. name = MPT_CFORMAT("{}: Parameter {}")(mpt::cfmt::dec0<2>(param), mpt::cfmt::dec0<2>(param));
  102. } else
  103. {
  104. name = MPT_CFORMAT("{}: {}")(mpt::cfmt::dec0<2>(param), paramName);
  105. }
  106. return name;
  107. }
  108. // Get a parameter's current value, represented by the plugin.
  109. CString IMixPlugin::GetFormattedParamValue(PlugParamIndex param)
  110. {
  111. CString paramDisplay = GetParamDisplay(param);
  112. CString paramUnits = GetParamLabel(param);
  113. paramDisplay.Trim();
  114. paramUnits.Trim();
  115. paramDisplay += _T(" ") + paramUnits;
  116. return paramDisplay;
  117. }
  118. CString IMixPlugin::GetFormattedProgramName(int32 index)
  119. {
  120. CString rawname = GetProgramName(index);
  121. // Let's start counting at 1 for the program name (as most MIDI hardware / software does)
  122. index++;
  123. CString formattedName;
  124. if(rawname[0] >= 0 && rawname[0] < _T(' '))
  125. formattedName = MPT_CFORMAT("{} - Program {}")(mpt::cfmt::dec0<2>(index), index);
  126. else
  127. formattedName = MPT_CFORMAT("{} - {}")(mpt::cfmt::dec0<2>(index), rawname);
  128. return formattedName;
  129. }
  130. void IMixPlugin::SetEditorPos(int32 x, int32 y)
  131. {
  132. m_pMixStruct->editorX = x;
  133. m_pMixStruct->editorY = y;
  134. }
  135. void IMixPlugin::GetEditorPos(int32 &x, int32 &y) const
  136. {
  137. x = m_pMixStruct->editorX;
  138. y = m_pMixStruct->editorY;
  139. }
  140. #endif // MODPLUG_TRACKER
  141. bool IMixPlugin::IsBypassed() const
  142. {
  143. return m_pMixStruct != nullptr && m_pMixStruct->IsBypassed();
  144. }
  145. void IMixPlugin::RecalculateGain()
  146. {
  147. float gain = 0.1f * static_cast<float>(m_pMixStruct ? m_pMixStruct->GetGain() : 10);
  148. if(gain < 0.1f) gain = 1.0f;
  149. if(IsInstrument())
  150. {
  151. gain /= m_SndFile.GetPlayConfig().getVSTiAttenuation();
  152. gain = static_cast<float>(gain * (m_SndFile.m_nVSTiVolume / m_SndFile.GetPlayConfig().getNormalVSTiVol()));
  153. }
  154. m_fGain = gain;
  155. }
  156. void IMixPlugin::SetDryRatio(float dryRatio)
  157. {
  158. m_pMixStruct->fDryRatio = std::clamp(dryRatio, 0.0f, 1.0f);
  159. #ifdef MODPLUG_TRACKER
  160. m_SndFile.m_pluginDryWetRatioChanged.set(m_nSlot);
  161. #endif // MODPLUG_TRACKER
  162. }
  163. void IMixPlugin::Bypass(bool bypass)
  164. {
  165. m_pMixStruct->Info.SetBypass(bypass);
  166. #ifdef MODPLUG_TRACKER
  167. if(m_SndFile.GetpModDoc())
  168. m_SndFile.GetpModDoc()->UpdateAllViews(nullptr, PluginHint(m_nSlot + 1).Info(), nullptr);
  169. #endif // MODPLUG_TRACKER
  170. }
  171. double IMixPlugin::GetOutputLatency() const
  172. {
  173. if(GetSoundFile().IsRenderingToDisc())
  174. return 0;
  175. else
  176. return GetSoundFile().m_TimingInfo.OutputLatency;
  177. }
  178. void IMixPlugin::ProcessMixOps(float * MPT_RESTRICT pOutL, float * MPT_RESTRICT pOutR, float * MPT_RESTRICT leftPlugOutput, float * MPT_RESTRICT rightPlugOutput, uint32 numFrames)
  179. {
  180. /* float *leftPlugOutput;
  181. float *rightPlugOutput;
  182. if(m_Effect.numOutputs == 1)
  183. {
  184. // If there was just the one plugin output we copy it into our 2 outputs
  185. leftPlugOutput = rightPlugOutput = mixBuffer.GetOutputBuffer(0);
  186. } else if(m_Effect.numOutputs > 1)
  187. {
  188. // Otherwise we actually only cater for two outputs max (outputs > 2 have been mixed together already).
  189. leftPlugOutput = mixBuffer.GetOutputBuffer(0);
  190. rightPlugOutput = mixBuffer.GetOutputBuffer(1);
  191. } else
  192. {
  193. return;
  194. }*/
  195. // -> mixop == 0 : normal processing
  196. // -> mixop == 1 : MIX += DRY - WET * wetRatio
  197. // -> mixop == 2 : MIX += WET - DRY * dryRatio
  198. // -> mixop == 3 : MIX -= WET - DRY * wetRatio
  199. // -> mixop == 4 : MIX -= middle - WET * wetRatio + middle - DRY
  200. // -> mixop == 5 : MIX_L += wetRatio * (WET_L - DRY_L) + dryRatio * (DRY_R - WET_R)
  201. // MIX_R += dryRatio * (WET_L - DRY_L) + wetRatio * (DRY_R - WET_R)
  202. MPT_ASSERT(m_pMixStruct != nullptr);
  203. int mixop;
  204. if(IsInstrument())
  205. {
  206. // Force normal mix mode for instruments
  207. mixop = 0;
  208. } else
  209. {
  210. mixop = m_pMixStruct->GetMixMode();
  211. }
  212. float wetRatio = 1 - m_pMixStruct->fDryRatio;
  213. float dryRatio = IsInstrument() ? 1 : m_pMixStruct->fDryRatio; // Always mix full dry if this is an instrument
  214. // Wet / Dry range expansion [0,1] -> [-1,1]
  215. if(GetNumInputChannels() > 0 && m_pMixStruct->IsExpandedMix())
  216. {
  217. wetRatio = 2.0f * wetRatio - 1.0f;
  218. dryRatio = -wetRatio;
  219. }
  220. wetRatio *= m_fGain;
  221. dryRatio *= m_fGain;
  222. float * MPT_RESTRICT plugInputL = m_mixBuffer.GetInputBuffer(0);
  223. float * MPT_RESTRICT plugInputR = m_mixBuffer.GetInputBuffer(1);
  224. // Mix operation
  225. switch(mixop)
  226. {
  227. // Default mix
  228. case 0:
  229. for(uint32 i = 0; i < numFrames; i++)
  230. {
  231. //rewbs.wetratio - added the factors. [20040123]
  232. pOutL[i] += leftPlugOutput[i] * wetRatio + plugInputL[i] * dryRatio;
  233. pOutR[i] += rightPlugOutput[i] * wetRatio + plugInputR[i] * dryRatio;
  234. }
  235. break;
  236. // Wet subtract
  237. case 1:
  238. for(uint32 i = 0; i < numFrames; i++)
  239. {
  240. pOutL[i] += plugInputL[i] - leftPlugOutput[i] * wetRatio;
  241. pOutR[i] += plugInputR[i] - rightPlugOutput[i] * wetRatio;
  242. }
  243. break;
  244. // Dry subtract
  245. case 2:
  246. for(uint32 i = 0; i < numFrames; i++)
  247. {
  248. pOutL[i] += leftPlugOutput[i] - plugInputL[i] * dryRatio;
  249. pOutR[i] += rightPlugOutput[i] - plugInputR[i] * dryRatio;
  250. }
  251. break;
  252. // Mix subtract
  253. case 3:
  254. for(uint32 i = 0; i < numFrames; i++)
  255. {
  256. pOutL[i] -= leftPlugOutput[i] - plugInputL[i] * wetRatio;
  257. pOutR[i] -= rightPlugOutput[i] - plugInputR[i] * wetRatio;
  258. }
  259. break;
  260. // Middle subtract
  261. case 4:
  262. for(uint32 i = 0; i < numFrames; i++)
  263. {
  264. float middle = (pOutL[i] + plugInputL[i] + pOutR[i] + plugInputR[i]) / 2.0f;
  265. pOutL[i] -= middle - leftPlugOutput[i] * wetRatio + middle - plugInputL[i];
  266. pOutR[i] -= middle - rightPlugOutput[i] * wetRatio + middle - plugInputR[i];
  267. }
  268. break;
  269. // Left / Right balance
  270. case 5:
  271. if(m_pMixStruct->IsExpandedMix())
  272. {
  273. wetRatio /= 2.0f;
  274. dryRatio /= 2.0f;
  275. }
  276. for(uint32 i = 0; i < numFrames; i++)
  277. {
  278. pOutL[i] += wetRatio * (leftPlugOutput[i] - plugInputL[i]) + dryRatio * (plugInputR[i] - rightPlugOutput[i]);
  279. pOutR[i] += dryRatio * (leftPlugOutput[i] - plugInputL[i]) + wetRatio * (plugInputR[i] - rightPlugOutput[i]);
  280. }
  281. break;
  282. }
  283. // If dry mix is ticked, we add the unprocessed buffer,
  284. // except if this is an instrument since then it has already been done:
  285. if(m_pMixStruct->IsWetMix() && !IsInstrument())
  286. {
  287. for(uint32 i = 0; i < numFrames; i++)
  288. {
  289. pOutL[i] += plugInputL[i];
  290. pOutR[i] += plugInputR[i];
  291. }
  292. }
  293. }
  294. // Render some silence and return maximum level returned by the plugin.
  295. float IMixPlugin::RenderSilence(uint32 numFrames)
  296. {
  297. // The JUCE framework doesn't like processing while being suspended.
  298. const bool wasSuspended = !IsResumed();
  299. if(wasSuspended)
  300. {
  301. Resume();
  302. }
  303. float out[2][MIXBUFFERSIZE]; // scratch buffers
  304. float maxVal = 0.0f;
  305. m_mixBuffer.ClearInputBuffers(MIXBUFFERSIZE);
  306. while(numFrames > 0)
  307. {
  308. uint32 renderSamples = numFrames;
  309. LimitMax(renderSamples, mpt::saturate_cast<uint32>(std::size(out[0])));
  310. MemsetZero(out);
  311. Process(out[0], out[1], renderSamples);
  312. for(size_t i = 0; i < renderSamples; i++)
  313. {
  314. maxVal = std::max(maxVal, std::fabs(out[0][i]));
  315. maxVal = std::max(maxVal, std::fabs(out[1][i]));
  316. }
  317. numFrames -= renderSamples;
  318. }
  319. if(wasSuspended)
  320. {
  321. Suspend();
  322. }
  323. return maxVal;
  324. }
  325. // Get list of plugins to which output is sent. A nullptr indicates master output.
  326. size_t IMixPlugin::GetOutputPlugList(std::vector<IMixPlugin *> &list)
  327. {
  328. // At the moment we know there will only be 1 output.
  329. // Returning nullptr means plugin outputs directly to master.
  330. list.clear();
  331. IMixPlugin *outputPlug = nullptr;
  332. if(!m_pMixStruct->IsOutputToMaster())
  333. {
  334. PLUGINDEX nOutput = m_pMixStruct->GetOutputPlugin();
  335. if(nOutput > m_nSlot && nOutput != PLUGINDEX_INVALID)
  336. {
  337. outputPlug = m_SndFile.m_MixPlugins[nOutput].pMixPlugin;
  338. }
  339. }
  340. list.push_back(outputPlug);
  341. return 1;
  342. }
  343. // Get a list of plugins that send data to this plugin.
  344. size_t IMixPlugin::GetInputPlugList(std::vector<IMixPlugin *> &list)
  345. {
  346. std::vector<IMixPlugin *> candidatePlugOutputs;
  347. list.clear();
  348. for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++)
  349. {
  350. IMixPlugin *candidatePlug = m_SndFile.m_MixPlugins[plug].pMixPlugin;
  351. if(candidatePlug)
  352. {
  353. candidatePlug->GetOutputPlugList(candidatePlugOutputs);
  354. for(auto &outPlug : candidatePlugOutputs)
  355. {
  356. if(outPlug == this)
  357. {
  358. list.push_back(candidatePlug);
  359. break;
  360. }
  361. }
  362. }
  363. }
  364. return list.size();
  365. }
  366. // Get a list of instruments that send data to this plugin.
  367. size_t IMixPlugin::GetInputInstrumentList(std::vector<INSTRUMENTINDEX> &list)
  368. {
  369. list.clear();
  370. const PLUGINDEX nThisMixPlug = m_nSlot + 1; //m_nSlot is position in mixplug array.
  371. for(INSTRUMENTINDEX ins = 0; ins <= m_SndFile.GetNumInstruments(); ins++)
  372. {
  373. if(m_SndFile.Instruments[ins] != nullptr && m_SndFile.Instruments[ins]->nMixPlug == nThisMixPlug)
  374. {
  375. list.push_back(ins);
  376. }
  377. }
  378. return list.size();
  379. }
  380. size_t IMixPlugin::GetInputChannelList(std::vector<CHANNELINDEX> &list)
  381. {
  382. list.clear();
  383. PLUGINDEX nThisMixPlug = m_nSlot + 1; //m_nSlot is position in mixplug array.
  384. const CHANNELINDEX chnCount = m_SndFile.GetNumChannels();
  385. for(CHANNELINDEX nChn=0; nChn<chnCount; nChn++)
  386. {
  387. if(m_SndFile.ChnSettings[nChn].nMixPlugin == nThisMixPlug)
  388. {
  389. list.push_back(nChn);
  390. }
  391. }
  392. return list.size();
  393. }
  394. void IMixPlugin::SaveAllParameters()
  395. {
  396. if (m_pMixStruct == nullptr)
  397. {
  398. return;
  399. }
  400. m_pMixStruct->defaultProgram = -1;
  401. // Default implementation: Save all parameter values
  402. PlugParamIndex numParams = std::min(GetNumParameters(), static_cast<PlugParamIndex>((std::numeric_limits<uint32>::max() - sizeof(uint32)) / sizeof(IEEE754binary32LE)));
  403. uint32 nLen = numParams * sizeof(IEEE754binary32LE);
  404. if (!nLen) return;
  405. nLen += sizeof(uint32);
  406. try
  407. {
  408. m_pMixStruct->pluginData.resize(nLen);
  409. auto memFile = std::make_pair(mpt::as_span(m_pMixStruct->pluginData), mpt::IO::Offset(0));
  410. mpt::IO::WriteIntLE<uint32>(memFile, 0); // Plugin data type
  411. BeginGetProgram();
  412. for(PlugParamIndex i = 0; i < numParams; i++)
  413. {
  414. mpt::IO::Write(memFile, IEEE754binary32LE(GetParameter(i)));
  415. }
  416. EndGetProgram();
  417. } catch(mpt::out_of_memory e)
  418. {
  419. m_pMixStruct->pluginData.clear();
  420. mpt::delete_out_of_memory(e);
  421. }
  422. }
  423. void IMixPlugin::RestoreAllParameters(int32 /*program*/)
  424. {
  425. if(m_pMixStruct != nullptr && m_pMixStruct->pluginData.size() >= sizeof(uint32))
  426. {
  427. FileReader memFile(mpt::as_span(m_pMixStruct->pluginData));
  428. uint32 type = memFile.ReadUint32LE();
  429. if(type == 0)
  430. {
  431. const uint32 numParams = GetNumParameters();
  432. if((m_pMixStruct->pluginData.size() - sizeof(uint32)) >= (numParams * sizeof(IEEE754binary32LE)))
  433. {
  434. BeginSetProgram();
  435. for(uint32 i = 0; i < numParams; i++)
  436. {
  437. const auto value = memFile.ReadFloatLE();
  438. SetParameter(i, std::isfinite(value) ? value : 0.0f);
  439. }
  440. EndSetProgram();
  441. }
  442. }
  443. }
  444. }
  445. #ifdef MODPLUG_TRACKER
  446. void IMixPlugin::ToggleEditor()
  447. {
  448. // We only really need this mutex for bridged plugins, as we may be processing window messages (in the same thread) while the editor opens.
  449. // The user could press the toggle button while the editor is loading and thus close the editor while still being initialized.
  450. // Note that this does not protect against closing the module while the editor is still loading.
  451. static bool initializing = false;
  452. if(initializing)
  453. return;
  454. initializing = true;
  455. if (m_pEditor)
  456. {
  457. CloseEditor();
  458. } else
  459. {
  460. m_pEditor = OpenEditor();
  461. if (m_pEditor)
  462. m_pEditor->OpenEditor(CMainFrame::GetMainFrame());
  463. }
  464. initializing = false;
  465. }
  466. // Provide default plugin editor
  467. CAbstractVstEditor *IMixPlugin::OpenEditor()
  468. {
  469. try
  470. {
  471. return new CDefaultVstEditor(*this);
  472. } catch(mpt::out_of_memory e)
  473. {
  474. mpt::delete_out_of_memory(e);
  475. return nullptr;
  476. }
  477. }
  478. void IMixPlugin::CloseEditor()
  479. {
  480. if(m_pEditor)
  481. {
  482. if (m_pEditor->m_hWnd) m_pEditor->DoClose();
  483. delete m_pEditor;
  484. m_pEditor = nullptr;
  485. }
  486. }
  487. // Automate a parameter from the plugin GUI (both custom and default plugin GUI)
  488. void IMixPlugin::AutomateParameter(PlugParamIndex param)
  489. {
  490. CModDoc *modDoc = GetModDoc();
  491. if(modDoc == nullptr)
  492. {
  493. return;
  494. }
  495. // TODO: Check if any params are actually automatable, and if there are but this one isn't, chicken out
  496. if(m_recordAutomation)
  497. {
  498. // Record parameter change
  499. modDoc->RecordParamChange(GetSlot(), param);
  500. }
  501. modDoc->SendNotifyMessageToAllViews(WM_MOD_PLUGPARAMAUTOMATE, m_nSlot, param);
  502. if(auto *vstEditor = GetEditor(); vstEditor && vstEditor->m_hWnd)
  503. {
  504. // Mark track modified if GUI is open and format supports plugins
  505. SetModified();
  506. // Do not use InputHandler in case we are coming from a bridged plugin editor
  507. if((GetAsyncKeyState(VK_SHIFT) & 0x8000) && TrackerSettings::Instance().midiMappingInPluginEditor)
  508. {
  509. // Shift pressed -> Open MIDI mapping dialog
  510. CMainFrame::GetMainFrame()->PostMessage(WM_MOD_MIDIMAPPING, m_nSlot, param);
  511. }
  512. // Learn macro
  513. int macroToLearn = vstEditor->GetLearnMacro();
  514. if (macroToLearn > -1)
  515. {
  516. modDoc->LearnMacro(macroToLearn, param);
  517. vstEditor->SetLearnMacro(-1);
  518. }
  519. }
  520. }
  521. void IMixPlugin::SetModified()
  522. {
  523. CModDoc *modDoc = GetModDoc();
  524. if(modDoc != nullptr && m_SndFile.GetModSpecifications().supportsPlugins)
  525. {
  526. modDoc->SetModified();
  527. }
  528. }
  529. bool IMixPlugin::SaveProgram()
  530. {
  531. mpt::PathString defaultDir = TrackerSettings::Instance().PathPluginPresets.GetWorkingDir();
  532. const bool useDefaultDir = !defaultDir.empty();
  533. if(!useDefaultDir && m_Factory.dllPath.IsFile())
  534. {
  535. defaultDir = m_Factory.dllPath.GetPath();
  536. }
  537. CString progName = m_Factory.libraryName.ToCString() + _T(" - ") + GetCurrentProgramName();
  538. SanitizeFilename(progName);
  539. FileDialog dlg = SaveFileDialog()
  540. .DefaultExtension("fxb")
  541. .DefaultFilename(progName)
  542. .ExtensionFilter("VST Plugin Programs (*.fxp)|*.fxp|"
  543. "VST Plugin Banks (*.fxb)|*.fxb||")
  544. .WorkingDirectory(defaultDir);
  545. if(!dlg.Show(m_pEditor)) return false;
  546. if(useDefaultDir)
  547. {
  548. TrackerSettings::Instance().PathPluginPresets.SetWorkingDir(dlg.GetWorkingDirectory());
  549. }
  550. const bool isBank = (dlg.GetExtension() == P_("fxb"));
  551. try
  552. {
  553. mpt::SafeOutputFile sf(dlg.GetFirstFile(), std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave));
  554. mpt::ofstream &f = sf;
  555. f.exceptions(f.exceptions() | std::ios::badbit | std::ios::failbit);
  556. if(f.good() && VSTPresets::SaveFile(f, *this, isBank))
  557. return true;
  558. } catch(const std::exception &)
  559. {
  560. }
  561. Reporting::Error("Error saving preset.", m_pEditor);
  562. return false;
  563. }
  564. bool IMixPlugin::LoadProgram(mpt::PathString fileName)
  565. {
  566. mpt::PathString defaultDir = TrackerSettings::Instance().PathPluginPresets.GetWorkingDir();
  567. bool useDefaultDir = !defaultDir.empty();
  568. if(!useDefaultDir && m_Factory.dllPath.IsFile())
  569. {
  570. defaultDir = m_Factory.dllPath.GetPath();
  571. }
  572. if(fileName.empty())
  573. {
  574. FileDialog dlg = OpenFileDialog()
  575. .DefaultExtension("fxp")
  576. .ExtensionFilter("VST Plugin Programs and Banks (*.fxp,*.fxb)|*.fxp;*.fxb|"
  577. "VST Plugin Programs (*.fxp)|*.fxp|"
  578. "VST Plugin Banks (*.fxb)|*.fxb|"
  579. "All Files|*.*||")
  580. .WorkingDirectory(defaultDir);
  581. if(!dlg.Show(m_pEditor)) return false;
  582. if(useDefaultDir)
  583. {
  584. TrackerSettings::Instance().PathPluginPresets.SetWorkingDir(dlg.GetWorkingDirectory());
  585. }
  586. fileName = dlg.GetFirstFile();
  587. }
  588. const char *errorStr = nullptr;
  589. InputFile f(fileName, SettingCacheCompleteFileBeforeLoading());
  590. if(f.IsValid())
  591. {
  592. FileReader file = GetFileReader(f);
  593. errorStr = VSTPresets::GetErrorMessage(VSTPresets::LoadFile(file, *this));
  594. } else
  595. {
  596. errorStr = "Can't open file.";
  597. }
  598. if(errorStr == nullptr)
  599. {
  600. if(GetModDoc() != nullptr && GetSoundFile().GetModSpecifications().supportsPlugins)
  601. {
  602. GetModDoc()->SetModified();
  603. }
  604. return true;
  605. } else
  606. {
  607. Reporting::Error(errorStr, m_pEditor);
  608. return false;
  609. }
  610. }
  611. #endif // MODPLUG_TRACKER
  612. ////////////////////////////////////////////////////////////////////
  613. // IMidiPlugin: Default implementation of plugins with MIDI input //
  614. ////////////////////////////////////////////////////////////////////
  615. IMidiPlugin::IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  616. : IMixPlugin(factory, sndFile, mixStruct)
  617. , m_MidiCh{{}}
  618. {
  619. for(auto &chn : m_MidiCh)
  620. {
  621. chn.midiPitchBendPos = EncodePitchBendParam(MIDIEvents::pitchBendCentre); // centre pitch bend on all channels
  622. chn.ResetProgram();
  623. }
  624. }
  625. void IMidiPlugin::ApplyPitchWheelDepth(int32 &value, int8 pwd)
  626. {
  627. if(pwd != 0)
  628. {
  629. value = (value * ((MIDIEvents::pitchBendMax - MIDIEvents::pitchBendCentre + 1) / 64)) / pwd;
  630. } else
  631. {
  632. value = 0;
  633. }
  634. }
  635. // Get the MIDI channel currently associated with a given tracker channel
  636. uint8 IMidiPlugin::GetMidiChannel(const ModChannel &chn, CHANNELINDEX trackChannel) const
  637. {
  638. if(auto ins = chn.pModInstrument; ins != nullptr)
  639. return ins->GetMIDIChannel(chn, trackChannel);
  640. else
  641. return 0;
  642. }
  643. uint8 IMidiPlugin::GetMidiChannel(CHANNELINDEX trackChannel) const
  644. {
  645. if(trackChannel < std::size(m_SndFile.m_PlayState.Chn))
  646. return GetMidiChannel(m_SndFile.m_PlayState.Chn[trackChannel], trackChannel);
  647. else
  648. return 0;
  649. }
  650. void IMidiPlugin::MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel)
  651. {
  652. //Error checking
  653. LimitMax(nController, MIDIEvents::MIDICC_end);
  654. LimitMax(nParam, uint8(127));
  655. auto midiCh = GetMidiChannel(trackChannel);
  656. if(m_SndFile.m_playBehaviour[kMIDICCBugEmulation])
  657. MidiSend(MIDIEvents::Event(MIDIEvents::evControllerChange, midiCh, nParam, static_cast<uint8>(nController))); // param and controller are swapped (old broken implementation)
  658. else
  659. MidiSend(MIDIEvents::CC(nController, midiCh, nParam));
  660. }
  661. // Set MIDI pitch for given MIDI channel to the specified raw 14-bit position
  662. void IMidiPlugin::MidiPitchBendRaw(int32 pitchbend, CHANNELINDEX trackerChn)
  663. {
  664. SendMidiPitchBend(GetMidiChannel(trackerChn), EncodePitchBendParam(Clamp(pitchbend, MIDIEvents::pitchBendMin, MIDIEvents::pitchBendMax)));
  665. }
  666. // Bend MIDI pitch for given MIDI channel using fine tracker param (one unit = 1/64th of a note step)
  667. void IMidiPlugin::MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn)
  668. {
  669. auto midiCh = GetMidiChannel(trackerChn);
  670. if(m_SndFile.m_playBehaviour[kOldMIDIPitchBends])
  671. {
  672. // OpenMPT Legacy: Old pitch slides never were really accurate, but setting the PWD to 13 in plugins would give the closest results.
  673. increment = (increment * 0x800 * 13) / (0xFF * pwd);
  674. increment = EncodePitchBendParam(increment);
  675. } else
  676. {
  677. increment = EncodePitchBendParam(increment);
  678. ApplyPitchWheelDepth(increment, pwd);
  679. }
  680. int32 newPitchBendPos = (increment + m_MidiCh[midiCh].midiPitchBendPos) & kPitchBendMask;
  681. Limit(newPitchBendPos, EncodePitchBendParam(MIDIEvents::pitchBendMin), EncodePitchBendParam(MIDIEvents::pitchBendMax));
  682. SendMidiPitchBend(midiCh, newPitchBendPos);
  683. }
  684. // Set MIDI pitch for given MIDI channel using fixed point pitch bend value (converted back to 0-16383 MIDI range)
  685. void IMidiPlugin::SendMidiPitchBend(uint8 midiCh, int32 newPitchBendPos)
  686. {
  687. MPT_ASSERT(EncodePitchBendParam(MIDIEvents::pitchBendMin) <= newPitchBendPos && newPitchBendPos <= EncodePitchBendParam(MIDIEvents::pitchBendMax));
  688. m_MidiCh[midiCh].midiPitchBendPos = newPitchBendPos;
  689. MidiSend(MIDIEvents::PitchBend(midiCh, DecodePitchBendParam(newPitchBendPos)));
  690. }
  691. // Apply vibrato effect through pitch wheel commands on a given MIDI channel.
  692. void IMidiPlugin::MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn)
  693. {
  694. auto midiCh = GetMidiChannel(trackerChn);
  695. depth = EncodePitchBendParam(depth);
  696. if(depth != 0 || (m_MidiCh[midiCh].midiPitchBendPos & kVibratoFlag))
  697. {
  698. ApplyPitchWheelDepth(depth, pwd);
  699. // Temporarily add vibrato offset to current pitch
  700. int32 newPitchBendPos = (depth + m_MidiCh[midiCh].midiPitchBendPos) & kPitchBendMask;
  701. Limit(newPitchBendPos, EncodePitchBendParam(MIDIEvents::pitchBendMin), EncodePitchBendParam(MIDIEvents::pitchBendMax));
  702. MidiSend(MIDIEvents::PitchBend(midiCh, DecodePitchBendParam(newPitchBendPos)));
  703. }
  704. // Update vibrato status
  705. if(depth != 0)
  706. m_MidiCh[midiCh].midiPitchBendPos |= kVibratoFlag;
  707. else
  708. m_MidiCh[midiCh].midiPitchBendPos &= ~kVibratoFlag;
  709. }
  710. void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel)
  711. {
  712. if(trackChannel >= MAX_CHANNELS)
  713. return;
  714. auto midiCh = GetMidiChannel(trackChannel);
  715. PlugInstrChannel &channel = m_MidiCh[midiCh];
  716. uint16 midiBank = instr.wMidiBank - 1;
  717. uint8 midiProg = instr.nMidiProgram - 1;
  718. bool bankChanged = (channel.currentBank != midiBank) && (midiBank < 0x4000);
  719. bool progChanged = (channel.currentProgram != midiProg) && (midiProg < 0x80);
  720. //get vol in [0,128[
  721. uint8 volume = static_cast<uint8>(std::min((vol + 1u) / 2u, 127u));
  722. // Bank change
  723. if(bankChanged)
  724. {
  725. uint8 high = static_cast<uint8>(midiBank >> 7);
  726. uint8 low = static_cast<uint8>(midiBank & 0x7F);
  727. //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.Global[MIDIOUT_BANKSEL], 0, m_nSlot + 1);
  728. MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Coarse, midiCh, high));
  729. MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Fine, midiCh, low));
  730. channel.currentBank = midiBank;
  731. }
  732. // Program change
  733. // According to the MIDI specs, a bank change alone doesn't have to change the active program - it will only change the bank of subsequent program changes.
  734. // Thus we send program changes also if only the bank has changed.
  735. if(progChanged || (midiProg < 0x80 && bankChanged))
  736. {
  737. channel.currentProgram = midiProg;
  738. //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.Global[MIDIOUT_PROGRAM], 0, m_nSlot + 1);
  739. MidiSend(MIDIEvents::ProgramChange(midiCh, midiProg));
  740. }
  741. // Specific Note Off
  742. if(note > NOTE_MAX_SPECIAL)
  743. {
  744. uint8 i = static_cast<uint8>(note - NOTE_MAX_SPECIAL - NOTE_MIN);
  745. if(channel.noteOnMap[i][trackChannel])
  746. {
  747. channel.noteOnMap[i][trackChannel]--;
  748. MidiSend(MIDIEvents::NoteOff(midiCh, i, 0));
  749. }
  750. }
  751. // "Hard core" All Sounds Off on this midi and tracker channel
  752. // This one doesn't check the note mask - just one note off per note.
  753. // Also less likely to cause a VST event buffer overflow.
  754. else if(note == NOTE_NOTECUT) // ^^
  755. {
  756. MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllNotesOff, midiCh, 0));
  757. MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllSoundOff, midiCh, 0));
  758. // Turn off all notes
  759. for(uint8 i = 0; i < std::size(channel.noteOnMap); i++)
  760. {
  761. channel.noteOnMap[i][trackChannel] = 0;
  762. MidiSend(MIDIEvents::NoteOff(midiCh, i, volume));
  763. }
  764. }
  765. // All "active" notes off on this midi and tracker channel
  766. // using note mask.
  767. else if(note == NOTE_KEYOFF || note == NOTE_FADE) // ==, ~~
  768. {
  769. for(uint8 i = 0; i < std::size(channel.noteOnMap); i++)
  770. {
  771. // Some VSTis need a note off for each instance of a note on, e.g. fabfilter.
  772. while(channel.noteOnMap[i][trackChannel])
  773. {
  774. MidiSend(MIDIEvents::NoteOff(midiCh, i, volume));
  775. channel.noteOnMap[i][trackChannel]--;
  776. }
  777. }
  778. }
  779. // Note On
  780. else if(note >= NOTE_MIN && note < NOTE_MIN + mpt::array_size<decltype(channel.noteOnMap)>::size)
  781. {
  782. note -= NOTE_MIN;
  783. // Reset pitch bend on each new note, tracker style.
  784. // This is done if the pitch wheel has been moved or there was a vibrato on the previous row (in which case the "vstVibratoFlag" bit of the pitch bend memory is set)
  785. auto newPitchBendPos = EncodePitchBendParam(Clamp(m_SndFile.m_PlayState.Chn[trackChannel].GetMIDIPitchBend(), MIDIEvents::pitchBendMin, MIDIEvents::pitchBendMax));
  786. if(m_MidiCh[midiCh].midiPitchBendPos != newPitchBendPos)
  787. {
  788. SendMidiPitchBend(midiCh, newPitchBendPos);
  789. }
  790. // count instances of active notes.
  791. // This is to send a note off for each instance of a note, for plugs like Fabfilter.
  792. // Problem: if a note dies out naturally and we never send a note off, this counter
  793. // will block at max until note off. Is this a problem?
  794. // Safe to assume we won't need more than 255 note offs max on a given note?
  795. if(channel.noteOnMap[note][trackChannel] < uint8_max)
  796. {
  797. channel.noteOnMap[note][trackChannel]++;
  798. }
  799. MidiSend(MIDIEvents::NoteOn(midiCh, static_cast<uint8>(note), volume));
  800. }
  801. }
  802. bool IMidiPlugin::IsNotePlaying(uint8 note, CHANNELINDEX trackerChn)
  803. {
  804. if(!ModCommand::IsNote(note) || trackerChn >= std::size(m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note]))
  805. return false;
  806. note -= NOTE_MIN;
  807. return (m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note][trackerChn] != 0);
  808. }
  809. void IMidiPlugin::ReceiveMidi(uint32 midiCode)
  810. {
  811. ResetSilence();
  812. // I think we should only route events to plugins that are explicitely specified as output plugins of the current plugin.
  813. // This should probably use GetOutputPlugList here if we ever get to support multiple output plugins.
  814. PLUGINDEX receiver;
  815. if(m_pMixStruct != nullptr && (receiver = m_pMixStruct->GetOutputPlugin()) != PLUGINDEX_INVALID)
  816. {
  817. IMixPlugin *plugin = m_SndFile.m_MixPlugins[receiver].pMixPlugin;
  818. // Add all events to the plugin's queue.
  819. plugin->MidiSend(midiCode);
  820. }
  821. #ifdef MODPLUG_TRACKER
  822. if(m_recordMIDIOut)
  823. {
  824. // Spam MIDI data to all views
  825. ::PostMessage(CMainFrame::GetMainFrame()->GetMidiRecordWnd(), WM_MOD_MIDIMSG, midiCode, reinterpret_cast<LPARAM>(this));
  826. }
  827. #endif // MODPLUG_TRACKER
  828. }
  829. void IMidiPlugin::ReceiveSysex(mpt::const_byte_span sysex)
  830. {
  831. ResetSilence();
  832. // I think we should only route events to plugins that are explicitely specified as output plugins of the current plugin.
  833. // This should probably use GetOutputPlugList here if we ever get to support multiple output plugins.
  834. PLUGINDEX receiver;
  835. if(m_pMixStruct != nullptr && (receiver = m_pMixStruct->GetOutputPlugin()) != PLUGINDEX_INVALID)
  836. {
  837. IMixPlugin *plugin = m_SndFile.m_MixPlugins[receiver].pMixPlugin;
  838. // Add all events to the plugin's queue.
  839. plugin->MidiSysexSend(sysex);
  840. }
  841. }
  842. // SNDMIXPLUGIN functions
  843. void SNDMIXPLUGIN::SetGain(uint8 gain)
  844. {
  845. Info.gain = gain;
  846. if(pMixPlugin != nullptr) pMixPlugin->RecalculateGain();
  847. }
  848. void SNDMIXPLUGIN::SetBypass(bool bypass)
  849. {
  850. if(pMixPlugin != nullptr)
  851. pMixPlugin->Bypass(bypass);
  852. else
  853. Info.SetBypass(bypass);
  854. }
  855. void SNDMIXPLUGIN::Destroy()
  856. {
  857. if(pMixPlugin)
  858. {
  859. pMixPlugin->Release();
  860. pMixPlugin = nullptr;
  861. }
  862. pluginData.clear();
  863. pluginData.shrink_to_fit();
  864. }
  865. OPENMPT_NAMESPACE_END
  866. #endif // NO_PLUGINS