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.

2415 lines
73KB

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