Collection of DPF-based plugins for packaging
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.

1744 lines
53KB

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