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.

1587 lines
46KB

  1. #ifdef USE_VST2
  2. /// vst2_main.cpp
  3. ///
  4. /// (c) 2018 bsp. very loosely based on pongasoft's "hello, world" example plugin.
  5. ///
  6. /// Licensed under the Apache License, Version 2.0 (the "License");
  7. /// you may not use this file except in compliance with the License.
  8. /// You may obtain a copy of the License at
  9. ///
  10. /// http://www.apache.org/licenses/LICENSE-2.0
  11. ///
  12. /// Unless required by applicable law or agreed to in writing, software
  13. /// distributed under the License is distributed on an "AS IS" BASIS,
  14. /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. /// See the License for the specific language governing permissions and
  16. /// limitations under the License.
  17. ///
  18. /// created: 25Jun2018
  19. /// changed: 26Jun2018, 27Jun2018, 29Jun2018, 01Jul2018, 02Jul2018
  20. ///
  21. ///
  22. ///
  23. // #define DEBUG_PRINT_EVENTS defined
  24. // #define DEBUG_PRINT_PARAMS defined
  25. #define NUM_INPUTS ( 8) // must match AudioInterface.cpp:AUDIO_INPUTS
  26. #define NUM_OUTPUTS ( 8) // must match AudioInterface.cpp:AUDIO_OUTPUTS
  27. // (note) causes reason to shut down when console is freed (when plugin is deleted)
  28. //#define USE_CONSOLE defined
  29. #undef RACK_HOST
  30. #include <aeffect.h>
  31. #include <aeffectx.h>
  32. #include <stdio.h>
  33. #include "../dep/yac/yac.h"
  34. #include "../dep/yac/yac_host.cpp"
  35. YAC_Host *yac_host; // not actually used, just to satisfy the linker
  36. #include "global_pre.hpp"
  37. #include "global.hpp"
  38. #include "global_ui.hpp"
  39. extern int vst2_init (int argc, char* argv[]);
  40. extern void vst2_exit (void);
  41. extern void vst2_editor_create (void);
  42. extern void vst2_editor_loop (void);
  43. extern void vst2_editor_destroy (void);
  44. extern void vst2_set_samplerate (sF32 _rate);
  45. extern void vst2_engine_process (float *const*_in, float **_out, unsigned int _numFrames);
  46. extern void vst2_process_midi_input_event (sU8 _a, sU8 _b, sU8 _c);
  47. extern void vst2_queue_param (int uniqueParamId, float normValue);
  48. extern void vst2_handle_queued_params (void);
  49. extern float vst2_get_param (int uniqueParamId);
  50. extern void vst2_get_param_name (int uniqueParamId, char *s, int sMaxLen);
  51. #include "../include/window.hpp"
  52. #include "../dep/include/osdialog.h"
  53. #include "../include/app.hpp"
  54. // using namespace rack;
  55. // extern void rack::windowRun(void);
  56. #if defined(_WIN32) || defined(_WIN64)
  57. #define HAVE_WINDOWS defined
  58. #define WIN32_LEAN_AND_MEAN defined
  59. #include <windows.h>
  60. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  61. // // HWND g_glfw_vst2_parent_hwnd; // read by modified version of GLFW (see glfw/src/win32_window.c)
  62. // Windows:
  63. #define VST_EXPORT extern "C" __declspec(dllexport)
  64. struct PluginMutex {
  65. CRITICAL_SECTION handle;
  66. PluginMutex(void) {
  67. ::InitializeCriticalSection( &handle );
  68. }
  69. ~PluginMutex() {
  70. ::DeleteCriticalSection( &handle );
  71. }
  72. void lock(void) {
  73. ::EnterCriticalSection(&handle);
  74. }
  75. void unlock(void) {
  76. ::LeaveCriticalSection(&handle);
  77. }
  78. };
  79. #else
  80. // MacOSX, Linux:
  81. #define HAVE_UNIX defined
  82. #define VST_EXPORT extern
  83. #include <pthread.h>
  84. #include <errno.h>
  85. #include <unistd.h>
  86. #include <fcntl.h>
  87. #include <sys/mman.h>
  88. //static pthread_mutex_t loc_pthread_mutex_t_init = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
  89. static pthread_mutex_t loc_pthread_mutex_t_init = PTHREAD_MUTEX_INITIALIZER;
  90. struct PluginMutex {
  91. pthread_mutex_t handle;
  92. PluginMutex(void) {
  93. ::memcpy((void*)&handle, (const void*)&loc_pthread_mutex_t_init, sizeof(pthread_mutex_t));
  94. }
  95. ~PluginMutex() {
  96. }
  97. void lock(void) {
  98. ::pthread_mutex_lock(&handle);
  99. }
  100. void unlock(void) {
  101. ::pthread_mutex_unlock(&handle);
  102. }
  103. };
  104. #endif // _WIN32||_WIN64
  105. // // extern "C" {
  106. // // extern void glfwSetInstance(void *_glfw);
  107. // // }
  108. class PluginString : public YAC_String {
  109. public:
  110. static const sUI QUOT2 =(sUI)(1<<26); // \'\'
  111. static const sUI STRFLQMASK = (QUOT | UTAG1 | QUOT2);
  112. void safeFreeChars (void);
  113. sSI _realloc (sSI _numChars);
  114. sSI lastIndexOf (sChar _c, sUI _start) const;
  115. void getDirName (PluginString *_r) const;
  116. void replace (sChar _c, sChar _o);
  117. };
  118. void PluginString::safeFreeChars(void) {
  119. if(bflags & PluginString::DEL)
  120. {
  121. // if(!(bflags & PluginString::LA))
  122. {
  123. Dyacfreechars(chars);
  124. }
  125. }
  126. }
  127. sSI PluginString::_realloc(sSI _numBytes) {
  128. // Force alloc if a very big string is about to shrink a lot or there is simply not enough space available
  129. if( ((buflen >= 1024) && ( (((sUI)_numBytes)<<3) < buflen )) ||
  130. (NULL == chars) ||
  131. (buflen < ((sUI)_numBytes))
  132. ) // xxx (!chars) hack added 180702
  133. {
  134. if(NULL != chars)
  135. {
  136. sUI l = length;
  137. if(((sUI)_numBytes) < l)
  138. {
  139. l = _numBytes;
  140. }
  141. sU8 *nc = Dyacallocchars(_numBytes + 1);
  142. sUI i = 0;
  143. for(; i<l; i++)
  144. {
  145. nc[i] = chars[i];
  146. }
  147. nc[i] = 0;
  148. safeFreeChars();
  149. buflen = (_numBytes + 1);
  150. bflags = PluginString::DEL | (bflags & PluginString::STRFLQMASK); // keep old stringflags
  151. length = i + 1;
  152. chars = nc;
  153. key = YAC_LOSTKEY;
  154. return YAC_TRUE;
  155. }
  156. else
  157. {
  158. return PluginString::alloc(_numBytes + 1);
  159. }
  160. }
  161. else
  162. {
  163. key = YAC_LOSTKEY; // new 010208
  164. return YAC_TRUE;
  165. }
  166. }
  167. sSI PluginString::lastIndexOf(sChar _c, sUI _start) const {
  168. sSI li = -1;
  169. if(NULL != chars)
  170. {
  171. sUI i = _start;
  172. for(; i<length; i++)
  173. {
  174. if(chars[i] == ((sChar)_c))
  175. {
  176. li = i;
  177. }
  178. }
  179. }
  180. return li;
  181. }
  182. void PluginString::replace(sChar _c, sChar _o) {
  183. if(NULL != chars)
  184. {
  185. for(sUI i = 0; i < length; i++)
  186. {
  187. if(chars[i] == _c)
  188. chars[i] = _o;
  189. }
  190. }
  191. }
  192. void PluginString::getDirName(PluginString *_r) const {
  193. sSI idxSlash = lastIndexOf('/', 0);
  194. sSI idxBackSlash = lastIndexOf('\\', 0);
  195. sSI idxDrive = lastIndexOf(':', 0);
  196. sSI idx = -1;
  197. if(idxSlash > idxBackSlash)
  198. {
  199. idx = idxSlash;
  200. }
  201. else
  202. {
  203. idx = idxBackSlash;
  204. }
  205. if(idxDrive > idx)
  206. {
  207. idx = idxDrive;
  208. }
  209. if(-1 != idx)
  210. {
  211. _r->_realloc(idx + 2);
  212. _r->length = idx + 2;
  213. sSI i;
  214. for(i=0; i<=idx; i++)
  215. {
  216. _r->chars[i] = chars[i];
  217. }
  218. _r->chars[i++] = 0;
  219. _r->key = YAC_LOSTKEY;
  220. }
  221. else
  222. {
  223. _r->empty();
  224. }
  225. }
  226. #define MAX_FLOATARRAYALLOCSIZE (1024*1024*64)
  227. class PluginFloatArray : public YAC_FloatArray {
  228. public:
  229. sSI alloc (sSI _maxelements);
  230. };
  231. sSI PluginFloatArray::alloc(sSI _max_elements) {
  232. if(((sUI)_max_elements)>MAX_FLOATARRAYALLOCSIZE)
  233. {
  234. printf("[---] FloatArray::insane array size (maxelements=%08x)\n", _max_elements);
  235. return 0;
  236. }
  237. if(own_data)
  238. {
  239. if(elements)
  240. {
  241. delete [] elements;
  242. elements = NULL;
  243. }
  244. }
  245. if(_max_elements)
  246. {
  247. elements = new(std::nothrow) sF32[_max_elements];
  248. if(elements)
  249. {
  250. max_elements = _max_elements;
  251. num_elements = 0;
  252. own_data = 1;
  253. return 1;
  254. }
  255. }
  256. num_elements = 0;
  257. max_elements = 0;
  258. return 0;
  259. }
  260. /*
  261. * I find the naming a bit confusing so I decided to use more meaningful names instead.
  262. */
  263. /**
  264. * The VSTHostCallback is a function pointer so that the plugin can communicate with the host (not used in this small example)
  265. */
  266. typedef audioMasterCallback VSTHostCallback;
  267. /**
  268. * The VSTPlugin structure (AEffect) contains information about the plugin (like version, number of inputs, ...) and
  269. * callbacks so that the host can call the plugin to do its work. The primary callback will be `processReplacing` for
  270. * single precision (float) sample processing (or `processDoubleReplacing` for double precision (double)).
  271. */
  272. typedef AEffect VSTPlugin;
  273. // Since the host is expecting a very specific API we need to make sure it has C linkage (not C++)
  274. extern "C" {
  275. /*
  276. * This is the main entry point to the VST plugin.
  277. *
  278. * The host (DAW like Maschine, Ableton Live, Reason, ...) will look for this function with this exact API.
  279. *
  280. * It is the equivalent to `int main(int argc, char *argv[])` for a C executable.
  281. *
  282. * @param vstHostCallback is a callback so that the plugin can communicate with the host (not used in this small example)
  283. * @return a pointer to the AEffect structure
  284. */
  285. VST_EXPORT VSTPlugin *VSTPluginMain (VSTHostCallback vstHostCallback);
  286. // note this looks like this without the type aliases (and is obviously 100% equivalent)
  287. // extern AEffect *VSTPluginMain(audioMasterCallback audioMaster);
  288. }
  289. /*
  290. * Constant for the version of the plugin. For example 1100 for version 1.1.0.0
  291. */
  292. const VstInt32 PLUGIN_VERSION = 1000;
  293. /**
  294. * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the
  295. * host (VSTPlugin). This class will be stored in the `VSTPlugin.object` field (circular reference) so that it can
  296. * be accessed when the host calls the plugin back (for example in `processDoubleReplacing`).
  297. */
  298. class VSTPluginWrapper {
  299. static const uint32_t MIN_SAMPLE_RATE = 8192u; // (note) cannot be float in C++
  300. static const uint32_t MAX_SAMPLE_RATE = 384000u;
  301. static const uint32_t MIN_BLOCK_SIZE = 64u;
  302. static const uint32_t MAX_BLOCK_SIZE = 65536u;
  303. public:
  304. rack::Global rack_global;
  305. rack::GlobalUI rack_global_ui;
  306. protected:
  307. PluginString dllname;
  308. PluginString cwd;
  309. float sample_rate; // e.g. 44100.0
  310. uint32_t block_size; // e.g. 64
  311. PluginMutex mtx_audio;
  312. public:
  313. PluginMutex mtx_mididev;
  314. public:
  315. bool b_open;
  316. bool b_processing; // true=generate output, false=suspended
  317. ERect editor_rect;
  318. char *last_program_chunk_str;
  319. static sSI instance_count;
  320. sSI instance_id;
  321. // // sU8 glfw_internal[64*1024]; // far larger than it needs to be, must be >=sizeof(_GLFWlibrary)
  322. public:
  323. #ifdef YAC_LINUX
  324. pthread_t pthread_id;
  325. #endif
  326. #ifdef YAC_WIN32
  327. HANDLE hThread;
  328. DWORD dwThreadId;
  329. #endif
  330. sBool b_thread_created;
  331. volatile sBool b_thread_started;
  332. volatile sBool b_thread_running;
  333. volatile sBool b_thread_done;
  334. volatile sBool b_queued_open_editor;
  335. volatile sBool b_queued_destroy_editor;
  336. volatile sBool b_editor_open;
  337. volatile sBool b_editor_created;
  338. struct {
  339. volatile uint32_t size;
  340. volatile uint8_t *addr;
  341. volatile bool b_ret;
  342. } queued_load_patch;
  343. public:
  344. VSTPluginWrapper(VSTHostCallback vstHostCallback,
  345. VstInt32 vendorUniqueID,
  346. VstInt32 vendorVersion,
  347. VstInt32 numParams,
  348. VstInt32 numPrograms,
  349. VstInt32 numInputs,
  350. VstInt32 numOutputs
  351. );
  352. ~VSTPluginWrapper();
  353. VSTPlugin *getVSTPlugin(void) {
  354. return &_vstPlugin;
  355. }
  356. void startUIThread (void);
  357. void stopUIThread (void);
  358. void setGlobals(void) {
  359. rack::global = &rack_global;
  360. rack::global_ui = &rack_global_ui;
  361. // // glfwSetInstance((void*)glfw_internal);
  362. }
  363. sSI openEffect(void) {
  364. printf("xxx vstrack_plugin::openEffect\n");
  365. // (todo) use mutex
  366. if(1 == instance_count)
  367. {
  368. int err = glfwInit();
  369. if (err != GLFW_TRUE) {
  370. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLFW.");
  371. return 0;
  372. }
  373. }
  374. instance_id = instance_count;
  375. printf("xxx vstrack_plugin::openEffect: instance_id=%d\n", instance_id);
  376. rack_global.vst2.wrapper = this;
  377. #ifdef USE_CONSOLE
  378. AllocConsole();
  379. freopen("CON", "w", stdout);
  380. freopen("CON", "w", stderr);
  381. freopen("CON", "r", stdin); // Note: "r", not "w".
  382. #endif // USE_CONSOLE
  383. setGlobals();
  384. rack_global.init();
  385. rack_global_ui.init();
  386. rack::global->vst2.last_seen_instance_count = instance_count;
  387. // // ::memset((void*)glfw_internal, 0, sizeof(glfw_internal));
  388. char oldCWD[1024];
  389. char dllnameraw[1024];
  390. ::GetCurrentDirectory(1024, (LPSTR) oldCWD);
  391. // ::GetModuleFileNameA(NULL, dllnameraw, 1024); // returns executable name (not the dll pathname)
  392. GetModuleFileNameA((HINSTANCE)&__ImageBase, dllnameraw, 1024);
  393. dllname.visit(dllnameraw);
  394. dllname.getDirName(&cwd);
  395. rack::global->vst2.program_dir = (const char*)cwd.chars;
  396. printf("xxx vstrack_plugin::openEffect: cd to \"%s\"\n", (const char*)cwd.chars);
  397. // // ::SetCurrentDirectory("f:/vst_64bit/vstrack_plugin");
  398. ::SetCurrentDirectory((const char*)cwd.chars);
  399. printf("xxx vstrack_plugin::openEffect: cwd change done\n");
  400. // cwd.replace('\\', '/');
  401. int argc = 1;
  402. char *argv[1];
  403. //argv[0] = (char*)cwd.chars;
  404. argv[0] = (char*)dllnameraw;
  405. printf("xxx vstrack_plugin::openEffect: dllname=\"%s\"\n", argv[0]);
  406. (void)vst2_init(argc, argv);
  407. printf("xxx vstrack_plugin::openEffect: vst2_init() done\n");
  408. queued_load_patch.size = 0u;
  409. queued_load_patch.addr = NULL;
  410. queued_load_patch.b_ret = false;
  411. startUIThread();
  412. printf("xxx vstrack_plugin::openEffect: restore cwd=\"%s\"\n", oldCWD);
  413. ::SetCurrentDirectory(oldCWD);
  414. setSampleRate(sample_rate);
  415. b_open = 1;
  416. printf("xxx vstrack_plugin::openEffect: LEAVE\n");
  417. return 1;
  418. }
  419. void closeEffect(void) {
  420. // (todo) use mutex
  421. printf("xxx vstrack_plugin::closeEffect: last_program_chunk_str=%p\n", last_program_chunk_str);
  422. if(NULL != last_program_chunk_str)
  423. {
  424. ::free(last_program_chunk_str);
  425. last_program_chunk_str = NULL;
  426. }
  427. printf("xxx vstrack_plugin::closeEffect: b_open=%d\n", b_open);
  428. if(b_open)
  429. {
  430. b_open = 0;
  431. setGlobals();
  432. rack::global->vst2.last_seen_instance_count = instance_count;
  433. b_queued_destroy_editor = true;
  434. rack::global_ui->vst2.b_close_window = 1;
  435. while(b_queued_destroy_editor)
  436. {
  437. printf("[dbg] vstrack_plugin: wait until editor's been destroyed\n");
  438. sleepMillisecs(100); // (todo) condition
  439. }
  440. stopUIThread();
  441. printf("xxx vstrack_plugin: call vst2_exit()\n");
  442. vst2_exit();
  443. printf("xxx vstrack_plugin: vst2_exit() done\n");
  444. if(1 == instance_count)
  445. {
  446. glfwTerminate();
  447. }
  448. #ifdef USE_CONSOLE
  449. // FreeConsole();
  450. #endif // USE_CONSOLE
  451. }
  452. }
  453. #ifdef YAC_WIN32
  454. void openEditor(HWND _hwnd) {
  455. // // g_glfw_vst2_parent_hwnd = _hwnd;
  456. #else
  457. #error implement me (openEditor)
  458. #endif
  459. printf("xxx vstrack_plugin: openEditor()\n");
  460. b_queued_open_editor = true;
  461. int iter = 0;
  462. while(iter++ < 50)
  463. {
  464. if(b_editor_open)
  465. break;
  466. sleepMillisecs(100); // (todo) condition
  467. }
  468. if(100 == iter)
  469. printf("xxx vstrack_plugin: failed to show editor after %d milliseconds!!\n", (iter*100));
  470. else
  471. printf("xxx vstrack_plugin: editor opened after %d milliseconds\n", (iter*100));
  472. // // vst2_show_editor();
  473. }
  474. void hideEditor(void) {
  475. printf("xxx vstrack_plugin: hideEditor() b_editor_open=%d\n", b_editor_open);
  476. if(b_editor_open)
  477. {
  478. setGlobals();
  479. rack::global_ui->vst2.b_hide_window = 1;
  480. }
  481. }
  482. void closeEditor(void) {
  483. printf("xxx vstrack_plugin: closeEditor() b_editor_open=%d\n", b_editor_open);
  484. if(b_editor_open)
  485. {
  486. setGlobals();
  487. rack::global_ui->vst2.b_close_window = 1;
  488. }
  489. }
  490. void lockAudio(void) {
  491. mtx_audio.lock();
  492. }
  493. void unlockAudio(void) {
  494. mtx_audio.unlock();
  495. }
  496. VstInt32 getNumInputs(void) const {
  497. return _vstPlugin.numInputs;
  498. }
  499. VstInt32 getNumOutputs(void) const {
  500. return _vstPlugin.numOutputs;
  501. }
  502. bool setSampleRate(float _rate) {
  503. bool r = false;
  504. if((_rate >= float(MIN_SAMPLE_RATE)) && (_rate <= float(MAX_SAMPLE_RATE)))
  505. {
  506. setGlobals();
  507. lockAudio();
  508. sample_rate = _rate;
  509. vst2_set_samplerate(sample_rate);
  510. unlockAudio();
  511. r = true;
  512. }
  513. return r;
  514. }
  515. bool setBlockSize(uint32_t _blockSize) {
  516. bool r = false;
  517. if((_blockSize >= MIN_BLOCK_SIZE) && (_blockSize <= MAX_BLOCK_SIZE))
  518. {
  519. lockAudio();
  520. block_size = _blockSize;
  521. unlockAudio();
  522. r = true;
  523. }
  524. return r;
  525. }
  526. void setEnableProcessingActive(bool _bEnable) {
  527. lockAudio();
  528. b_processing = _bEnable;
  529. unlockAudio();
  530. }
  531. sUI getBankChunk(uint8_t **_addr) {
  532. return 0;
  533. }
  534. sUI getProgramChunk(uint8_t **_addr) {
  535. setGlobals();
  536. if(NULL != last_program_chunk_str)
  537. {
  538. ::free(last_program_chunk_str);
  539. }
  540. last_program_chunk_str = rack::global_ui->app.gRackWidget->savePatchToString();
  541. if(NULL != last_program_chunk_str)
  542. {
  543. *_addr = (uint8_t*)last_program_chunk_str;
  544. return strlen(last_program_chunk_str) + 1/*ASCIIZ*/;
  545. }
  546. return 0;
  547. }
  548. bool setBankChunk(size_t _size, uint8_t*_addr) {
  549. bool r = false;
  550. return r;
  551. }
  552. bool setProgramChunk_Async(size_t _size, uint8_t*_addr) {
  553. bool r = false;
  554. setGlobals();
  555. if(NULL != _addr)
  556. {
  557. queued_load_patch.b_ret = false;
  558. queued_load_patch.size = _size;
  559. queued_load_patch.addr = _addr; // triggers loader in UI thread
  560. int iter = 0;
  561. for(;;)
  562. {
  563. if(NULL == queued_load_patch.addr)
  564. {
  565. queued_load_patch.b_ret = r;
  566. break;
  567. }
  568. else if(++iter > 500)
  569. {
  570. printf("[---] vstrack_plugin:queueSetProgramChunk: timeout while waiting for UI thread.\n");
  571. break;
  572. }
  573. sleepMillisecs(10);
  574. }
  575. }
  576. return r;
  577. }
  578. void handleSetQueuedProgramChunk(void) {
  579. if(NULL != queued_load_patch.addr)
  580. {
  581. setGlobals();
  582. lockAudio();
  583. #if 0
  584. printf("xxx vstrack_plugin:setProgramChunk: size=%u str=\n-------------------%s\n------------------\n", queued_load_patch.size, (const char*)queued_load_patch.addr);
  585. #else
  586. printf("xxx vstrack_plugin:setProgramChunk: size=%u\n", queued_load_patch.size);
  587. #endif
  588. bool r = rack::global_ui->app.gRackWidget->loadPatchFromString((const char*)queued_load_patch.addr);
  589. printf("xxx vstrack_plugin:setProgramChunk: r=%d\n", r);
  590. queued_load_patch.b_ret = r;
  591. queued_load_patch.addr = NULL;
  592. unlockAudio();
  593. }
  594. }
  595. #ifdef HAVE_WINDOWS
  596. void sleepMillisecs(uint32_t _num) {
  597. ::Sleep((DWORD)_num);
  598. }
  599. #elif defined(HAVE_UNIX)
  600. void sleepMillisecs(uint32_t _num) {
  601. ::usleep(1000u * _num);
  602. }
  603. #endif
  604. const volatile float *getNextInputChannelChunk(void) {
  605. volatile float *r = NULL;
  606. return r;
  607. }
  608. volatile float *lockNextOutputChannelChunk(void) {
  609. volatile float *r = NULL;
  610. return r;
  611. }
  612. void handleUIParam(int uniqueParamId, float normValue) {
  613. if(NULL != _vstHostCallback)
  614. _vstHostCallback(&_vstPlugin, audioMasterAutomate, uniqueParamId, 0/*value*/, NULL/*ptr*/, normValue/*opt*/);
  615. }
  616. private:
  617. // the host callback (a function pointer)
  618. VSTHostCallback _vstHostCallback;
  619. // the actual structure required by the host
  620. VSTPlugin _vstPlugin;
  621. };
  622. sSI VSTPluginWrapper::instance_count = 0;
  623. #ifdef YAC_LINUX
  624. static void *vst2_ui_thread_entry(VSTPluginWrapper *_wrapper) {
  625. #elif defined(YAC_WIN32)
  626. static DWORD WINAPI vst2_ui_thread_entry(VSTPluginWrapper *_wrapper) {
  627. #endif
  628. printf("xxx vstrack_plugin: UI thread started\n");
  629. _wrapper->setGlobals();
  630. printf("xxx vstrack_plugin<ui>: global=%p global_ui=%p\n", rack::global, rack::global_ui);
  631. printf("xxx vstrack_plugin<ui>: call vst2_editor_create()\n");
  632. _wrapper->lockAudio();
  633. vst2_editor_create();
  634. printf("xxx vstrack_plugin<ui>: vst2_editor_create() done\n");
  635. _wrapper->b_editor_created = YAC_TRUE;
  636. _wrapper->unlockAudio();
  637. _wrapper->b_thread_started = YAC_TRUE;
  638. while(_wrapper->b_thread_running || _wrapper->b_queued_destroy_editor || _wrapper->queued_load_patch.addr)
  639. {
  640. // printf("xxx vstrack_plugin<ui>: idle loop\n");
  641. if(_wrapper->b_queued_open_editor && !_wrapper->b_editor_open)
  642. {
  643. _wrapper->b_queued_open_editor = YAC_FALSE;
  644. // Show previously hidden window
  645. glfwShowWindow(rack::global_ui->window.gWindow);
  646. _wrapper->b_editor_open = YAC_TRUE;
  647. rack::global_ui->vst2.b_close_window = 0;
  648. printf("xxx vstrack_plugin[%d]: entering editor_loop\n", _wrapper->instance_id);
  649. vst2_editor_loop(); // sets b_editor_open=true and b_queued_open_editor=false (must be delayed until window is actually visible or window create/focus tracking will not work)
  650. _wrapper->b_editor_open = YAC_FALSE;
  651. printf("xxx vstrack_plugin[%d]: editor_loop finished\n", _wrapper->instance_id);
  652. // if(!_wrapper->b_window_created)
  653. // {
  654. // ShowUIWindow();
  655. // use metahost_onTimer for SDL.onTimer;
  656. // b_window_created = true;
  657. // trace "[dbg] eureka: entering eventloop 2";
  658. // b_editor_open = true;
  659. // UI.Run();
  660. // }
  661. }
  662. else if(_wrapper->b_queued_destroy_editor)
  663. {
  664. if(_wrapper->b_editor_created)
  665. {
  666. vst2_editor_destroy();
  667. _wrapper->b_editor_created = YAC_FALSE;
  668. }
  669. _wrapper->b_queued_destroy_editor = YAC_FALSE;
  670. }
  671. else if(NULL != _wrapper->queued_load_patch.addr)
  672. {
  673. _wrapper->handleSetQueuedProgramChunk();
  674. }
  675. else
  676. {
  677. _wrapper->sleepMillisecs(100);
  678. }
  679. }
  680. printf("xxx vstrack_plugin: UI thread finished\n");
  681. _wrapper->b_thread_done = YAC_TRUE;
  682. return 0;
  683. }
  684. void VSTPluginWrapper::startUIThread(void) {
  685. b_queued_open_editor = false;
  686. b_queued_destroy_editor = false;
  687. b_editor_open = false;
  688. b_editor_created = false;
  689. queued_load_patch.b_ret = false;
  690. queued_load_patch.size = 0u;
  691. queued_load_patch.addr = NULL;
  692. b_thread_created = YAC_FALSE;
  693. b_thread_running = YAC_TRUE;
  694. b_thread_done = YAC_FALSE;
  695. #ifdef YAC_LINUX
  696. b_thread_created = (pthread_create( &pthread_id, NULL, vst2_ui_thread_entry, (void*) this ) == 0);
  697. if(b_thread_created)
  698. {
  699. /* wait for lwp_id field to become valid */
  700. while(!b_thread_started)
  701. {
  702. pthread_yield();
  703. }
  704. }
  705. #endif // YAC_POSIX
  706. #ifdef YAC_WIN32
  707. hThread = CreateThread(
  708. NULL, // default security attributes
  709. 0, // use default stack size
  710. (LPTHREAD_START_ROUTINE)vst2_ui_thread_entry,// thread function
  711. (LPVOID)this, // argument to thread function
  712. 0, // use default creation flags
  713. &dwThreadId); // returns the thread identifier
  714. b_thread_created = (hThread != NULL);
  715. while(!b_thread_started)
  716. sleepMillisecs(10); // (todo) use condition
  717. #endif
  718. }
  719. void VSTPluginWrapper::stopUIThread(void) {
  720. b_thread_running = YAC_FALSE;
  721. while(!b_thread_done)
  722. {
  723. sleepMillisecs(10); // (todo) use condition
  724. }
  725. #ifdef YAC_LINUX
  726. ///pthread_join( pthread_id, NULL);
  727. pthread_detach( pthread_id );
  728. pthread_cancel( pthread_id );
  729. pthread_id = 0;
  730. #endif
  731. #ifdef YAC_WIN32
  732. SuspendThread( hThread );
  733. TerminateThread( hThread, 10 ); // 10 = exit code
  734. WaitForMultipleObjects(1, &hThread, TRUE, 5000 /*INFINITE*/); // wait max. 5sec
  735. CloseHandle(hThread);
  736. hThread = NULL;
  737. #endif
  738. }
  739. /*******************************************
  740. * Callbacks: Host -> Plugin
  741. *
  742. * Defined here because they are used in the rest of the code later
  743. */
  744. /**
  745. * This is the callback that will be called to process the samples in the case of single precision. This is where the
  746. * meat of the logic happens!
  747. *
  748. * @param vstPlugin the object returned by VSTPluginMain
  749. * @param inputs an array of array of input samples. You read from it. First dimension is for inputs, second dimension is for samples: inputs[numInputs][sampleFrames]
  750. * @param outputs an array of array of output samples. You write to it. First dimension is for outputs, second dimension is for samples: outputs[numOuputs][sampleFrames]
  751. * @param sampleFrames the number of samples (second dimension in both arrays)
  752. */
  753. void VSTPluginProcessReplacingFloat32(VSTPlugin *vstPlugin,
  754. float **inputs,
  755. float **outputs,
  756. VstInt32 sampleFrames
  757. ) {
  758. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  759. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  760. // printf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: ENTER\n");
  761. wrapper->lockAudio();
  762. wrapper->setGlobals();
  763. // // rack::global->engine.vipMutex.lock();
  764. rack::global->engine.mutex.lock();
  765. rack::global->vst2.last_seen_num_frames = sUI(sampleFrames);
  766. vst2_handle_queued_params();
  767. //printf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: lockAudio done\n");
  768. //printf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: wrapper=%p\n", wrapper);
  769. sUI chIdx;
  770. sUI i;
  771. sUI k = 0u;
  772. if(wrapper->b_processing)
  773. {
  774. // Clear output buffers
  775. // (note) AudioInterface instances accumulate samples in the output buffer
  776. for(i = 0u; i < uint32_t(sampleFrames); i++)
  777. {
  778. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  779. {
  780. outputs[chIdx][i] = 0.0f;
  781. }
  782. }
  783. vst2_engine_process(inputs, outputs, sampleFrames);
  784. }
  785. else
  786. {
  787. // Not processing, output silence
  788. // printf("xxx vstrack_plugin: output silence\n");
  789. for(i = 0u; i < uint32_t(sampleFrames); i++)
  790. {
  791. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  792. {
  793. outputs[chIdx][i] = 0.0f;
  794. }
  795. }
  796. }
  797. // // rack::global->engine.vipMutex.unlock();
  798. rack::global->engine.mutex.unlock();
  799. wrapper->unlockAudio();
  800. //printf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: LEAVE\n");
  801. // // glfwSetInstance(NULL); // xxxx test TLS (=> not working in mingw64!)
  802. }
  803. #if 0
  804. /**
  805. * This is the callback that will be called to process the samples in the case of double precision. This is where the
  806. * meat of the logic happens!
  807. *
  808. * @param vstPlugin the object returned by VSTPluginMain
  809. * @param inputs an array of array of input samples. You read from it. First dimension is for inputs, second dimension is for samples: inputs[numInputs][sampleFrames]
  810. * @param outputs an array of array of output samples. You write to it. First dimension is for outputs, second dimension is for samples: outputs[numOuputs][sampleFrames]
  811. * @param sampleFrames the number of samples (second dimension in both arrays)
  812. */
  813. void VSTPluginProcessReplacingFloat64(VSTPlugin *vstPlugin,
  814. double **inputs,
  815. double **outputs,
  816. VstInt32 sampleFrames
  817. ) {
  818. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  819. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  820. wrapper->lockAudio();
  821. if(wrapper->b_processing)
  822. {
  823. // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the
  824. // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound)
  825. for(int i = 0; i < wrapper->getNumInputs(); i++)
  826. {
  827. auto inputSamples = inputs[i];
  828. auto outputSamples = outputs[i];
  829. for(int j = 0; j < sampleFrames; j++)
  830. {
  831. outputSamples[j] = inputSamples[j] * 0.5;
  832. }
  833. }
  834. }
  835. wrapper->unlockAudio();
  836. }
  837. #endif // 0
  838. /**
  839. * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the
  840. * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...)
  841. *
  842. * @param vstPlugin the object returned by VSTPluginMain
  843. * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand
  844. * total of 79 of them! Only a few of them are implemented in this small plugin.
  845. * @param index depend on the opcode
  846. * @param value depend on the opcode
  847. * @param ptr depend on the opcode
  848. * @param opt depend on the opcode
  849. * @return depend on the opcode (0 is ok when you don't implement an opcode...)
  850. */
  851. VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin,
  852. VstInt32 opCode,
  853. VstInt32 index,
  854. VstIntPtr value,
  855. void *ptr,
  856. float opt
  857. ) {
  858. // printf("vstrack_plugin: called VSTPluginDispatcher(%d)\n", opCode);
  859. VstIntPtr r = 0;
  860. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  861. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  862. // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them
  863. switch(opCode)
  864. {
  865. case effGetPlugCategory:
  866. // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed
  867. // to generating sound)
  868. #ifdef VST2_EFFECT
  869. return kPlugCategEffect;
  870. #else
  871. return kPlugCategSynth;
  872. #endif // VST2_EFFECT
  873. case effOpen:
  874. // called by the host after it has obtained the effect instance (but _not_ during plugin scans)
  875. // (note) any heavy-lifting init code should go here
  876. ::printf("vstrack_plugin<dispatcher>: effOpen\n");
  877. r = wrapper->openEffect();
  878. break;
  879. case effClose:
  880. // called by the host when the plugin was called... time to reclaim memory!
  881. wrapper->closeEffect();
  882. // (note) hosts usually call effStopProcess before effClose
  883. delete wrapper;
  884. break;
  885. case effSetProgram:
  886. r = 1;
  887. break;
  888. case effGetProgram:
  889. r = 0;
  890. break;
  891. case effGetVendorString:
  892. // request for the vendor string (usually used in the UI for plugin grouping)
  893. ::strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen);
  894. r = 1;
  895. break;
  896. case effGetVendorVersion:
  897. // request for the version
  898. return PLUGIN_VERSION;
  899. case effGetEffectName:
  900. #ifdef VST2_EFFECT
  901. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1", kVstMaxEffectNameLen);
  902. #else
  903. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 I", kVstMaxEffectNameLen);
  904. #endif // VST2_EFFECT
  905. r = 1;
  906. break;
  907. case effGetProductString:
  908. #ifdef VST2_EFFECT
  909. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 VST2 Plugin v0.4", kVstMaxProductStrLen);
  910. #else
  911. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 I VST2 Plugin v0.4", kVstMaxProductStrLen);
  912. #endif // VST2_EFFECT
  913. r = 1;
  914. break;
  915. case effGetNumMidiInputChannels:
  916. r = 16;
  917. break;
  918. case effGetNumMidiOutputChannels:
  919. r = 0;
  920. break;
  921. case effGetInputProperties:
  922. {
  923. VstPinProperties *pin = (VstPinProperties*)ptr;
  924. ::snprintf(pin->label, kVstMaxLabelLen, "Input #%d", index);
  925. pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
  926. pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
  927. ::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "in%d", index);
  928. memset((void*)pin->future, 0, 48);
  929. r = 1;
  930. }
  931. break;
  932. case effGetOutputProperties:
  933. {
  934. VstPinProperties *pin = (VstPinProperties*)ptr;
  935. ::snprintf(pin->label, kVstMaxLabelLen, "Output #%d", index);
  936. pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
  937. pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
  938. ::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "out%d", index);
  939. memset((void*)pin->future, 0, 48);
  940. r = 1;
  941. }
  942. break;
  943. case effSetSampleRate:
  944. r = wrapper->setSampleRate(opt) ? 1 : 0;
  945. break;
  946. case effSetBlockSize:
  947. r = wrapper->setBlockSize(uint32_t(value)) ? 1 : 0;
  948. break;
  949. case effCanDo:
  950. // ptr:
  951. // "sendVstEvents"
  952. // "sendVstMidiEvent"
  953. // "sendVstTimeInfo"
  954. // "receiveVstEvents"
  955. // "receiveVstMidiEvent"
  956. // "receiveVstTimeInfo"
  957. // "offline"
  958. // "plugAsChannelInsert"
  959. // "plugAsSend"
  960. // "mixDryWet"
  961. // "noRealTime"
  962. // "multipass"
  963. // "metapass"
  964. // "1in1out"
  965. // "1in2out"
  966. // "2in1out"
  967. // "2in2out"
  968. // "2in4out"
  969. // "4in2out"
  970. // "4in4out"
  971. // "4in8out"
  972. // "8in4out"
  973. // "8in8out"
  974. // "midiProgramNames"
  975. // "conformsToWindowRules"
  976. if(!strcmp((char*)ptr, "receiveVstEvents"))
  977. r = 1;
  978. else
  979. r = 0;
  980. break;
  981. case effGetProgramName:
  982. ::snprintf((char*)ptr, kVstMaxProgNameLen, "default");
  983. r = 1;
  984. break;
  985. case effSetProgramName:
  986. r = 1;
  987. break;
  988. case effGetProgramNameIndexed:
  989. ::sprintf((char*)ptr, "default");
  990. r = 1;
  991. break;
  992. case effGetParamName:
  993. case effGetParamLabel:
  994. // kVstMaxParamStrLen(8), much longer in other plugins
  995. // printf("xxx vstrack_plugin: effGetParamName: ptr=%p\n", ptr);
  996. wrapper->setGlobals();
  997. vst2_get_param_name(index, (char*)ptr, kVstMaxParamStrLen);
  998. r = 1;
  999. break;
  1000. case effGetParameterProperties:
  1001. r = 0;
  1002. break;
  1003. case effGetChunk:
  1004. // Query bank (index=0) or program (index=1) state
  1005. // value: 0
  1006. // ptr: buffer address
  1007. // r: buffer size
  1008. printf("xxx effGetChunk index=%d ptr=%p\n", index, ptr);
  1009. // if(0 == index)
  1010. // {
  1011. // r = wrapper->getBankChunk((uint8_t**)ptr);
  1012. // }
  1013. // else
  1014. // {
  1015. r = wrapper->getProgramChunk((uint8_t**)ptr);
  1016. // }
  1017. break;
  1018. case effSetChunk:
  1019. // Restore bank (index=0) or program (index=1) state
  1020. // value: buffer size
  1021. // ptr: buffer address
  1022. // r: 1
  1023. printf("xxx effSetChunk index=%d size=%lld ptr=%p\n", index, value, ptr);
  1024. // if(0 == index)
  1025. // {
  1026. // r = wrapper->setBankChunk(size_t(value), (uint8_t*)ptr) ? 1 : 0;
  1027. // }
  1028. // else
  1029. // {
  1030. r = wrapper->setProgramChunk_Async(size_t(value), (uint8_t*)ptr) ? 1 : 0;
  1031. // }
  1032. break;
  1033. case effShellGetNextPlugin:
  1034. // For shell plugins (e.g. Waves), returns next sub-plugin UID (or 0)
  1035. // (note) plugin uses audioMasterCurrentId while it's being instantiated to query the currently selected sub-plugin
  1036. // if the host returns 0, it will then call effShellGetNextPlugin to enumerate the sub-plugins
  1037. // ptr: effect name string ptr (filled out by the plugin)
  1038. r = 0;
  1039. break;
  1040. case effMainsChanged:
  1041. // value = 0=suspend, 1=resume
  1042. wrapper->setEnableProcessingActive((value > 0) ? true : false);
  1043. r = 1;
  1044. break;
  1045. case effStartProcess:
  1046. wrapper->setEnableProcessingActive(true);
  1047. r = 1;
  1048. break;
  1049. case effStopProcess:
  1050. wrapper->setEnableProcessingActive(false);
  1051. r = 1;
  1052. break;
  1053. case effProcessEvents:
  1054. // ptr: VstEvents*
  1055. {
  1056. VstEvents *events = (VstEvents*)ptr;
  1057. //printf("vstrack_plugin:effProcessEvents: recvd %d events", events->numEvents);
  1058. VstEvent**evAddr = &events->events[0];
  1059. if(events->numEvents > 0)
  1060. {
  1061. wrapper->setGlobals();
  1062. wrapper->mtx_mididev.lock();
  1063. for(uint32_t evIdx = 0u; evIdx < uint32_t(events->numEvents); evIdx++, evAddr++)
  1064. {
  1065. VstEvent *ev = *evAddr;
  1066. if(NULL != ev) // paranoia
  1067. {
  1068. #ifdef DEBUG_PRINT_EVENTS
  1069. printf("vstrack_plugin:effProcessEvents: ev[%u].byteSize = %u\n", evIdx, uint32_t(ev->byteSize)); // sizeof(VstMidiEvent) = 32
  1070. printf("vstrack_plugin:effProcessEvents: ev[%u].deltaFrames = %u\n", evIdx, uint32_t(ev->deltaFrames));
  1071. #endif // DEBUG_PRINT_EVENTS
  1072. switch(ev->type)
  1073. {
  1074. default:
  1075. //case kVstAudioType: // deprecated
  1076. //case kVstVideoType: // deprecated
  1077. //case kVstParameterType: // deprecated
  1078. //case kVstTriggerType: // deprecated
  1079. break;
  1080. case kVstMidiType:
  1081. // (note) ev->data stores the actual payload (up to 16 bytes)
  1082. // (note) e.g. 0x90 0x30 0x7F for a C-4 note-on on channel 1 with velocity 127
  1083. // (note) don't forget to use a mutex (lockAudio(), unlockAudio()) when modifying the audio processor state!
  1084. {
  1085. VstMidiEvent *mev = (VstMidiEvent *)ev;
  1086. #ifdef DEBUG_PRINT_EVENTS
  1087. printf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteLength = %u\n", evIdx, uint32_t(mev->noteLength)); // #frames
  1088. printf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteOffset = %u\n", evIdx, uint32_t(mev->noteOffset)); // #frames
  1089. printf("vstrack_plugin:effProcessEvents<midi>: ev[%u].midiData = %02x %02x %02x %02x\n", evIdx, uint8_t(mev->midiData[0]), uint8_t(mev->midiData[1]), uint8_t(mev->midiData[2]), uint8_t(mev->midiData[3]));
  1090. printf("vstrack_plugin:effProcessEvents<midi>: ev[%u].detune = %d\n", evIdx, mev->detune); // -64..63
  1091. printf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteOffVelocity = %d\n", evIdx, mev->noteOffVelocity); // 0..127
  1092. #endif // DEBUG_PRINT_EVENTS
  1093. vst2_process_midi_input_event(mev->midiData[0],
  1094. mev->midiData[1],
  1095. mev->midiData[2]
  1096. );
  1097. }
  1098. break;
  1099. case kVstSysExType:
  1100. {
  1101. VstMidiSysexEvent *xev = (VstMidiSysexEvent*)ev;
  1102. #ifdef DEBUG_PRINT_EVENTS
  1103. printf("vstrack_plugin:effProcessEvents<syx>: ev[%u].dumpBytes = %u\n", evIdx, uint32_t(xev->dumpBytes)); // size
  1104. printf("vstrack_plugin:effProcessEvents<syx>: ev[%u].sysexDump = %p\n", evIdx, xev->sysexDump); // buffer addr
  1105. #endif // DEBUG_PRINT_EVENTS
  1106. // (note) don't forget to use a mutex (lockAudio(), unlockAudio()) when modifying the audio processor state!
  1107. }
  1108. break;
  1109. }
  1110. } // if ev
  1111. } // loop events
  1112. wrapper->mtx_mididev.unlock();
  1113. } // if events
  1114. }
  1115. break;
  1116. case effGetTailSize: // 52
  1117. break;
  1118. #if 1
  1119. //case effIdle:
  1120. case 53:
  1121. // Periodic idle call (from UI thread), e.g. at 20ms intervals (depending on host)
  1122. // (note) deprecated in vst2.4 (but some plugins still rely on this)
  1123. r = 1;
  1124. break;
  1125. #endif
  1126. case effEditIdle:
  1127. break;
  1128. case effEditGetRect:
  1129. // Query editor window geometry
  1130. // ptr: ERect* (on Windows)
  1131. if(NULL != ptr) // yeah, this should never be NULL
  1132. {
  1133. // ...
  1134. wrapper->editor_rect.top = 20;
  1135. wrapper->editor_rect.left = 20;
  1136. wrapper->editor_rect.bottom = 50;
  1137. wrapper->editor_rect.right = 80;
  1138. *(void**)ptr = (void*) &wrapper->editor_rect;
  1139. r = 1;
  1140. }
  1141. else
  1142. {
  1143. r = 0;
  1144. }
  1145. break;
  1146. #if 0
  1147. case effEditTop:
  1148. // deprecated in vst2.4
  1149. r = 0;
  1150. break;
  1151. #endif
  1152. case effEditOpen:
  1153. // Show editor window
  1154. // ptr: native window handle (hWnd on Windows)
  1155. #ifdef YAC_WIN32
  1156. wrapper->openEditor((HWND)ptr);
  1157. #endif
  1158. r = 1;
  1159. break;
  1160. case effEditClose:
  1161. // Hide editor window
  1162. // // wrapper->closeEditor();
  1163. wrapper->hideEditor();
  1164. r = 1;
  1165. break;
  1166. default:
  1167. // ignoring all other opcodes
  1168. printf("vstrack_plugin:dispatcher: unhandled opCode %d [ignored] \n", opCode);
  1169. break;
  1170. }
  1171. return r;
  1172. }
  1173. /**
  1174. * Set parameter setting
  1175. */
  1176. void VSTPluginSetParameter(VSTPlugin *vstPlugin,
  1177. VstInt32 index,
  1178. float parameter
  1179. ) {
  1180. #ifdef DEBUG_PRINT_PARAMS
  1181. printf("vstrack_plugin: called VSTPluginSetParameter(%d, %f)\n", index, parameter);
  1182. #endif // DEBUG_PRINT_PARAMS
  1183. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1184. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1185. wrapper->lockAudio();
  1186. wrapper->setGlobals();
  1187. vst2_queue_param(index, parameter);
  1188. wrapper->unlockAudio();
  1189. }
  1190. /**
  1191. * Query parameter
  1192. */
  1193. float VSTPluginGetParameter(VSTPlugin *vstPlugin,
  1194. VstInt32 index
  1195. ) {
  1196. #ifdef DEBUG_PRINT_PARAMS
  1197. printf("vstrack_plugin: called VSTPluginGetParameter(%d)\n", index);
  1198. #endif // DEBUG_PRINT_PARAMS
  1199. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1200. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1201. wrapper->lockAudio(); // don't query a param while the module is deleted
  1202. wrapper->setGlobals();
  1203. float r = vst2_get_param(index);
  1204. wrapper->unlockAudio();
  1205. return r;
  1206. }
  1207. /**
  1208. * Main constructor for our C++ class
  1209. */
  1210. VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback,
  1211. VstInt32 vendorUniqueID,
  1212. VstInt32 vendorVersion,
  1213. VstInt32 numParams,
  1214. VstInt32 numPrograms,
  1215. VstInt32 numInputs,
  1216. VstInt32 numOutputs
  1217. ) : _vstHostCallback(vstHostCallback)
  1218. {
  1219. instance_count++;
  1220. // Make sure that the memory is properly initialized
  1221. memset(&_vstPlugin, 0, sizeof(_vstPlugin));
  1222. // this field must be set with this constant...
  1223. _vstPlugin.magic = kEffectMagic;
  1224. // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use)
  1225. _vstPlugin.object = this;
  1226. // specifying that we handle both single and NOT double precision (there are other flags see aeffect.h/VstAEffectFlags)
  1227. _vstPlugin.flags =
  1228. #ifndef VST2_EFFECT
  1229. effFlagsIsSynth |
  1230. #endif
  1231. effFlagsCanReplacing |
  1232. (effFlagsCanDoubleReplacing & 0) |
  1233. effFlagsProgramChunks |
  1234. effFlagsHasEditor ;
  1235. // initializing the plugin with the various values
  1236. _vstPlugin.uniqueID = vendorUniqueID;
  1237. _vstPlugin.version = vendorVersion;
  1238. _vstPlugin.numParams = numParams;
  1239. _vstPlugin.numPrograms = numPrograms;
  1240. _vstPlugin.numInputs = numInputs;
  1241. _vstPlugin.numOutputs = numOutputs;
  1242. // setting the callbacks to the previously defined functions
  1243. _vstPlugin.dispatcher = &VSTPluginDispatcher;
  1244. _vstPlugin.getParameter = &VSTPluginGetParameter;
  1245. _vstPlugin.setParameter = &VSTPluginSetParameter;
  1246. _vstPlugin.processReplacing = &VSTPluginProcessReplacingFloat32;
  1247. _vstPlugin.processDoubleReplacing = NULL;//&VSTPluginProcessReplacingFloat64;
  1248. // report latency
  1249. _vstPlugin.initialDelay = 0;
  1250. sample_rate = 44100.0f;
  1251. block_size = 64u;
  1252. b_processing = true;
  1253. last_program_chunk_str = NULL;
  1254. b_open = false;
  1255. // script_context = NULL;
  1256. }
  1257. /**
  1258. * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin
  1259. * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a
  1260. * memory leak which may end up slowing down and/or crashing the host
  1261. */
  1262. VSTPluginWrapper::~VSTPluginWrapper() {
  1263. closeEffect();
  1264. instance_count--;
  1265. }
  1266. void vst2_lock_midi_device() {
  1267. rack::global->vst2.wrapper->mtx_mididev.lock();
  1268. }
  1269. void vst2_unlock_midi_device() {
  1270. rack::global->vst2.wrapper->mtx_mididev.unlock();
  1271. }
  1272. void vst2_handle_queued_set_program_chunk(void) {
  1273. (void)rack::global->vst2.wrapper->handleSetQueuedProgramChunk();
  1274. }
  1275. void vst2_handle_ui_param(int uniqueParamId, float normValue) {
  1276. // Called by engineSetParam()
  1277. rack::global->vst2.wrapper->handleUIParam(uniqueParamId, normValue);
  1278. }
  1279. /**
  1280. * Implementation of the main entry point of the plugin
  1281. */
  1282. VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback) {
  1283. printf("vstrack_plugin: called VSTPluginMain... \n");
  1284. // // if(0 == VSTPluginWrapper::instance_count)
  1285. {
  1286. // simply create our plugin C++ class
  1287. VSTPluginWrapper *plugin =
  1288. new VSTPluginWrapper(vstHostCallback,
  1289. // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm)
  1290. #ifdef VST2_EFFECT
  1291. CCONST('g', 'v', 'g', 'y'),
  1292. #else
  1293. CCONST('v', '5', 'k', 'v'),
  1294. #endif
  1295. PLUGIN_VERSION, // version
  1296. VST2_MAX_UNIQUE_PARAM_IDS, // num params
  1297. 0, // no programs
  1298. NUM_INPUTS,
  1299. NUM_OUTPUTS
  1300. );
  1301. // return the plugin per the contract of the API
  1302. return plugin->getVSTPlugin();
  1303. }
  1304. // // else
  1305. // // {
  1306. // // // Can only instantiate once
  1307. // // // (global/static vars in VCV rack)
  1308. // // return NULL;
  1309. // // }
  1310. }
  1311. #endif // USE_VST2