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.

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