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.

459 lines
15KB

  1. #include <aeffect.h>
  2. #include <aeffectx.h>
  3. #include <stdio.h>
  4. #if defined(_WIN32) || defined(_WIN64)
  5. #include <windows.h>
  6. #include <wingdi.h>
  7. #define VST_EXPORT extern "C" __declspec(dllexport)
  8. #else
  9. #include <GL/gl.h>
  10. #include <X11/Xlib.h>
  11. #include <X11/Xutil.h>
  12. #include <X11/Xos.h>
  13. #define VST_EXPORT extern
  14. #endif
  15. #include "lglw.h"
  16. #define EDITWIN_X 20
  17. #define EDITWIN_Y 20
  18. #define EDITWIN_W 640
  19. #define EDITWIN_H 480
  20. /*
  21. * I find the naming a bit confusing so I decided to use more meaningful names instead.
  22. */
  23. /**
  24. * The VSTHostCallback is a function pointer so that the plugin can communicate with the host (not used in this small example)
  25. */
  26. typedef audioMasterCallback VSTHostCallback;
  27. /**
  28. * The VSTPlugin structure (AEffect) contains information about the plugin (like version, number of inputs, ...) and
  29. * callbacks so that the host can call the plugin to do its work. The primary callback will be `processReplacing` for
  30. * single precision (float) sample processing (or `processDoubleReplacing` for double precision (double)).
  31. */
  32. typedef AEffect VSTPlugin;
  33. // Since the host is expecting a very specific API we need to make sure it has C linkage (not C++)
  34. extern "C" {
  35. /*
  36. * This is the main entry point to the VST plugin.
  37. *
  38. * The host (DAW like Maschine, Ableton Live, Reason, ...) will look for this function with this exact API.
  39. *
  40. * It is the equivalent to `int main(int argc, char *argv[])` for a C executable.
  41. *
  42. * @param vstHostCallback is a callback so that the plugin can communicate with the host (not used in this small example)
  43. * @return a pointer to the AEffect structure
  44. */
  45. VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback);
  46. // note this looks like this without the type aliases (and is obviously 100% equivalent)
  47. // extern AEffect *VSTPluginMain(audioMasterCallback audioMaster);
  48. }
  49. /*
  50. * Constant for the version of the plugin. For example 1100 for version 1.1.0.0
  51. */
  52. const VstInt32 PLUGIN_VERSION = 1000;
  53. // extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
  54. void loc_mouse_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _buttonState, uint32_t _changedButtonState) {
  55. printf("xxx lglw_mouse_cbk: lglw=%p p=(%d; %d) bt=0x%08x changedBt=0x%08x\n", _lglw, _x, _y, _buttonState, _changedButtonState);
  56. if(LGLW_IS_MOUSE_LBUTTON_DOWN())
  57. {
  58. // lglw_mouse_grab(_lglw, LGLW_MOUSE_GRAB_CAPTURE);
  59. lglw_mouse_grab(_lglw, LGLW_MOUSE_GRAB_WARP);
  60. }
  61. else if(LGLW_IS_MOUSE_LBUTTON_UP())
  62. {
  63. lglw_mouse_ungrab(_lglw);
  64. }
  65. }
  66. void loc_focus_cbk(lglw_t _lglw, uint32_t _focusState, uint32_t _changedFocusState) {
  67. printf("xxx lglw_focus_cbk: lglw=%p focusState=0x%08x changedFocusState=0x%08x\n", _lglw, _focusState, _changedFocusState);
  68. }
  69. lglw_bool_t loc_keyboard_cbk(lglw_t _lglw, uint32_t _vkey, uint32_t _kmod, lglw_bool_t _bPressed) {
  70. printf("xxx lglw_keyboard_cbk: lglw=%p vkey=0x%08x (\'%c\') kmod=0x%08x bPressed=%d\n", _lglw, _vkey, _vkey, _kmod, _bPressed);
  71. return LGLW_FALSE;
  72. }
  73. void loc_timer_cbk(lglw_t _lglw) {
  74. printf("xxx lglw_timer_cbk: tick\n");
  75. }
  76. /**
  77. * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the
  78. * host (VSTPlugin). This class will be stored in the `VSTPlugin.object` field (circular reference) so that it can
  79. * be accessed when the host calls the plugin back (for example in `processDoubleReplacing`).
  80. */
  81. class VSTPluginWrapper
  82. {
  83. public:
  84. ERect editor_rect;
  85. lglw_t lglw;
  86. float clear_color = 0.0f;
  87. static VSTPluginWrapper *window_to_wrapper;
  88. public:
  89. VSTPluginWrapper(VSTHostCallback vstHostCallback,
  90. VstInt32 vendorUniqueID,
  91. VstInt32 vendorVersion,
  92. VstInt32 numParams,
  93. VstInt32 numPrograms,
  94. VstInt32 numInputs,
  95. VstInt32 numOutputs);
  96. ~VSTPluginWrapper();
  97. inline VSTPlugin *getVSTPlugin()
  98. {
  99. return &_vstPlugin;
  100. }
  101. inline VstInt32 getNumInputs() const
  102. {
  103. return _vstPlugin.numInputs;
  104. }
  105. inline VstInt32 getNumOutputs() const
  106. {
  107. return _vstPlugin.numOutputs;
  108. }
  109. void openEditor(void *_hwnd) {
  110. (void)lglw_window_open(lglw, _hwnd, 0/*x*/, 0/*y*/, EDITWIN_W, EDITWIN_H);
  111. lglw_mouse_callback_set(lglw, &loc_mouse_cbk);
  112. lglw_focus_callback_set(lglw, &loc_focus_cbk);
  113. lglw_keyboard_callback_set(lglw, &loc_keyboard_cbk);
  114. lglw_timer_callback_set(lglw, &loc_timer_cbk);
  115. lglw_timer_start(lglw, 200);
  116. window_to_wrapper = this;
  117. }
  118. void closeEditor(void) {
  119. if(NULL != window_to_wrapper)
  120. {
  121. lglw_window_close(lglw);
  122. window_to_wrapper = NULL;
  123. }
  124. }
  125. static VSTPluginWrapper *FindWrapperByWindow(Window hwnd) {
  126. return window_to_wrapper;
  127. }
  128. void redrawWindow(void) {
  129. // Save host GL context
  130. lglw_glcontext_push(lglw);
  131. // Draw something
  132. ::glClearColor(0.0, 1.0, clear_color, 1.0);
  133. clear_color += 0.05f;
  134. if(clear_color >= 1.0f)
  135. clear_color -= 1.0f;
  136. ::glClear(GL_COLOR_BUFFER_BIT);
  137. ::glFlush();
  138. lglw_swap_buffers(lglw);
  139. // Restore host GL context
  140. lglw_glcontext_pop(lglw);
  141. }
  142. private:
  143. // the host callback (a function pointer)
  144. VSTHostCallback _vstHostCallback;
  145. // the actual structure required by the host
  146. VSTPlugin _vstPlugin;
  147. };
  148. VSTPluginWrapper *VSTPluginWrapper::window_to_wrapper = NULL;
  149. /*******************************************
  150. * Callbacks: Host -> Plugin
  151. *
  152. * Defined here because they are used in the rest of the code later
  153. */
  154. /**
  155. * This is the callback that will be called to process the samples in the case of single precision. This is where the
  156. * meat of the logic happens!
  157. *
  158. * @param vstPlugin the object returned by VSTPluginMain
  159. * @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]
  160. * @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]
  161. * @param sampleFrames the number of samples (second dimension in both arrays)
  162. */
  163. void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float **outputs, VstInt32 sampleFrames)
  164. {
  165. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  166. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  167. // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the
  168. // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound)
  169. for(int i = 0; i < wrapper->getNumInputs(); i++)
  170. {
  171. auto inputSamples = inputs[i];
  172. auto outputSamples = outputs[i];
  173. for(int j = 0; j < sampleFrames; j++)
  174. {
  175. outputSamples[j] = inputSamples[j] * 0.5f;
  176. }
  177. }
  178. }
  179. /**
  180. * This is the callback that will be called to process the samples in the case of double precision. This is where the
  181. * meat of the logic happens!
  182. *
  183. * @param vstPlugin the object returned by VSTPluginMain
  184. * @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]
  185. * @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]
  186. * @param sampleFrames the number of samples (second dimension in both arrays)
  187. */
  188. void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, double **outputs, VstInt32 sampleFrames)
  189. {
  190. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  191. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  192. // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the
  193. // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound)
  194. for(int i = 0; i < wrapper->getNumInputs(); i++)
  195. {
  196. auto inputSamples = inputs[i];
  197. auto outputSamples = outputs[i];
  198. for(int j = 0; j < sampleFrames; j++)
  199. {
  200. outputSamples[j] = inputSamples[j] * 0.5;
  201. }
  202. }
  203. }
  204. /**
  205. * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the
  206. * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...)
  207. *
  208. * @param vstPlugin the object returned by VSTPluginMain
  209. * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand
  210. * total of 79 of them! Only a few of them are implemented in this small plugin.
  211. * @param index depend on the opcode
  212. * @param value depend on the opcode
  213. * @param ptr depend on the opcode
  214. * @param opt depend on the opcode
  215. * @return depend on the opcode (0 is ok when you don't implement an opcode...)
  216. */
  217. VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt)
  218. {
  219. // printf("vstgltest: called VSTPluginDispatcher(%d)\n", opCode);
  220. VstIntPtr r = 0;
  221. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  222. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  223. // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them
  224. switch(opCode)
  225. {
  226. // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed
  227. // to generating sound)
  228. case effGetPlugCategory:
  229. return kPlugCategEffect;
  230. // called by the host when the plugin was called... time to reclaim memory!
  231. case effClose:
  232. delete wrapper;
  233. break;
  234. case effGetEffectName:
  235. ::strncpy((char*)ptr, "VST GL Test", kVstMaxEffectNameLen);
  236. r = 1;
  237. break;
  238. // request for the vendor string (usually used in the UI for plugin grouping)
  239. case effGetVendorString:
  240. strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen);
  241. r = 1;
  242. break;
  243. // request for the version
  244. case effGetVendorVersion:
  245. return PLUGIN_VERSION;
  246. case effGetParamName:
  247. strncpy(static_cast<char *>(ptr), "myparam", kVstMaxParamStrLen);
  248. r = 1;
  249. break;
  250. case effEditIdle:
  251. printf("xxx vstgltest: redraw window\n");
  252. // (void)::RedrawWindow(wrapper->hwnd, NULL, NULL, RDW_INTERNALPAINT);
  253. //(void)::UpdateWindow(wrapper->hwnd);
  254. if(lglw_window_is_visible(wrapper->lglw))
  255. {
  256. wrapper->redrawWindow();
  257. }
  258. break;
  259. case effEditGetRect:
  260. // Query editor window geometry
  261. // ptr: ERect* (on Windows)
  262. if(NULL != ptr) // yeah, this should never be NULL
  263. {
  264. // ...
  265. wrapper->editor_rect.left = EDITWIN_X;
  266. wrapper->editor_rect.top = EDITWIN_Y;
  267. wrapper->editor_rect.right = EDITWIN_X + EDITWIN_W;
  268. wrapper->editor_rect.bottom = EDITWIN_Y + EDITWIN_H;
  269. *(void**)ptr = (void*) &wrapper->editor_rect;
  270. r = 1;
  271. }
  272. else
  273. {
  274. r = 0;
  275. }
  276. break;
  277. #if 0
  278. case effEditTop:
  279. // deprecated in vst2.4
  280. r = 0;
  281. break;
  282. #endif
  283. case effEditOpen:
  284. // Show editor window
  285. // ptr: native window handle (hWnd on Windows)
  286. wrapper->openEditor(ptr);
  287. r = 1;
  288. break;
  289. case effEditClose:
  290. // Hide editor window
  291. wrapper->closeEditor();
  292. r = 1;
  293. break;
  294. // ignoring all other opcodes
  295. default:
  296. // printf("Unknown opCode %d [ignored] \n", opCode);
  297. break;
  298. }
  299. return r;
  300. }
  301. /**
  302. * Used for parameter setting (not used by this plugin)
  303. */
  304. void VSTPluginSetParameter(VSTPlugin *vstPlugin, VstInt32 index, float parameter)
  305. {
  306. printf("called VSTPluginSetParameter(%d, %f)\n", index, parameter);
  307. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  308. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  309. }
  310. /**
  311. * Used for parameter (not used by this plugin)
  312. */
  313. float VSTPluginGetParameter(VSTPlugin *vstPlugin, VstInt32 index)
  314. {
  315. printf("called VSTPluginGetParameter(%d)\n", index);
  316. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  317. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  318. return 0;
  319. }
  320. /**
  321. * Main constructor for our C++ class
  322. */
  323. VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback,
  324. VstInt32 vendorUniqueID,
  325. VstInt32 vendorVersion,
  326. VstInt32 numParams,
  327. VstInt32 numPrograms,
  328. VstInt32 numInputs,
  329. VstInt32 numOutputs) :
  330. _vstHostCallback(vstHostCallback)
  331. {
  332. // Make sure that the memory is properly initialized
  333. memset(&_vstPlugin, 0, sizeof(_vstPlugin));
  334. // this field must be set with this constant...
  335. _vstPlugin.magic = kEffectMagic;
  336. // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use)
  337. _vstPlugin.object = this;
  338. // specifying that we handle both single and double precision (there are other flags see aeffect.h/VstAEffectFlags)
  339. _vstPlugin.flags = effFlagsCanReplacing | effFlagsCanDoubleReplacing | effFlagsHasEditor;
  340. // initializing the plugin with the various values
  341. _vstPlugin.uniqueID = vendorUniqueID;
  342. _vstPlugin.version = vendorVersion;
  343. _vstPlugin.numParams = numParams;
  344. _vstPlugin.numPrograms = numPrograms;
  345. _vstPlugin.numInputs = numInputs;
  346. _vstPlugin.numOutputs = numOutputs;
  347. // setting the callbacks to the previously defined functions
  348. _vstPlugin.dispatcher = VSTPluginDispatcher;
  349. _vstPlugin.getParameter = VSTPluginGetParameter;
  350. _vstPlugin.setParameter = VSTPluginSetParameter;
  351. _vstPlugin.processReplacing = VSTPluginProcessSamplesFloat32;
  352. _vstPlugin.processDoubleReplacing = VSTPluginProcessSamplesFloat64;
  353. lglw = lglw_init(EDITWIN_W, EDITWIN_H);
  354. }
  355. /**
  356. * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin
  357. * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a
  358. * memory leak which may end up slowing down and/or crashing the host
  359. */
  360. VSTPluginWrapper::~VSTPluginWrapper() {
  361. lglw_exit(lglw);
  362. }
  363. /**
  364. * Implementation of the main entry point of the plugin
  365. */
  366. VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback)
  367. {
  368. printf("called VSTPluginMain... \n");
  369. // simply create our plugin C++ class
  370. VSTPluginWrapper *plugin =
  371. new VSTPluginWrapper(vstHostCallback,
  372. CCONST('u', 's', 'a', 'ยง'), // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm)
  373. PLUGIN_VERSION, // version
  374. 2, // no params
  375. 0, // no programs
  376. 2, // 2 inputs
  377. 2); // 2 outputs
  378. // return the plugin per the contract of the API
  379. return plugin->getVSTPlugin();
  380. }