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.

2162 lines
66KB

  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, 06Jul2018, 13Jul2018
  20. /// 26Jul2018, 04Aug2018, 05Aug2018, 06Aug2018, 07Aug2018, 09Aug2018, 11Aug2018
  21. /// 18Aug2018, 19Aug2018, 05Sep2018, 06Sep2018
  22. ///
  23. ///
  24. // #define DEBUG_PRINT_EVENTS defined
  25. // #define DEBUG_PRINT_PARAMS defined
  26. #define NUM_INPUTS ( 8) // must match AudioInterface.cpp:AUDIO_INPUTS
  27. #define NUM_OUTPUTS ( 8) // must match AudioInterface.cpp:AUDIO_OUTPUTS
  28. // (note) causes reason to shut down when console is freed (when plugin is deleted)
  29. // #define USE_CONSOLE defined
  30. #undef RACK_HOST
  31. #define Dprintf if(0);else printf
  32. // #define Dprintf if(1);else printf
  33. // #define Dprintf_idle if(0);else printf
  34. #define Dprintf_idle if(1);else printf
  35. #include <aeffect.h>
  36. #include <aeffectx.h>
  37. #include <stdio.h>
  38. #include "../dep/yac/yac.h"
  39. #include "../dep/yac/yac_host.cpp"
  40. YAC_Host *yac_host; // not actually used, just to satisfy the linker
  41. #include "global_pre.hpp"
  42. #include "global.hpp"
  43. #include "global_ui.hpp"
  44. #define EDITWIN_X 0
  45. #define EDITWIN_Y 0
  46. #define EDITWIN_W 1200
  47. #define EDITWIN_H 800
  48. #define Dfltequal(a, b) ( (((a)-(b)) < 0.0f) ? (((a)-(b)) > -0.0001f) : (((a)-(b)) < 0.0001f) )
  49. typedef union cmemptr_u {
  50. const sUI *u32;
  51. const sF32 *f32;
  52. const void *any;
  53. } cmemptr_t;
  54. typedef union mem_u {
  55. sUI u32;
  56. sF32 f32;
  57. } mem_t;
  58. extern int vst2_init (int argc, char* argv[], bool _bFX);
  59. extern void vst2_exit (void);
  60. namespace rack {
  61. extern void vst2_editor_redraw (void);
  62. }
  63. extern void vst2_set_samplerate (sF32 _rate);
  64. extern void vst2_engine_process (float *const*_in, float **_out, unsigned int _numFrames);
  65. extern void vst2_process_midi_input_event (sU8 _a, sU8 _b, sU8 _c);
  66. extern void vst2_queue_param (int uniqueParamId, float value, bool bNormalized);
  67. extern void vst2_handle_queued_params (void);
  68. extern float vst2_get_param (int uniqueParamId);
  69. extern void vst2_get_param_name (int uniqueParamId, char *s, int sMaxLen);
  70. extern void vst2_set_shared_plugin_tls_globals (void); // see plugin.cpp
  71. extern "C" extern int vst2_handle_effeditkeydown (unsigned int _vkey);
  72. namespace rack {
  73. extern bool b_touchkeyboard_enable;
  74. extern void settingsLoad(std::string filename, bool bWindowSizeOnly);
  75. }
  76. #include "../include/window.hpp"
  77. #include "../dep/include/osdialog.h"
  78. #include "../include/app.hpp"
  79. #include <speex/speex_resampler.h>
  80. // using namespace rack;
  81. // extern void rack::windowRun(void);
  82. #if defined(_WIN32) || defined(_WIN64)
  83. #define HAVE_WINDOWS defined
  84. #define WIN32_LEAN_AND_MEAN defined
  85. #include <windows.h>
  86. #include <xmmintrin.h>
  87. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  88. // Windows:
  89. #define VST_EXPORT extern "C" __declspec(dllexport)
  90. struct PluginMutex {
  91. CRITICAL_SECTION handle;
  92. PluginMutex(void) {
  93. ::InitializeCriticalSection( &handle );
  94. }
  95. ~PluginMutex() {
  96. ::DeleteCriticalSection( &handle );
  97. }
  98. void lock(void) {
  99. ::EnterCriticalSection(&handle);
  100. }
  101. void unlock(void) {
  102. ::LeaveCriticalSection(&handle);
  103. }
  104. };
  105. #else
  106. // MacOSX, Linux:
  107. #define HAVE_UNIX defined
  108. #define VST_EXPORT extern
  109. #include <pthread.h>
  110. #include <errno.h>
  111. #include <unistd.h>
  112. #include <fcntl.h>
  113. #include <sys/mman.h>
  114. //static pthread_mutex_t loc_pthread_mutex_t_init = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
  115. static pthread_mutex_t loc_pthread_mutex_t_init = PTHREAD_MUTEX_INITIALIZER;
  116. struct PluginMutex {
  117. pthread_mutex_t handle;
  118. PluginMutex(void) {
  119. ::memcpy((void*)&handle, (const void*)&loc_pthread_mutex_t_init, sizeof(pthread_mutex_t));
  120. }
  121. ~PluginMutex() {
  122. }
  123. void lock(void) {
  124. ::pthread_mutex_lock(&handle);
  125. }
  126. void unlock(void) {
  127. ::pthread_mutex_unlock(&handle);
  128. }
  129. };
  130. #endif // _WIN32||_WIN64
  131. // // extern "C" {
  132. // // extern void glfwSetInstance(void *_glfw);
  133. // // }
  134. class PluginString : public YAC_String {
  135. public:
  136. static const sUI QUOT2 =(sUI)(1<<26); // \'\'
  137. static const sUI STRFLQMASK = (QUOT | UTAG1 | QUOT2);
  138. void safeFreeChars (void);
  139. sSI _realloc (sSI _numChars);
  140. sSI lastIndexOf (sChar _c, sUI _start) const;
  141. void getDirName (PluginString *_r) const;
  142. void replace (sChar _c, sChar _o);
  143. };
  144. void PluginString::safeFreeChars(void) {
  145. if(bflags & PluginString::DEL)
  146. {
  147. // if(!(bflags & PluginString::LA))
  148. {
  149. Dyacfreechars(chars);
  150. }
  151. }
  152. }
  153. sSI PluginString::_realloc(sSI _numBytes) {
  154. // Force alloc if a very big string is about to shrink a lot or there is simply not enough space available
  155. if( ((buflen >= 1024) && ( (((sUI)_numBytes)<<3) < buflen )) ||
  156. (NULL == chars) ||
  157. (buflen < ((sUI)_numBytes))
  158. ) // xxx (!chars) hack added 180702
  159. {
  160. if(NULL != chars)
  161. {
  162. sUI l = length;
  163. if(((sUI)_numBytes) < l)
  164. {
  165. l = _numBytes;
  166. }
  167. sU8 *nc = Dyacallocchars(_numBytes + 1);
  168. sUI i = 0;
  169. for(; i<l; i++)
  170. {
  171. nc[i] = chars[i];
  172. }
  173. nc[i] = 0;
  174. safeFreeChars();
  175. buflen = (_numBytes + 1);
  176. bflags = PluginString::DEL | (bflags & PluginString::STRFLQMASK); // keep old stringflags
  177. length = i + 1;
  178. chars = nc;
  179. key = YAC_LOSTKEY;
  180. return YAC_TRUE;
  181. }
  182. else
  183. {
  184. return PluginString::alloc(_numBytes + 1);
  185. }
  186. }
  187. else
  188. {
  189. key = YAC_LOSTKEY; // new 010208
  190. return YAC_TRUE;
  191. }
  192. }
  193. sSI PluginString::lastIndexOf(sChar _c, sUI _start) const {
  194. sSI li = -1;
  195. if(NULL != chars)
  196. {
  197. sUI i = _start;
  198. for(; i<length; i++)
  199. {
  200. if(chars[i] == ((sChar)_c))
  201. {
  202. li = i;
  203. }
  204. }
  205. }
  206. return li;
  207. }
  208. void PluginString::replace(sChar _c, sChar _o) {
  209. if(NULL != chars)
  210. {
  211. for(sUI i = 0; i < length; i++)
  212. {
  213. if(chars[i] == _c)
  214. chars[i] = _o;
  215. }
  216. }
  217. }
  218. void PluginString::getDirName(PluginString *_r) const {
  219. sSI idxSlash = lastIndexOf('/', 0);
  220. sSI idxBackSlash = lastIndexOf('\\', 0);
  221. sSI idxDrive = lastIndexOf(':', 0);
  222. sSI idx = -1;
  223. if(idxSlash > idxBackSlash)
  224. {
  225. idx = idxSlash;
  226. }
  227. else
  228. {
  229. idx = idxBackSlash;
  230. }
  231. if(idxDrive > idx)
  232. {
  233. idx = idxDrive;
  234. }
  235. if(-1 != idx)
  236. {
  237. _r->_realloc(idx + 2);
  238. _r->length = idx + 2;
  239. sSI i;
  240. for(i=0; i<=idx; i++)
  241. {
  242. _r->chars[i] = chars[i];
  243. }
  244. _r->chars[i++] = 0;
  245. _r->key = YAC_LOSTKEY;
  246. }
  247. else
  248. {
  249. _r->empty();
  250. }
  251. }
  252. #define MAX_FLOATARRAYALLOCSIZE (1024*1024*64)
  253. class PluginFloatArray : public YAC_FloatArray {
  254. public:
  255. sSI alloc (sSI _maxelements);
  256. };
  257. sSI PluginFloatArray::alloc(sSI _max_elements) {
  258. if(((sUI)_max_elements)>MAX_FLOATARRAYALLOCSIZE)
  259. {
  260. printf("[---] FloatArray::insane array size (maxelements=%08x)\n", _max_elements);
  261. return 0;
  262. }
  263. if(own_data)
  264. {
  265. if(elements)
  266. {
  267. delete [] elements;
  268. elements = NULL;
  269. }
  270. }
  271. if(_max_elements)
  272. {
  273. elements = new(std::nothrow) sF32[_max_elements];
  274. if(elements)
  275. {
  276. max_elements = _max_elements;
  277. num_elements = 0;
  278. own_data = 1;
  279. return 1;
  280. }
  281. }
  282. num_elements = 0;
  283. max_elements = 0;
  284. return 0;
  285. }
  286. /*
  287. * I find the naming a bit confusing so I decided to use more meaningful names instead.
  288. */
  289. /**
  290. * The VSTHostCallback is a function pointer so that the plugin can communicate with the host (not used in this small example)
  291. */
  292. typedef audioMasterCallback VSTHostCallback;
  293. /**
  294. * The VSTPlugin structure (AEffect) contains information about the plugin (like version, number of inputs, ...) and
  295. * callbacks so that the host can call the plugin to do its work. The primary callback will be `processReplacing` for
  296. * single precision (float) sample processing (or `processDoubleReplacing` for double precision (double)).
  297. */
  298. typedef AEffect VSTPlugin;
  299. // Since the host is expecting a very specific API we need to make sure it has C linkage (not C++)
  300. extern "C" {
  301. /*
  302. * This is the main entry point to the VST plugin.
  303. *
  304. * The host (DAW like Maschine, Ableton Live, Reason, ...) will look for this function with this exact API.
  305. *
  306. * It is the equivalent to `int main(int argc, char *argv[])` for a C executable.
  307. *
  308. * @param vstHostCallback is a callback so that the plugin can communicate with the host (not used in this small example)
  309. * @return a pointer to the AEffect structure
  310. */
  311. VST_EXPORT VSTPlugin *VSTPluginMain (VSTHostCallback vstHostCallback);
  312. // note this looks like this without the type aliases (and is obviously 100% equivalent)
  313. // extern AEffect *VSTPluginMain(audioMasterCallback audioMaster);
  314. }
  315. /*
  316. * Constant for the version of the plugin. For example 1100 for version 1.1.0.0
  317. */
  318. const VstInt32 PLUGIN_VERSION = 1000;
  319. /**
  320. * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the
  321. * host (VSTPlugin). This class will be stored in the `VSTPlugin.object` field (circular reference) so that it can
  322. * be accessed when the host calls the plugin back (for example in `processDoubleReplacing`).
  323. */
  324. class VSTPluginWrapper {
  325. public:
  326. static const uint32_t MIN_SAMPLE_RATE = 8192u;
  327. static const uint32_t MAX_SAMPLE_RATE = 384000u;
  328. static const uint32_t MIN_BLOCK_SIZE = 64u;
  329. static const uint32_t MAX_BLOCK_SIZE = 16384u;
  330. static const uint32_t MAX_OVERSAMPLE_FACTOR = 16u;
  331. static const uint32_t IDLE_DETECT_NONE = 0u; // always active
  332. static const uint32_t IDLE_DETECT_MIDI = 1u; // become idle when output is silence, reactivate when there's MIDI input activity
  333. static const uint32_t IDLE_DETECT_AUDIO = 2u; // become idle when output is silence, reactivate when there's audio input activity
  334. public:
  335. rack::Global rack_global;
  336. rack::GlobalUI rack_global_ui;
  337. protected:
  338. PluginString dllname;
  339. PluginString cwd;
  340. public:
  341. struct {
  342. float factor; // 1=no SR conversion, 2=oversample x2, 4=oversample x4, 0.5=undersample /2, ..
  343. int quality; // SPEEX_RESAMPLER_QUALITY_xxx
  344. float realtime_factor; // used during realtime rendering
  345. int realtime_quality;
  346. float offline_factor; // used during offline rendering (bounce)
  347. int offline_quality; //
  348. sUI num_in; // hack that limits oversampling to "n" input channels. default = NUM_INPUTS
  349. sUI num_out; // hack that limits oversampling to "n" input channels. default = NUM_OUTPUTS
  350. SpeexResamplerState *srs_in;
  351. SpeexResamplerState *srs_out;
  352. sF32 in_buffers[NUM_INPUTS * MAX_BLOCK_SIZE * MAX_OVERSAMPLE_FACTOR];
  353. sF32 out_buffers[NUM_OUTPUTS * MAX_BLOCK_SIZE];
  354. } oversample;
  355. public:
  356. float sample_rate; // e.g. 44100.0
  357. protected:
  358. uint32_t block_size; // e.g. 64
  359. PluginMutex mtx_audio;
  360. public:
  361. PluginMutex mtx_mididev;
  362. public:
  363. bool b_open;
  364. bool b_processing; // true=generate output, false=suspended
  365. bool b_offline; // true=offline rendering (HQ)
  366. bool b_check_offline; // true=ask host if it's in offline rendering mode
  367. sUI idle_detect_mode;
  368. sUI idle_detect_mode_fx;
  369. sUI idle_detect_mode_instr;
  370. sF32 idle_input_level_threshold;
  371. sF32 idle_output_level_threshold;
  372. sF32 idle_output_sec_threshold;
  373. sUI idle_output_framecount;
  374. bool b_idle;
  375. ERect editor_rect;
  376. sBool b_editor_open;
  377. char *last_program_chunk_str;
  378. static sSI instance_count;
  379. sSI instance_id;
  380. sF32 tmp_input_buffers[NUM_INPUTS * MAX_BLOCK_SIZE];
  381. sUI redraw_ival_ms; // 0=use DAW timer (effEditIdle)
  382. public:
  383. VSTPluginWrapper(VSTHostCallback vstHostCallback,
  384. VstInt32 vendorUniqueID,
  385. VstInt32 vendorVersion,
  386. VstInt32 numParams,
  387. VstInt32 numPrograms,
  388. VstInt32 numInputs,
  389. VstInt32 numOutputs
  390. );
  391. ~VSTPluginWrapper();
  392. VSTPlugin *getVSTPlugin(void) {
  393. return &_vstPlugin;
  394. }
  395. void setGlobals(void) {
  396. rack::global = &rack_global;
  397. rack::global_ui = &rack_global_ui;
  398. }
  399. sSI openEffect(void) {
  400. Dprintf("xxx vstrack_plugin::openEffect\n");
  401. // (todo) use mutex
  402. instance_id = instance_count;
  403. Dprintf("xxx vstrack_plugin::openEffect: instance_id=%d\n", instance_id);
  404. rack_global.vst2.wrapper = this;
  405. #ifdef USE_CONSOLE
  406. AllocConsole();
  407. freopen("CON", "w", stdout);
  408. freopen("CON", "w", stderr);
  409. freopen("CON", "r", stdin); // Note: "r", not "w".
  410. #endif // USE_CONSOLE
  411. setGlobals();
  412. rack_global.init();
  413. rack_global_ui.init();
  414. rack::global->vst2.last_seen_instance_count = instance_count;
  415. char oldCWD[1024];
  416. char dllnameraw[1024];
  417. ::GetCurrentDirectory(1024, (LPSTR) oldCWD);
  418. // ::GetModuleFileNameA(NULL, dllnameraw, 1024); // returns executable name (not the dll pathname)
  419. GetModuleFileNameA((HINSTANCE)&__ImageBase, dllnameraw, 1024);
  420. dllname.visit(dllnameraw);
  421. dllname.getDirName(&cwd);
  422. rack::global->vst2.program_dir = (const char*)cwd.chars;
  423. Dprintf("xxx vstrack_plugin::openEffect: cd to \"%s\"\n", (const char*)cwd.chars);
  424. // // ::SetCurrentDirectory("f:/vst_64bit/vstrack_plugin");
  425. ::SetCurrentDirectory((const char*)cwd.chars);
  426. Dprintf("xxx vstrack_plugin::openEffect: cwd change done\n");
  427. // cwd.replace('\\', '/');
  428. int argc = 1;
  429. char *argv[1];
  430. //argv[0] = (char*)cwd.chars;
  431. argv[0] = (char*)dllnameraw;
  432. Dprintf("xxx vstrack_plugin::openEffect: dllname=\"%s\"\n", argv[0]);
  433. (void)vst2_init(argc, argv,
  434. #ifdef VST2_EFFECT
  435. true/*bFX*/
  436. #else
  437. false/*bFX*/
  438. #endif // VST2_EFFECT
  439. );
  440. Dprintf("xxx vstrack_plugin::openEffect: vst2_init() done\n");
  441. vst2_set_shared_plugin_tls_globals();
  442. Dprintf("xxx vstrack_plugin::openEffect: restore cwd=\"%s\"\n", oldCWD);
  443. ::SetCurrentDirectory(oldCWD);
  444. setSampleRate(sample_rate);
  445. b_open = true;
  446. b_editor_open = false;
  447. Dprintf("xxx vstrack_plugin::openEffect: LEAVE\n");
  448. return 1;
  449. }
  450. void setWindowSize(int _width, int _height) {
  451. if(_width < 640)
  452. _width = 640;
  453. if(_height < 480)
  454. _height = 480;
  455. editor_rect.right = EDITWIN_X + _width;
  456. editor_rect.bottom = EDITWIN_Y + _height;
  457. (void)lglw_window_resize(rack_global_ui.window.lglw, _width, _height);
  458. }
  459. void setRefreshRate(float _hz) {
  460. if(_hz < 15.0f)
  461. redraw_ival_ms = 0u;
  462. else
  463. redraw_ival_ms = sUI(1000.0f / _hz);
  464. if(b_editor_open)
  465. {
  466. lglw_timer_stop(rack_global_ui.window.lglw);
  467. if(0u != redraw_ival_ms)
  468. {
  469. lglw_timer_start(rack_global_ui.window.lglw, redraw_ival_ms);
  470. }
  471. }
  472. }
  473. float getRefreshRate(void) {
  474. if(redraw_ival_ms > 0u)
  475. return (1000.0f / redraw_ival_ms);
  476. else
  477. return 0.0f;
  478. }
  479. void destroyResamplerStates(void) {
  480. if(NULL != oversample.srs_in)
  481. {
  482. speex_resampler_destroy(oversample.srs_in);
  483. oversample.srs_in = NULL;
  484. }
  485. if(NULL != oversample.srs_out)
  486. {
  487. speex_resampler_destroy(oversample.srs_out);
  488. oversample.srs_out = NULL;
  489. }
  490. }
  491. void openEditor(void *_hwnd) {
  492. Dprintf("xxx vstrack_plugin: openEditor() parentHWND=%p\n", _hwnd);
  493. setGlobals();
  494. (void)lglw_window_open(rack_global_ui.window.lglw,
  495. _hwnd,
  496. 0/*x*/, 0/*y*/,
  497. (editor_rect.right - editor_rect.left),
  498. (editor_rect.bottom - editor_rect.top)
  499. );
  500. if(0u != redraw_ival_ms)
  501. {
  502. lglw_timer_start(rack_global_ui.window.lglw, redraw_ival_ms);
  503. }
  504. b_editor_open = true;
  505. rack::global_ui->param_info.placeholder_framecount = (30*30)-10;
  506. rack::global_ui->param_info.last_param_widget = NULL;
  507. }
  508. void closeEditor(void) {
  509. Dprintf("xxx vstrack_plugin: closeEditor() b_editor_open=%d\n", b_editor_open);
  510. if(b_editor_open)
  511. {
  512. setGlobals();
  513. lglw_timer_stop(rack_global_ui.window.lglw);
  514. lglw_window_close(rack_global_ui.window.lglw);
  515. b_editor_open = false;
  516. }
  517. }
  518. void closeEffect(void) {
  519. closeEditor();
  520. // (todo) use mutex
  521. Dprintf("xxx vstrack_plugin::closeEffect: last_program_chunk_str=%p\n", last_program_chunk_str);
  522. if(NULL != last_program_chunk_str)
  523. {
  524. ::free(last_program_chunk_str);
  525. last_program_chunk_str = NULL;
  526. }
  527. Dprintf("xxx vstrack_plugin::closeEffect: b_open=%d\n", b_open);
  528. if(b_open)
  529. {
  530. b_open = false;
  531. setGlobals();
  532. vst2_set_shared_plugin_tls_globals();
  533. rack::global->vst2.last_seen_instance_count = instance_count;
  534. Dprintf("xxx vstrack_plugin: call vst2_exit()\n");
  535. vst2_exit();
  536. Dprintf("xxx vstrack_plugin: vst2_exit() done\n");
  537. destroyResamplerStates();
  538. Dprintf("xxx vstrack_plugin: destroyResamplerStates() done\n");
  539. #ifdef USE_CONSOLE
  540. // FreeConsole();
  541. #endif // USE_CONSOLE
  542. }
  543. }
  544. void lockAudio(void) {
  545. mtx_audio.lock();
  546. }
  547. void unlockAudio(void) {
  548. mtx_audio.unlock();
  549. }
  550. VstInt32 getNumInputs(void) const {
  551. return _vstPlugin.numInputs;
  552. }
  553. VstInt32 getNumOutputs(void) const {
  554. return _vstPlugin.numOutputs;
  555. }
  556. void setOversample(float _factor, int _quality, bool _bLock = true) {
  557. oversample.factor = _factor;
  558. oversample.quality = _quality;
  559. setSampleRate(sample_rate, _bLock);
  560. }
  561. void setOversampleRealtime(float _factor, int _quality) {
  562. if(_factor < 0.0f)
  563. _factor = oversample.realtime_factor; // keep
  564. if(_quality < 0)
  565. _quality = oversample.realtime_quality; // keep
  566. if(_factor < 0.001f)
  567. _factor = 1.0f;
  568. else if(_factor > float(MAX_OVERSAMPLE_FACTOR))
  569. _factor = float(MAX_OVERSAMPLE_FACTOR);
  570. if(_quality < SPEEX_RESAMPLER_QUALITY_MIN/*0*/)
  571. _quality = SPEEX_RESAMPLER_QUALITY_MIN;
  572. else if(_quality > SPEEX_RESAMPLER_QUALITY_MAX/*10*/)
  573. _quality = SPEEX_RESAMPLER_QUALITY_MAX;
  574. oversample.realtime_factor = _factor;
  575. oversample.realtime_quality = _quality;
  576. if(!b_offline)
  577. {
  578. setOversample(oversample.realtime_factor, oversample.realtime_quality);
  579. }
  580. }
  581. void setOversampleOffline(float _factor, int _quality) {
  582. if(_factor < 0.0f)
  583. _factor = oversample.offline_factor; // keep
  584. if(_quality < 0)
  585. _quality = oversample.offline_quality; // keep
  586. if(_factor < 0.001f)
  587. _factor = 1.0f;
  588. else if(_factor > float(MAX_OVERSAMPLE_FACTOR))
  589. _factor = float(MAX_OVERSAMPLE_FACTOR);
  590. if(_quality < SPEEX_RESAMPLER_QUALITY_MIN/*0*/)
  591. _quality = SPEEX_RESAMPLER_QUALITY_MIN;
  592. else if(_quality > SPEEX_RESAMPLER_QUALITY_MAX/*10*/)
  593. _quality = SPEEX_RESAMPLER_QUALITY_MAX;
  594. oversample.offline_factor = _factor;
  595. oversample.offline_quality = _quality;
  596. if(b_offline)
  597. {
  598. setOversample(oversample.offline_factor, oversample.offline_quality);
  599. }
  600. }
  601. void setOversampleChannels(int _numIn, int _numOut) {
  602. if(_numIn < 0)
  603. _numIn = int(oversample.num_in); // keep
  604. if(_numOut < 0)
  605. _numOut = int(oversample.num_out); // keep
  606. if(_numIn < 0)
  607. _numIn = 0;
  608. else if(_numIn > NUM_INPUTS)
  609. _numIn = NUM_INPUTS;
  610. if(_numOut < 1)
  611. _numOut = 1;
  612. else if(_numOut > NUM_OUTPUTS)
  613. _numOut = NUM_OUTPUTS;
  614. lockAudio();
  615. oversample.num_in = sUI(_numIn);
  616. oversample.num_out = sUI(_numOut);
  617. unlockAudio();
  618. }
  619. bool setSampleRate(float _rate, bool _bLock = true) {
  620. bool r = false;
  621. if((_rate >= float(MIN_SAMPLE_RATE)) && (_rate <= float(MAX_SAMPLE_RATE)))
  622. {
  623. if(_bLock)
  624. {
  625. setGlobals();
  626. lockAudio();
  627. }
  628. sample_rate = _rate;
  629. vst2_set_samplerate(sample_rate * oversample.factor); // see engine.cpp
  630. destroyResamplerStates();
  631. // Lazy-alloc resampler state
  632. if(!Dfltequal(oversample.factor, 1.0f))
  633. {
  634. int err;
  635. oversample.srs_in = speex_resampler_init(NUM_INPUTS,
  636. sUI(sample_rate), // in rate
  637. sUI(sample_rate * oversample.factor), // out rate
  638. oversample.quality,
  639. &err
  640. );
  641. oversample.srs_out = speex_resampler_init(NUM_OUTPUTS,
  642. sUI(sample_rate * oversample.factor), // in rate
  643. sUI(sample_rate), // out rate
  644. oversample.quality,
  645. &err
  646. );
  647. Dprintf("xxx vstrack_plugin: initialize speex resampler (rate=%f factor=%f quality=%d)\n", sample_rate, oversample.factor, oversample.quality);
  648. }
  649. if(_bLock)
  650. {
  651. unlockAudio();
  652. }
  653. r = true;
  654. }
  655. return r;
  656. }
  657. bool setBlockSize(uint32_t _blockSize) {
  658. bool r = false;
  659. if((_blockSize >= MIN_BLOCK_SIZE) && (_blockSize <= MAX_BLOCK_SIZE))
  660. {
  661. lockAudio();
  662. block_size = _blockSize;
  663. unlockAudio();
  664. r = true;
  665. }
  666. return r;
  667. }
  668. void setEnableProcessingActive(bool _bEnable) {
  669. lockAudio();
  670. b_processing = _bEnable;
  671. unlockAudio();
  672. }
  673. void checkOffline(void) {
  674. // Called by VSTPluginProcessReplacingFloat32()
  675. if(b_check_offline)
  676. {
  677. if(NULL != _vstHostCallback)
  678. {
  679. int level = (int)_vstHostCallback(&_vstPlugin, audioMasterGetCurrentProcessLevel, 0, 0/*value*/, NULL/*ptr*/, 0.0f/*opt*/);
  680. // (note) Reason sets process level to kVstProcessLevelUser during "bounce in place"
  681. bool bOffline = (kVstProcessLevelOffline == level) || (kVstProcessLevelUser == level);
  682. #if 0
  683. {
  684. static int i = 0;
  685. if(0 == (++i & 127))
  686. {
  687. Dprintf("xxx vstrack_plugin: audioMasterGetCurrentProcessLevel: level=%d\n", level);
  688. }
  689. }
  690. #endif
  691. if(b_offline ^ bOffline)
  692. {
  693. // Offline mode changed, update resampler
  694. b_offline = bOffline;
  695. if(bOffline)
  696. {
  697. Dprintf("xxx vstrack_plugin: enter OFFLINE mode. factor=%f quality=%d\n", oversample.offline_factor, oversample.offline_quality);
  698. setOversample(oversample.offline_factor, oversample.offline_quality, false/*bLock*/);
  699. }
  700. else
  701. {
  702. Dprintf("xxx vstrack_plugin: enter REALTIME mode. factor=%f quality=%d\n", oversample.realtime_factor, oversample.realtime_quality);
  703. setOversample(oversample.realtime_factor, oversample.realtime_quality, false/*bLock*/);
  704. }
  705. Dprintf("xxx vstrack_plugin: mode changed to %d\n", int(bOffline));
  706. }
  707. }
  708. }
  709. }
  710. sUI getBankChunk(uint8_t **_addr) {
  711. return 0;
  712. }
  713. void setIdleDetectMode(uint32_t _mode) {
  714. switch(_mode)
  715. {
  716. default:
  717. case IDLE_DETECT_NONE:
  718. idle_detect_mode = IDLE_DETECT_NONE;
  719. break;
  720. case IDLE_DETECT_MIDI:
  721. idle_detect_mode = IDLE_DETECT_MIDI;
  722. break;
  723. case IDLE_DETECT_AUDIO:
  724. idle_detect_mode = IDLE_DETECT_AUDIO;
  725. break;
  726. }
  727. b_idle = false;
  728. idle_output_framecount = 0u;
  729. }
  730. void setIdleDetectModeFx(uint32_t _mode) {
  731. idle_detect_mode_fx = _mode;
  732. #ifdef VST2_EFFECT
  733. setIdleDetectMode(uint32_t(_mode));
  734. #endif // VST2_EFFECT
  735. }
  736. void setIdleDetectModeInstr(uint32_t _mode) {
  737. idle_detect_mode_instr = _mode;
  738. #ifndef VST2_EFFECT
  739. setIdleDetectMode(uint32_t(_mode));
  740. #endif // VST2_EFFECT
  741. }
  742. sUI getProgramChunk(uint8_t**_addr) {
  743. setGlobals();
  744. vst2_set_shared_plugin_tls_globals();
  745. if(NULL != last_program_chunk_str)
  746. {
  747. ::free(last_program_chunk_str);
  748. }
  749. last_program_chunk_str = rack::global_ui->app.gRackWidget->savePatchToString();
  750. if(NULL != last_program_chunk_str)
  751. {
  752. *_addr = (uint8_t*)last_program_chunk_str;
  753. return (sUI)strlen(last_program_chunk_str) + 1/*ASCIIZ*/;
  754. }
  755. return 0;
  756. }
  757. bool setBankChunk(size_t _size, uint8_t *_addr) {
  758. bool r = false;
  759. return r;
  760. }
  761. bool setProgramChunk(size_t _size, uint8_t *_addr) {
  762. setGlobals();
  763. lockAudio();
  764. vst2_set_shared_plugin_tls_globals();
  765. #if 0
  766. Dprintf("xxx vstrack_plugin:setProgramChunk: size=%u\n", _size);
  767. #endif
  768. lglw_glcontext_push(rack::global_ui->window.lglw);
  769. bool r = rack::global_ui->app.gRackWidget->loadPatchFromString((const char*)_addr);
  770. rack::global_ui->ui.gScene->step(); // w/o this the patch is bypassed
  771. lglw_glcontext_pop(rack::global_ui->window.lglw);
  772. Dprintf("xxx vstrack_plugin:setProgramChunk: r=%d\n", r);
  773. unlockAudio();
  774. return r;
  775. }
  776. #ifdef HAVE_WINDOWS
  777. void sleepMillisecs(uint32_t _num) {
  778. ::Sleep((DWORD)_num);
  779. }
  780. #elif defined(HAVE_UNIX)
  781. void sleepMillisecs(uint32_t _num) {
  782. ::usleep(1000u * _num);
  783. }
  784. #endif
  785. const volatile float *getNextInputChannelChunk(void) {
  786. volatile float *r = NULL;
  787. return r;
  788. }
  789. volatile float *lockNextOutputChannelChunk(void) {
  790. volatile float *r = NULL;
  791. return r;
  792. }
  793. sBool getParameterProperties(sUI _index, struct VstParameterProperties *_ret) {
  794. sBool r = 0;
  795. ::memset((void*)_ret, 0, sizeof(struct VstParameterProperties));
  796. if(_index < VST2_MAX_UNIQUE_PARAM_IDS)
  797. {
  798. _ret->stepFloat = 0.001f;
  799. _ret->smallStepFloat = 0.001f;
  800. _ret->largeStepFloat = 0.01f;
  801. _ret->flags = 0;
  802. _ret->displayIndex = VstInt16(_index);
  803. _ret->category = 0; // 0=no category
  804. _ret->numParametersInCategory = 0;
  805. vst2_get_param_name(_index, _ret->label, kVstMaxLabelLen);
  806. vst2_get_param_name(_index, _ret->shortLabel, kVstMaxShortLabelLen);
  807. r = 1;
  808. }
  809. return r;
  810. }
  811. void handleUIParam(int uniqueParamId, float normValue) {
  812. if(NULL != _vstHostCallback)
  813. (void)_vstHostCallback(&_vstPlugin, audioMasterAutomate, uniqueParamId, 0/*value*/, NULL/*ptr*/, normValue/*opt*/);
  814. }
  815. void getTimingInfo(int *_retPlaying, float *_retBPM, float *_retSongPosPPQ) {
  816. *_retPlaying = 0;
  817. if(NULL != _vstHostCallback)
  818. {
  819. VstIntPtr result = _vstHostCallback(&_vstPlugin, audioMasterGetTime, 0, 0/*value*/, NULL/*ptr*/, 0.0f/*opt*/);
  820. if(NULL != result)
  821. {
  822. const struct VstTimeInfo *timeInfo = (const struct VstTimeInfo *)result;
  823. *_retPlaying = (0 != (timeInfo->flags & kVstTransportPlaying));
  824. if(0 != (timeInfo->flags & kVstTempoValid))
  825. {
  826. *_retBPM = float(timeInfo->tempo);
  827. }
  828. if(0 != (timeInfo->flags & kVstPpqPosValid))
  829. {
  830. *_retSongPosPPQ = (float)timeInfo->ppqPos;
  831. }
  832. }
  833. }
  834. }
  835. void queueRedraw(void) {
  836. setGlobals();
  837. if(lglw_window_is_visible(rack::global_ui->window.lglw))
  838. {
  839. lglw_redraw(rack::global_ui->window.lglw);
  840. }
  841. }
  842. void redraw(void) {
  843. setGlobals();
  844. if(lglw_window_is_visible(rack::global_ui->window.lglw))
  845. {
  846. vst2_set_shared_plugin_tls_globals();
  847. // Save DAW GL context and bind our own
  848. lglw_glcontext_push(rack::global_ui->window.lglw);
  849. rack::vst2_editor_redraw();
  850. // Restore the DAW's GL context
  851. lglw_glcontext_pop(rack::global_ui->window.lglw);
  852. }
  853. }
  854. private:
  855. // the host callback (a function pointer)
  856. VSTHostCallback _vstHostCallback;
  857. // the actual structure required by the host
  858. VSTPlugin _vstPlugin;
  859. };
  860. sSI VSTPluginWrapper::instance_count = 0;
  861. /*******************************************
  862. * Callbacks: Host -> Plugin
  863. *
  864. * Defined here because they are used in the rest of the code later
  865. */
  866. /**
  867. * This is the callback that will be called to process the samples in the case of single precision. This is where the
  868. * meat of the logic happens!
  869. *
  870. * @param vstPlugin the object returned by VSTPluginMain
  871. * @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]
  872. * @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]
  873. * @param sampleFrames the number of samples (second dimension in both arrays)
  874. */
  875. void VSTPluginProcessReplacingFloat32(VSTPlugin *vstPlugin,
  876. float **_inputs,
  877. float **_outputs,
  878. VstInt32 sampleFrames
  879. ) {
  880. if(sUI(sampleFrames) > VSTPluginWrapper::MAX_BLOCK_SIZE)
  881. return; // should not be reachable
  882. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  883. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  884. // Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: ENTER\n");
  885. wrapper->lockAudio();
  886. wrapper->setGlobals();
  887. vst2_set_shared_plugin_tls_globals();
  888. if(wrapper->b_check_offline)
  889. {
  890. // Check if offline rendering state changed and update resampler when necessary
  891. wrapper->checkOffline();
  892. }
  893. // // rack::global->engine.vipMutex.lock();
  894. rack::global->engine.mutex.lock();
  895. rack::global->vst2.last_seen_num_frames = sUI(sampleFrames);
  896. vst2_handle_queued_params();
  897. //Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: lockAudio done\n");
  898. //Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: wrapper=%p\n", wrapper);
  899. #ifdef HAVE_WINDOWS
  900. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
  901. #endif // HAVE_WINDOWS
  902. sUI chIdx;
  903. if(wrapper->b_idle)
  904. {
  905. switch(wrapper->idle_detect_mode)
  906. {
  907. default:
  908. case VSTPluginWrapper::IDLE_DETECT_NONE:
  909. // should not be reachable
  910. wrapper->b_idle = false;
  911. wrapper->idle_output_framecount = 0u;
  912. break;
  913. case VSTPluginWrapper::IDLE_DETECT_MIDI:
  914. break;
  915. case VSTPluginWrapper::IDLE_DETECT_AUDIO:
  916. {
  917. wrapper->b_idle = true;
  918. for(chIdx = 0u; chIdx < NUM_INPUTS; chIdx++)
  919. {
  920. if(chIdx < wrapper->oversample.num_in)
  921. {
  922. cmemptr_t s;
  923. s.f32 = _inputs[chIdx];
  924. sF32 sum = 0.0f;
  925. for(sUI i = 0u; i < sUI(sampleFrames); i++)
  926. {
  927. mem_t m;
  928. m.u32 = s.u32[i] & ~0x80000000u; // abs
  929. sum += m.f32;
  930. // sum += (s.f32[i] * s.f32[i]); // RMS
  931. }
  932. sum = (sum / float(sampleFrames));
  933. // sum = sqrtf(sum / float(sampleFrames)); // RMS
  934. if(sum >= wrapper->idle_input_level_threshold)
  935. {
  936. wrapper->b_idle = false;
  937. Dprintf_idle("xxx vstrack_plugin: become active after input (sum=%f, threshold=%f)\n", sum, wrapper->idle_input_level_threshold);
  938. wrapper->idle_output_framecount = 0u;
  939. break;
  940. }
  941. }
  942. }
  943. }
  944. break;
  945. } // switch idle_detect_mode
  946. } // if idle
  947. if(!wrapper->b_idle)
  948. {
  949. if( !Dfltequal(wrapper->oversample.factor, 1.0f) &&
  950. (NULL != wrapper->oversample.srs_in) &&
  951. (NULL != wrapper->oversample.srs_out)
  952. )
  953. {
  954. sF32 *inputs[NUM_INPUTS];
  955. sF32 *outputs[NUM_INPUTS];
  956. sUI hostNumFrames = sampleFrames;
  957. sUI overNumFrames = sUI((sampleFrames * wrapper->oversample.factor) + 0.5f);
  958. // Up-sample inputs
  959. {
  960. sUI inNumFrames = hostNumFrames;
  961. sUI outNumFrames = overNumFrames;
  962. sF32 *d = wrapper->oversample.in_buffers;
  963. for(chIdx = 0u; chIdx < NUM_INPUTS; chIdx++)
  964. {
  965. if(chIdx < wrapper->oversample.num_in)
  966. {
  967. sF32 *s = _inputs[chIdx];
  968. int err = speex_resampler_process_float(wrapper->oversample.srs_in,
  969. chIdx,
  970. s,
  971. &inNumFrames,
  972. d,
  973. &outNumFrames
  974. );
  975. }
  976. else
  977. {
  978. // Skip channel
  979. ::memset(d, 0, sizeof(sF32) * outNumFrames);
  980. }
  981. inputs[chIdx] = d;
  982. // Next input channel
  983. d += outNumFrames;
  984. }
  985. }
  986. // Clear output buffers
  987. // (note) AudioInterface instances accumulate samples in the output buffer
  988. {
  989. sF32 *d = wrapper->oversample.out_buffers;
  990. ::memset((void*)d, 0, (sizeof(sF32) * wrapper->oversample.num_out * overNumFrames));
  991. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  992. {
  993. outputs[chIdx] = d;
  994. d += overNumFrames;
  995. }
  996. }
  997. // Process rack modules
  998. if(wrapper->b_processing)
  999. {
  1000. vst2_engine_process(inputs, outputs, overNumFrames);
  1001. }
  1002. // Down-sample outputs
  1003. {
  1004. sF32 *s = wrapper->oversample.out_buffers;
  1005. sUI inNumFrames = overNumFrames;
  1006. sUI outNumFrames = hostNumFrames;
  1007. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  1008. {
  1009. sF32 *d = _outputs[chIdx];
  1010. if(chIdx < wrapper->oversample.num_out)
  1011. {
  1012. int err = speex_resampler_process_float(wrapper->oversample.srs_out,
  1013. chIdx,
  1014. s,
  1015. &inNumFrames,
  1016. d,
  1017. &outNumFrames
  1018. );
  1019. // Next output channel
  1020. s += inNumFrames;
  1021. }
  1022. else
  1023. {
  1024. // Skip output
  1025. ::memset((void*)d, 0, sizeof(sF32) * outNumFrames);
  1026. }
  1027. }
  1028. }
  1029. }
  1030. else
  1031. {
  1032. // No oversampling
  1033. // (note) Cubase (tested with 9.5.30) uses the same buffer(s) for both input&output
  1034. // => back up the inputs before clearing the outputs
  1035. sF32 *inputs[NUM_INPUTS];
  1036. sUI k = 0u;
  1037. for(chIdx = 0u; chIdx < NUM_INPUTS; chIdx++)
  1038. {
  1039. inputs[chIdx] = &wrapper->tmp_input_buffers[k];
  1040. ::memcpy((void*)inputs[chIdx], _inputs[chIdx], sizeof(sF32)*sampleFrames);
  1041. k += sampleFrames;
  1042. }
  1043. // Clear output buffers
  1044. // (note) AudioInterface instances accumulate samples in the output buffer
  1045. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  1046. {
  1047. ::memset((void*)_outputs[chIdx], 0, sizeof(sF32)*sampleFrames);
  1048. }
  1049. if(wrapper->b_processing)
  1050. {
  1051. vst2_engine_process(inputs, _outputs, sampleFrames);
  1052. }
  1053. }
  1054. if(VSTPluginWrapper::IDLE_DETECT_NONE != wrapper->idle_detect_mode)
  1055. {
  1056. bool bSilence = true;
  1057. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  1058. {
  1059. if(chIdx < wrapper->oversample.num_out)
  1060. {
  1061. cmemptr_t d;
  1062. d.f32 = _outputs[chIdx];
  1063. sF32 sum = 0.0f;
  1064. for(sUI i = 0u; i < sUI(sampleFrames); i++)
  1065. {
  1066. mem_t m;
  1067. m.u32 = d.u32[i] & ~0x80000000u; // abs
  1068. sum += m.f32;
  1069. // sum += d.f32[i] * d.f32[i]; // RMS
  1070. }
  1071. sum = (sum / float(sampleFrames));
  1072. // sum = sqrtf(sum / float(sampleFrames)); // RMS
  1073. {
  1074. static int x = 0;
  1075. if(0 == (++x & 127))
  1076. Dprintf_idle("xxx vstrack_plugin: output avg is %f\n", sum);
  1077. }
  1078. if(sum >= wrapper->idle_output_level_threshold)
  1079. {
  1080. bSilence = false;
  1081. break;
  1082. }
  1083. }
  1084. }
  1085. if(bSilence)
  1086. {
  1087. wrapper->idle_output_framecount += sampleFrames;
  1088. if(wrapper->idle_output_framecount >= sUI(wrapper->idle_output_sec_threshold * wrapper->sample_rate))
  1089. {
  1090. // Frame threshold exceeded, become idle
  1091. wrapper->b_idle = true;
  1092. Dprintf_idle("xxx vstrack_plugin: now idle\n");
  1093. }
  1094. }
  1095. else
  1096. {
  1097. wrapper->idle_output_framecount = 0u;
  1098. }
  1099. }
  1100. } // if !wrapper->b_idle
  1101. else
  1102. {
  1103. // Idle, clear output buffers
  1104. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  1105. {
  1106. ::memset((void*)_outputs[chIdx], 0, sizeof(sF32)*sampleFrames);
  1107. }
  1108. }
  1109. // // rack::global->engine.vipMutex.unlock();
  1110. rack::global->engine.mutex.unlock();
  1111. wrapper->unlockAudio();
  1112. //Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: LEAVE\n");
  1113. }
  1114. /**
  1115. * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the
  1116. * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...)
  1117. *
  1118. * @param vstPlugin the object returned by VSTPluginMain
  1119. * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand
  1120. * total of 79 of them! Only a few of them are implemented in this small plugin.
  1121. * @param index depend on the opcode
  1122. * @param value depend on the opcode
  1123. * @param ptr depend on the opcode
  1124. * @param opt depend on the opcode
  1125. * @return depend on the opcode (0 is ok when you don't implement an opcode...)
  1126. */
  1127. VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin,
  1128. VstInt32 opCode,
  1129. VstInt32 index,
  1130. VstIntPtr value,
  1131. void *ptr,
  1132. float opt
  1133. ) {
  1134. // Dprintf("vstrack_plugin: called VSTPluginDispatcher(%d)\n", opCode);
  1135. VstIntPtr r = 0;
  1136. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1137. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1138. // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them
  1139. switch(opCode)
  1140. {
  1141. case effGetPlugCategory:
  1142. // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed
  1143. // to generating sound)
  1144. #ifdef VST2_EFFECT
  1145. return kPlugCategEffect;
  1146. #else
  1147. return kPlugCategSynth;
  1148. #endif // VST2_EFFECT
  1149. case effOpen:
  1150. // called by the host after it has obtained the effect instance (but _not_ during plugin scans)
  1151. // (note) any heavy-lifting init code should go here
  1152. Dprintf("vstrack_plugin<dispatcher>: effOpen\n");
  1153. r = wrapper->openEffect();
  1154. break;
  1155. case effClose:
  1156. // called by the host when the plugin was called... time to reclaim memory!
  1157. wrapper->closeEffect();
  1158. // (note) hosts usually call effStopProcess before effClose
  1159. delete wrapper;
  1160. break;
  1161. case effSetProgram:
  1162. r = 1;
  1163. break;
  1164. case effGetProgram:
  1165. r = 0;
  1166. break;
  1167. case effGetVendorString:
  1168. // request for the vendor string (usually used in the UI for plugin grouping)
  1169. ::strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen);
  1170. r = 1;
  1171. break;
  1172. case effGetVendorVersion:
  1173. // request for the version
  1174. return PLUGIN_VERSION;
  1175. case effGetEffectName:
  1176. #ifdef VST2_EFFECT
  1177. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1", kVstMaxEffectNameLen);
  1178. #else
  1179. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 I", kVstMaxEffectNameLen);
  1180. #endif // VST2_EFFECT
  1181. r = 1;
  1182. break;
  1183. case effGetProductString:
  1184. #ifdef VST2_EFFECT
  1185. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 VST2 Plugin v0.4", kVstMaxProductStrLen);
  1186. #else
  1187. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 I VST2 Plugin v0.4", kVstMaxProductStrLen);
  1188. #endif // VST2_EFFECT
  1189. r = 1;
  1190. break;
  1191. case effGetNumMidiInputChannels:
  1192. r = 16;
  1193. break;
  1194. case effGetNumMidiOutputChannels:
  1195. r = 0;
  1196. break;
  1197. case effGetInputProperties:
  1198. {
  1199. VstPinProperties *pin = (VstPinProperties*)ptr;
  1200. ::snprintf(pin->label, kVstMaxLabelLen, "Input #%d", index);
  1201. pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
  1202. pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
  1203. ::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "in%d", index);
  1204. memset((void*)pin->future, 0, 48);
  1205. r = 1;
  1206. }
  1207. break;
  1208. case effGetOutputProperties:
  1209. {
  1210. VstPinProperties *pin = (VstPinProperties*)ptr;
  1211. ::snprintf(pin->label, kVstMaxLabelLen, "Output #%d", index);
  1212. pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
  1213. pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
  1214. ::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "out%d", index);
  1215. memset((void*)pin->future, 0, 48);
  1216. r = 1;
  1217. }
  1218. break;
  1219. case effSetSampleRate:
  1220. r = wrapper->setSampleRate(opt) ? 1 : 0;
  1221. break;
  1222. case effSetBlockSize:
  1223. r = wrapper->setBlockSize(uint32_t(value)) ? 1 : 0;
  1224. break;
  1225. case effCanDo:
  1226. // ptr:
  1227. // "sendVstEvents"
  1228. // "sendVstMidiEvent"
  1229. // "sendVstTimeInfo"
  1230. // "receiveVstEvents"
  1231. // "receiveVstMidiEvent"
  1232. // "receiveVstTimeInfo"
  1233. // "offline"
  1234. // "plugAsChannelInsert"
  1235. // "plugAsSend"
  1236. // "mixDryWet"
  1237. // "noRealTime"
  1238. // "multipass"
  1239. // "metapass"
  1240. // "1in1out"
  1241. // "1in2out"
  1242. // "2in1out"
  1243. // "2in2out"
  1244. // "2in4out"
  1245. // "4in2out"
  1246. // "4in4out"
  1247. // "4in8out"
  1248. // "8in4out"
  1249. // "8in8out"
  1250. // "midiProgramNames"
  1251. // "conformsToWindowRules"
  1252. if(!strcmp((char*)ptr, "receiveVstEvents"))
  1253. r = 1;
  1254. else if(!strcmp((char*)ptr, "receiveVstMidiEvent")) // (note) required by Jeskola Buzz
  1255. r = 1;
  1256. else if(!strcmp((char*)ptr, "noRealTime"))
  1257. r = 1;
  1258. else
  1259. r = 0;
  1260. break;
  1261. case effGetProgramName:
  1262. ::snprintf((char*)ptr, kVstMaxProgNameLen, "default");
  1263. r = 1;
  1264. break;
  1265. case effSetProgramName:
  1266. r = 1;
  1267. break;
  1268. case effGetProgramNameIndexed:
  1269. ::sprintf((char*)ptr, "default");
  1270. r = 1;
  1271. break;
  1272. case effGetParamName:
  1273. // kVstMaxParamStrLen(8), much longer in other plugins
  1274. // printf("xxx vstrack_plugin: effGetParamName: ptr=%p\n", ptr);
  1275. wrapper->setGlobals();
  1276. vst2_get_param_name(index, (char*)ptr, kVstMaxParamStrLen);
  1277. r = 1;
  1278. break;
  1279. case effCanBeAutomated:
  1280. // fix Propellerhead Reason VST parameter support
  1281. r = 1;
  1282. break;
  1283. case effGetParamLabel:
  1284. // e.g. "dB"
  1285. break;
  1286. case effGetParamDisplay:
  1287. // e.g. "-20"
  1288. break;
  1289. #if 0
  1290. case effGetParameterProperties:
  1291. // [index]: parameter index [ptr]: #VstParameterProperties* [return value]: 1 if supported
  1292. wrapper->setGlobals();
  1293. r = wrapper->getParameterProperties(sUI(index), (struct VstParameterProperties*)ptr);
  1294. break;
  1295. #endif
  1296. case effGetChunk:
  1297. // Query bank (index=0) or program (index=1) state
  1298. // value: 0
  1299. // ptr: buffer address
  1300. // r: buffer size
  1301. Dprintf("xxx vstrack_plugin: effGetChunk index=%d ptr=%p\n", index, ptr);
  1302. // // if(0 == index)
  1303. // // {
  1304. // // r = wrapper->getBankChunk((uint8_t**)ptr);
  1305. // // }
  1306. // // else
  1307. // // {
  1308. r = wrapper->getProgramChunk((uint8_t**)ptr);
  1309. // // }
  1310. break;
  1311. case effSetChunk:
  1312. // Restore bank (index=0) or program (index=1) state
  1313. // value: buffer size
  1314. // ptr: buffer address
  1315. // r: 1
  1316. Dprintf("xxx vstrack_plugin: effSetChunk index=%d size=%lld ptr=%p\n", index, value, ptr);
  1317. // // if(0 == index)
  1318. // // {
  1319. // // r = wrapper->setBankChunk(size_t(value), (uint8_t*)ptr) ? 1 : 0;
  1320. // // }
  1321. // // else
  1322. // // {
  1323. r = wrapper->setProgramChunk(size_t(value), (uint8_t*)ptr) ? 1 : 0;
  1324. // // }
  1325. break;
  1326. case effShellGetNextPlugin:
  1327. // For shell plugins (e.g. Waves), returns next sub-plugin UID (or 0)
  1328. // (note) plugin uses audioMasterCurrentId while it's being instantiated to query the currently selected sub-plugin
  1329. // if the host returns 0, it will then call effShellGetNextPlugin to enumerate the sub-plugins
  1330. // ptr: effect name string ptr (filled out by the plugin)
  1331. r = 0;
  1332. break;
  1333. case effMainsChanged:
  1334. // value = 0=suspend, 1=resume
  1335. wrapper->setEnableProcessingActive((value > 0) ? true : false);
  1336. r = 1;
  1337. break;
  1338. case effStartProcess:
  1339. wrapper->setEnableProcessingActive(true);
  1340. r = 1;
  1341. break;
  1342. case effStopProcess:
  1343. wrapper->setEnableProcessingActive(false);
  1344. r = 1;
  1345. break;
  1346. case effProcessEvents:
  1347. // ptr: VstEvents*
  1348. {
  1349. VstEvents *events = (VstEvents*)ptr;
  1350. // Dprintf("vstrack_plugin:effProcessEvents: recvd %d events", events->numEvents);
  1351. VstEvent**evAddr = &events->events[0];
  1352. if(events->numEvents > 0)
  1353. {
  1354. wrapper->setGlobals();
  1355. wrapper->mtx_mididev.lock();
  1356. for(uint32_t evIdx = 0u; evIdx < uint32_t(events->numEvents); evIdx++, evAddr++)
  1357. {
  1358. VstEvent *ev = *evAddr;
  1359. if(NULL != ev) // paranoia
  1360. {
  1361. #ifdef DEBUG_PRINT_EVENTS
  1362. Dprintf("vstrack_plugin:effProcessEvents: ev[%u].byteSize = %u\n", evIdx, uint32_t(ev->byteSize)); // sizeof(VstMidiEvent) = 32
  1363. Dprintf("vstrack_plugin:effProcessEvents: ev[%u].deltaFrames = %u\n", evIdx, uint32_t(ev->deltaFrames));
  1364. #endif // DEBUG_PRINT_EVENTS
  1365. switch(ev->type)
  1366. {
  1367. default:
  1368. //case kVstAudioType: // deprecated
  1369. //case kVstVideoType: // deprecated
  1370. //case kVstParameterType: // deprecated
  1371. //case kVstTriggerType: // deprecated
  1372. break;
  1373. case kVstMidiType:
  1374. // (note) ev->data stores the actual payload (up to 16 bytes)
  1375. // (note) e.g. 0x90 0x30 0x7F for a C-4 note-on on channel 1 with velocity 127
  1376. // (note) don't forget to use a mutex (lockAudio(), unlockAudio()) when modifying the audio processor state!
  1377. {
  1378. VstMidiEvent *mev = (VstMidiEvent *)ev;
  1379. #ifdef DEBUG_PRINT_EVENTS
  1380. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteLength = %u\n", evIdx, uint32_t(mev->noteLength)); // #frames
  1381. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteOffset = %u\n", evIdx, uint32_t(mev->noteOffset)); // #frames
  1382. Dprintf("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]));
  1383. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].detune = %d\n", evIdx, mev->detune); // -64..63
  1384. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteOffVelocity = %d\n", evIdx, mev->noteOffVelocity); // 0..127
  1385. #endif // DEBUG_PRINT_EVENTS
  1386. if((VSTPluginWrapper::IDLE_DETECT_MIDI == wrapper->idle_detect_mode) && wrapper->b_idle)
  1387. {
  1388. if(0x90u == (mev->midiData[0] & 0xF0u)) // Note on ?
  1389. {
  1390. wrapper->lockAudio();
  1391. wrapper->b_idle = false;
  1392. wrapper->idle_output_framecount = 0u;
  1393. wrapper->unlockAudio();
  1394. Dprintf_idle("xxx vstrack_plugin: become active after MIDI note on\n");
  1395. }
  1396. }
  1397. vst2_process_midi_input_event(mev->midiData[0],
  1398. mev->midiData[1],
  1399. mev->midiData[2]
  1400. );
  1401. }
  1402. break;
  1403. case kVstSysExType:
  1404. {
  1405. VstMidiSysexEvent *xev = (VstMidiSysexEvent*)ev;
  1406. #ifdef DEBUG_PRINT_EVENTS
  1407. Dprintf("vstrack_plugin:effProcessEvents<syx>: ev[%u].dumpBytes = %u\n", evIdx, uint32_t(xev->dumpBytes)); // size
  1408. Dprintf("vstrack_plugin:effProcessEvents<syx>: ev[%u].sysexDump = %p\n", evIdx, xev->sysexDump); // buffer addr
  1409. #endif // DEBUG_PRINT_EVENTS
  1410. // (note) don't forget to use a mutex (lockAudio(), unlockAudio()) when modifying the audio processor state!
  1411. }
  1412. break;
  1413. }
  1414. } // if ev
  1415. } // loop events
  1416. wrapper->mtx_mididev.unlock();
  1417. } // if events
  1418. }
  1419. break;
  1420. case effGetTailSize: // 52
  1421. break;
  1422. #if 1
  1423. //case effIdle:
  1424. case 53:
  1425. // Periodic idle call (from UI thread), e.g. at 20ms intervals (depending on host)
  1426. // (note) deprecated in vst2.4 (but some plugins still rely on this)
  1427. r = 1;
  1428. break;
  1429. #endif
  1430. case effEditIdle:
  1431. if(0 == wrapper->redraw_ival_ms)
  1432. {
  1433. wrapper->queueRedraw();
  1434. }
  1435. break;
  1436. case effEditGetRect:
  1437. // Query editor window geometry
  1438. // ptr: ERect* (on Windows)
  1439. if(NULL != ptr)
  1440. {
  1441. // ...
  1442. printf("xxx vstrack_plugin: effEditGetRect: (%d; %d; %d; %d)\n",
  1443. wrapper->editor_rect.top,
  1444. wrapper->editor_rect.left,
  1445. wrapper->editor_rect.bottom,
  1446. wrapper->editor_rect.right
  1447. );
  1448. *(void**)ptr = (void*) &wrapper->editor_rect;
  1449. r = 1;
  1450. }
  1451. else
  1452. {
  1453. r = 0;
  1454. }
  1455. break;
  1456. #if 0
  1457. case effEditTop:
  1458. // deprecated in vst2.4
  1459. r = 0;
  1460. break;
  1461. #endif
  1462. case effEditOpen:
  1463. // Show editor window
  1464. // ptr: native window handle (hWnd on Windows)
  1465. wrapper->openEditor(ptr);
  1466. r = 1;
  1467. break;
  1468. case effEditClose:
  1469. // Close editor window
  1470. wrapper->closeEditor();
  1471. r = 1;
  1472. break;
  1473. case effEditKeyDown:
  1474. // [index]: ASCII character [value]: virtual key [opt]: modifiers [return value]: 1 if key used @see AEffEditor::onKeyDown
  1475. // (note) only used for touch input
  1476. // Dprintf("xxx effEditKeyDown: ascii=%d (\'%c\') vkey=0x%08x mod=0x%08x\n", index, index, value, opt);
  1477. if(rack::b_touchkeyboard_enable)
  1478. {
  1479. wrapper->setGlobals();
  1480. {
  1481. uint32_t vkey = 0u;
  1482. switch(uint32_t(value))
  1483. {
  1484. // see aeffectx.h
  1485. case VKEY_BACK: vkey = LGLW_VKEY_BACKSPACE; break;
  1486. case VKEY_TAB: vkey = LGLW_VKEY_TAB; break;
  1487. case VKEY_RETURN: vkey = LGLW_VKEY_RETURN; break;
  1488. case VKEY_ESCAPE: vkey = LGLW_VKEY_ESCAPE; break;
  1489. case VKEY_END: vkey = LGLW_VKEY_END; break;
  1490. case VKEY_HOME: vkey = LGLW_VKEY_HOME; break;
  1491. case VKEY_LEFT: vkey = LGLW_VKEY_LEFT; break;
  1492. case VKEY_UP: vkey = LGLW_VKEY_UP; break;
  1493. case VKEY_RIGHT: vkey = LGLW_VKEY_RIGHT; break;
  1494. case VKEY_DOWN: vkey = LGLW_VKEY_DOWN; break;
  1495. case VKEY_PAGEUP: vkey = LGLW_VKEY_PAGEUP; break;
  1496. case VKEY_PAGEDOWN: vkey = LGLW_VKEY_PAGEDOWN; break;
  1497. case VKEY_ENTER: vkey = LGLW_VKEY_RETURN; break;
  1498. case VKEY_INSERT: vkey = LGLW_VKEY_INSERT; break;
  1499. case VKEY_DELETE: vkey = LGLW_VKEY_DELETE; break;
  1500. break;
  1501. default:
  1502. vkey = (char)index;
  1503. // (note) some(most?) DAWs don't send the correct VKEY_xxx values for all special keys
  1504. switch(vkey)
  1505. {
  1506. case 8: vkey = LGLW_VKEY_BACKSPACE; break;
  1507. // case 13: vkey = LGLW_VKEY_RETURN; break;
  1508. // case 9: vkey = LGLW_VKEY_TAB; break;
  1509. // case 27: vkey = LGLW_VKEY_ESCAPE; break;
  1510. default:
  1511. if(vkey >= 128)
  1512. vkey = 0u;
  1513. break;
  1514. }
  1515. break;
  1516. }
  1517. if(vkey > 0u)
  1518. {
  1519. r = vst2_handle_effeditkeydown(vkey);
  1520. }
  1521. }
  1522. }
  1523. break;
  1524. default:
  1525. // ignoring all other opcodes
  1526. Dprintf("vstrack_plugin:dispatcher: unhandled opCode %d [ignored] \n", opCode);
  1527. break;
  1528. }
  1529. return r;
  1530. }
  1531. /**
  1532. * Set parameter setting
  1533. */
  1534. void VSTPluginSetParameter(VSTPlugin *vstPlugin,
  1535. VstInt32 index,
  1536. float parameter
  1537. ) {
  1538. #ifdef DEBUG_PRINT_PARAMS
  1539. Dprintf("vstrack_plugin: called VSTPluginSetParameter(%d, %f)\n", index, parameter);
  1540. #endif // DEBUG_PRINT_PARAMS
  1541. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1542. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1543. // // wrapper->lockAudio();
  1544. wrapper->setGlobals();
  1545. rack::global_ui->app.mtx_param.lock();
  1546. vst2_queue_param(index, parameter, true/*bNormalized*/);
  1547. rack::global_ui->app.mtx_param.unlock();
  1548. // // wrapper->unlockAudio();
  1549. }
  1550. void vst2_queue_param_sync(int _uniqueParamId, float _value, bool _bNormalized) {
  1551. // Called when parameter is edited numerically via textfield
  1552. printf("xxx vst2_queue_param_sync ENTER: uniqueParamId=%d value=%f bNormalized=%d\n", _uniqueParamId, _value, _bNormalized);
  1553. VSTPluginWrapper *wrapper = rack::global->vst2.wrapper;
  1554. // // wrapper->lockAudio();
  1555. rack::global_ui->app.mtx_param.lock();
  1556. vst2_queue_param(_uniqueParamId, _value, _bNormalized);
  1557. rack::global_ui->app.mtx_param.unlock();
  1558. // // wrapper->unlockAudio();
  1559. printf("xxx vst2_queue_param_sync LEAVE\n");
  1560. }
  1561. /**
  1562. * Query parameter
  1563. */
  1564. float VSTPluginGetParameter(VSTPlugin *vstPlugin,
  1565. VstInt32 index
  1566. ) {
  1567. #ifdef DEBUG_PRINT_PARAMS
  1568. Dprintf("vstrack_plugin: called VSTPluginGetParameter(%d)\n", index);
  1569. #endif // DEBUG_PRINT_PARAMS
  1570. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1571. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1572. wrapper->lockAudio(); // don't query a param while the module is deleted
  1573. wrapper->setGlobals();
  1574. float r = vst2_get_param(index);
  1575. wrapper->unlockAudio();
  1576. return r;
  1577. }
  1578. /**
  1579. * Main constructor for our C++ class
  1580. */
  1581. VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback,
  1582. VstInt32 vendorUniqueID,
  1583. VstInt32 vendorVersion,
  1584. VstInt32 numParams,
  1585. VstInt32 numPrograms,
  1586. VstInt32 numInputs,
  1587. VstInt32 numOutputs
  1588. ) : _vstHostCallback(vstHostCallback)
  1589. {
  1590. instance_count++;
  1591. // Make sure that the memory is properly initialized
  1592. memset(&_vstPlugin, 0, sizeof(_vstPlugin));
  1593. // this field must be set with this constant...
  1594. _vstPlugin.magic = kEffectMagic;
  1595. // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use)
  1596. _vstPlugin.object = this;
  1597. // specifying that we handle both single and NOT double precision (there are other flags see aeffect.h/VstAEffectFlags)
  1598. _vstPlugin.flags =
  1599. #ifndef VST2_EFFECT
  1600. effFlagsIsSynth |
  1601. #endif
  1602. effFlagsCanReplacing |
  1603. // (effFlagsCanDoubleReplacing & 0) |
  1604. effFlagsProgramChunks |
  1605. effFlagsHasEditor ;
  1606. // initializing the plugin with the various values
  1607. _vstPlugin.uniqueID = vendorUniqueID;
  1608. _vstPlugin.version = vendorVersion;
  1609. _vstPlugin.numParams = numParams;
  1610. _vstPlugin.numPrograms = numPrograms;
  1611. _vstPlugin.numInputs = numInputs;
  1612. _vstPlugin.numOutputs = numOutputs;
  1613. // setting the callbacks to the previously defined functions
  1614. _vstPlugin.dispatcher = &VSTPluginDispatcher;
  1615. _vstPlugin.getParameter = &VSTPluginGetParameter;
  1616. _vstPlugin.setParameter = &VSTPluginSetParameter;
  1617. _vstPlugin.processReplacing = &VSTPluginProcessReplacingFloat32;
  1618. _vstPlugin.processDoubleReplacing = NULL;//&VSTPluginProcessReplacingFloat64;
  1619. // report latency
  1620. _vstPlugin.initialDelay = 0;
  1621. oversample.factor = 1.0f;
  1622. oversample.quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
  1623. oversample.realtime_factor = 1.0f;
  1624. oversample.realtime_quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
  1625. oversample.offline_factor = 1.0f;
  1626. oversample.offline_quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
  1627. oversample.srs_in = NULL;
  1628. oversample.srs_out = NULL;
  1629. oversample.num_in = NUM_INPUTS;
  1630. oversample.num_out = NUM_OUTPUTS;
  1631. sample_rate = 44100.0f;
  1632. block_size = 64u;
  1633. b_processing = true;
  1634. b_offline = false;
  1635. b_check_offline = false;
  1636. idle_detect_mode = IDLE_DETECT_NONE;
  1637. idle_detect_mode_fx = IDLE_DETECT_AUDIO;
  1638. idle_detect_mode_instr = IDLE_DETECT_MIDI;
  1639. b_idle = false;
  1640. idle_input_level_threshold = 0.00018f;//0.00007f;
  1641. idle_output_level_threshold = 0.00018f;//0.00003f;
  1642. idle_output_sec_threshold = 120.0f / 1000.0f; // idle after 120ms of silence
  1643. idle_output_framecount = 0u;
  1644. last_program_chunk_str = NULL;
  1645. b_open = false;
  1646. b_editor_open = false;
  1647. editor_rect.left = EDITWIN_X;
  1648. editor_rect.top = EDITWIN_Y;
  1649. editor_rect.right = EDITWIN_X + EDITWIN_W;
  1650. editor_rect.bottom = EDITWIN_Y + EDITWIN_H;
  1651. redraw_ival_ms = 0;
  1652. }
  1653. /**
  1654. * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin
  1655. * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a
  1656. * memory leak which may end up slowing down and/or crashing the host
  1657. */
  1658. VSTPluginWrapper::~VSTPluginWrapper() {
  1659. closeEffect();
  1660. instance_count--;
  1661. }
  1662. void vst2_lock_midi_device() {
  1663. rack::global->vst2.wrapper->mtx_mididev.lock();
  1664. }
  1665. void vst2_unlock_midi_device() {
  1666. rack::global->vst2.wrapper->mtx_mididev.unlock();
  1667. }
  1668. void vst2_handle_ui_param(int uniqueParamId, float normValue) {
  1669. // Called by engineSetParam()
  1670. // printf("xxx vst2_handle_ui_param: uniqueParamId=%d &global=%p global=%p wrapper=%p\n", uniqueParamId, &rack::global, rack::global, rack::global->vst2.wrapper);
  1671. rack::global->vst2.wrapper->handleUIParam(uniqueParamId, normValue);
  1672. }
  1673. void vst2_get_timing_info(int *_retPlaying, float *_retBPM, float *_retSongPosPPQ) {
  1674. // updates the requested fields when query was successful
  1675. rack::global->vst2.wrapper->getTimingInfo(_retPlaying, _retBPM, _retSongPosPPQ);
  1676. }
  1677. void vst2_set_globals(void *_wrapper) {
  1678. VSTPluginWrapper *wrapper = (VSTPluginWrapper *)_wrapper;
  1679. wrapper->setGlobals();
  1680. vst2_set_shared_plugin_tls_globals();
  1681. }
  1682. void vst2_window_size_set(int _width, int _height) {
  1683. rack::global->vst2.wrapper->setWindowSize(_width, _height);
  1684. }
  1685. void vst2_refresh_rate_set(float _hz) {
  1686. rack::global->vst2.wrapper->setRefreshRate(_hz);
  1687. }
  1688. float vst2_refresh_rate_get(void) {
  1689. return rack::global->vst2.wrapper->getRefreshRate();
  1690. }
  1691. extern "C" void lglw_timer_cbk(lglw_t _lglw) {
  1692. VSTPluginWrapper *wrapper = (VSTPluginWrapper*)lglw_userdata_get(_lglw);
  1693. wrapper->queueRedraw();
  1694. }
  1695. extern "C" void lglw_redraw_cbk(lglw_t _lglw) {
  1696. VSTPluginWrapper *wrapper = (VSTPluginWrapper*)lglw_userdata_get(_lglw);
  1697. wrapper->redraw();
  1698. }
  1699. void vst2_oversample_realtime_set(float _factor, int _quality) {
  1700. rack::global->vst2.wrapper->setOversampleRealtime(_factor, _quality);
  1701. }
  1702. void vst2_oversample_realtime_get(float *_factor, int *_quality) {
  1703. *_factor = rack::global->vst2.wrapper->oversample.realtime_factor;
  1704. *_quality = int(rack::global->vst2.wrapper->oversample.realtime_quality);
  1705. }
  1706. void vst2_oversample_offline_set(float _factor, int _quality) {
  1707. rack::global->vst2.wrapper->setOversampleOffline(_factor, _quality);
  1708. }
  1709. void vst2_oversample_offline_get(float *_factor, int *_quality) {
  1710. *_factor = rack::global->vst2.wrapper->oversample.offline_factor;
  1711. *_quality = int(rack::global->vst2.wrapper->oversample.offline_quality);
  1712. }
  1713. void vst2_oversample_offline_check_set(int _bEnable) {
  1714. rack::global->vst2.wrapper->b_check_offline = (0 != _bEnable);
  1715. }
  1716. int32_t vst2_oversample_offline_check_get(void) {
  1717. return int32_t(rack::global->vst2.wrapper->b_check_offline);
  1718. }
  1719. void vst2_oversample_channels_set(int _numIn, int _numOut) {
  1720. rack::global->vst2.wrapper->setOversampleChannels(_numIn, _numOut);
  1721. }
  1722. void vst2_oversample_channels_get(int *_numIn, int *_numOut) {
  1723. *_numIn = int(rack::global->vst2.wrapper->oversample.num_in);
  1724. *_numOut = int(rack::global->vst2.wrapper->oversample.num_out);
  1725. }
  1726. void vst2_idle_detect_mode_fx_set(int _mode) {
  1727. rack::global->vst2.wrapper->setIdleDetectModeFx(uint32_t(_mode));
  1728. }
  1729. int vst2_idle_detect_mode_fx_get(void) {
  1730. return rack::global->vst2.wrapper->idle_detect_mode_fx;
  1731. }
  1732. void vst2_idle_detect_mode_instr_set(int _mode) {
  1733. rack::global->vst2.wrapper->setIdleDetectModeInstr(uint32_t(_mode));
  1734. }
  1735. int vst2_idle_detect_mode_instr_get(void) {
  1736. return rack::global->vst2.wrapper->idle_detect_mode_instr;
  1737. }
  1738. void vst2_idle_detect_mode_set(int _mode) {
  1739. rack::global->vst2.wrapper->setIdleDetectMode(uint32_t(_mode));
  1740. }
  1741. void vst2_idle_detect_mode_get(int *_mode) {
  1742. *_mode = int(rack::global->vst2.wrapper->idle_detect_mode);
  1743. }
  1744. /**
  1745. * Implementation of the main entry point of the plugin
  1746. */
  1747. VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback) {
  1748. Dprintf("vstrack_plugin: called VSTPluginMain... \n");
  1749. #if 0
  1750. if(!vstHostCallback(0, audioMasterVersion, 0, 0, 0, 0))
  1751. {
  1752. return 0; // old version
  1753. }
  1754. #endif
  1755. // simply create our plugin C++ class
  1756. VSTPluginWrapper *plugin =
  1757. new VSTPluginWrapper(vstHostCallback,
  1758. // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm)
  1759. #ifdef VST2_EFFECT
  1760. CCONST('g', 'v', 'g', 'y'),
  1761. #else
  1762. CCONST('v', '5', 'k', 'v'),
  1763. #endif // VST2_EFFECT
  1764. PLUGIN_VERSION, // version
  1765. VST2_MAX_UNIQUE_PARAM_IDS, // num params
  1766. 1, // one program
  1767. NUM_INPUTS,
  1768. NUM_OUTPUTS
  1769. );
  1770. // return the plugin per the contract of the API
  1771. return plugin->getVSTPlugin();
  1772. }
  1773. #endif // USE_VST2