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.

1283 lines
38KB

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