DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1728 lines
56KB

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