Audio plugin host https://kx.studio/carla
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.

DistrhoPluginVST.cpp 33KB

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