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.

2512 lines
75KB

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