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.

2080 lines
63KB

  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
  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[]);
  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 normValue);
  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);
  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. sF32 idle_input_level_threshold;
  369. sF32 idle_output_level_threshold;
  370. sF32 idle_output_sec_threshold;
  371. sUI idle_output_framecount;
  372. bool b_idle;
  373. ERect editor_rect;
  374. sBool b_editor_open;
  375. char *last_program_chunk_str;
  376. static sSI instance_count;
  377. sSI instance_id;
  378. sF32 tmp_input_buffers[NUM_INPUTS * MAX_BLOCK_SIZE];
  379. sUI redraw_ival_ms; // 0=use DAW timer (effEditIdle)
  380. public:
  381. VSTPluginWrapper(VSTHostCallback vstHostCallback,
  382. VstInt32 vendorUniqueID,
  383. VstInt32 vendorVersion,
  384. VstInt32 numParams,
  385. VstInt32 numPrograms,
  386. VstInt32 numInputs,
  387. VstInt32 numOutputs
  388. );
  389. ~VSTPluginWrapper();
  390. VSTPlugin *getVSTPlugin(void) {
  391. return &_vstPlugin;
  392. }
  393. void setGlobals(void) {
  394. rack::global = &rack_global;
  395. rack::global_ui = &rack_global_ui;
  396. }
  397. sSI openEffect(void) {
  398. Dprintf("xxx vstrack_plugin::openEffect\n");
  399. // (todo) use mutex
  400. instance_id = instance_count;
  401. Dprintf("xxx vstrack_plugin::openEffect: instance_id=%d\n", instance_id);
  402. rack_global.vst2.wrapper = this;
  403. #ifdef USE_CONSOLE
  404. AllocConsole();
  405. freopen("CON", "w", stdout);
  406. freopen("CON", "w", stderr);
  407. freopen("CON", "r", stdin); // Note: "r", not "w".
  408. #endif // USE_CONSOLE
  409. setGlobals();
  410. rack_global.init();
  411. rack_global_ui.init();
  412. rack::global->vst2.last_seen_instance_count = instance_count;
  413. char oldCWD[1024];
  414. char dllnameraw[1024];
  415. ::GetCurrentDirectory(1024, (LPSTR) oldCWD);
  416. // ::GetModuleFileNameA(NULL, dllnameraw, 1024); // returns executable name (not the dll pathname)
  417. GetModuleFileNameA((HINSTANCE)&__ImageBase, dllnameraw, 1024);
  418. dllname.visit(dllnameraw);
  419. dllname.getDirName(&cwd);
  420. rack::global->vst2.program_dir = (const char*)cwd.chars;
  421. Dprintf("xxx vstrack_plugin::openEffect: cd to \"%s\"\n", (const char*)cwd.chars);
  422. // // ::SetCurrentDirectory("f:/vst_64bit/vstrack_plugin");
  423. ::SetCurrentDirectory((const char*)cwd.chars);
  424. Dprintf("xxx vstrack_plugin::openEffect: cwd change done\n");
  425. // cwd.replace('\\', '/');
  426. int argc = 1;
  427. char *argv[1];
  428. //argv[0] = (char*)cwd.chars;
  429. argv[0] = (char*)dllnameraw;
  430. Dprintf("xxx vstrack_plugin::openEffect: dllname=\"%s\"\n", argv[0]);
  431. (void)vst2_init(argc, argv);
  432. Dprintf("xxx vstrack_plugin::openEffect: vst2_init() done\n");
  433. vst2_set_shared_plugin_tls_globals();
  434. Dprintf("xxx vstrack_plugin::openEffect: restore cwd=\"%s\"\n", oldCWD);
  435. ::SetCurrentDirectory(oldCWD);
  436. setSampleRate(sample_rate);
  437. b_open = true;
  438. b_editor_open = false;
  439. Dprintf("xxx vstrack_plugin::openEffect: LEAVE\n");
  440. return 1;
  441. }
  442. void setWindowSize(int _width, int _height) {
  443. if(_width < 640)
  444. _width = 640;
  445. if(_height < 480)
  446. _height = 480;
  447. editor_rect.right = EDITWIN_X + _width;
  448. editor_rect.bottom = EDITWIN_Y + _height;
  449. }
  450. void setRefreshRate(float _hz) {
  451. if(_hz < 15.0f)
  452. redraw_ival_ms = 0u;
  453. else
  454. redraw_ival_ms = sUI(1000.0f / _hz);
  455. }
  456. void destroyResamplerStates(void) {
  457. if(NULL != oversample.srs_in)
  458. {
  459. speex_resampler_destroy(oversample.srs_in);
  460. oversample.srs_in = NULL;
  461. }
  462. if(NULL != oversample.srs_out)
  463. {
  464. speex_resampler_destroy(oversample.srs_out);
  465. oversample.srs_out = NULL;
  466. }
  467. }
  468. void openEditor(void *_hwnd) {
  469. Dprintf("xxx vstrack_plugin: openEditor() parentHWND=%p\n", _hwnd);
  470. setGlobals();
  471. (void)lglw_window_open(rack_global_ui.window.lglw,
  472. _hwnd,
  473. 0/*x*/, 0/*y*/,
  474. (editor_rect.right - editor_rect.left),
  475. (editor_rect.bottom - editor_rect.top)
  476. );
  477. if(0u != redraw_ival_ms)
  478. {
  479. lglw_timer_start(rack_global_ui.window.lglw, redraw_ival_ms);
  480. }
  481. b_editor_open = true;
  482. }
  483. void closeEditor(void) {
  484. Dprintf("xxx vstrack_plugin: closeEditor() b_editor_open=%d\n", b_editor_open);
  485. if(b_editor_open)
  486. {
  487. setGlobals();
  488. lglw_timer_stop(rack_global_ui.window.lglw);
  489. lglw_window_close(rack_global_ui.window.lglw);
  490. b_editor_open = false;
  491. }
  492. }
  493. void closeEffect(void) {
  494. closeEditor();
  495. // (todo) use mutex
  496. Dprintf("xxx vstrack_plugin::closeEffect: last_program_chunk_str=%p\n", last_program_chunk_str);
  497. if(NULL != last_program_chunk_str)
  498. {
  499. ::free(last_program_chunk_str);
  500. last_program_chunk_str = NULL;
  501. }
  502. Dprintf("xxx vstrack_plugin::closeEffect: b_open=%d\n", b_open);
  503. if(b_open)
  504. {
  505. b_open = false;
  506. setGlobals();
  507. vst2_set_shared_plugin_tls_globals();
  508. rack::global->vst2.last_seen_instance_count = instance_count;
  509. Dprintf("xxx vstrack_plugin: call vst2_exit()\n");
  510. vst2_exit();
  511. Dprintf("xxx vstrack_plugin: vst2_exit() done\n");
  512. destroyResamplerStates();
  513. Dprintf("xxx vstrack_plugin: destroyResamplerStates() done\n");
  514. #ifdef USE_CONSOLE
  515. // FreeConsole();
  516. #endif // USE_CONSOLE
  517. }
  518. }
  519. void lockAudio(void) {
  520. mtx_audio.lock();
  521. }
  522. void unlockAudio(void) {
  523. mtx_audio.unlock();
  524. }
  525. VstInt32 getNumInputs(void) const {
  526. return _vstPlugin.numInputs;
  527. }
  528. VstInt32 getNumOutputs(void) const {
  529. return _vstPlugin.numOutputs;
  530. }
  531. void setOversample(float _factor, int _quality, bool _bLock = true) {
  532. oversample.factor = _factor;
  533. oversample.quality = _quality;
  534. setSampleRate(sample_rate, _bLock);
  535. }
  536. void setOversampleRealtime(float _factor, int _quality) {
  537. if(_factor < 0.0f)
  538. _factor = oversample.realtime_factor; // keep
  539. if(_quality < 0)
  540. _quality = oversample.realtime_quality; // keep
  541. if(_factor < 0.001f)
  542. _factor = 1.0f;
  543. else if(_factor > float(MAX_OVERSAMPLE_FACTOR))
  544. _factor = float(MAX_OVERSAMPLE_FACTOR);
  545. if(_quality < SPEEX_RESAMPLER_QUALITY_MIN/*0*/)
  546. _quality = SPEEX_RESAMPLER_QUALITY_MIN;
  547. else if(_quality > SPEEX_RESAMPLER_QUALITY_MAX/*10*/)
  548. _quality = SPEEX_RESAMPLER_QUALITY_MAX;
  549. oversample.realtime_factor = _factor;
  550. oversample.realtime_quality = _quality;
  551. if(!b_offline)
  552. {
  553. setOversample(oversample.realtime_factor, oversample.realtime_quality);
  554. }
  555. }
  556. void setOversampleOffline(float _factor, int _quality) {
  557. if(_factor < 0.0f)
  558. _factor = oversample.offline_factor; // keep
  559. if(_quality < 0)
  560. _quality = oversample.offline_quality; // keep
  561. if(_factor < 0.001f)
  562. _factor = 1.0f;
  563. else if(_factor > float(MAX_OVERSAMPLE_FACTOR))
  564. _factor = float(MAX_OVERSAMPLE_FACTOR);
  565. if(_quality < SPEEX_RESAMPLER_QUALITY_MIN/*0*/)
  566. _quality = SPEEX_RESAMPLER_QUALITY_MIN;
  567. else if(_quality > SPEEX_RESAMPLER_QUALITY_MAX/*10*/)
  568. _quality = SPEEX_RESAMPLER_QUALITY_MAX;
  569. oversample.offline_factor = _factor;
  570. oversample.offline_quality = _quality;
  571. if(b_offline)
  572. {
  573. setOversample(oversample.offline_factor, oversample.offline_quality);
  574. }
  575. }
  576. void setOversampleChannels(int _numIn, int _numOut) {
  577. if(_numIn < 0)
  578. _numIn = int(oversample.num_in); // keep
  579. if(_numOut < 0)
  580. _numOut = int(oversample.num_out); // keep
  581. if(_numIn < 0)
  582. _numIn = 0;
  583. else if(_numIn > NUM_INPUTS)
  584. _numIn = NUM_INPUTS;
  585. if(_numOut < 1)
  586. _numOut = 1;
  587. else if(_numOut > NUM_OUTPUTS)
  588. _numOut = NUM_OUTPUTS;
  589. lockAudio();
  590. oversample.num_in = sUI(_numIn);
  591. oversample.num_out = sUI(_numOut);
  592. unlockAudio();
  593. }
  594. bool setSampleRate(float _rate, bool _bLock = true) {
  595. bool r = false;
  596. if((_rate >= float(MIN_SAMPLE_RATE)) && (_rate <= float(MAX_SAMPLE_RATE)))
  597. {
  598. if(_bLock)
  599. {
  600. setGlobals();
  601. lockAudio();
  602. }
  603. sample_rate = _rate;
  604. vst2_set_samplerate(sample_rate * oversample.factor); // see engine.cpp
  605. destroyResamplerStates();
  606. // Lazy-alloc resampler state
  607. if(!Dfltequal(oversample.factor, 1.0f))
  608. {
  609. int err;
  610. oversample.srs_in = speex_resampler_init(NUM_INPUTS,
  611. sUI(sample_rate), // in rate
  612. sUI(sample_rate * oversample.factor), // out rate
  613. oversample.quality,
  614. &err
  615. );
  616. oversample.srs_out = speex_resampler_init(NUM_OUTPUTS,
  617. sUI(sample_rate * oversample.factor), // in rate
  618. sUI(sample_rate), // out rate
  619. oversample.quality,
  620. &err
  621. );
  622. Dprintf("xxx vstrack_plugin: initialize speex resampler (rate=%f factor=%f quality=%d)\n", sample_rate, oversample.factor, oversample.quality);
  623. }
  624. if(_bLock)
  625. {
  626. unlockAudio();
  627. }
  628. r = true;
  629. }
  630. return r;
  631. }
  632. bool setBlockSize(uint32_t _blockSize) {
  633. bool r = false;
  634. if((_blockSize >= MIN_BLOCK_SIZE) && (_blockSize <= MAX_BLOCK_SIZE))
  635. {
  636. lockAudio();
  637. block_size = _blockSize;
  638. unlockAudio();
  639. r = true;
  640. }
  641. return r;
  642. }
  643. void setEnableProcessingActive(bool _bEnable) {
  644. lockAudio();
  645. b_processing = _bEnable;
  646. unlockAudio();
  647. }
  648. void checkOffline(void) {
  649. // Called by VSTPluginProcessReplacingFloat32()
  650. if(b_check_offline)
  651. {
  652. if(NULL != _vstHostCallback)
  653. {
  654. int level = (int)_vstHostCallback(&_vstPlugin, audioMasterGetCurrentProcessLevel, 0, 0/*value*/, NULL/*ptr*/, 0.0f/*opt*/);
  655. // (note) Reason sets process level to kVstProcessLevelUser during "bounce in place"
  656. bool bOffline = (kVstProcessLevelOffline == level) || (kVstProcessLevelUser == level);
  657. #if 0
  658. {
  659. static int i = 0;
  660. if(0 == (++i & 127))
  661. {
  662. Dprintf("xxx vstrack_plugin: audioMasterGetCurrentProcessLevel: level=%d\n", level);
  663. }
  664. }
  665. #endif
  666. if(b_offline ^ bOffline)
  667. {
  668. // Offline mode changed, update resampler
  669. b_offline = bOffline;
  670. if(bOffline)
  671. {
  672. Dprintf("xxx vstrack_plugin: enter OFFLINE mode. factor=%f quality=%d\n", oversample.offline_factor, oversample.offline_quality);
  673. setOversample(oversample.offline_factor, oversample.offline_quality, false/*bLock*/);
  674. }
  675. else
  676. {
  677. Dprintf("xxx vstrack_plugin: enter REALTIME mode. factor=%f quality=%d\n", oversample.realtime_factor, oversample.realtime_quality);
  678. setOversample(oversample.realtime_factor, oversample.realtime_quality, false/*bLock*/);
  679. }
  680. Dprintf("xxx vstrack_plugin: mode changed to %d\n", int(bOffline));
  681. }
  682. }
  683. }
  684. }
  685. sUI getBankChunk(uint8_t **_addr) {
  686. return 0;
  687. }
  688. void setIdleDetectMode(uint32_t _mode) {
  689. switch(_mode)
  690. {
  691. default:
  692. case IDLE_DETECT_NONE:
  693. idle_detect_mode = IDLE_DETECT_NONE;
  694. break;
  695. case IDLE_DETECT_MIDI:
  696. idle_detect_mode = IDLE_DETECT_MIDI;
  697. break;
  698. case IDLE_DETECT_AUDIO:
  699. idle_detect_mode = IDLE_DETECT_AUDIO;
  700. break;
  701. }
  702. b_idle = false;
  703. idle_output_framecount = 0u;
  704. }
  705. sUI getProgramChunk(uint8_t**_addr) {
  706. setGlobals();
  707. if(NULL != last_program_chunk_str)
  708. {
  709. ::free(last_program_chunk_str);
  710. }
  711. last_program_chunk_str = rack::global_ui->app.gRackWidget->savePatchToString();
  712. if(NULL != last_program_chunk_str)
  713. {
  714. *_addr = (uint8_t*)last_program_chunk_str;
  715. return (sUI)strlen(last_program_chunk_str) + 1/*ASCIIZ*/;
  716. }
  717. return 0;
  718. }
  719. bool setBankChunk(size_t _size, uint8_t *_addr) {
  720. bool r = false;
  721. return r;
  722. }
  723. bool setProgramChunk(size_t _size, uint8_t *_addr) {
  724. setGlobals();
  725. lockAudio();
  726. #if 0
  727. Dprintf("xxx vstrack_plugin:setProgramChunk: size=%u\n", _size);
  728. #endif
  729. lglw_glcontext_push(rack::global_ui->window.lglw);
  730. bool r = rack::global_ui->app.gRackWidget->loadPatchFromString((const char*)_addr);
  731. rack::global_ui->ui.gScene->step(); // w/o this the patch is bypassed
  732. lglw_glcontext_pop(rack::global_ui->window.lglw);
  733. Dprintf("xxx vstrack_plugin:setProgramChunk: r=%d\n", r);
  734. unlockAudio();
  735. return r;
  736. }
  737. #ifdef HAVE_WINDOWS
  738. void sleepMillisecs(uint32_t _num) {
  739. ::Sleep((DWORD)_num);
  740. }
  741. #elif defined(HAVE_UNIX)
  742. void sleepMillisecs(uint32_t _num) {
  743. ::usleep(1000u * _num);
  744. }
  745. #endif
  746. const volatile float *getNextInputChannelChunk(void) {
  747. volatile float *r = NULL;
  748. return r;
  749. }
  750. volatile float *lockNextOutputChannelChunk(void) {
  751. volatile float *r = NULL;
  752. return r;
  753. }
  754. sBool getParameterProperties(sUI _index, struct VstParameterProperties *_ret) {
  755. sBool r = 0;
  756. ::memset((void*)_ret, 0, sizeof(struct VstParameterProperties));
  757. if(_index < VST2_MAX_UNIQUE_PARAM_IDS)
  758. {
  759. _ret->stepFloat = 0.001f;
  760. _ret->smallStepFloat = 0.001f;
  761. _ret->largeStepFloat = 0.01f;
  762. _ret->flags = 0;
  763. _ret->displayIndex = VstInt16(_index);
  764. _ret->category = 0; // 0=no category
  765. _ret->numParametersInCategory = 0;
  766. vst2_get_param_name(_index, _ret->label, kVstMaxLabelLen);
  767. vst2_get_param_name(_index, _ret->shortLabel, kVstMaxShortLabelLen);
  768. r = 1;
  769. }
  770. return r;
  771. }
  772. void handleUIParam(int uniqueParamId, float normValue) {
  773. if(NULL != _vstHostCallback)
  774. (void)_vstHostCallback(&_vstPlugin, audioMasterAutomate, uniqueParamId, 0/*value*/, NULL/*ptr*/, normValue/*opt*/);
  775. }
  776. void getTimingInfo(int *_retPlaying, float *_retBPM, float *_retSongPosPPQ) {
  777. *_retPlaying = 0;
  778. if(NULL != _vstHostCallback)
  779. {
  780. VstIntPtr result = _vstHostCallback(&_vstPlugin, audioMasterGetTime, 0, 0/*value*/, NULL/*ptr*/, 0.0f/*opt*/);
  781. if(NULL != result)
  782. {
  783. const struct VstTimeInfo *timeInfo = (const struct VstTimeInfo *)result;
  784. *_retPlaying = (0 != (timeInfo->flags & kVstTransportPlaying));
  785. if(0 != (timeInfo->flags & kVstTempoValid))
  786. {
  787. *_retBPM = float(timeInfo->tempo);
  788. }
  789. if(0 != (timeInfo->flags & kVstPpqPosValid))
  790. {
  791. *_retSongPosPPQ = (float)timeInfo->ppqPos;
  792. }
  793. }
  794. }
  795. }
  796. void queueRedraw(void) {
  797. setGlobals();
  798. if(lglw_window_is_visible(rack::global_ui->window.lglw))
  799. {
  800. lglw_redraw(rack::global_ui->window.lglw);
  801. }
  802. }
  803. void redraw(void) {
  804. setGlobals();
  805. if(lglw_window_is_visible(rack::global_ui->window.lglw))
  806. {
  807. vst2_set_shared_plugin_tls_globals();
  808. // Save DAW GL context and bind our own
  809. lglw_glcontext_push(rack::global_ui->window.lglw);
  810. rack::vst2_editor_redraw();
  811. // Restore the DAW's GL context
  812. lglw_glcontext_pop(rack::global_ui->window.lglw);
  813. }
  814. }
  815. private:
  816. // the host callback (a function pointer)
  817. VSTHostCallback _vstHostCallback;
  818. // the actual structure required by the host
  819. VSTPlugin _vstPlugin;
  820. };
  821. sSI VSTPluginWrapper::instance_count = 0;
  822. /*******************************************
  823. * Callbacks: Host -> Plugin
  824. *
  825. * Defined here because they are used in the rest of the code later
  826. */
  827. /**
  828. * This is the callback that will be called to process the samples in the case of single precision. This is where the
  829. * meat of the logic happens!
  830. *
  831. * @param vstPlugin the object returned by VSTPluginMain
  832. * @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]
  833. * @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]
  834. * @param sampleFrames the number of samples (second dimension in both arrays)
  835. */
  836. void VSTPluginProcessReplacingFloat32(VSTPlugin *vstPlugin,
  837. float **_inputs,
  838. float **_outputs,
  839. VstInt32 sampleFrames
  840. ) {
  841. if(sUI(sampleFrames) > VSTPluginWrapper::MAX_BLOCK_SIZE)
  842. return; // should not be reachable
  843. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  844. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  845. // Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: ENTER\n");
  846. wrapper->lockAudio();
  847. wrapper->setGlobals();
  848. vst2_set_shared_plugin_tls_globals();
  849. if(wrapper->b_check_offline)
  850. {
  851. // Check if offline rendering state changed and update resampler when necessary
  852. wrapper->checkOffline();
  853. }
  854. // // rack::global->engine.vipMutex.lock();
  855. rack::global->engine.mutex.lock();
  856. rack::global->vst2.last_seen_num_frames = sUI(sampleFrames);
  857. vst2_handle_queued_params();
  858. //Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: lockAudio done\n");
  859. //Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: wrapper=%p\n", wrapper);
  860. #ifdef HAVE_WINDOWS
  861. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
  862. #endif // HAVE_WINDOWS
  863. sUI chIdx;
  864. if(wrapper->b_idle)
  865. {
  866. switch(wrapper->idle_detect_mode)
  867. {
  868. default:
  869. case VSTPluginWrapper::IDLE_DETECT_NONE:
  870. // should not be reachable
  871. wrapper->b_idle = false;
  872. wrapper->idle_output_framecount = 0u;
  873. break;
  874. case VSTPluginWrapper::IDLE_DETECT_MIDI:
  875. break;
  876. case VSTPluginWrapper::IDLE_DETECT_AUDIO:
  877. {
  878. wrapper->b_idle = true;
  879. for(chIdx = 0u; chIdx < NUM_INPUTS; chIdx++)
  880. {
  881. if(chIdx < wrapper->oversample.num_in)
  882. {
  883. cmemptr_t s;
  884. s.f32 = _inputs[chIdx];
  885. sF32 sum = 0.0f;
  886. for(sUI i = 0u; i < sUI(sampleFrames); i++)
  887. {
  888. mem_t m;
  889. m.u32 = s.u32[i] & ~0x80000000u; // abs
  890. sum += m.f32;
  891. // sum += (s.f32[i] * s.f32[i]); // RMS
  892. }
  893. sum = (sum / float(sampleFrames));
  894. // sum = sqrtf(sum / float(sampleFrames)); // RMS
  895. if(sum >= wrapper->idle_input_level_threshold)
  896. {
  897. wrapper->b_idle = false;
  898. Dprintf_idle("xxx vstrack_plugin: become active after input (sum=%f, threshold=%f)\n", sum, wrapper->idle_input_level_threshold);
  899. wrapper->idle_output_framecount = 0u;
  900. break;
  901. }
  902. }
  903. }
  904. }
  905. break;
  906. } // switch idle_detect_mode
  907. } // if idle
  908. if(!wrapper->b_idle)
  909. {
  910. if( !Dfltequal(wrapper->oversample.factor, 1.0f) &&
  911. (NULL != wrapper->oversample.srs_in) &&
  912. (NULL != wrapper->oversample.srs_out)
  913. )
  914. {
  915. sF32 *inputs[NUM_INPUTS];
  916. sF32 *outputs[NUM_INPUTS];
  917. sUI hostNumFrames = sampleFrames;
  918. sUI overNumFrames = sUI((sampleFrames * wrapper->oversample.factor) + 0.5f);
  919. // Up-sample inputs
  920. {
  921. sUI inNumFrames = hostNumFrames;
  922. sUI outNumFrames = overNumFrames;
  923. sF32 *d = wrapper->oversample.in_buffers;
  924. for(chIdx = 0u; chIdx < NUM_INPUTS; chIdx++)
  925. {
  926. if(chIdx < wrapper->oversample.num_in)
  927. {
  928. sF32 *s = _inputs[chIdx];
  929. int err = speex_resampler_process_float(wrapper->oversample.srs_in,
  930. chIdx,
  931. s,
  932. &inNumFrames,
  933. d,
  934. &outNumFrames
  935. );
  936. }
  937. else
  938. {
  939. // Skip channel
  940. ::memset(d, 0, sizeof(sF32) * outNumFrames);
  941. }
  942. inputs[chIdx] = d;
  943. // Next input channel
  944. d += outNumFrames;
  945. }
  946. }
  947. // Clear output buffers
  948. // (note) AudioInterface instances accumulate samples in the output buffer
  949. {
  950. sF32 *d = wrapper->oversample.out_buffers;
  951. ::memset((void*)d, 0, (sizeof(sF32) * wrapper->oversample.num_out * overNumFrames));
  952. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  953. {
  954. outputs[chIdx] = d;
  955. d += overNumFrames;
  956. }
  957. }
  958. // Process rack modules
  959. if(wrapper->b_processing)
  960. {
  961. vst2_engine_process(inputs, outputs, overNumFrames);
  962. }
  963. // Down-sample outputs
  964. {
  965. sF32 *s = wrapper->oversample.out_buffers;
  966. sUI inNumFrames = overNumFrames;
  967. sUI outNumFrames = hostNumFrames;
  968. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  969. {
  970. sF32 *d = _outputs[chIdx];
  971. if(chIdx < wrapper->oversample.num_out)
  972. {
  973. int err = speex_resampler_process_float(wrapper->oversample.srs_out,
  974. chIdx,
  975. s,
  976. &inNumFrames,
  977. d,
  978. &outNumFrames
  979. );
  980. // Next output channel
  981. s += inNumFrames;
  982. }
  983. else
  984. {
  985. // Skip output
  986. ::memset((void*)d, 0, sizeof(sF32) * outNumFrames);
  987. }
  988. }
  989. }
  990. }
  991. else
  992. {
  993. // No oversampling
  994. // (note) Cubase (tested with 9.5.30) uses the same buffer(s) for both input&output
  995. // => back up the inputs before clearing the outputs
  996. sF32 *inputs[NUM_INPUTS];
  997. sUI k = 0u;
  998. for(chIdx = 0u; chIdx < NUM_INPUTS; chIdx++)
  999. {
  1000. inputs[chIdx] = &wrapper->tmp_input_buffers[k];
  1001. ::memcpy((void*)inputs[chIdx], _inputs[chIdx], sizeof(sF32)*sampleFrames);
  1002. k += sampleFrames;
  1003. }
  1004. // Clear output buffers
  1005. // (note) AudioInterface instances accumulate samples in the output buffer
  1006. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  1007. {
  1008. ::memset((void*)_outputs[chIdx], 0, sizeof(sF32)*sampleFrames);
  1009. }
  1010. if(wrapper->b_processing)
  1011. {
  1012. vst2_engine_process(inputs, _outputs, sampleFrames);
  1013. }
  1014. }
  1015. if(VSTPluginWrapper::IDLE_DETECT_NONE != wrapper->idle_detect_mode)
  1016. {
  1017. bool bSilence = true;
  1018. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  1019. {
  1020. if(chIdx < wrapper->oversample.num_out)
  1021. {
  1022. cmemptr_t d;
  1023. d.f32 = _outputs[chIdx];
  1024. sF32 sum = 0.0f;
  1025. for(sUI i = 0u; i < sUI(sampleFrames); i++)
  1026. {
  1027. mem_t m;
  1028. m.u32 = d.u32[i] & ~0x80000000u; // abs
  1029. sum += m.f32;
  1030. // sum += d.f32[i] * d.f32[i]; // RMS
  1031. }
  1032. sum = (sum / float(sampleFrames));
  1033. // sum = sqrtf(sum / float(sampleFrames)); // RMS
  1034. {
  1035. static int x = 0;
  1036. if(0 == (++x & 127))
  1037. Dprintf_idle("xxx vstrack_plugin: output avg is %f\n", sum);
  1038. }
  1039. if(sum >= wrapper->idle_output_level_threshold)
  1040. {
  1041. bSilence = false;
  1042. break;
  1043. }
  1044. }
  1045. }
  1046. if(bSilence)
  1047. {
  1048. wrapper->idle_output_framecount += sampleFrames;
  1049. if(wrapper->idle_output_framecount >= sUI(wrapper->idle_output_sec_threshold * wrapper->sample_rate))
  1050. {
  1051. // Frame threshold exceeded, become idle
  1052. wrapper->b_idle = true;
  1053. Dprintf_idle("xxx vstrack_plugin: now idle\n");
  1054. }
  1055. }
  1056. else
  1057. {
  1058. wrapper->idle_output_framecount = 0u;
  1059. }
  1060. }
  1061. } // if !wrapper->b_idle
  1062. else
  1063. {
  1064. // Idle, clear output buffers
  1065. for(chIdx = 0u; chIdx < NUM_OUTPUTS; chIdx++)
  1066. {
  1067. ::memset((void*)_outputs[chIdx], 0, sizeof(sF32)*sampleFrames);
  1068. }
  1069. }
  1070. // // rack::global->engine.vipMutex.unlock();
  1071. rack::global->engine.mutex.unlock();
  1072. wrapper->unlockAudio();
  1073. //Dprintf("xxx vstrack_plugin: VSTPluginProcessReplacingFloat32: LEAVE\n");
  1074. }
  1075. /**
  1076. * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the
  1077. * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...)
  1078. *
  1079. * @param vstPlugin the object returned by VSTPluginMain
  1080. * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand
  1081. * total of 79 of them! Only a few of them are implemented in this small plugin.
  1082. * @param index depend on the opcode
  1083. * @param value depend on the opcode
  1084. * @param ptr depend on the opcode
  1085. * @param opt depend on the opcode
  1086. * @return depend on the opcode (0 is ok when you don't implement an opcode...)
  1087. */
  1088. VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin,
  1089. VstInt32 opCode,
  1090. VstInt32 index,
  1091. VstIntPtr value,
  1092. void *ptr,
  1093. float opt
  1094. ) {
  1095. // Dprintf("vstrack_plugin: called VSTPluginDispatcher(%d)\n", opCode);
  1096. VstIntPtr r = 0;
  1097. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1098. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1099. // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them
  1100. switch(opCode)
  1101. {
  1102. case effGetPlugCategory:
  1103. // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed
  1104. // to generating sound)
  1105. #ifdef VST2_EFFECT
  1106. return kPlugCategEffect;
  1107. #else
  1108. return kPlugCategSynth;
  1109. #endif // VST2_EFFECT
  1110. case effOpen:
  1111. // called by the host after it has obtained the effect instance (but _not_ during plugin scans)
  1112. // (note) any heavy-lifting init code should go here
  1113. Dprintf("vstrack_plugin<dispatcher>: effOpen\n");
  1114. r = wrapper->openEffect();
  1115. break;
  1116. case effClose:
  1117. // called by the host when the plugin was called... time to reclaim memory!
  1118. wrapper->closeEffect();
  1119. // (note) hosts usually call effStopProcess before effClose
  1120. delete wrapper;
  1121. break;
  1122. case effSetProgram:
  1123. r = 1;
  1124. break;
  1125. case effGetProgram:
  1126. r = 0;
  1127. break;
  1128. case effGetVendorString:
  1129. // request for the vendor string (usually used in the UI for plugin grouping)
  1130. ::strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen);
  1131. r = 1;
  1132. break;
  1133. case effGetVendorVersion:
  1134. // request for the version
  1135. return PLUGIN_VERSION;
  1136. case effGetEffectName:
  1137. #ifdef VST2_EFFECT
  1138. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1", kVstMaxEffectNameLen);
  1139. #else
  1140. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 I", kVstMaxEffectNameLen);
  1141. #endif // VST2_EFFECT
  1142. r = 1;
  1143. break;
  1144. case effGetProductString:
  1145. #ifdef VST2_EFFECT
  1146. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 VST2 Plugin v0.4", kVstMaxProductStrLen);
  1147. #else
  1148. ::strncpy((char*)ptr, "VeeSeeVST Rack 0.6.1 I VST2 Plugin v0.4", kVstMaxProductStrLen);
  1149. #endif // VST2_EFFECT
  1150. r = 1;
  1151. break;
  1152. case effGetNumMidiInputChannels:
  1153. r = 16;
  1154. break;
  1155. case effGetNumMidiOutputChannels:
  1156. r = 0;
  1157. break;
  1158. case effGetInputProperties:
  1159. {
  1160. VstPinProperties *pin = (VstPinProperties*)ptr;
  1161. ::snprintf(pin->label, kVstMaxLabelLen, "Input #%d", index);
  1162. pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
  1163. pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
  1164. ::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "in%d", index);
  1165. memset((void*)pin->future, 0, 48);
  1166. r = 1;
  1167. }
  1168. break;
  1169. case effGetOutputProperties:
  1170. {
  1171. VstPinProperties *pin = (VstPinProperties*)ptr;
  1172. ::snprintf(pin->label, kVstMaxLabelLen, "Output #%d", index);
  1173. pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
  1174. pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
  1175. ::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "out%d", index);
  1176. memset((void*)pin->future, 0, 48);
  1177. r = 1;
  1178. }
  1179. break;
  1180. case effSetSampleRate:
  1181. r = wrapper->setSampleRate(opt) ? 1 : 0;
  1182. break;
  1183. case effSetBlockSize:
  1184. r = wrapper->setBlockSize(uint32_t(value)) ? 1 : 0;
  1185. break;
  1186. case effCanDo:
  1187. // ptr:
  1188. // "sendVstEvents"
  1189. // "sendVstMidiEvent"
  1190. // "sendVstTimeInfo"
  1191. // "receiveVstEvents"
  1192. // "receiveVstMidiEvent"
  1193. // "receiveVstTimeInfo"
  1194. // "offline"
  1195. // "plugAsChannelInsert"
  1196. // "plugAsSend"
  1197. // "mixDryWet"
  1198. // "noRealTime"
  1199. // "multipass"
  1200. // "metapass"
  1201. // "1in1out"
  1202. // "1in2out"
  1203. // "2in1out"
  1204. // "2in2out"
  1205. // "2in4out"
  1206. // "4in2out"
  1207. // "4in4out"
  1208. // "4in8out"
  1209. // "8in4out"
  1210. // "8in8out"
  1211. // "midiProgramNames"
  1212. // "conformsToWindowRules"
  1213. if(!strcmp((char*)ptr, "receiveVstEvents"))
  1214. r = 1;
  1215. else if(!strcmp((char*)ptr, "receiveVstMidiEvent")) // (note) required by Jeskola Buzz
  1216. r = 1;
  1217. else if(!strcmp((char*)ptr, "noRealTime"))
  1218. r = 1;
  1219. else
  1220. r = 0;
  1221. break;
  1222. case effGetProgramName:
  1223. ::snprintf((char*)ptr, kVstMaxProgNameLen, "default");
  1224. r = 1;
  1225. break;
  1226. case effSetProgramName:
  1227. r = 1;
  1228. break;
  1229. case effGetProgramNameIndexed:
  1230. ::sprintf((char*)ptr, "default");
  1231. r = 1;
  1232. break;
  1233. case effGetParamName:
  1234. // kVstMaxParamStrLen(8), much longer in other plugins
  1235. // printf("xxx vstrack_plugin: effGetParamName: ptr=%p\n", ptr);
  1236. wrapper->setGlobals();
  1237. vst2_get_param_name(index, (char*)ptr, kVstMaxParamStrLen);
  1238. r = 1;
  1239. break;
  1240. case effCanBeAutomated:
  1241. // fix Propellerhead Reason VST parameter support
  1242. r = 1;
  1243. break;
  1244. case effGetParamLabel:
  1245. // e.g. "dB"
  1246. break;
  1247. case effGetParamDisplay:
  1248. // e.g. "-20"
  1249. break;
  1250. #if 0
  1251. case effGetParameterProperties:
  1252. // [index]: parameter index [ptr]: #VstParameterProperties* [return value]: 1 if supported
  1253. wrapper->setGlobals();
  1254. r = wrapper->getParameterProperties(sUI(index), (struct VstParameterProperties*)ptr);
  1255. break;
  1256. #endif
  1257. case effGetChunk:
  1258. // Query bank (index=0) or program (index=1) state
  1259. // value: 0
  1260. // ptr: buffer address
  1261. // r: buffer size
  1262. Dprintf("xxx vstrack_plugin: effGetChunk index=%d ptr=%p\n", index, ptr);
  1263. // // if(0 == index)
  1264. // // {
  1265. // // r = wrapper->getBankChunk((uint8_t**)ptr);
  1266. // // }
  1267. // // else
  1268. // // {
  1269. r = wrapper->getProgramChunk((uint8_t**)ptr);
  1270. // // }
  1271. break;
  1272. case effSetChunk:
  1273. // Restore bank (index=0) or program (index=1) state
  1274. // value: buffer size
  1275. // ptr: buffer address
  1276. // r: 1
  1277. Dprintf("xxx vstrack_plugin: effSetChunk index=%d size=%lld ptr=%p\n", index, value, ptr);
  1278. // // if(0 == index)
  1279. // // {
  1280. // // r = wrapper->setBankChunk(size_t(value), (uint8_t*)ptr) ? 1 : 0;
  1281. // // }
  1282. // // else
  1283. // // {
  1284. r = wrapper->setProgramChunk(size_t(value), (uint8_t*)ptr) ? 1 : 0;
  1285. // // }
  1286. break;
  1287. case effShellGetNextPlugin:
  1288. // For shell plugins (e.g. Waves), returns next sub-plugin UID (or 0)
  1289. // (note) plugin uses audioMasterCurrentId while it's being instantiated to query the currently selected sub-plugin
  1290. // if the host returns 0, it will then call effShellGetNextPlugin to enumerate the sub-plugins
  1291. // ptr: effect name string ptr (filled out by the plugin)
  1292. r = 0;
  1293. break;
  1294. case effMainsChanged:
  1295. // value = 0=suspend, 1=resume
  1296. wrapper->setEnableProcessingActive((value > 0) ? true : false);
  1297. r = 1;
  1298. break;
  1299. case effStartProcess:
  1300. wrapper->setEnableProcessingActive(true);
  1301. r = 1;
  1302. break;
  1303. case effStopProcess:
  1304. wrapper->setEnableProcessingActive(false);
  1305. r = 1;
  1306. break;
  1307. case effProcessEvents:
  1308. // ptr: VstEvents*
  1309. {
  1310. VstEvents *events = (VstEvents*)ptr;
  1311. // Dprintf("vstrack_plugin:effProcessEvents: recvd %d events", events->numEvents);
  1312. VstEvent**evAddr = &events->events[0];
  1313. if(events->numEvents > 0)
  1314. {
  1315. wrapper->setGlobals();
  1316. wrapper->mtx_mididev.lock();
  1317. for(uint32_t evIdx = 0u; evIdx < uint32_t(events->numEvents); evIdx++, evAddr++)
  1318. {
  1319. VstEvent *ev = *evAddr;
  1320. if(NULL != ev) // paranoia
  1321. {
  1322. #ifdef DEBUG_PRINT_EVENTS
  1323. Dprintf("vstrack_plugin:effProcessEvents: ev[%u].byteSize = %u\n", evIdx, uint32_t(ev->byteSize)); // sizeof(VstMidiEvent) = 32
  1324. Dprintf("vstrack_plugin:effProcessEvents: ev[%u].deltaFrames = %u\n", evIdx, uint32_t(ev->deltaFrames));
  1325. #endif // DEBUG_PRINT_EVENTS
  1326. switch(ev->type)
  1327. {
  1328. default:
  1329. //case kVstAudioType: // deprecated
  1330. //case kVstVideoType: // deprecated
  1331. //case kVstParameterType: // deprecated
  1332. //case kVstTriggerType: // deprecated
  1333. break;
  1334. case kVstMidiType:
  1335. // (note) ev->data stores the actual payload (up to 16 bytes)
  1336. // (note) e.g. 0x90 0x30 0x7F for a C-4 note-on on channel 1 with velocity 127
  1337. // (note) don't forget to use a mutex (lockAudio(), unlockAudio()) when modifying the audio processor state!
  1338. {
  1339. VstMidiEvent *mev = (VstMidiEvent *)ev;
  1340. #ifdef DEBUG_PRINT_EVENTS
  1341. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteLength = %u\n", evIdx, uint32_t(mev->noteLength)); // #frames
  1342. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteOffset = %u\n", evIdx, uint32_t(mev->noteOffset)); // #frames
  1343. 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]));
  1344. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].detune = %d\n", evIdx, mev->detune); // -64..63
  1345. Dprintf("vstrack_plugin:effProcessEvents<midi>: ev[%u].noteOffVelocity = %d\n", evIdx, mev->noteOffVelocity); // 0..127
  1346. #endif // DEBUG_PRINT_EVENTS
  1347. if((VSTPluginWrapper::IDLE_DETECT_MIDI == wrapper->idle_detect_mode) && wrapper->b_idle)
  1348. {
  1349. if(0x90u == (mev->midiData[0] & 0xF0u)) // Note on ?
  1350. {
  1351. wrapper->lockAudio();
  1352. wrapper->b_idle = false;
  1353. wrapper->idle_output_framecount = 0u;
  1354. wrapper->unlockAudio();
  1355. Dprintf_idle("xxx vstrack_plugin: become active after MIDI note on\n");
  1356. }
  1357. }
  1358. vst2_process_midi_input_event(mev->midiData[0],
  1359. mev->midiData[1],
  1360. mev->midiData[2]
  1361. );
  1362. }
  1363. break;
  1364. case kVstSysExType:
  1365. {
  1366. VstMidiSysexEvent *xev = (VstMidiSysexEvent*)ev;
  1367. #ifdef DEBUG_PRINT_EVENTS
  1368. Dprintf("vstrack_plugin:effProcessEvents<syx>: ev[%u].dumpBytes = %u\n", evIdx, uint32_t(xev->dumpBytes)); // size
  1369. Dprintf("vstrack_plugin:effProcessEvents<syx>: ev[%u].sysexDump = %p\n", evIdx, xev->sysexDump); // buffer addr
  1370. #endif // DEBUG_PRINT_EVENTS
  1371. // (note) don't forget to use a mutex (lockAudio(), unlockAudio()) when modifying the audio processor state!
  1372. }
  1373. break;
  1374. }
  1375. } // if ev
  1376. } // loop events
  1377. wrapper->mtx_mididev.unlock();
  1378. } // if events
  1379. }
  1380. break;
  1381. case effGetTailSize: // 52
  1382. break;
  1383. #if 1
  1384. //case effIdle:
  1385. case 53:
  1386. // Periodic idle call (from UI thread), e.g. at 20ms intervals (depending on host)
  1387. // (note) deprecated in vst2.4 (but some plugins still rely on this)
  1388. r = 1;
  1389. break;
  1390. #endif
  1391. case effEditIdle:
  1392. if(0 == wrapper->redraw_ival_ms)
  1393. {
  1394. wrapper->queueRedraw();
  1395. }
  1396. break;
  1397. case effEditGetRect:
  1398. // Query editor window geometry
  1399. // ptr: ERect* (on Windows)
  1400. if(NULL != ptr)
  1401. {
  1402. // ...
  1403. *(void**)ptr = (void*) &wrapper->editor_rect;
  1404. r = 1;
  1405. }
  1406. else
  1407. {
  1408. r = 0;
  1409. }
  1410. break;
  1411. #if 0
  1412. case effEditTop:
  1413. // deprecated in vst2.4
  1414. r = 0;
  1415. break;
  1416. #endif
  1417. case effEditOpen:
  1418. // Show editor window
  1419. // ptr: native window handle (hWnd on Windows)
  1420. wrapper->openEditor(ptr);
  1421. r = 1;
  1422. break;
  1423. case effEditClose:
  1424. // Close editor window
  1425. wrapper->closeEditor();
  1426. r = 1;
  1427. break;
  1428. case effEditKeyDown:
  1429. // [index]: ASCII character [value]: virtual key [opt]: modifiers [return value]: 1 if key used @see AEffEditor::onKeyDown
  1430. // (note) only used for touch input
  1431. // Dprintf("xxx effEditKeyDown: ascii=%d (\'%c\') vkey=0x%08x mod=0x%08x\n", index, index, value, opt);
  1432. if(rack::b_touchkeyboard_enable)
  1433. {
  1434. wrapper->setGlobals();
  1435. {
  1436. uint32_t vkey = 0u;
  1437. switch(uint32_t(value))
  1438. {
  1439. // see aeffectx.h
  1440. case VKEY_BACK: vkey = LGLW_VKEY_BACKSPACE; break;
  1441. case VKEY_TAB: vkey = LGLW_VKEY_TAB; break;
  1442. case VKEY_RETURN: vkey = LGLW_VKEY_RETURN; break;
  1443. case VKEY_ESCAPE: vkey = LGLW_VKEY_ESCAPE; break;
  1444. case VKEY_END: vkey = LGLW_VKEY_END; break;
  1445. case VKEY_HOME: vkey = LGLW_VKEY_HOME; break;
  1446. case VKEY_LEFT: vkey = LGLW_VKEY_LEFT; break;
  1447. case VKEY_UP: vkey = LGLW_VKEY_UP; break;
  1448. case VKEY_RIGHT: vkey = LGLW_VKEY_RIGHT; break;
  1449. case VKEY_DOWN: vkey = LGLW_VKEY_DOWN; break;
  1450. case VKEY_PAGEUP: vkey = LGLW_VKEY_PAGEUP; break;
  1451. case VKEY_PAGEDOWN: vkey = LGLW_VKEY_PAGEDOWN; break;
  1452. case VKEY_ENTER: vkey = LGLW_VKEY_RETURN; break;
  1453. case VKEY_INSERT: vkey = LGLW_VKEY_INSERT; break;
  1454. case VKEY_DELETE: vkey = LGLW_VKEY_DELETE; break;
  1455. break;
  1456. default:
  1457. vkey = (char)index;
  1458. // (note) some(most?) DAWs don't send the correct VKEY_xxx values for all special keys
  1459. switch(vkey)
  1460. {
  1461. case 8: vkey = LGLW_VKEY_BACKSPACE; break;
  1462. // case 13: vkey = LGLW_VKEY_RETURN; break;
  1463. // case 9: vkey = LGLW_VKEY_TAB; break;
  1464. // case 27: vkey = LGLW_VKEY_ESCAPE; break;
  1465. default:
  1466. if(vkey >= 128)
  1467. vkey = 0u;
  1468. break;
  1469. }
  1470. break;
  1471. }
  1472. if(vkey > 0u)
  1473. {
  1474. r = vst2_handle_effeditkeydown(vkey);
  1475. }
  1476. }
  1477. }
  1478. break;
  1479. default:
  1480. // ignoring all other opcodes
  1481. Dprintf("vstrack_plugin:dispatcher: unhandled opCode %d [ignored] \n", opCode);
  1482. break;
  1483. }
  1484. return r;
  1485. }
  1486. /**
  1487. * Set parameter setting
  1488. */
  1489. void VSTPluginSetParameter(VSTPlugin *vstPlugin,
  1490. VstInt32 index,
  1491. float parameter
  1492. ) {
  1493. #ifdef DEBUG_PRINT_PARAMS
  1494. Dprintf("vstrack_plugin: called VSTPluginSetParameter(%d, %f)\n", index, parameter);
  1495. #endif // DEBUG_PRINT_PARAMS
  1496. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1497. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1498. wrapper->lockAudio();
  1499. wrapper->setGlobals();
  1500. vst2_queue_param(index, parameter);
  1501. wrapper->unlockAudio();
  1502. }
  1503. /**
  1504. * Query parameter
  1505. */
  1506. float VSTPluginGetParameter(VSTPlugin *vstPlugin,
  1507. VstInt32 index
  1508. ) {
  1509. #ifdef DEBUG_PRINT_PARAMS
  1510. Dprintf("vstrack_plugin: called VSTPluginGetParameter(%d)\n", index);
  1511. #endif // DEBUG_PRINT_PARAMS
  1512. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  1513. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  1514. wrapper->lockAudio(); // don't query a param while the module is deleted
  1515. wrapper->setGlobals();
  1516. float r = vst2_get_param(index);
  1517. wrapper->unlockAudio();
  1518. return r;
  1519. }
  1520. /**
  1521. * Main constructor for our C++ class
  1522. */
  1523. VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback,
  1524. VstInt32 vendorUniqueID,
  1525. VstInt32 vendorVersion,
  1526. VstInt32 numParams,
  1527. VstInt32 numPrograms,
  1528. VstInt32 numInputs,
  1529. VstInt32 numOutputs
  1530. ) : _vstHostCallback(vstHostCallback)
  1531. {
  1532. instance_count++;
  1533. // Make sure that the memory is properly initialized
  1534. memset(&_vstPlugin, 0, sizeof(_vstPlugin));
  1535. // this field must be set with this constant...
  1536. _vstPlugin.magic = kEffectMagic;
  1537. // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use)
  1538. _vstPlugin.object = this;
  1539. // specifying that we handle both single and NOT double precision (there are other flags see aeffect.h/VstAEffectFlags)
  1540. _vstPlugin.flags =
  1541. #ifndef VST2_EFFECT
  1542. effFlagsIsSynth |
  1543. #endif
  1544. effFlagsCanReplacing |
  1545. // (effFlagsCanDoubleReplacing & 0) |
  1546. effFlagsProgramChunks |
  1547. effFlagsHasEditor ;
  1548. // initializing the plugin with the various values
  1549. _vstPlugin.uniqueID = vendorUniqueID;
  1550. _vstPlugin.version = vendorVersion;
  1551. _vstPlugin.numParams = numParams;
  1552. _vstPlugin.numPrograms = numPrograms;
  1553. _vstPlugin.numInputs = numInputs;
  1554. _vstPlugin.numOutputs = numOutputs;
  1555. // setting the callbacks to the previously defined functions
  1556. _vstPlugin.dispatcher = &VSTPluginDispatcher;
  1557. _vstPlugin.getParameter = &VSTPluginGetParameter;
  1558. _vstPlugin.setParameter = &VSTPluginSetParameter;
  1559. _vstPlugin.processReplacing = &VSTPluginProcessReplacingFloat32;
  1560. _vstPlugin.processDoubleReplacing = NULL;//&VSTPluginProcessReplacingFloat64;
  1561. // report latency
  1562. _vstPlugin.initialDelay = 0;
  1563. oversample.factor = 1.0f;
  1564. oversample.quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
  1565. oversample.realtime_factor = 1.0f;
  1566. oversample.realtime_quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
  1567. oversample.offline_factor = 1.0f;
  1568. oversample.offline_quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
  1569. oversample.srs_in = NULL;
  1570. oversample.srs_out = NULL;
  1571. oversample.num_in = NUM_INPUTS;
  1572. oversample.num_out = NUM_OUTPUTS;
  1573. sample_rate = 44100.0f;
  1574. block_size = 64u;
  1575. b_processing = true;
  1576. b_offline = false;
  1577. b_check_offline = false;
  1578. idle_detect_mode = IDLE_DETECT_NONE;
  1579. b_idle = false;
  1580. idle_input_level_threshold = 0.00018f;//0.00007f;
  1581. idle_output_level_threshold = 0.00018f;//0.00003f;
  1582. idle_output_sec_threshold = 120.0f / 1000.0f; // idle after 120ms of silence
  1583. idle_output_framecount = 0u;
  1584. last_program_chunk_str = NULL;
  1585. b_open = false;
  1586. b_editor_open = false;
  1587. editor_rect.left = EDITWIN_X;
  1588. editor_rect.top = EDITWIN_Y;
  1589. editor_rect.right = EDITWIN_X + EDITWIN_W;
  1590. editor_rect.bottom = EDITWIN_Y + EDITWIN_H;
  1591. redraw_ival_ms = 0;
  1592. }
  1593. /**
  1594. * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin
  1595. * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a
  1596. * memory leak which may end up slowing down and/or crashing the host
  1597. */
  1598. VSTPluginWrapper::~VSTPluginWrapper() {
  1599. closeEffect();
  1600. instance_count--;
  1601. }
  1602. void vst2_lock_midi_device() {
  1603. rack::global->vst2.wrapper->mtx_mididev.lock();
  1604. }
  1605. void vst2_unlock_midi_device() {
  1606. rack::global->vst2.wrapper->mtx_mididev.unlock();
  1607. }
  1608. void vst2_handle_ui_param(int uniqueParamId, float normValue) {
  1609. // Called by engineSetParam()
  1610. rack::global->vst2.wrapper->handleUIParam(uniqueParamId, normValue);
  1611. }
  1612. void vst2_get_timing_info(int *_retPlaying, float *_retBPM, float *_retSongPosPPQ) {
  1613. // updates the requested fields when query was successful
  1614. rack::global->vst2.wrapper->getTimingInfo(_retPlaying, _retBPM, _retSongPosPPQ);
  1615. }
  1616. void vst2_set_globals(void *_wrapper) {
  1617. VSTPluginWrapper *wrapper = (VSTPluginWrapper *)_wrapper;
  1618. wrapper->setGlobals();
  1619. }
  1620. void vst2_window_size_set(int _width, int _height) {
  1621. rack::global->vst2.wrapper->setWindowSize(_width, _height);
  1622. }
  1623. void vst2_refresh_rate_set(float _hz) {
  1624. rack::global->vst2.wrapper->setRefreshRate(_hz);
  1625. }
  1626. extern "C" void lglw_timer_cbk(lglw_t _lglw) {
  1627. VSTPluginWrapper *wrapper = (VSTPluginWrapper*)lglw_userdata_get(_lglw);
  1628. wrapper->queueRedraw();
  1629. }
  1630. extern "C" void lglw_redraw_cbk(lglw_t _lglw) {
  1631. VSTPluginWrapper *wrapper = (VSTPluginWrapper*)lglw_userdata_get(_lglw);
  1632. wrapper->redraw();
  1633. }
  1634. void vst2_oversample_realtime_set(float _factor, int _quality) {
  1635. rack::global->vst2.wrapper->setOversampleRealtime(_factor, _quality);
  1636. }
  1637. void vst2_oversample_realtime_get(float *_factor, int *_quality) {
  1638. *_factor = rack::global->vst2.wrapper->oversample.realtime_factor;
  1639. *_quality = int(rack::global->vst2.wrapper->oversample.realtime_quality);
  1640. }
  1641. void vst2_oversample_offline_set(float _factor, int _quality) {
  1642. rack::global->vst2.wrapper->setOversampleOffline(_factor, _quality);
  1643. }
  1644. void vst2_oversample_offline_get(float *_factor, int *_quality) {
  1645. *_factor = rack::global->vst2.wrapper->oversample.offline_factor;
  1646. *_quality = int(rack::global->vst2.wrapper->oversample.offline_quality);
  1647. }
  1648. void vst2_oversample_offline_check_set(int _bEnable) {
  1649. rack::global->vst2.wrapper->b_check_offline = (0 != _bEnable);
  1650. }
  1651. void vst2_oversample_channels_set(int _numIn, int _numOut) {
  1652. rack::global->vst2.wrapper->setOversampleChannels(_numIn, _numOut);
  1653. }
  1654. void vst2_oversample_channels_get(int *_numIn, int *_numOut) {
  1655. *_numIn = int(rack::global->vst2.wrapper->oversample.num_in);
  1656. *_numOut = int(rack::global->vst2.wrapper->oversample.num_out);
  1657. }
  1658. void vst2_idle_detect_mode_fx_set(int _mode) {
  1659. #ifdef VST2_EFFECT
  1660. rack::global->vst2.wrapper->setIdleDetectMode(uint32_t(_mode));
  1661. #endif // VST2_EFFECT
  1662. }
  1663. void vst2_idle_detect_mode_instr_set(int _mode) {
  1664. #ifndef VST2_EFFECT
  1665. rack::global->vst2.wrapper->setIdleDetectMode(uint32_t(_mode));
  1666. #endif // VST2_EFFECT
  1667. }
  1668. void vst2_idle_detect_mode_set(int _mode) {
  1669. rack::global->vst2.wrapper->setIdleDetectMode(uint32_t(_mode));
  1670. }
  1671. void vst2_idle_detect_mode_get(int *_mode) {
  1672. *_mode = int(rack::global->vst2.wrapper->idle_detect_mode);
  1673. }
  1674. /**
  1675. * Implementation of the main entry point of the plugin
  1676. */
  1677. VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback) {
  1678. Dprintf("vstrack_plugin: called VSTPluginMain... \n");
  1679. #if 0
  1680. if(!vstHostCallback(0, audioMasterVersion, 0, 0, 0, 0))
  1681. {
  1682. return 0; // old version
  1683. }
  1684. #endif
  1685. // simply create our plugin C++ class
  1686. VSTPluginWrapper *plugin =
  1687. new VSTPluginWrapper(vstHostCallback,
  1688. // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm)
  1689. #ifdef VST2_EFFECT
  1690. CCONST('g', 'v', 'g', 'y'),
  1691. #else
  1692. CCONST('v', '5', 'k', 'v'),
  1693. #endif
  1694. PLUGIN_VERSION, // version
  1695. VST2_MAX_UNIQUE_PARAM_IDS, // num params
  1696. 1, // one program
  1697. NUM_INPUTS,
  1698. NUM_OUTPUTS
  1699. );
  1700. // return the plugin per the contract of the API
  1701. return plugin->getVSTPlugin();
  1702. }
  1703. #endif // USE_VST2