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.

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