DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1478 lines
44KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "DistrhoPluginInternal.hpp"
  17. #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI
  18. # undef DISTRHO_PLUGIN_HAS_UI
  19. # define DISTRHO_PLUGIN_HAS_UI 0
  20. #endif
  21. #if DISTRHO_PLUGIN_HAS_UI
  22. # include "DistrhoUIInternal.hpp"
  23. #endif
  24. #ifndef __cdecl
  25. # define __cdecl
  26. #endif
  27. #define VESTIGE_HEADER
  28. #define VST_FORCE_DEPRECATED 0
  29. #include <clocale>
  30. #include <map>
  31. #include <string>
  32. #ifdef VESTIGE_HEADER
  33. # include "vestige/vestige.h"
  34. #define effFlagsProgramChunks (1 << 5)
  35. #define effSetProgramName 4
  36. #define effGetParamLabel 6
  37. #define effGetParamDisplay 7
  38. #define effGetChunk 23
  39. #define effSetChunk 24
  40. #define effCanBeAutomated 26
  41. #define effGetProgramNameIndexed 29
  42. #define effGetPlugCategory 35
  43. #define effEditKeyDown 59
  44. #define effEditKeyUp 60
  45. #define kVstVersion 2400
  46. struct ERect {
  47. int16_t top, left, bottom, right;
  48. };
  49. #else
  50. # include "vst/aeffectx.h"
  51. #endif
  52. START_NAMESPACE_DISTRHO
  53. typedef std::map<const String, String> StringMap;
  54. static const int kVstMidiEventSize = static_cast<int>(sizeof(VstMidiEvent));
  55. #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  56. static const writeMidiFunc writeMidiCallback = nullptr;
  57. #endif
  58. // -----------------------------------------------------------------------
  59. void strncpy(char* const dst, const char* const src, const size_t size)
  60. {
  61. std::strncpy(dst, src, size-1);
  62. dst[size-1] = '\0';
  63. }
  64. void snprintf_param(char* const dst, const float value, const size_t size)
  65. {
  66. std::snprintf(dst, size-1, "%f", value);
  67. dst[size-1] = '\0';
  68. }
  69. void snprintf_iparam(char* const dst, const int32_t value, const size_t size)
  70. {
  71. std::snprintf(dst, size-1, "%d", value);
  72. dst[size-1] = '\0';
  73. }
  74. // -----------------------------------------------------------------------
  75. class ScopedSafeLocale {
  76. public:
  77. ScopedSafeLocale() noexcept
  78. : locale(::strdup(::setlocale(LC_NUMERIC, nullptr)))
  79. {
  80. ::setlocale(LC_NUMERIC, "C");
  81. }
  82. ~ScopedSafeLocale() noexcept
  83. {
  84. if (locale != nullptr)
  85. {
  86. ::setlocale(LC_NUMERIC, locale);
  87. std::free(locale);
  88. }
  89. }
  90. private:
  91. char* const locale;
  92. DISTRHO_DECLARE_NON_COPY_CLASS(ScopedSafeLocale)
  93. DISTRHO_PREVENT_HEAP_ALLOCATION
  94. };
  95. // -----------------------------------------------------------------------
  96. class ParameterCheckHelper
  97. {
  98. public:
  99. ParameterCheckHelper()
  100. : parameterChecks(nullptr),
  101. parameterValues(nullptr) {}
  102. virtual ~ParameterCheckHelper()
  103. {
  104. if (parameterChecks != nullptr)
  105. {
  106. delete[] parameterChecks;
  107. parameterChecks = nullptr;
  108. }
  109. if (parameterValues != nullptr)
  110. {
  111. delete[] parameterValues;
  112. parameterValues = nullptr;
  113. }
  114. }
  115. bool* parameterChecks;
  116. float* parameterValues;
  117. #if DISTRHO_PLUGIN_WANT_STATE
  118. virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0;
  119. #endif
  120. };
  121. #if DISTRHO_PLUGIN_HAS_UI
  122. // -----------------------------------------------------------------------
  123. class UIVst
  124. {
  125. public:
  126. UIVst(const audioMasterCallback audioMaster, AEffect* const effect, ParameterCheckHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId)
  127. : fAudioMaster(audioMaster),
  128. fEffect(effect),
  129. fUiHelper(uiHelper),
  130. fPlugin(plugin),
  131. fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, plugin->getInstancePointer()),
  132. fShouldCaptureVstKeys(false)
  133. {
  134. // FIXME only needed for windows?
  135. //#ifdef DISTRHO_OS_WINDOWS
  136. char strBuf[0xff+1];
  137. std::memset(strBuf, 0, sizeof(char)*(0xff+1));
  138. hostCallback(audioMasterGetProductString, 0, 0, strBuf);
  139. d_stdout("Plugin UI running in '%s'", strBuf);
  140. // TODO make a white-list of needed hosts
  141. if (/*std::strcmp(strBuf, "") == 0*/ true)
  142. fShouldCaptureVstKeys = true;
  143. //#endif
  144. }
  145. // -------------------------------------------------------------------
  146. void idle()
  147. {
  148. for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i)
  149. {
  150. if (fUiHelper->parameterChecks[i])
  151. {
  152. fUiHelper->parameterChecks[i] = false;
  153. fUI.parameterChanged(i, fUiHelper->parameterValues[i]);
  154. }
  155. }
  156. fUI.idle();
  157. }
  158. int16_t getWidth() const
  159. {
  160. return fUI.getWidth();
  161. }
  162. int16_t getHeight() const
  163. {
  164. return fUI.getHeight();
  165. }
  166. void setSampleRate(const double newSampleRate)
  167. {
  168. fUI.setSampleRate(newSampleRate, true);
  169. }
  170. // -------------------------------------------------------------------
  171. // functions called from the plugin side, may block
  172. # if DISTRHO_PLUGIN_WANT_STATE
  173. void setStateFromPlugin(const char* const key, const char* const value)
  174. {
  175. fUI.stateChanged(key, value);
  176. }
  177. # endif
  178. int handlePluginKeyEvent(const bool down, int32_t index, const intptr_t value)
  179. {
  180. # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  181. if (! fShouldCaptureVstKeys)
  182. return 0;
  183. d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value);
  184. int special = 0;
  185. switch (value)
  186. {
  187. // convert some specials to normal keys
  188. case 1: index = kCharBackspace; break;
  189. case 6: index = kCharEscape; break;
  190. case 7: index = ' '; break;
  191. case 22: index = kCharDelete; break;
  192. // handle rest of special keys
  193. case 40: special = kKeyF1; break;
  194. case 41: special = kKeyF2; break;
  195. case 42: special = kKeyF3; break;
  196. case 43: special = kKeyF4; break;
  197. case 44: special = kKeyF5; break;
  198. case 45: special = kKeyF6; break;
  199. case 46: special = kKeyF7; break;
  200. case 47: special = kKeyF8; break;
  201. case 48: special = kKeyF9; break;
  202. case 49: special = kKeyF10; break;
  203. case 50: special = kKeyF11; break;
  204. case 51: special = kKeyF12; break;
  205. case 11: special = kKeyLeft; break;
  206. case 12: special = kKeyUp; break;
  207. case 13: special = kKeyRight; break;
  208. case 14: special = kKeyDown; break;
  209. case 15: special = kKeyPageUp; break;
  210. case 16: special = kKeyPageDown; break;
  211. case 10: special = kKeyHome; break;
  212. case 9: special = kKeyEnd; break;
  213. case 21: special = kKeyInsert; break;
  214. case 54: special = kKeyShift; break;
  215. case 55: special = kKeyControl; break;
  216. case 56: special = kKeyAlt; break;
  217. }
  218. if (special != 0)
  219. return fUI.handlePluginSpecial(down, static_cast<Key>(special));
  220. if (index >= 0)
  221. return fUI.handlePluginKeyboard(down, static_cast<uint>(index));
  222. # endif
  223. return 0;
  224. }
  225. // -------------------------------------------------------------------
  226. protected:
  227. intptr_t hostCallback(const int32_t opcode,
  228. const int32_t index = 0,
  229. const intptr_t value = 0,
  230. void* const ptr = nullptr,
  231. const float opt = 0.0f)
  232. {
  233. return fAudioMaster(fEffect, opcode, index, value, ptr, opt);
  234. }
  235. void editParameter(const uint32_t index, const bool started)
  236. {
  237. hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index);
  238. }
  239. void setParameterValue(const uint32_t index, const float realValue)
  240. {
  241. const ParameterRanges& ranges(fPlugin->getParameterRanges(index));
  242. const float perValue(ranges.getNormalizedValue(realValue));
  243. fPlugin->setParameterValue(index, realValue);
  244. hostCallback(audioMasterAutomate, index, 0, nullptr, perValue);
  245. }
  246. void setState(const char* const key, const char* const value)
  247. {
  248. # if DISTRHO_PLUGIN_WANT_STATE
  249. fUiHelper->setStateFromUI(key, value);
  250. # else
  251. return; // unused
  252. (void)key;
  253. (void)value;
  254. # endif
  255. }
  256. void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
  257. {
  258. # if 0 //DISTRHO_PLUGIN_WANT_MIDI_INPUT
  259. // TODO
  260. # else
  261. return; // unused
  262. (void)channel;
  263. (void)note;
  264. (void)velocity;
  265. # endif
  266. }
  267. void setSize(const uint width, const uint height)
  268. {
  269. fUI.setWindowSize(width, height);
  270. hostCallback(audioMasterSizeWindow, width, height);
  271. }
  272. private:
  273. // Vst stuff
  274. const audioMasterCallback fAudioMaster;
  275. AEffect* const fEffect;
  276. ParameterCheckHelper* const fUiHelper;
  277. PluginExporter* const fPlugin;
  278. // Plugin UI
  279. UIExporter fUI;
  280. bool fShouldCaptureVstKeys;
  281. // -------------------------------------------------------------------
  282. // Callbacks
  283. #define handlePtr ((UIVst*)ptr)
  284. static void editParameterCallback(void* ptr, uint32_t index, bool started)
  285. {
  286. handlePtr->editParameter(index, started);
  287. }
  288. static void setParameterCallback(void* ptr, uint32_t rindex, float value)
  289. {
  290. handlePtr->setParameterValue(rindex, value);
  291. }
  292. static void setStateCallback(void* ptr, const char* key, const char* value)
  293. {
  294. handlePtr->setState(key, value);
  295. }
  296. static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
  297. {
  298. handlePtr->sendNote(channel, note, velocity);
  299. }
  300. static void setSizeCallback(void* ptr, uint width, uint height)
  301. {
  302. handlePtr->setSize(width, height);
  303. }
  304. #undef handlePtr
  305. };
  306. #endif
  307. // -----------------------------------------------------------------------
  308. class PluginVst : public ParameterCheckHelper
  309. {
  310. public:
  311. PluginVst(const audioMasterCallback audioMaster, AEffect* const effect)
  312. : fPlugin(this, writeMidiCallback),
  313. fAudioMaster(audioMaster),
  314. fEffect(effect)
  315. {
  316. std::memset(fProgramName, 0, sizeof(char)*(32+1));
  317. std::strcpy(fProgramName, "Default");
  318. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  319. fMidiEventCount = 0;
  320. #endif
  321. #if DISTRHO_PLUGIN_HAS_UI
  322. fVstUI = nullptr;
  323. fVstRect.top = 0;
  324. fVstRect.left = 0;
  325. fVstRect.bottom = 0;
  326. fVstRect.right = 0;
  327. if (const uint32_t paramCount = fPlugin.getParameterCount())
  328. {
  329. parameterChecks = new bool[paramCount];
  330. parameterValues = new float[paramCount];
  331. for (uint32_t i=0; i < paramCount; ++i)
  332. {
  333. parameterChecks[i] = false;
  334. parameterValues[i] = NAN;
  335. }
  336. }
  337. # if DISTRHO_OS_MAC
  338. # ifdef __LP64__
  339. fUsingNsView = true;
  340. # else
  341. # ifndef DISTRHO_NO_WARNINGS
  342. # warning 32bit VST UIs on OSX only work if the host supports "hasCockosViewAsConfig"
  343. # endif
  344. fUsingNsView = false;
  345. # endif
  346. # endif // DISTRHO_OS_MAC
  347. #endif // DISTRHO_PLUGIN_HAS_UI
  348. #if DISTRHO_PLUGIN_WANT_STATE
  349. fStateChunk = nullptr;
  350. for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i)
  351. {
  352. const String& dkey(fPlugin.getStateKey(i));
  353. fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
  354. }
  355. #endif
  356. }
  357. ~PluginVst()
  358. {
  359. #if DISTRHO_PLUGIN_WANT_STATE
  360. if (fStateChunk != nullptr)
  361. {
  362. delete[] fStateChunk;
  363. fStateChunk = nullptr;
  364. }
  365. fStateMap.clear();
  366. #endif
  367. }
  368. intptr_t vst_dispatcher(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
  369. {
  370. #if DISTRHO_PLUGIN_WANT_STATE
  371. intptr_t ret = 0;
  372. #endif
  373. switch (opcode)
  374. {
  375. case effGetProgram:
  376. return 0;
  377. case effSetProgramName:
  378. if (char* const programName = (char*)ptr)
  379. {
  380. DISTRHO_NAMESPACE::strncpy(fProgramName, programName, 32);
  381. return 1;
  382. }
  383. break;
  384. case effGetProgramName:
  385. if (char* const programName = (char*)ptr)
  386. {
  387. DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24);
  388. return 1;
  389. }
  390. break;
  391. case effGetProgramNameIndexed:
  392. if (char* const programName = (char*)ptr)
  393. {
  394. DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24);
  395. return 1;
  396. }
  397. break;
  398. case effGetParamDisplay:
  399. if (ptr != nullptr && index < static_cast<int32_t>(fPlugin.getParameterCount()))
  400. {
  401. const uint32_t hints = fPlugin.getParameterHints(index);
  402. float value = fPlugin.getParameterValue(index);
  403. if (hints & kParameterIsBoolean)
  404. {
  405. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  406. const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
  407. value = value > midRange ? ranges.max : ranges.min;
  408. }
  409. else if (hints & kParameterIsInteger)
  410. {
  411. value = std::round(value);
  412. }
  413. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
  414. for (uint8_t i = 0; i < enumValues.count; ++i)
  415. {
  416. if (d_isNotEqual(value, enumValues.values[i].value))
  417. continue;
  418. DISTRHO_NAMESPACE::strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24);
  419. return 1;
  420. }
  421. if (hints & kParameterIsInteger)
  422. DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)value, 24);
  423. else
  424. DISTRHO_NAMESPACE::snprintf_param((char*)ptr, value, 24);
  425. return 1;
  426. }
  427. break;
  428. case effSetSampleRate:
  429. fPlugin.setSampleRate(opt, true);
  430. #if DISTRHO_PLUGIN_HAS_UI
  431. if (fVstUI != nullptr)
  432. fVstUI->setSampleRate(opt);
  433. #endif
  434. break;
  435. case effSetBlockSize:
  436. fPlugin.setBufferSize(value, true);
  437. break;
  438. case effMainsChanged:
  439. if (value != 0)
  440. {
  441. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  442. fMidiEventCount = 0;
  443. // tell host we want MIDI events
  444. hostCallback(audioMasterWantMidi);
  445. #endif
  446. // deactivate for possible changes
  447. fPlugin.deactivateIfNeeded();
  448. // check if something changed
  449. const uint32_t bufferSize = static_cast<uint32_t>(hostCallback(audioMasterGetBlockSize));
  450. const double sampleRate = static_cast<double>(hostCallback(audioMasterGetSampleRate));
  451. if (bufferSize != 0)
  452. fPlugin.setBufferSize(bufferSize, true);
  453. if (sampleRate != 0.0)
  454. fPlugin.setSampleRate(sampleRate, true);
  455. fPlugin.activate();
  456. }
  457. else
  458. {
  459. fPlugin.deactivate();
  460. }
  461. break;
  462. #if DISTRHO_PLUGIN_HAS_UI
  463. case effEditGetRect:
  464. if (fVstUI != nullptr)
  465. {
  466. fVstRect.right = fVstUI->getWidth();
  467. fVstRect.bottom = fVstUI->getHeight();
  468. }
  469. else
  470. {
  471. d_lastUiSampleRate = fPlugin.getSampleRate();
  472. UIExporter tmpUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr, fPlugin.getInstancePointer());
  473. fVstRect.right = tmpUI.getWidth();
  474. fVstRect.bottom = tmpUI.getHeight();
  475. tmpUI.quit();
  476. }
  477. *(ERect**)ptr = &fVstRect;
  478. return 1;
  479. case effEditOpen:
  480. if (fVstUI == nullptr)
  481. {
  482. # if DISTRHO_OS_MAC
  483. if (! fUsingNsView)
  484. {
  485. d_stderr("Host doesn't support hasCockosViewAsConfig, cannot use UI");
  486. return 0;
  487. }
  488. # endif
  489. d_lastUiSampleRate = fPlugin.getSampleRate();
  490. fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr);
  491. # if DISTRHO_PLUGIN_WANT_FULL_STATE
  492. // Update current state from plugin side
  493. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  494. {
  495. const String& key = cit->first;
  496. fStateMap[key] = fPlugin.getState(key);
  497. }
  498. # endif
  499. # if DISTRHO_PLUGIN_WANT_STATE
  500. // Set state
  501. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  502. {
  503. const String& key = cit->first;
  504. const String& value = cit->second;
  505. fVstUI->setStateFromPlugin(key, value);
  506. }
  507. # endif
  508. for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
  509. setParameterValueFromPlugin(i, fPlugin.getParameterValue(i));
  510. fVstUI->idle();
  511. return 1;
  512. }
  513. break;
  514. case effEditClose:
  515. if (fVstUI != nullptr)
  516. {
  517. delete fVstUI;
  518. fVstUI = nullptr;
  519. return 1;
  520. }
  521. break;
  522. //case effIdle:
  523. case effEditIdle:
  524. if (fVstUI != nullptr)
  525. fVstUI->idle();
  526. break;
  527. case effEditKeyDown:
  528. if (fVstUI != nullptr)
  529. return fVstUI->handlePluginKeyEvent(true, index, value);
  530. break;
  531. case effEditKeyUp:
  532. if (fVstUI != nullptr)
  533. return fVstUI->handlePluginKeyEvent(false, index, value);
  534. break;
  535. #endif // DISTRHO_PLUGIN_HAS_UI
  536. #if DISTRHO_PLUGIN_WANT_STATE
  537. case effGetChunk:
  538. {
  539. if (ptr == nullptr)
  540. return 0;
  541. if (fStateChunk != nullptr)
  542. {
  543. delete[] fStateChunk;
  544. fStateChunk = nullptr;
  545. }
  546. const uint32_t paramCount = fPlugin.getParameterCount();
  547. if (fPlugin.getStateCount() == 0 && paramCount == 0)
  548. {
  549. fStateChunk = new char[1];
  550. fStateChunk[0] = '\0';
  551. ret = 1;
  552. }
  553. else
  554. {
  555. # if DISTRHO_PLUGIN_WANT_FULL_STATE
  556. // Update current state
  557. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  558. {
  559. const String& key = cit->first;
  560. fStateMap[key] = fPlugin.getState(key);
  561. }
  562. # endif
  563. String chunkStr;
  564. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  565. {
  566. const String& key = cit->first;
  567. const String& value = cit->second;
  568. // join key and value
  569. String tmpStr;
  570. tmpStr = key;
  571. tmpStr += "\xff";
  572. tmpStr += value;
  573. tmpStr += "\xff";
  574. chunkStr += tmpStr;
  575. }
  576. if (paramCount != 0)
  577. {
  578. // add another separator
  579. chunkStr += "\xff";
  580. // temporarily set locale to "C" while converting floats
  581. const ScopedSafeLocale ssl;
  582. for (uint32_t i=0; i<paramCount; ++i)
  583. {
  584. if (fPlugin.isParameterOutputOrTrigger(i))
  585. continue;
  586. // join key and value
  587. String tmpStr;
  588. tmpStr = fPlugin.getParameterSymbol(i);
  589. tmpStr += "\xff";
  590. tmpStr += String(fPlugin.getParameterValue(i));
  591. tmpStr += "\xff";
  592. chunkStr += tmpStr;
  593. }
  594. }
  595. const std::size_t chunkSize(chunkStr.length()+1);
  596. fStateChunk = new char[chunkSize];
  597. std::memcpy(fStateChunk, chunkStr.buffer(), chunkStr.length());
  598. fStateChunk[chunkSize-1] = '\0';
  599. for (std::size_t i=0; i<chunkSize; ++i)
  600. {
  601. if (fStateChunk[i] == '\xff')
  602. fStateChunk[i] = '\0';
  603. }
  604. ret = chunkSize;
  605. }
  606. *(void**)ptr = fStateChunk;
  607. return ret;
  608. }
  609. case effSetChunk:
  610. {
  611. if (value <= 1 || ptr == nullptr)
  612. return 0;
  613. const size_t chunkSize = static_cast<size_t>(value);
  614. const char* key = (const char*)ptr;
  615. const char* value = nullptr;
  616. size_t size, bytesRead = 0;
  617. while (bytesRead < chunkSize)
  618. {
  619. if (key[0] == '\0')
  620. break;
  621. size = std::strlen(key)+1;
  622. value = key + size;
  623. bytesRead += size;
  624. setStateFromUI(key, value);
  625. # if DISTRHO_PLUGIN_HAS_UI
  626. if (fVstUI != nullptr)
  627. fVstUI->setStateFromPlugin(key, value);
  628. # endif
  629. // get next key
  630. size = std::strlen(value)+1;
  631. key = value + size;
  632. bytesRead += size;
  633. }
  634. const uint32_t paramCount = fPlugin.getParameterCount();
  635. if (bytesRead+4 < chunkSize && paramCount != 0)
  636. {
  637. ++key;
  638. float fvalue;
  639. // temporarily set locale to "C" while converting floats
  640. const ScopedSafeLocale ssl;
  641. while (bytesRead < chunkSize)
  642. {
  643. if (key[0] == '\0')
  644. break;
  645. size = std::strlen(key)+1;
  646. value = key + size;
  647. bytesRead += size;
  648. // find parameter with this symbol, and set its value
  649. for (uint32_t i=0; i<paramCount; ++i)
  650. {
  651. if (fPlugin.isParameterOutputOrTrigger(i))
  652. continue;
  653. if (fPlugin.getParameterSymbol(i) != key)
  654. continue;
  655. fvalue = std::atof(value);
  656. fPlugin.setParameterValue(i, fvalue);
  657. # if DISTRHO_PLUGIN_HAS_UI
  658. if (fVstUI != nullptr)
  659. setParameterValueFromPlugin(i, fvalue);
  660. # endif
  661. break;
  662. }
  663. // get next key
  664. size = std::strlen(value)+1;
  665. key = value + size;
  666. bytesRead += size;
  667. }
  668. }
  669. return 1;
  670. }
  671. #endif // DISTRHO_PLUGIN_WANT_STATE
  672. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  673. case effProcessEvents:
  674. if (! fPlugin.isActive())
  675. {
  676. // host has not activated the plugin yet, nasty!
  677. vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f);
  678. }
  679. if (const VstEvents* const events = (const VstEvents*)ptr)
  680. {
  681. if (events->numEvents == 0)
  682. break;
  683. for (int i=0, count=events->numEvents; i < count; ++i)
  684. {
  685. const VstMidiEvent* const vstMidiEvent((const VstMidiEvent*)events->events[i]);
  686. if (vstMidiEvent == nullptr)
  687. break;
  688. if (vstMidiEvent->type != kVstMidiType)
  689. continue;
  690. if (fMidiEventCount >= kMaxMidiEvents)
  691. break;
  692. MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
  693. midiEvent.frame = vstMidiEvent->deltaFrames;
  694. midiEvent.size = 3;
  695. std::memcpy(midiEvent.data, vstMidiEvent->midiData, sizeof(uint8_t)*3);
  696. }
  697. }
  698. break;
  699. #endif
  700. case effCanBeAutomated:
  701. if (index < static_cast<int32_t>(fPlugin.getParameterCount()))
  702. {
  703. const uint32_t hints(fPlugin.getParameterHints(index));
  704. // must be automable, and not output
  705. if ((hints & kParameterIsAutomable) != 0 && (hints & kParameterIsOutput) == 0)
  706. return 1;
  707. }
  708. break;
  709. case effCanDo:
  710. if (const char* const canDo = (const char*)ptr)
  711. {
  712. #if DISTRHO_OS_MAC && DISTRHO_PLUGIN_HAS_UI
  713. if (std::strcmp(canDo, "hasCockosViewAsConfig") == 0)
  714. {
  715. fUsingNsView = true;
  716. return 0xbeef0000;
  717. }
  718. #endif
  719. if (std::strcmp(canDo, "receiveVstEvents") == 0 ||
  720. std::strcmp(canDo, "receiveVstMidiEvent") == 0)
  721. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  722. return 1;
  723. #else
  724. return -1;
  725. #endif
  726. if (std::strcmp(canDo, "sendVstEvents") == 0 ||
  727. std::strcmp(canDo, "sendVstMidiEvent") == 0)
  728. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  729. return 1;
  730. #else
  731. return -1;
  732. #endif
  733. if (std::strcmp(canDo, "receiveVstTimeInfo") == 0)
  734. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  735. return 1;
  736. #else
  737. return -1;
  738. #endif
  739. }
  740. break;
  741. //case effStartProcess:
  742. //case effStopProcess:
  743. // unused
  744. // break;
  745. }
  746. return 0;
  747. }
  748. float vst_getParameter(const int32_t index)
  749. {
  750. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  751. return ranges.getNormalizedValue(fPlugin.getParameterValue(index));
  752. }
  753. void vst_setParameter(const int32_t index, float value)
  754. {
  755. const uint32_t hints(fPlugin.getParameterHints(index));
  756. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  757. if (hints & kParameterIsBoolean)
  758. {
  759. const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
  760. value = value > midRange ? ranges.max : ranges.min;
  761. }
  762. else if (hints & kParameterIsInteger)
  763. {
  764. value = std::round(value);
  765. }
  766. const float realValue(ranges.getUnnormalizedValue(value));
  767. fPlugin.setParameterValue(index, realValue);
  768. #if DISTRHO_PLUGIN_HAS_UI
  769. if (fVstUI != nullptr)
  770. setParameterValueFromPlugin(index, realValue);
  771. #endif
  772. }
  773. void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames)
  774. {
  775. if (sampleFrames <= 0)
  776. {
  777. updateParameterOutputsAndTriggers();
  778. return;
  779. }
  780. if (! fPlugin.isActive())
  781. {
  782. // host has not activated the plugin yet, nasty!
  783. vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f);
  784. }
  785. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  786. static const int kWantVstTimeFlags(kVstTransportPlaying|kVstPpqPosValid|kVstTempoValid|kVstTimeSigValid);
  787. if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)hostCallback(audioMasterGetTime, 0, kWantVstTimeFlags))
  788. {
  789. fTimePosition.frame = vstTimeInfo->samplePos;
  790. fTimePosition.playing = (vstTimeInfo->flags & kVstTransportPlaying);
  791. fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0);
  792. // ticksPerBeat is not possible with VST
  793. fTimePosition.bbt.ticksPerBeat = 960.0;
  794. if (vstTimeInfo->flags & kVstTempoValid)
  795. fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo;
  796. else
  797. fTimePosition.bbt.beatsPerMinute = 120.0;
  798. if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid))
  799. {
  800. const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator;
  801. const double barBeats = (std::fmod(vstTimeInfo->ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigDenominator;
  802. const double rest = std::fmod(barBeats, 1.0);
  803. fTimePosition.bbt.bar = int(vstTimeInfo->ppqPos)/ppqPerBar + 1;
  804. fTimePosition.bbt.beat = barBeats-rest+1;
  805. fTimePosition.bbt.tick = rest*fTimePosition.bbt.ticksPerBeat+0.5;
  806. fTimePosition.bbt.beatsPerBar = vstTimeInfo->timeSigNumerator;
  807. fTimePosition.bbt.beatType = vstTimeInfo->timeSigDenominator;
  808. }
  809. else
  810. {
  811. fTimePosition.bbt.bar = 1;
  812. fTimePosition.bbt.beat = 1;
  813. fTimePosition.bbt.tick = 0;
  814. fTimePosition.bbt.beatsPerBar = 4.0f;
  815. fTimePosition.bbt.beatType = 4.0f;
  816. }
  817. fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*fTimePosition.bbt.beatsPerBar*(fTimePosition.bbt.bar-1);
  818. fPlugin.setTimePosition(fTimePosition);
  819. }
  820. #endif
  821. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  822. fPlugin.run(inputs, outputs, sampleFrames, fMidiEvents, fMidiEventCount);
  823. fMidiEventCount = 0;
  824. #else
  825. fPlugin.run(inputs, outputs, sampleFrames);
  826. #endif
  827. updateParameterOutputsAndTriggers();
  828. }
  829. // -------------------------------------------------------------------
  830. friend class UIVst;
  831. private:
  832. // Plugin
  833. PluginExporter fPlugin;
  834. // VST stuff
  835. const audioMasterCallback fAudioMaster;
  836. AEffect* const fEffect;
  837. // Temporary data
  838. char fProgramName[32+1];
  839. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  840. uint32_t fMidiEventCount;
  841. MidiEvent fMidiEvents[kMaxMidiEvents];
  842. #endif
  843. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  844. TimePosition fTimePosition;
  845. #endif
  846. // UI stuff
  847. #if DISTRHO_PLUGIN_HAS_UI
  848. UIVst* fVstUI;
  849. ERect fVstRect;
  850. # if DISTRHO_OS_MAC
  851. bool fUsingNsView;
  852. # endif
  853. #endif
  854. #if DISTRHO_PLUGIN_WANT_STATE
  855. char* fStateChunk;
  856. StringMap fStateMap;
  857. #endif
  858. // -------------------------------------------------------------------
  859. // host callback
  860. intptr_t hostCallback(const int32_t opcode,
  861. const int32_t index = 0,
  862. const intptr_t value = 0,
  863. void* const ptr = nullptr,
  864. const float opt = 0.0f)
  865. {
  866. return fAudioMaster(fEffect, opcode, index, value, ptr, opt);
  867. }
  868. // -------------------------------------------------------------------
  869. // functions called from the plugin side, RT no block
  870. void updateParameterOutputsAndTriggers()
  871. {
  872. float curValue;
  873. for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
  874. {
  875. if (fPlugin.isParameterOutput(i))
  876. {
  877. // NOTE: no output parameter support in VST, simulate it here
  878. curValue = fPlugin.getParameterValue(i);
  879. if (d_isEqual(curValue, parameterValues[i]))
  880. continue;
  881. #if DISTRHO_PLUGIN_HAS_UI
  882. if (fVstUI != nullptr)
  883. setParameterValueFromPlugin(i, curValue);
  884. else
  885. #endif
  886. parameterValues[i] = curValue;
  887. #ifndef DPF_VST_SHOW_PARAMETER_OUTPUTS
  888. // skip automating parameter outputs from plugin if we disable them on VST
  889. continue;
  890. #endif
  891. }
  892. else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger)
  893. {
  894. // NOTE: no trigger support in VST parameters, simulate it here
  895. curValue = fPlugin.getParameterValue(i);
  896. if (d_isEqual(curValue, fPlugin.getParameterRanges(i).def))
  897. continue;
  898. #if DISTRHO_PLUGIN_HAS_UI
  899. if (fVstUI != nullptr)
  900. setParameterValueFromPlugin(i, curValue);
  901. #endif
  902. fPlugin.setParameterValue(i, curValue);
  903. }
  904. else
  905. {
  906. continue;
  907. }
  908. const ParameterRanges& ranges(fPlugin.getParameterRanges(i));
  909. hostCallback(audioMasterAutomate, i, 0, nullptr, ranges.getNormalizedValue(curValue));
  910. }
  911. }
  912. #if DISTRHO_PLUGIN_HAS_UI
  913. void setParameterValueFromPlugin(const uint32_t index, const float realValue)
  914. {
  915. parameterValues[index] = realValue;
  916. parameterChecks[index] = true;
  917. }
  918. #endif
  919. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  920. bool writeMidi(const MidiEvent& midiEvent)
  921. {
  922. if (midiEvent.size > 4)
  923. return true;
  924. VstEvents vstEvents;
  925. std::memset(&vstEvents, 0, sizeof(VstEvents));
  926. VstMidiEvent vstMidiEvent;
  927. std::memset(&vstMidiEvent, 0, sizeof(VstMidiEvent));
  928. vstEvents.numEvents = 1;
  929. vstEvents.events[0] = (VstEvent*)&vstMidiEvent;
  930. vstMidiEvent.type = kVstMidiType;
  931. vstMidiEvent.byteSize = kVstMidiEventSize;
  932. vstMidiEvent.deltaFrames = midiEvent.frame;
  933. for (uint8_t i=0; i<midiEvent.size; ++i)
  934. vstMidiEvent.midiData[i] = midiEvent.data[i];
  935. return hostCallback(audioMasterProcessEvents, 0, 0, &vstEvents) == 1;
  936. }
  937. static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
  938. {
  939. return ((PluginVst*)ptr)->writeMidi(midiEvent);
  940. }
  941. #endif
  942. #if DISTRHO_PLUGIN_WANT_STATE
  943. // -------------------------------------------------------------------
  944. // functions called from the UI side, may block
  945. # if DISTRHO_PLUGIN_HAS_UI
  946. void setStateFromUI(const char* const key, const char* const newValue) override
  947. # else
  948. void setStateFromUI(const char* const key, const char* const newValue)
  949. # endif
  950. {
  951. fPlugin.setState(key, newValue);
  952. // check if we want to save this key
  953. if (! fPlugin.wantStateKey(key))
  954. return;
  955. // check if key already exists
  956. for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it)
  957. {
  958. const String& dkey(it->first);
  959. if (dkey == key)
  960. {
  961. it->second = newValue;
  962. return;
  963. }
  964. }
  965. d_stderr("Failed to find plugin state with key \"%s\"", key);
  966. }
  967. #endif
  968. };
  969. // -----------------------------------------------------------------------
  970. struct VstObject {
  971. audioMasterCallback audioMaster;
  972. PluginVst* plugin;
  973. };
  974. #define validObject effect != nullptr && effect->object != nullptr
  975. #define validPlugin effect != nullptr && effect->object != nullptr && ((VstObject*)effect->object)->plugin != nullptr
  976. #define vstObjectPtr (VstObject*)effect->object
  977. #define pluginPtr (vstObjectPtr)->plugin
  978. static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt)
  979. {
  980. // first internal init
  981. const bool doInternalInit = (opcode == -1729 && index == 0xdead && value == 0xf00d);
  982. if (doInternalInit)
  983. {
  984. // set valid but dummy values
  985. d_lastBufferSize = 512;
  986. d_lastSampleRate = 44100.0;
  987. }
  988. // Create dummy plugin to get data from
  989. static PluginExporter plugin(nullptr, nullptr);
  990. if (doInternalInit)
  991. {
  992. // unset
  993. d_lastBufferSize = 0;
  994. d_lastSampleRate = 0.0;
  995. *(PluginExporter**)ptr = &plugin;
  996. return 0;
  997. }
  998. // handle base opcodes
  999. switch (opcode)
  1000. {
  1001. case effOpen:
  1002. if (VstObject* const obj = vstObjectPtr)
  1003. {
  1004. // this must always be valid
  1005. DISTRHO_SAFE_ASSERT_RETURN(obj->audioMaster != nullptr, 0);
  1006. // some hosts call effOpen twice
  1007. DISTRHO_SAFE_ASSERT_RETURN(obj->plugin == nullptr, 1);
  1008. audioMasterCallback audioMaster = (audioMasterCallback)obj->audioMaster;
  1009. d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f);
  1010. d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f);
  1011. // some hosts are not ready at this point or return 0 buffersize/samplerate
  1012. if (d_lastBufferSize == 0)
  1013. d_lastBufferSize = 2048;
  1014. if (d_lastSampleRate <= 0.0)
  1015. d_lastSampleRate = 44100.0;
  1016. obj->plugin = new PluginVst(audioMaster, effect);
  1017. return 1;
  1018. }
  1019. return 0;
  1020. case effClose:
  1021. if (VstObject* const obj = vstObjectPtr)
  1022. {
  1023. if (obj->plugin != nullptr)
  1024. {
  1025. delete obj->plugin;
  1026. obj->plugin = nullptr;
  1027. }
  1028. #if 0
  1029. /* This code invalidates the object created in VSTPluginMain
  1030. * Probably not safe against all hosts */
  1031. obj->audioMaster = nullptr;
  1032. effect->object = nullptr;
  1033. delete obj;
  1034. #endif
  1035. return 1;
  1036. }
  1037. //delete effect;
  1038. return 0;
  1039. case effGetParamLabel:
  1040. if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount()))
  1041. {
  1042. DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterUnit(index), 8);
  1043. return 1;
  1044. }
  1045. return 0;
  1046. case effGetParamName:
  1047. if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount()))
  1048. {
  1049. DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterName(index), 16);
  1050. return 1;
  1051. }
  1052. return 0;
  1053. case effGetParameterProperties:
  1054. if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount()))
  1055. {
  1056. if (VstParameterProperties* const properties = (VstParameterProperties*)ptr)
  1057. {
  1058. memset(properties, 0, sizeof(VstParameterProperties));
  1059. const uint32_t hints = plugin.getParameterHints(index);
  1060. if (hints & kParameterIsOutput)
  1061. return 1;
  1062. if (hints & kParameterIsBoolean)
  1063. {
  1064. properties->flags |= kVstParameterIsSwitch;
  1065. }
  1066. if (hints & kParameterIsInteger)
  1067. {
  1068. properties->flags |= kVstParameterUsesIntegerMinMax;
  1069. const ParameterRanges& ranges(plugin.getParameterRanges(index));
  1070. properties->minInteger = static_cast<int32_t>(ranges.min);
  1071. properties->maxInteger = static_cast<int32_t>(ranges.max);
  1072. }
  1073. if (hints & kParameterIsLogarithmic)
  1074. {
  1075. properties->flags |= kVstParameterCanRamp;
  1076. }
  1077. return 1;
  1078. }
  1079. }
  1080. return 0;
  1081. case effGetPlugCategory:
  1082. #if DISTRHO_PLUGIN_IS_SYNTH
  1083. return kPlugCategSynth;
  1084. #else
  1085. return kPlugCategEffect;
  1086. #endif
  1087. case effGetEffectName:
  1088. if (char* const cptr = (char*)ptr)
  1089. {
  1090. DISTRHO_NAMESPACE::strncpy(cptr, plugin.getName(), 32);
  1091. return 1;
  1092. }
  1093. return 0;
  1094. case effGetVendorString:
  1095. if (char* const cptr = (char*)ptr)
  1096. {
  1097. DISTRHO_NAMESPACE::strncpy(cptr, plugin.getMaker(), 32);
  1098. return 1;
  1099. }
  1100. return 0;
  1101. case effGetProductString:
  1102. if (char* const cptr = (char*)ptr)
  1103. {
  1104. DISTRHO_NAMESPACE::strncpy(cptr, plugin.getLabel(), 32);
  1105. return 1;
  1106. }
  1107. return 0;
  1108. case effGetVendorVersion:
  1109. return plugin.getVersion();
  1110. case effGetVstVersion:
  1111. return kVstVersion;
  1112. };
  1113. // handle advanced opcodes
  1114. if (validPlugin)
  1115. return pluginPtr->vst_dispatcher(opcode, index, value, ptr, opt);
  1116. return 0;
  1117. }
  1118. static float vst_getParameterCallback(AEffect* effect, int32_t index)
  1119. {
  1120. if (validPlugin)
  1121. return pluginPtr->vst_getParameter(index);
  1122. return 0.0f;
  1123. }
  1124. static void vst_setParameterCallback(AEffect* effect, int32_t index, float value)
  1125. {
  1126. if (validPlugin)
  1127. pluginPtr->vst_setParameter(index, value);
  1128. }
  1129. static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
  1130. {
  1131. if (validPlugin)
  1132. pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
  1133. }
  1134. static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
  1135. {
  1136. if (validPlugin)
  1137. pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
  1138. }
  1139. #undef pluginPtr
  1140. #undef validObject
  1141. #undef validPlugin
  1142. #undef vstObjectPtr
  1143. // -----------------------------------------------------------------------
  1144. END_NAMESPACE_DISTRHO
  1145. DISTRHO_PLUGIN_EXPORT
  1146. #if DISTRHO_OS_WINDOWS || DISTRHO_OS_MAC
  1147. const AEffect* VSTPluginMain(audioMasterCallback audioMaster);
  1148. #else
  1149. const AEffect* VSTPluginMain(audioMasterCallback audioMaster) asm ("main");
  1150. #endif
  1151. DISTRHO_PLUGIN_EXPORT
  1152. const AEffect* VSTPluginMain(audioMasterCallback audioMaster)
  1153. {
  1154. USE_NAMESPACE_DISTRHO
  1155. // old version
  1156. if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0)
  1157. return nullptr;
  1158. // first internal init
  1159. PluginExporter* plugin = nullptr;
  1160. vst_dispatcherCallback(nullptr, -1729, 0xdead, 0xf00d, &plugin, 0.0f);
  1161. DISTRHO_SAFE_ASSERT_RETURN(plugin != nullptr, nullptr);
  1162. AEffect* const effect(new AEffect);
  1163. std::memset(effect, 0, sizeof(AEffect));
  1164. // vst fields
  1165. effect->magic = kEffectMagic;
  1166. effect->uniqueID = plugin->getUniqueId();
  1167. effect->version = plugin->getVersion();
  1168. // VST doesn't support parameter outputs. we can fake them, but it is a hack. Disabled by default.
  1169. #ifdef DPF_VST_SHOW_PARAMETER_OUTPUTS
  1170. const int numParams = plugin->getParameterCount();
  1171. #else
  1172. int numParams = 0;
  1173. bool outputsReached = false;
  1174. for (uint32_t i=0, count=plugin->getParameterCount(); i < count; ++i)
  1175. {
  1176. if (plugin->isParameterInput(i))
  1177. {
  1178. // parameter outputs must be all at the end
  1179. DISTRHO_SAFE_ASSERT_BREAK(! outputsReached);
  1180. ++numParams;
  1181. continue;
  1182. }
  1183. outputsReached = true;
  1184. }
  1185. #endif
  1186. // plugin fields
  1187. effect->numParams = numParams;
  1188. effect->numPrograms = 1;
  1189. effect->numInputs = DISTRHO_PLUGIN_NUM_INPUTS;
  1190. effect->numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS;
  1191. // plugin flags
  1192. effect->flags |= effFlagsCanReplacing;
  1193. #if DISTRHO_PLUGIN_IS_SYNTH
  1194. effect->flags |= effFlagsIsSynth;
  1195. #endif
  1196. #if DISTRHO_PLUGIN_HAS_UI
  1197. effect->flags |= effFlagsHasEditor;
  1198. #endif
  1199. #if DISTRHO_PLUGIN_WANT_STATE
  1200. effect->flags |= effFlagsProgramChunks;
  1201. #endif
  1202. // static calls
  1203. effect->dispatcher = vst_dispatcherCallback;
  1204. effect->process = vst_processCallback;
  1205. effect->getParameter = vst_getParameterCallback;
  1206. effect->setParameter = vst_setParameterCallback;
  1207. effect->processReplacing = vst_processReplacingCallback;
  1208. // pointers
  1209. VstObject* const obj(new VstObject());
  1210. obj->audioMaster = audioMaster;
  1211. obj->plugin = nullptr;
  1212. // done
  1213. effect->object = obj;
  1214. return effect;
  1215. }
  1216. // -----------------------------------------------------------------------