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.

1736 lines
56KB

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