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.

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