Collection of DPF-based plugins for packaging
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1735 lines
54KB

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