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.

2323 lines
71KB

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