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.

528 lines
17KB

  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. #define VST_EXPORT extern
  10. #endif
  11. #include <GL/GL.h>
  12. #define EDITWIN_X 20
  13. #define EDITWIN_Y 20
  14. #define EDITWIN_W 640
  15. #define EDITWIN_H 480
  16. #define EDITWIN_CLASS_NAME "VeeSeeVSTWindow"
  17. /*
  18. * I find the naming a bit confusing so I decided to use more meaningful names instead.
  19. */
  20. /**
  21. * The VSTHostCallback is a function pointer so that the plugin can communicate with the host (not used in this small example)
  22. */
  23. typedef audioMasterCallback VSTHostCallback;
  24. /**
  25. * The VSTPlugin structure (AEffect) contains information about the plugin (like version, number of inputs, ...) and
  26. * callbacks so that the host can call the plugin to do its work. The primary callback will be `processReplacing` for
  27. * single precision (float) sample processing (or `processDoubleReplacing` for double precision (double)).
  28. */
  29. typedef AEffect VSTPlugin;
  30. // Since the host is expecting a very specific API we need to make sure it has C linkage (not C++)
  31. extern "C" {
  32. /*
  33. * This is the main entry point to the VST plugin.
  34. *
  35. * The host (DAW like Maschine, Ableton Live, Reason, ...) will look for this function with this exact API.
  36. *
  37. * It is the equivalent to `int main(int argc, char *argv[])` for a C executable.
  38. *
  39. * @param vstHostCallback is a callback so that the plugin can communicate with the host (not used in this small example)
  40. * @return a pointer to the AEffect structure
  41. */
  42. VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback);
  43. // note this looks like this without the type aliases (and is obviously 100% equivalent)
  44. // extern AEffect *VSTPluginMain(audioMasterCallback audioMaster);
  45. }
  46. /*
  47. * Constant for the version of the plugin. For example 1100 for version 1.1.0.0
  48. */
  49. const VstInt32 PLUGIN_VERSION = 1000;
  50. extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
  51. /**
  52. * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the
  53. * host (VSTPlugin). This class will be stored in the `VSTPlugin.object` field (circular reference) so that it can
  54. * be accessed when the host calls the plugin back (for example in `processDoubleReplacing`).
  55. */
  56. class VSTPluginWrapper
  57. {
  58. public:
  59. ERect editor_rect;
  60. HWND parent_hwnd; // created by host
  61. HWND hwnd;
  62. HDC hdc;
  63. HGLRC hglrc;
  64. float clear_color = 0.0f;
  65. static const char *registered_class_name;
  66. static VSTPluginWrapper *window_to_wrapper;
  67. public:
  68. VSTPluginWrapper(VSTHostCallback vstHostCallback,
  69. VstInt32 vendorUniqueID,
  70. VstInt32 vendorVersion,
  71. VstInt32 numParams,
  72. VstInt32 numPrograms,
  73. VstInt32 numInputs,
  74. VstInt32 numOutputs);
  75. ~VSTPluginWrapper();
  76. inline VSTPlugin *getVSTPlugin()
  77. {
  78. return &_vstPlugin;
  79. }
  80. inline VstInt32 getNumInputs() const
  81. {
  82. return _vstPlugin.numInputs;
  83. }
  84. inline VstInt32 getNumOutputs() const
  85. {
  86. return _vstPlugin.numOutputs;
  87. }
  88. void openEditor(HWND _hwnd) {
  89. parent_hwnd = _hwnd;
  90. if(NULL == registered_class_name)
  91. {
  92. WNDCLASS wc;
  93. ::ZeroMemory(&wc, sizeof(wc));
  94. wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  95. wc.lpfnWndProc = (WNDPROC) WndProc;
  96. wc.hInstance = ::GetModuleHandle(NULL);
  97. wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
  98. wc.lpszClassName = EDITWIN_CLASS_NAME;
  99. if(!::RegisterClass(&wc))
  100. {
  101. // something went terribly wrong
  102. printf("[---] vstgltest: failed to register window class\n");
  103. return;
  104. }
  105. registered_class_name = EDITWIN_CLASS_NAME;
  106. }
  107. DWORD dwExStyle = 0;
  108. DWORD dwStyle = WS_CHILD | WS_VISIBLE;
  109. hwnd = ::CreateWindowEx(dwExStyle,
  110. EDITWIN_CLASS_NAME,
  111. "Test",
  112. dwStyle,
  113. 0/*xpos*/, 0/*ypos*/,
  114. EDITWIN_W, EDITWIN_H,
  115. parent_hwnd,
  116. NULL, // window menu
  117. ::GetModuleHandle(NULL),
  118. NULL
  119. );
  120. window_to_wrapper = this;
  121. PIXELFORMATDESCRIPTOR pfd = {
  122. sizeof(PIXELFORMATDESCRIPTOR),
  123. 1,
  124. PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags
  125. PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
  126. 32, // Colordepth of the framebuffer.
  127. 0, 0, 0, 0, 0, 0,
  128. 0,
  129. 0,
  130. 0,
  131. 0, 0, 0, 0,
  132. 24, // Number of bits for the depthbuffer
  133. 8, // Number of bits for the stencilbuffer
  134. 0, // Number of Aux buffers in the framebuffer.
  135. PFD_MAIN_PLANE,
  136. 0,
  137. 0, 0, 0
  138. };
  139. hdc = ::GetDC(hwnd);
  140. int pfmt = ::ChoosePixelFormat(hdc, &pfd);
  141. printf("xxx vstgltest: pfmt=%d", pfmt);
  142. ::SetPixelFormat(hdc, pfmt, &pfd);
  143. hglrc = ::wglCreateContext(hdc);
  144. }
  145. void closeEditor(void) {
  146. if(NULL != hwnd)
  147. {
  148. ::wglDeleteContext(hglrc);
  149. ::DestroyWindow(hwnd);
  150. // (todo) unregister when last window is closed
  151. ::UnregisterClass(EDITWIN_CLASS_NAME, ::GetModuleHandle(NULL));
  152. registered_class_name = NULL;
  153. window_to_wrapper = NULL;
  154. }
  155. }
  156. static VSTPluginWrapper *FindWrapperByWindow(HWND hwnd) {
  157. return window_to_wrapper;
  158. }
  159. void redrawWindow(void) {
  160. // Save host GL context
  161. HDC hostHDC = ::wglGetCurrentDC();
  162. HGLRC hostHGLRC = ::wglGetCurrentContext();
  163. printf("xxx vstgltest: WM_PAINT: hostHDC=%p hostGLRC=%p\n", hostHDC, hostHGLRC);
  164. // Bind plugin GL context
  165. printf("xxx vstgltest: WM_PAINT: pluginHDC=%p pluginGLRC=%p\n", hdc, hglrc);
  166. ::wglMakeCurrent(hdc, hglrc);
  167. // Draw something
  168. ::glClearColor(0.0, 1.0, clear_color, 1.0);
  169. clear_color += 0.05f;
  170. if(clear_color >= 1.0f)
  171. clear_color -= 1.0f;
  172. ::glClear(GL_COLOR_BUFFER_BIT);
  173. ::glFlush();
  174. ::wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE);
  175. // Restore host GL context
  176. ::wglMakeCurrent(hostHDC, hostHGLRC);
  177. }
  178. private:
  179. // the host callback (a function pointer)
  180. VSTHostCallback _vstHostCallback;
  181. // the actual structure required by the host
  182. VSTPlugin _vstPlugin;
  183. };
  184. const char *VSTPluginWrapper::registered_class_name = NULL;
  185. VSTPluginWrapper *VSTPluginWrapper::window_to_wrapper = NULL;
  186. extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  187. switch(message)
  188. {
  189. case WM_CREATE:
  190. printf("xxx vstgltest: WM_CREATE\n");
  191. break;
  192. case WM_DESTROY:
  193. printf("xxx vstgltest: WM_DESTROY\n");
  194. break;
  195. case WM_LBUTTONDOWN:
  196. printf("xxx vstgltest: WM_LBUTTONDOWN\n");
  197. break;
  198. case WM_LBUTTONUP:
  199. printf("xxx vstgltest: WM_LBUTTONUP\n");
  200. break;
  201. case WM_PAINT:
  202. // https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-redrawwindow
  203. printf("xxx vstgltest: WM_PAINT\n");
  204. VSTPluginWrapper *wrapper = VSTPluginWrapper::FindWrapperByWindow(hWnd);
  205. wrapper->redrawWindow();
  206. break;
  207. }
  208. return DefWindowProc(hWnd, message, wParam, lParam);
  209. }
  210. /*******************************************
  211. * Callbacks: Host -> Plugin
  212. *
  213. * Defined here because they are used in the rest of the code later
  214. */
  215. /**
  216. * This is the callback that will be called to process the samples in the case of single precision. This is where the
  217. * meat of the logic happens!
  218. *
  219. * @param vstPlugin the object returned by VSTPluginMain
  220. * @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]
  221. * @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]
  222. * @param sampleFrames the number of samples (second dimension in both arrays)
  223. */
  224. void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float **outputs, VstInt32 sampleFrames)
  225. {
  226. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  227. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  228. // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the
  229. // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound)
  230. for(int i = 0; i < wrapper->getNumInputs(); i++)
  231. {
  232. auto inputSamples = inputs[i];
  233. auto outputSamples = outputs[i];
  234. for(int j = 0; j < sampleFrames; j++)
  235. {
  236. outputSamples[j] = inputSamples[j] * 0.5f;
  237. }
  238. }
  239. }
  240. /**
  241. * This is the callback that will be called to process the samples in the case of double precision. This is where the
  242. * meat of the logic happens!
  243. *
  244. * @param vstPlugin the object returned by VSTPluginMain
  245. * @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]
  246. * @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]
  247. * @param sampleFrames the number of samples (second dimension in both arrays)
  248. */
  249. void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, double **outputs, VstInt32 sampleFrames)
  250. {
  251. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  252. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  253. // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the
  254. // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound)
  255. for(int i = 0; i < wrapper->getNumInputs(); i++)
  256. {
  257. auto inputSamples = inputs[i];
  258. auto outputSamples = outputs[i];
  259. for(int j = 0; j < sampleFrames; j++)
  260. {
  261. outputSamples[j] = inputSamples[j] * 0.5;
  262. }
  263. }
  264. }
  265. /**
  266. * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the
  267. * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...)
  268. *
  269. * @param vstPlugin the object returned by VSTPluginMain
  270. * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand
  271. * total of 79 of them! Only a few of them are implemented in this small plugin.
  272. * @param index depend on the opcode
  273. * @param value depend on the opcode
  274. * @param ptr depend on the opcode
  275. * @param opt depend on the opcode
  276. * @return depend on the opcode (0 is ok when you don't implement an opcode...)
  277. */
  278. VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt)
  279. {
  280. printf("vstgltest: called VSTPluginDispatcher(%d)\n", opCode);
  281. VstIntPtr r = 0;
  282. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  283. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  284. // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them
  285. switch(opCode)
  286. {
  287. // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed
  288. // to generating sound)
  289. case effGetPlugCategory:
  290. return kPlugCategEffect;
  291. // called by the host when the plugin was called... time to reclaim memory!
  292. case effClose:
  293. delete wrapper;
  294. break;
  295. case effGetEffectName:
  296. ::strncpy((char*)ptr, "VST GL Test", kVstMaxEffectNameLen);
  297. r = 1;
  298. break;
  299. // request for the vendor string (usually used in the UI for plugin grouping)
  300. case effGetVendorString:
  301. strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen);
  302. r = 1;
  303. break;
  304. // request for the version
  305. case effGetVendorVersion:
  306. return PLUGIN_VERSION;
  307. case effEditIdle:
  308. if(NULL != wrapper->hwnd)
  309. {
  310. printf("xxx vstgltest: redraw window\n");
  311. // (void)::RedrawWindow(wrapper->hwnd, NULL, NULL, RDW_INTERNALPAINT);
  312. //(void)::UpdateWindow(wrapper->hwnd);
  313. if(::IsWindowVisible(wrapper->hwnd))
  314. {
  315. wrapper->redrawWindow();
  316. }
  317. }
  318. break;
  319. case effEditGetRect:
  320. // Query editor window geometry
  321. // ptr: ERect* (on Windows)
  322. if(NULL != ptr) // yeah, this should never be NULL
  323. {
  324. // ...
  325. wrapper->editor_rect.left = EDITWIN_X;
  326. wrapper->editor_rect.top = EDITWIN_Y;
  327. wrapper->editor_rect.right = EDITWIN_X + EDITWIN_W;
  328. wrapper->editor_rect.bottom = EDITWIN_Y + EDITWIN_H;
  329. *(void**)ptr = (void*) &wrapper->editor_rect;
  330. r = 1;
  331. }
  332. else
  333. {
  334. r = 0;
  335. }
  336. break;
  337. #if 0
  338. case effEditTop:
  339. // deprecated in vst2.4
  340. r = 0;
  341. break;
  342. #endif
  343. case effEditOpen:
  344. // Show editor window
  345. // ptr: native window handle (hWnd on Windows)
  346. wrapper->openEditor((HWND)ptr);
  347. r = 1;
  348. break;
  349. case effEditClose:
  350. // Hide editor window
  351. wrapper->closeEditor();
  352. r = 1;
  353. break;
  354. // ignoring all other opcodes
  355. default:
  356. printf("Unknown opCode %d [ignored] \n", opCode);
  357. break;
  358. }
  359. return r;
  360. }
  361. /**
  362. * Used for parameter setting (not used by this plugin)
  363. */
  364. void VSTPluginSetParameter(VSTPlugin *vstPlugin, VstInt32 index, float parameter)
  365. {
  366. printf("called VSTPluginSetParameter(%d, %f)\n", index, parameter);
  367. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  368. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  369. }
  370. /**
  371. * Used for parameter (not used by this plugin)
  372. */
  373. float VSTPluginGetParameter(VSTPlugin *vstPlugin, VstInt32 index)
  374. {
  375. printf("called VSTPluginGetParameter(%d)\n", index);
  376. // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
  377. VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
  378. return 0;
  379. }
  380. /**
  381. * Main constructor for our C++ class
  382. */
  383. VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback,
  384. VstInt32 vendorUniqueID,
  385. VstInt32 vendorVersion,
  386. VstInt32 numParams,
  387. VstInt32 numPrograms,
  388. VstInt32 numInputs,
  389. VstInt32 numOutputs) :
  390. _vstHostCallback(vstHostCallback)
  391. {
  392. // Make sure that the memory is properly initialized
  393. memset(&_vstPlugin, 0, sizeof(_vstPlugin));
  394. // this field must be set with this constant...
  395. _vstPlugin.magic = kEffectMagic;
  396. // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use)
  397. _vstPlugin.object = this;
  398. // specifying that we handle both single and double precision (there are other flags see aeffect.h/VstAEffectFlags)
  399. _vstPlugin.flags = effFlagsCanReplacing | effFlagsCanDoubleReplacing | effFlagsHasEditor;
  400. // initializing the plugin with the various values
  401. _vstPlugin.uniqueID = vendorUniqueID;
  402. _vstPlugin.version = vendorVersion;
  403. _vstPlugin.numParams = numParams;
  404. _vstPlugin.numPrograms = numPrograms;
  405. _vstPlugin.numInputs = numInputs;
  406. _vstPlugin.numOutputs = numOutputs;
  407. // setting the callbacks to the previously defined functions
  408. _vstPlugin.dispatcher = VSTPluginDispatcher;
  409. _vstPlugin.getParameter = VSTPluginGetParameter;
  410. _vstPlugin.setParameter = VSTPluginSetParameter;
  411. _vstPlugin.processReplacing = VSTPluginProcessSamplesFloat32;
  412. _vstPlugin.processDoubleReplacing = VSTPluginProcessSamplesFloat64;
  413. parent_hwnd = NULL;
  414. hwnd = NULL;
  415. hdc = NULL;
  416. hglrc = NULL;
  417. }
  418. /**
  419. * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin
  420. * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a
  421. * memory leak which may end up slowing down and/or crashing the host
  422. */
  423. VSTPluginWrapper::~VSTPluginWrapper()
  424. {
  425. }
  426. /**
  427. * Implementation of the main entry point of the plugin
  428. */
  429. VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback)
  430. {
  431. printf("called VSTPluginMain... \n");
  432. // simply create our plugin C++ class
  433. VSTPluginWrapper *plugin =
  434. new VSTPluginWrapper(vstHostCallback,
  435. CCONST('u', 's', 'a', 'ยง'), // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm)
  436. PLUGIN_VERSION, // version
  437. 0, // no params
  438. 0, // no programs
  439. 2, // 2 inputs
  440. 2); // 2 outputs
  441. // return the plugin per the contract of the API
  442. return plugin->getVSTPlugin();
  443. }