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.

1565 lines
46KB

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