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.

1644 lines
49KB

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