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.

1363 lines
40KB

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