V0.6 linux lglwpull/1639/head
| @@ -24,7 +24,7 @@ | |||
| * ---- info : This is part of the "lglw" package. | |||
| * ---- | |||
| * ---- created: 04Aug2018 | |||
| * ---- changed: 05Aug2018, 06Aug2018, 07Aug2018, 08Aug2018, 09Aug2018, 18Aug2018, 10Oct2018 | |||
| * ---- changed: 05Aug2018, 06Aug2018, 07Aug2018, 08Aug2018, 09Aug2018, 18Aug2018, 10Oct2018, 16Oct2018 | |||
| * ---- | |||
| * ---- | |||
| */ | |||
| @@ -35,6 +35,13 @@ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <X11/Xlib.h> | |||
| #include <X11/Xutil.h> | |||
| #include <X11/Xos.h> | |||
| #include <GL/gl.h> | |||
| #include <GL/glx.h> | |||
| #define Dprintf if(0);else printf | |||
| // #define Dprintf if(1);else printf | |||
| @@ -57,18 +64,29 @@ | |||
| // ---------------------------------------------------------------------------- structs and typedefs | |||
| typedef struct lglw_int_s { | |||
| void *user_data; // arbitrary user data | |||
| void *user_data; // arbitrary user data | |||
| Display *xdsp; | |||
| XVisualInfo *vi; | |||
| Colormap cmap; | |||
| Window parent_xwnd; // created by host | |||
| struct { | |||
| lglw_vec2i_t size; | |||
| Window xwnd; | |||
| } hidden; | |||
| struct { | |||
| lglw_vec2i_t size; | |||
| Window xwnd; | |||
| lglw_bool_t mapped; | |||
| int32_t swap_interval; | |||
| } win; | |||
| GLXContext ctx; | |||
| struct { | |||
| GLXContext ctx; | |||
| GLXDrawable drw; | |||
| } prev; | |||
| struct { | |||
| @@ -142,27 +160,56 @@ static void loc_enable_dropfiles (lglw_int_t *lglw, lglw_bool_t _bEnable); | |||
| static lglw_int_t *khook_lglw = NULL; // currently key-hooked lglw instance (one at a time) | |||
| // TODO: remove and/or improve debug logging for a debug build | |||
| // ---------------------------------------------------------------------------- lglw_log | |||
| static FILE *logfile; | |||
| void lglw_log(const char *logData, ...) { | |||
| fprintf(logfile, logData); | |||
| fflush(logfile); | |||
| } | |||
| // TODO: remove, or maybe not in some specific use cases | |||
| // ---------------------------------------------------------------------------- xerror_log | |||
| static int xerror_handler(Display *display, XErrorEvent *error) { | |||
| char error_text[1024]; | |||
| XGetErrorText(display, error->error_code, error_text, 1024); | |||
| lglw_log("XERROR (%d): %s, %d, %d\n", error->error_code, error_text, error->request_code, error->minor_code); | |||
| return 0; | |||
| } | |||
| // ---------------------------------------------------------------------------- lglw_init | |||
| lglw_t lglw_init(int32_t _w, int32_t _h) { | |||
| lglw_int_t *lglw = malloc(sizeof(lglw_int_t)); | |||
| // TODO: remove/improve | |||
| logfile = fopen("/home/cameron/src/VeeSeeVSTRack/other/log.txt", "w"); | |||
| XSetErrorHandler(xerror_handler); | |||
| if(NULL != lglw) | |||
| { | |||
| memset(lglw, 0, sizeof(lglw_int_t)); | |||
| lglw_log("lglw:lglw_init: 1\n"); | |||
| if(_w <= 16) | |||
| _w = LGLW_DEFAULT_HIDDEN_W; | |||
| if(_h <= 16) | |||
| _h = LGLW_DEFAULT_HIDDEN_H; | |||
| lglw_log("lglw:lglw_init: 2\n"); | |||
| if(!loc_create_hidden_window(lglw, _w, _h)) | |||
| { | |||
| free(lglw); | |||
| lglw = NULL; | |||
| } | |||
| lglw_log("lglw:lglw_init: 3\n"); | |||
| } | |||
| lglw_log("lglw:lglw_init: EXIT\n"); | |||
| return lglw; | |||
| } | |||
| @@ -173,8 +220,13 @@ void lglw_exit(lglw_t _lglw) { | |||
| if(NULL != lglw) | |||
| { | |||
| lglw_log("lglw:lglw_exit: 1\n"); | |||
| loc_destroy_hidden_window(lglw); | |||
| lglw_log("lglw:lglw_exit: 2\n"); | |||
| fclose(logfile); | |||
| free(lglw); | |||
| } | |||
| } | |||
| @@ -186,6 +238,7 @@ void lglw_userdata_set(lglw_t _lglw, void *_userData) { | |||
| if(NULL != lglw) | |||
| { | |||
| lglw_log("lglw:lglw_userdata_set: 1\n"); | |||
| lglw->user_data = _userData; | |||
| } | |||
| } | |||
| @@ -196,6 +249,7 @@ void *lglw_userdata_get(lglw_t _lglw) { | |||
| if(NULL != lglw) | |||
| { | |||
| lglw_log("lglw:lglw_userdata_get: 1\n"); | |||
| return lglw->user_data; | |||
| } | |||
| @@ -206,40 +260,232 @@ void *lglw_userdata_get(lglw_t _lglw) { | |||
| // ---------------------------------------------------------------------------- loc_create_hidden_window | |||
| static lglw_bool_t loc_create_hidden_window(lglw_int_t *lglw, int32_t _w, int32_t _h) { | |||
| // (todo) implement me | |||
| // TODO: compare to 'WindowClass' from Windows implementation | |||
| lglw_log("lglw:loc_create_hidden_window: 1\n"); | |||
| XSetWindowAttributes swa; | |||
| int attrib[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, None }; | |||
| int screen; | |||
| lglw_log("lglw:loc_create_hidden_window: 2\n"); | |||
| lglw->xdsp = XOpenDisplay(NULL); | |||
| screen = DefaultScreen(lglw->xdsp); | |||
| lglw_log("lglw:loc_create_hidden_window: 3\n"); | |||
| lglw->vi = glXChooseVisual(lglw->xdsp, screen, attrib); | |||
| lglw_log("lglw:loc_create_hidden_window: 4\n"); | |||
| if(NULL == lglw->vi) | |||
| { | |||
| lglw_log("[---] lglw: failed to find GLX Visual for hidden window\n"); | |||
| return LGLW_FALSE; | |||
| } | |||
| lglw_log("lglw:loc_create_hidden_window: 5\n"); | |||
| lglw->ctx = glXCreateContext(lglw->xdsp, lglw->vi, None, True); | |||
| lglw_log("lglw:loc_create_hidden_window: 6\n"); | |||
| if(NULL == lglw->ctx) | |||
| { | |||
| lglw_log("[---] lglw: failed to create GLX Context for hidden window\n"); | |||
| return LGLW_FALSE; | |||
| } | |||
| lglw_log("lglw:loc_create_hidden_window: 7\n"); | |||
| lglw->cmap = XCreateColormap(lglw->xdsp, RootWindow(lglw->xdsp, lglw->vi->screen), | |||
| lglw->vi->visual, AllocNone); | |||
| lglw_log("lglw:loc_create_hidden_window: 8\n"); | |||
| swa.border_pixel = 0; | |||
| swa.colormap = lglw->cmap; | |||
| lglw->hidden.xwnd = XCreateWindow(lglw->xdsp, DefaultRootWindow(lglw->xdsp), | |||
| 0, 0, LGLW_DEFAULT_HIDDEN_W, LGLW_DEFAULT_HIDDEN_H, 0, CopyFromParent, InputOutput, | |||
| lglw->vi->visual, CWBorderPixel | CWColormap, &swa); | |||
| lglw_log("lglw:loc_create_hidden_window: 9\n"); | |||
| XSetStandardProperties(lglw->xdsp, lglw->hidden.xwnd, "LGLW_hidden", "LGLW_hidden", None, NULL, 0, NULL); | |||
| XSync(lglw->xdsp, False); | |||
| lglw_log("lglw:loc_create_hidden_window: EXIT\n"); | |||
| lglw->hidden.size.x = _w; | |||
| lglw->hidden.size.y = _h; | |||
| return LGLW_FALSE; | |||
| return LGLW_TRUE; | |||
| } | |||
| // ---------------------------------------------------------------------------- loc_destroy_hidden_window | |||
| static void loc_destroy_hidden_window(lglw_int_t *lglw) { | |||
| // (todo) implement me | |||
| lglw_log("lglw:loc_destroy_hidden_window: 1\n"); | |||
| if(NULL != lglw->xdsp && NULL != lglw->ctx) | |||
| { | |||
| glXMakeCurrent(lglw->xdsp, None, NULL); | |||
| glXDestroyContext(lglw->xdsp, lglw->ctx); | |||
| } | |||
| lglw_log("lglw:loc_destroy_hidden_window: 2\n"); | |||
| if(NULL != lglw->xdsp && 0 != lglw->hidden.xwnd) XDestroyWindow(lglw->xdsp, lglw->hidden.xwnd); | |||
| lglw_log("lglw:loc_destroy_hidden_window: 3\n"); | |||
| if(NULL != lglw->xdsp && 0 != lglw->cmap) XFreeColormap(lglw->xdsp, lglw->cmap); | |||
| lglw_log("lglw:loc_destroy_hidden_window: 4\n"); | |||
| if(NULL != lglw->vi) XFree(lglw->vi); | |||
| lglw_log("lglw:loc_destroy_hidden_window: 5\n"); | |||
| XSync(lglw->xdsp, False); | |||
| if(NULL != lglw->xdsp) XCloseDisplay(lglw->xdsp); | |||
| } | |||
| // ---------------------------------------------------------------------------- loc_setEventProc | |||
| // https://www.kvraudio.com/forum/viewtopic.php?t=387924 | |||
| // https://github.com/Ardour/ardour/blob/master/gtk2_ardour/linux_vst_gui_support.cc | |||
| // https://discourse.ardour.org/t/overtonedsp-plugins/90115/22 | |||
| // https://github.com/amsynth/amsynth/blob/4a87798e650c6d71d70274a961c9b8d98fc6da7e/src/amsynth_vst.cpp | |||
| // https://github.com/rsenn/eXT2/blob/7f00a09561ded8175ffed2f4912dad74e466a1c7/vstplugins/vstgui/vstgui.cpp | |||
| // https://github.com/COx2/DistortionFilter/blob/c6a34fb56b503a6e95bf0975e00f438bbf4ff52a/juce/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp | |||
| // Very simple function to test _XEventProc is properly called | |||
| void loc_eventProc(void *xevent) { | |||
| // lglw_log("XEventProc"); | |||
| printf("XEventProc\n"); | |||
| } | |||
| // Pulled from the Renoise 64-bit callback example | |||
| // Unsure what data was supposed to be, but swapping it to a function name did not work | |||
| // This does nothing, no event proc found | |||
| // TODO: 32-bit support | |||
| // void loc_setEventProc (Display *display, Window window) { | |||
| // size_t data = (size_t)loc_eventProc; | |||
| // long temp[2]; | |||
| // // Split the 64 bit pointer into a little-endian long array | |||
| // temp[0] = (long)(data & 0xffffffffUL); | |||
| // temp[1] = (long)(data >> 32L); | |||
| // Atom atom = XInternAtom(display, "_XEventProc", False); | |||
| // XChangeProperty(display, window, atom, atom, 32, | |||
| // PropModeReplace, (unsigned char*)temp, 2); | |||
| // } | |||
| // Pulled from the eXT2 example | |||
| // TODO: 32-bit support | |||
| void loc_setEventProc (Display *display, Window window) { | |||
| void* data = (void*)&loc_eventProc; // swapped the function name here | |||
| Atom atom = XInternAtom(display, "_XEventProc", False); | |||
| XChangeProperty(display, window, atom, atom, 32, | |||
| PropModeReplace, (unsigned char*)&data, 1); // 1 instead of 2 will crash Ardour, 2 will not do anything | |||
| } | |||
| // Pulled from the amsynth example | |||
| // Simply swapped out the function names, crashes Ardour in the same was as the others | |||
| // void loc_setEventProc (Display *display, Window window) { | |||
| // #ifdef BUILD_64 | |||
| // // | |||
| // // JUCE calls XGetWindowProperty with long_length = 1 which means it only fetches the lower 32 bits of the address. | |||
| // // Therefore we need to ensure we return an address in the lower 32-bits of address space. | |||
| // // | |||
| // // based on mach_override | |||
| // static const unsigned char kJumpInstructions[] = { | |||
| // 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, | |||
| // 0x00, 0x00, 0x00, 0x00, | |||
| // 0x00, 0x00, 0x00, 0x00 | |||
| // }; | |||
| // static const int kJumpAddress = 6; | |||
| // static char *ptr; | |||
| // if (!ptr) { | |||
| // ptr = (char *)mmap(0, | |||
| // PAGE_SIZE, | |||
| // PROT_READ | PROT_WRITE | PROT_EXEC, | |||
| // MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, | |||
| // 0, 0); | |||
| // if (ptr == MAP_FAILED) { | |||
| // perror("mmap"); | |||
| // ptr = 0; | |||
| // return; | |||
| // } else { | |||
| // memcpy(ptr, kJumpInstructions, sizeof(kJumpInstructions)); | |||
| // *((uint64_t *)(ptr + kJumpAddress)) = (uint64_t)(&loc_eventProc); | |||
| // msync(ptr, sizeof(kJumpInstructions), MS_INVALIDATE); | |||
| // } | |||
| // } | |||
| // long temp[2] = {(long)ptr, 0}; | |||
| // Atom atom = XInternAtom(display, "_XEventProc", False); | |||
| // XChangeProperty(display, window, atom, atom, 32, PropModeReplace, (unsigned char *)temp, 2); | |||
| // #else | |||
| // long temp[1] = {(long)(void *)(&loc_eventProc)}; | |||
| // Atom atom = XInternAtom(display, "_XEventProc", False); | |||
| // XChangeProperty(display, window, atom, atom, 32, PropModeReplace, (unsigned char *)temp, 1); | |||
| // #endif | |||
| // } | |||
| // ---------------------------------------------------------------------------- lglw_window_open | |||
| lglw_bool_t lglw_window_open (lglw_t _lglw, void *_parentHWNDOrNull, int32_t _x, int32_t _y, int32_t _w, int32_t _h) { | |||
| lglw_bool_t r = LGLW_FALSE; | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| lglw_log("lglw:lglw_window_open: 1, %p, %i\n", (Window)_parentHWNDOrNull, (Window)_parentHWNDOrNull); | |||
| lglw->parent_xwnd = (0 == _parentHWNDOrNull) ? DefaultRootWindow(lglw->xdsp) : (Window)_parentHWNDOrNull; | |||
| lglw_log("lglw:lglw_window_open: 2\n"); | |||
| if(_w <= 16) | |||
| _w = lglw->hidden.size.x; | |||
| lglw_log("lglw:lglw_window_open: 3\n"); | |||
| if(_h <= 16) | |||
| _h = lglw->hidden.size.y; | |||
| // TODO: compare to 'WindowClass' from Windows implementation | |||
| lglw_log("lglw:lglw_window_open: 4\n"); | |||
| XSetWindowAttributes swa; | |||
| XEvent event; | |||
| XSync(lglw->xdsp, False); | |||
| lglw_log("lglw:lglw_window_open: 5\n"); | |||
| swa.border_pixel = 0; | |||
| swa.colormap = lglw->cmap; | |||
| swa.event_mask = EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask; | |||
| lglw->win.xwnd = XCreateWindow(lglw->xdsp, DefaultRootWindow(lglw->xdsp), | |||
| 0, 0, _w, _h, 0, CopyFromParent, InputOutput, | |||
| lglw->vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); | |||
| lglw_log("lglw:lglw_window_open: 6\n"); | |||
| XSetStandardProperties(lglw->xdsp, lglw->win.xwnd, "LGLW", "LGLW", None, NULL, 0, NULL); | |||
| // Setup the event proc now, on the parent window as well just for the debug host | |||
| // It was simpler to do this than check in the debug host for the reparent event | |||
| lglw_log("lglw:lglw_window_open: 7\n"); | |||
| loc_setEventProc(lglw->xdsp, lglw->win.xwnd); | |||
| loc_setEventProc(lglw->xdsp, lglw->parent_xwnd); | |||
| // Some hosts only check and store the callback when the Window is reparented | |||
| // Since creating the Window with a Parent may or may not do that, but the callback is not set, | |||
| // ... it's created as a root window, the callback is set, and then it's reparented | |||
| if (0 != _parentHWNDOrNull) | |||
| { | |||
| lglw_log("lglw:lglw_window_open: 8\n"); | |||
| XReparentWindow(lglw->xdsp, lglw->win.xwnd, lglw->parent_xwnd, 0, 0); | |||
| } | |||
| lglw_log("lglw:lglw_window_open: 9\n"); | |||
| XMapRaised(lglw->xdsp, lglw->win.xwnd); | |||
| XSync(lglw->xdsp, False); | |||
| lglw->win.mapped = LGLW_TRUE; | |||
| lglw_log("lglw:lglw_window_open: 10\n"); | |||
| lglw->win.size.x = _w; | |||
| lglw->win.size.y = _h; | |||
| lglw_log("lglw:lglw_window_open: 11\n"); | |||
| loc_enable_dropfiles(lglw, (NULL != lglw->dropfiles.cbk)); | |||
| lglw_log("lglw:lglw_window_open: EXIT\n"); | |||
| } | |||
| return r; | |||
| } | |||
| @@ -250,10 +496,56 @@ lglw_bool_t lglw_window_resize (lglw_t _lglw, int32_t _w, int32_t _h) { | |||
| lglw_bool_t r = LGLW_FALSE; | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| if(0 != lglw->win.xwnd) | |||
| { | |||
| lglw_log("lglw:lglw_window_resize: 1\n"); | |||
| r = LGLW_TRUE; | |||
| lglw_log("lglw:lglw_window_resize: 2\n"); | |||
| XResizeWindow(lglw->xdsp, lglw->win.xwnd, _w, _h); | |||
| XRaiseWindow(lglw->xdsp, lglw->win.xwnd); | |||
| lglw_log("lglw:lglw_window_resize: 3\n"); | |||
| int deltaW = _w - lglw->win.size.x; | |||
| int deltaH = _h - lglw->win.size.y; | |||
| lglw_log("lglw:lglw_window_resize: 4\n"); | |||
| lglw->win.size.x = _w; | |||
| lglw->win.size.y = _h; | |||
| lglw_log("lglw:lglw_window_resize: 5\n"); | |||
| Window root, parent, *children = NULL; | |||
| unsigned int num_children; | |||
| lglw_log("lglw:lglw_window_resize: 6\n"); | |||
| if(!XQueryTree(lglw->xdsp, lglw->win.xwnd, &root, &parent, &children, &num_children)) | |||
| return r; | |||
| lglw_log("lglw:lglw_window_resize: 7\n"); | |||
| if(children) | |||
| XFree((char *)children); | |||
| lglw_log("lglw:lglw_window_resize: 8\n"); | |||
| // Resize parent window (if any) | |||
| if(0 != parent) | |||
| { | |||
| lglw_log("lglw:lglw_window_resize: 8.1\n"); | |||
| int x, y; | |||
| unsigned int width, height; | |||
| unsigned int border_width; | |||
| unsigned int depth; | |||
| lglw_log("lglw:lglw_window_resize: 8.2\n"); | |||
| if(!XGetGeometry(lglw->xdsp, lglw->win.xwnd, &root, &x, &y, &width, &height, &border_width, &depth)) | |||
| return r; | |||
| lglw_log("lglw:lglw_window_resize: 8.3\n"); | |||
| XResizeWindow(lglw->xdsp, parent, width + deltaW, height + deltaH); | |||
| } | |||
| lglw_log("lglw:lglw_window_resize: EXIT\n"); | |||
| } | |||
| } | |||
| return r; | |||
| @@ -264,17 +556,27 @@ lglw_bool_t lglw_window_resize (lglw_t _lglw, int32_t _w, int32_t _h) { | |||
| void lglw_window_close (lglw_t _lglw) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| // if(NULL != lglw->win.hwnd) | |||
| if(0 != lglw->win.xwnd) | |||
| { | |||
| lglw_log("lglw:lglw_window_close: 1\n"); | |||
| lglw_timer_stop(_lglw); | |||
| lglw_log("lglw:lglw_window_close: 2\n"); | |||
| loc_key_unhook(lglw); | |||
| lglw_log("lglw:lglw_window_close: 3\n"); | |||
| glXMakeCurrent(lglw->xdsp, None, NULL); | |||
| lglw_log("lglw:lglw_window_close: 4\n"); | |||
| XDestroyWindow(lglw->xdsp, lglw->win.xwnd); | |||
| XSync(lglw->xdsp, False); | |||
| lglw->win.xwnd = 0; | |||
| lglw->win.mapped = LGLW_FALSE; | |||
| } | |||
| } | |||
| lglw_log("lglw:lglw_window_close: EXIT\n"); | |||
| } | |||
| @@ -282,11 +584,14 @@ void lglw_window_close (lglw_t _lglw) { | |||
| void lglw_window_show(lglw_t _lglw) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| lglw_log("lglw:lglw_window_show: 1\n"); | |||
| XMapRaised(lglw->xdsp, lglw->win.xwnd); | |||
| lglw->win.mapped = LGLW_TRUE; | |||
| } | |||
| lglw_log("lglw:lglw_window_show: EXIT\n"); | |||
| } | |||
| @@ -294,11 +599,14 @@ void lglw_window_show(lglw_t _lglw) { | |||
| void lglw_window_hide(lglw_t _lglw) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| lglw_log("lglw:lglw_window_hide: 1\n"); | |||
| XUnmapWindow(lglw->xdsp, lglw->win.xwnd); | |||
| lglw->win.mapped = LGLW_FALSE; | |||
| } | |||
| lglw_log("lglw:lglw_window_hide: EXIT\n"); | |||
| } | |||
| @@ -307,12 +615,14 @@ lglw_bool_t lglw_window_is_visible(lglw_t _lglw) { | |||
| lglw_bool_t r = LGLW_FALSE; | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| // lglw_log("lglw:lglw_window_is_visible: 1\n"); | |||
| if(NULL != lglw && 0 != lglw->win.xwnd) | |||
| { | |||
| // lglw_log("lglw:lglw_window_is_visible: 2\n"); | |||
| r = lglw->win.mapped; | |||
| } | |||
| // lglw_log("lglw:lglw_window_is_visible: EXIT\n"); | |||
| return r; | |||
| } | |||
| @@ -321,11 +631,19 @@ lglw_bool_t lglw_window_is_visible(lglw_t _lglw) { | |||
| void lglw_window_size_get(lglw_t _lglw, int32_t *_retX, int32_t *_retY) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| lglw_log("lglw:lglw_window_size_get: 1\n"); | |||
| if(NULL != lglw) | |||
| { | |||
| if(0 != lglw->win.xwnd) | |||
| { | |||
| if(NULL != _retX) | |||
| *_retX = lglw->win.size.x; | |||
| if(NULL != _retY) | |||
| *_retY = lglw->win.size.y; | |||
| } | |||
| } | |||
| lglw_log("lglw:lglw_window_size_get: EXIT\n"); | |||
| } | |||
| @@ -337,6 +655,13 @@ void lglw_redraw(lglw_t _lglw) { | |||
| if(NULL != lglw) | |||
| { | |||
| if(0 != lglw->win.xwnd) | |||
| { | |||
| // TODO Event Loop | |||
| lglw_log("lglw:lglw_redraw: 1\n"); | |||
| XClearArea(lglw->xdsp, lglw->win.xwnd, 0, 0, 1, 1, True); // clear tiny area for exposing | |||
| XFlush(lglw->xdsp); | |||
| } | |||
| } | |||
| } | |||
| @@ -355,11 +680,18 @@ void lglw_redraw_callback_set(lglw_t _lglw, lglw_redraw_fxn_t _cbk) { | |||
| // ---------------------------------------------------------------------------- lglw_glcontext_push | |||
| void lglw_glcontext_push(lglw_t _lglw) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| lglw->prev.drw = glXGetCurrentDrawable(); | |||
| lglw->prev.ctx = glXGetCurrentContext(); | |||
| // lglw_log("lglw:lglw_glcontext_push: win.xwnd=%p hidden.xwnd=%p ctx=%p\n", | |||
| // lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx); | |||
| if(!glXMakeCurrent(lglw->xdsp, (0 == lglw->win.xwnd) ? lglw->hidden.xwnd : lglw->win.xwnd, lglw->ctx)) | |||
| { | |||
| lglw_log("[---] lglw_glcontext_push: glXMakeCurrent() failed. win.xwnd=%p hidden.xwnd=%p ctx=%p glGetError()=%d\n", lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx, glGetError()); | |||
| } | |||
| } | |||
| } | |||
| @@ -367,11 +699,15 @@ void lglw_glcontext_push(lglw_t _lglw) { | |||
| // ---------------------------------------------------------------------------- lglw_glcontext_pop | |||
| void lglw_glcontext_pop(lglw_t _lglw) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| // lglw_log("lglw:lglw_glcontext_pop: prev.drw=%p prev.ctx=%p\n", | |||
| // lglw->prev.drw, lglw->prev.ctx); | |||
| if(!glXMakeCurrent(lglw->xdsp, lglw->prev.drw, lglw->prev.ctx)) | |||
| { | |||
| lglw_log("[---] lglw_glcontext_pop: glXMakeCurrent() failed. prev.drw=%p ctx=%p glGetError()=%d\n", lglw->prev.drw, lglw->prev.ctx, glGetError()); | |||
| } | |||
| } | |||
| } | |||
| @@ -380,22 +716,33 @@ void lglw_glcontext_pop(lglw_t _lglw) { | |||
| void lglw_swap_buffers(lglw_t _lglw) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| if(0 != lglw->win.xwnd) | |||
| { | |||
| // lglw_log("lglw:lglw_swap_buffers: 1\n"); | |||
| glXSwapBuffers(lglw->xdsp, lglw->win.xwnd); | |||
| } | |||
| } | |||
| } | |||
| // ---------------------------------------------------------------------------- lglw_swap_interval_set | |||
| typedef void (APIENTRY *PFNWGLEXTSWAPINTERVALPROC) (int); | |||
| void lglw_swap_interval_set(lglw_t _lglw, int32_t _ival) { | |||
| LGLW(_lglw); | |||
| // (todo) implement me | |||
| if(NULL != lglw) | |||
| { | |||
| lglw_log("lglw:lglw_swap_interval_set: 1\n"); | |||
| PFNWGLEXTSWAPINTERVALPROC glXSwapIntervalEXT; | |||
| glXSwapIntervalEXT = (PFNWGLEXTSWAPINTERVALPROC) glXGetProcAddress("glXSwapIntervalEXT"); | |||
| if(NULL != glXSwapIntervalEXT) | |||
| { | |||
| lglw_log("lglw:lglw_swap_interval_set: 2\n"); | |||
| glXSwapIntervalEXT(_ival); | |||
| lglw->win.swap_interval = _ival; | |||
| } | |||
| } | |||
| } | |||
| @@ -444,7 +791,7 @@ static void loc_handle_mouseleave(lglw_int_t *lglw) { | |||
| lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE); | |||
| } | |||
| Dprintf("xxx lglw:loc_handle_mouseleave: LEAVE\n"); | |||
| lglw_log("xxx lglw:loc_handle_mouseleave: LEAVE\n"); | |||
| } | |||
| @@ -460,7 +807,7 @@ static void loc_handle_mouseenter(lglw_int_t *lglw) { | |||
| lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE); | |||
| } | |||
| Dprintf("xxx lglw:loc_handle_mouseenter: LEAVE\n"); | |||
| lglw_log("xxx lglw:loc_handle_mouseenter: LEAVE\n"); | |||
| } | |||
| @@ -6,7 +6,7 @@ include ../../dep/yac/install_linux.mk | |||
| .PHONY: bin | |||
| bin: vst2_debug_host.o | |||
| $(CPP) -o vst2_debug_host vst2_debug_host.o -ldl | |||
| $(CPP) -o vst2_debug_host vst2_debug_host.o -ldl -L/usr/lib -lpthread -lX11 | |||
| .PHONY: clean | |||
| clean: | |||
| @@ -2,7 +2,8 @@ | |||
| // | |||
| #define DLL_PATH "../../vst2_bin/veeseevstrack_effect.dll" | |||
| #define SO_PATH "../../vst2_bin/veeseevstrack_effect.so" | |||
| // #define SO_PATH "../../vst2_bin/veeseevstrack_effect.so" | |||
| #define SO_PATH "../vst2_lglw_debug_plugin/debug_lglw.so" | |||
| #include <yac.h> | |||
| @@ -12,6 +13,11 @@ | |||
| #include <windows.h> | |||
| #else | |||
| #include <dlfcn.h> | |||
| #include <unistd.h> | |||
| #include <X11/Xlib.h> | |||
| #include <X11/Xutil.h> | |||
| #include <X11/Xos.h> | |||
| #include <X11/Xatom.h> | |||
| #endif | |||
| #include <aeffect.h> | |||
| @@ -19,6 +25,49 @@ | |||
| typedef AEffect* (*PluginEntryProc) (audioMasterCallback audioMaster); | |||
| #ifndef YAC_WIN32 | |||
| // https://github.com/Ardour/ardour/blob/master/gtk2_ardour/linux_vst_gui_support.cc | |||
| long getXWindowProperty(Display* display, Window window, Atom atom) | |||
| { | |||
| long result = 0; | |||
| int userSize; | |||
| unsigned long bytes; | |||
| unsigned long userCount; | |||
| unsigned char *data; | |||
| Atom userType; | |||
| // LXVST_xerror = false; | |||
| /*Use our own Xerror handler while we're in here - in an | |||
| attempt to stop the brain dead default Xerror behaviour of | |||
| qutting the entire application because of e.g. an invalid | |||
| window ID*/ | |||
| // XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler); | |||
| XGetWindowProperty(display, | |||
| window, | |||
| atom, | |||
| 0, | |||
| 2, | |||
| false, | |||
| AnyPropertyType, | |||
| &userType, | |||
| &userSize, | |||
| &userCount, | |||
| &bytes, | |||
| &data); | |||
| if(userCount == 1) | |||
| result = *(long*)data; | |||
| // XSetErrorHandler(olderrorhandler); | |||
| /*Hopefully this will return zero if the property is not set*/ | |||
| return result; | |||
| } | |||
| #endif | |||
| static VstIntPtr VSTCALLBACK HostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) { | |||
| static VstInt32 lastOpcode = -1; | |||
| @@ -48,6 +97,15 @@ void open_and_close(void) { | |||
| } | |||
| #endif | |||
| #ifndef YAC_WIN32 | |||
| Display *d; | |||
| Window w; | |||
| int s; | |||
| d = XOpenDisplay(NULL); | |||
| s = DefaultScreen(d); | |||
| #endif | |||
| for(int i = 0; i < 48; i++) | |||
| { | |||
| inputBuffers[i] = new float[4096]; | |||
| @@ -79,18 +137,75 @@ void open_and_close(void) { | |||
| if(NULL != effect) | |||
| { | |||
| #ifndef YAC_WIN32 | |||
| w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1, | |||
| BlackPixel(d, s), WhitePixel(d, s)); | |||
| XSelectInput(d, w, ExposureMask | KeyPressMask); | |||
| XMapRaised(d, w); | |||
| XFlush(d); | |||
| #endif | |||
| printf("xxx calling effect->dispatcher<effOpen>\n"); | |||
| effect->dispatcher(effect, effOpen, 0, 0, NULL, 0.0f); | |||
| printf("xxx effect->dispatcher<effOpen> returned\n"); | |||
| #ifdef YAC_WIN32 | |||
| VstIntPtr ip = effect->dispatcher(effect, effEditOpen, 0, 0, NULL/*hWnd*/, 0.0f); | |||
| #else | |||
| VstIntPtr ip = effect->dispatcher(effect, effEditOpen, 0, 0, (void*)(w)/*hWnd*/, 0.0f); | |||
| #endif | |||
| (void)ip; | |||
| sleep(2); | |||
| #ifndef YAC_WIN32 | |||
| long result = getXWindowProperty(d, w, XInternAtom(d, "_XEventProc", false)); | |||
| if(result == 0) | |||
| { | |||
| printf("xxx no XEventProc found\n"); | |||
| } | |||
| else | |||
| { | |||
| printf("xxx XEventProc found... calling\n"); | |||
| void (* eventProc) (void * event); | |||
| eventProc = (void (*) (void* event))result; | |||
| eventProc(NULL); | |||
| } | |||
| #endif | |||
| printf("xxx calling effect->dispatcher<effEditIdle>\n"); | |||
| effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); | |||
| sleep(1); | |||
| printf("xxx calling effect->dispatcher<effEditIdle>\n"); | |||
| effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); | |||
| sleep(1); | |||
| printf("xxx calling effect->dispatcher<effEditClose>\n"); | |||
| effect->dispatcher(effect, effEditClose, 0, 0, NULL, 0.0f); | |||
| sleep(1); | |||
| printf("xxx calling effect->dispatcher<effEditOpen> again\n"); | |||
| #ifdef YAC_WIN32 | |||
| effect->dispatcher(effect, effEditOpen, 0, 0, NULL/*hWnd*/, 0.0f); | |||
| #else | |||
| effect->dispatcher(effect, effEditOpen, 0, 0, (void*)(w)/*hWnd*/, 0.0f); | |||
| #endif | |||
| sleep(1); | |||
| printf("xxx calling effect->dispatcher<effEditIdle>\n"); | |||
| effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); | |||
| sleep(1); | |||
| printf("xxx call processreplacing\n"); | |||
| for(int i = 0; i < 1024; i++) | |||
| { | |||
| effect->processReplacing(effect, inputBuffers, outputBuffers, (VstInt32)64); | |||
| } | |||
| printf("xxx calling effect->dispatcher<effEditClose>\n"); | |||
| effect->dispatcher(effect, effEditClose, 0, 0, NULL, 0.0f); | |||
| sleep(1); | |||
| printf("xxx calling effect->dispatcher<effClose>\n"); | |||
| effect->dispatcher(effect, effClose, 0, 0, NULL, 0.0f); | |||
| sleep(1); | |||
| #ifndef YAC_WIN32 | |||
| XDestroyWindow(d, w); | |||
| #endif | |||
| } | |||
| } | |||
| else | |||
| @@ -110,10 +225,14 @@ void open_and_close(void) { | |||
| delete [] inputBuffers[i]; | |||
| delete [] outputBuffers[i]; | |||
| } | |||
| #ifndef YAC_WIN32 | |||
| XCloseDisplay(d); | |||
| #endif | |||
| } | |||
| int main() { | |||
| for(;;) | |||
| for(int i = 0; i < 5; i++) | |||
| { | |||
| open_and_close(); | |||
| } | |||
| @@ -0,0 +1,4 @@ | |||
| .DS_Store | |||
| M3dB/.idea | |||
| vst2.x/aeffect.h | |||
| vst2.x/aeffectx.h | |||
| @@ -0,0 +1,266 @@ | |||
| // !$*UTF8*$! | |||
| { | |||
| archiveVersion = 1; | |||
| classes = { | |||
| }; | |||
| objectVersion = 46; | |||
| objects = { | |||
| /* Begin PBXBuildFile section */ | |||
| BD2A1F5751BB6465EFEB5889 /* plugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BD2A144C3B32EFA5180A38F5 /* plugin.cpp */; }; | |||
| /* End PBXBuildFile section */ | |||
| /* Begin PBXFileReference section */ | |||
| BD2A1226DD4B6ED23F230900 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = "<group>"; }; | |||
| BD2A144C3B32EFA5180A38F5 /* plugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = plugin.cpp; sourceTree = "<group>"; }; | |||
| BD2A17C571405D51496892C4 /* M3dB.___VARIABLE_bundleExtension___ */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "M3dB.___VARIABLE_bundleExtension___"; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| BD2A18D6CA1C93179E40396A /* aeffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = aeffect.h; path = ../vst2.x/aeffect.h; sourceTree = "<group>"; }; | |||
| BD2A1CFE51F03D1B40DA1230 /* aeffectx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = aeffectx.h; path = ../vst2.x/aeffectx.h; sourceTree = "<group>"; }; | |||
| /* End PBXFileReference section */ | |||
| /* Begin PBXFrameworksBuildPhase section */ | |||
| BD2A17410135F1BFA2BB6503 /* Frameworks */ = { | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXFrameworksBuildPhase section */ | |||
| /* Begin PBXGroup section */ | |||
| BD2A15554318E7C2EF2CD5DC /* Products */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| BD2A17C571405D51496892C4 /* M3dB.___VARIABLE_bundleExtension___ */, | |||
| ); | |||
| name = Products; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| BD2A1DCFA27E77A6603B70A2 = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| BD2A15554318E7C2EF2CD5DC /* Products */, | |||
| BD2A1F10D41E35E7FCDFA898 /* M3dB */, | |||
| BD2A144C3B32EFA5180A38F5 /* plugin.cpp */, | |||
| BD2A18D6CA1C93179E40396A /* aeffect.h */, | |||
| BD2A1CFE51F03D1B40DA1230 /* aeffectx.h */, | |||
| ); | |||
| sourceTree = "<group>"; | |||
| }; | |||
| BD2A1F10D41E35E7FCDFA898 /* M3dB */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| BD2A1226DD4B6ED23F230900 /* Info.plist */, | |||
| ); | |||
| path = M3dB; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| /* End PBXGroup section */ | |||
| /* Begin PBXNativeTarget section */ | |||
| BD2A186B82B2C6E8A0E9022C /* M3dB */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = BD2A12A4CFEA12B6C7A8873E /* Build configuration list for PBXNativeTarget "M3dB" */; | |||
| buildPhases = ( | |||
| BD2A15C927C3AD450E2DD52C /* Sources */, | |||
| BD2A17410135F1BFA2BB6503 /* Frameworks */, | |||
| BD2A175E365F9ED8C1C35C6F /* Resources */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| ); | |||
| name = M3dB; | |||
| productName = M3dB; | |||
| productReference = BD2A17C571405D51496892C4 /* M3dB.___VARIABLE_bundleExtension___ */; | |||
| productType = "com.apple.product-type.bundle"; | |||
| }; | |||
| /* End PBXNativeTarget section */ | |||
| /* Begin PBXProject section */ | |||
| BD2A116B4EC711555868B9BA /* Project object */ = { | |||
| isa = PBXProject; | |||
| attributes = { | |||
| ORGANIZATIONNAME = pongasoft; | |||
| }; | |||
| buildConfigurationList = BD2A1B7627DAA8FF541E84EC /* Build configuration list for PBXProject "M3dB" */; | |||
| compatibilityVersion = "Xcode 3.2"; | |||
| developmentRegion = English; | |||
| hasScannedForEncodings = 0; | |||
| knownRegions = ( | |||
| en, | |||
| ); | |||
| mainGroup = BD2A1DCFA27E77A6603B70A2; | |||
| productRefGroup = BD2A15554318E7C2EF2CD5DC /* Products */; | |||
| projectDirPath = ""; | |||
| projectRoot = ""; | |||
| targets = ( | |||
| BD2A186B82B2C6E8A0E9022C /* M3dB */, | |||
| ); | |||
| }; | |||
| /* End PBXProject section */ | |||
| /* Begin PBXResourcesBuildPhase section */ | |||
| BD2A175E365F9ED8C1C35C6F /* Resources */ = { | |||
| isa = PBXResourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXResourcesBuildPhase section */ | |||
| /* Begin PBXSourcesBuildPhase section */ | |||
| BD2A15C927C3AD450E2DD52C /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| BD2A1F5751BB6465EFEB5889 /* plugin.cpp in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXSourcesBuildPhase section */ | |||
| /* Begin XCBuildConfiguration section */ | |||
| BD2A1223BF2A633FEE5D6099 /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| COMBINE_HIDPI_IMAGES = YES; | |||
| INFOPLIST_FILE = M3dB/Info.plist; | |||
| INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = org.pongasoft.vst24.M3dB; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SKIP_INSTALL = YES; | |||
| WRAPPER_EXTENSION = vst; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| BD2A17437E006499CC8685D1 /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| CLANG_ANALYZER_NONNULL = YES; | |||
| CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | |||
| CLANG_CXX_LIBRARY = "libc++"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CLANG_ENABLE_OBJC_ARC = YES; | |||
| CLANG_WARN_BOOL_CONVERSION = YES; | |||
| CLANG_WARN_CONSTANT_CONVERSION = YES; | |||
| CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | |||
| CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | |||
| CLANG_WARN_EMPTY_BODY = YES; | |||
| CLANG_WARN_ENUM_CONVERSION = YES; | |||
| CLANG_WARN_INFINITE_RECURSION = YES; | |||
| CLANG_WARN_INT_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | |||
| CLANG_WARN_SUSPICIOUS_MOVES = YES; | |||
| CLANG_WARN_UNREACHABLE_CODE = YES; | |||
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | |||
| CODE_SIGN_IDENTITY = "-"; | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUG_INFORMATION_FORMAT = dwarf; | |||
| ENABLE_STRICT_OBJC_MSGSEND = YES; | |||
| ENABLE_TESTABILITY = YES; | |||
| GCC_C_LANGUAGE_STANDARD = gnu99; | |||
| GCC_DYNAMIC_NO_PIC = NO; | |||
| GCC_NO_COMMON_BLOCKS = YES; | |||
| GCC_OPTIMIZATION_LEVEL = 0; | |||
| GCC_PREPROCESSOR_DEFINITIONS = ( | |||
| "DEBUG=1", | |||
| "$(inherited)", | |||
| ); | |||
| GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | |||
| GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | |||
| GCC_WARN_UNDECLARED_SELECTOR = YES; | |||
| GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | |||
| GCC_WARN_UNUSED_FUNCTION = YES; | |||
| GCC_WARN_UNUSED_VARIABLE = YES; | |||
| MACOSX_DEPLOYMENT_TARGET = 10.11; | |||
| MTL_ENABLE_DEBUG_INFO = YES; | |||
| ONLY_ACTIVE_ARCH = YES; | |||
| SDKROOT = macosx; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| BD2A186609800B3BD0ECFF98 /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| COMBINE_HIDPI_IMAGES = YES; | |||
| DEPLOYMENT_POSTPROCESSING = YES; | |||
| INFOPLIST_FILE = M3dB/Info.plist; | |||
| INSTALL_PATH = "$(HOME)/Library/Audio/Plug-Ins/VST"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = org.pongasoft.vst24.M3dB; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SKIP_INSTALL = NO; | |||
| WRAPPER_EXTENSION = vst; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| BD2A1D1EC28D17DC43ACBA09 /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| CLANG_ANALYZER_NONNULL = YES; | |||
| CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | |||
| CLANG_CXX_LIBRARY = "libc++"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CLANG_ENABLE_OBJC_ARC = YES; | |||
| CLANG_WARN_BOOL_CONVERSION = YES; | |||
| CLANG_WARN_CONSTANT_CONVERSION = YES; | |||
| CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | |||
| CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | |||
| CLANG_WARN_EMPTY_BODY = YES; | |||
| CLANG_WARN_ENUM_CONVERSION = YES; | |||
| CLANG_WARN_INFINITE_RECURSION = YES; | |||
| CLANG_WARN_INT_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | |||
| CLANG_WARN_SUSPICIOUS_MOVES = YES; | |||
| CLANG_WARN_UNREACHABLE_CODE = YES; | |||
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | |||
| CODE_SIGN_IDENTITY = "-"; | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | |||
| DEPLOYMENT_LOCATION = NO; | |||
| DEPLOYMENT_POSTPROCESSING = NO; | |||
| ENABLE_NS_ASSERTIONS = NO; | |||
| ENABLE_STRICT_OBJC_MSGSEND = YES; | |||
| GCC_C_LANGUAGE_STANDARD = gnu99; | |||
| GCC_NO_COMMON_BLOCKS = YES; | |||
| GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | |||
| GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | |||
| GCC_WARN_UNDECLARED_SELECTOR = YES; | |||
| GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | |||
| GCC_WARN_UNUSED_FUNCTION = YES; | |||
| GCC_WARN_UNUSED_VARIABLE = YES; | |||
| INSTALL_PATH = ""; | |||
| MACOSX_DEPLOYMENT_TARGET = 10.11; | |||
| MTL_ENABLE_DEBUG_INFO = NO; | |||
| SDKROOT = macosx; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| /* End XCBuildConfiguration section */ | |||
| /* Begin XCConfigurationList section */ | |||
| BD2A12A4CFEA12B6C7A8873E /* Build configuration list for PBXNativeTarget "M3dB" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| BD2A1223BF2A633FEE5D6099 /* Debug */, | |||
| BD2A186609800B3BD0ECFF98 /* Release */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| }; | |||
| BD2A1B7627DAA8FF541E84EC /* Build configuration list for PBXProject "M3dB" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| BD2A17437E006499CC8685D1 /* Debug */, | |||
| BD2A1D1EC28D17DC43ACBA09 /* Release */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| /* End XCConfigurationList section */ | |||
| }; | |||
| rootObject = BD2A116B4EC711555868B9BA /* Project object */; | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Workspace | |||
| version = "1.0"> | |||
| <FileRef | |||
| location = "self:"> | |||
| </FileRef> | |||
| </Workspace> | |||
| @@ -0,0 +1,5 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict /> | |||
| </plist> | |||
| @@ -0,0 +1,22 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Scheme | |||
| version = "1.3"> | |||
| <BuildAction> | |||
| <BuildActionEntries> | |||
| <BuildActionEntry | |||
| buildForRunning = "YES"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "BD2A186B82B2C6E8A0E9022C" | |||
| BuildableName = "M3dB.vst" | |||
| BlueprintName = "M3dB" | |||
| ReferencedContainer = "container:M3dB.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildActionEntry> | |||
| </BuildActionEntries> | |||
| </BuildAction> | |||
| <LaunchAction | |||
| useCustomWorkingDirectory = "NO" | |||
| buildConfiguration = "Debug"> | |||
| </LaunchAction> | |||
| </Scheme> | |||
| @@ -0,0 +1,22 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Scheme | |||
| version = "1.3"> | |||
| <BuildAction> | |||
| <BuildActionEntries> | |||
| <BuildActionEntry | |||
| buildForRunning = "YES"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "BD2A186B82B2C6E8A0E9022C" | |||
| BuildableName = "M3dB.vst" | |||
| BlueprintName = "M3dB" | |||
| ReferencedContainer = "container:M3dB.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildActionEntry> | |||
| </BuildActionEntries> | |||
| </BuildAction> | |||
| <LaunchAction | |||
| useCustomWorkingDirectory = "NO" | |||
| buildConfiguration = "Release"> | |||
| </LaunchAction> | |||
| </Scheme> | |||
| @@ -0,0 +1,19 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>SchemeUserState</key> | |||
| <dict> | |||
| <key>M3dB - Debug.xcscheme</key> | |||
| <dict> | |||
| <key>orderHint</key> | |||
| <integer>0</integer> | |||
| </dict> | |||
| <key>M3dB - Release.xcscheme</key> | |||
| <dict> | |||
| <key>orderHint</key> | |||
| <integer>1</integer> | |||
| </dict> | |||
| </dict> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,26 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>CFBundleDevelopmentRegion</key> | |||
| <string>en</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>$(EXECUTABLE_NAME)</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | |||
| <key>CFBundleInfoDictionaryVersion</key> | |||
| <string>6.0</string> | |||
| <key>CFBundleName</key> | |||
| <string>$(PRODUCT_NAME)</string> | |||
| <key>CFBundlePackageType</key> | |||
| <string>BNDL</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>1.0</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>1</string> | |||
| <key>NSHumanReadableCopyright</key> | |||
| <string>Copyright © 2017 pongasoft. All rights reserved.</string> | |||
| <key>NSPrincipalClass</key> | |||
| <string></string> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,19 @@ | |||
| include ../../dep/yac/install_linux.mk | |||
| .cpp.o: | |||
| $(CPP) $(CPPFLAGS) $(OPTFLAGS_PLUGIN) -I"$(VSVR_BASE_DIR)/dep/yac/" -I"$(VST2_SDK_DIR)" -I"$(VSVR_BASE_DIR)/dep/lglw/" -shared -fPIC -c "$<" -o"$@" | |||
| .PHONY: bin | |||
| bin: plugin.o | |||
| $(CPP) -shared -o debug_lglw.so -Wl,-soname,debug_lglw.so -mtls-dialect=gnu2 plugin.o "$(VSVR_BASE_DIR)/dep/lglw/lglw_linux.o" -ldl -lGL -L/usr/lib -lm -lpthread | |||
| .PHONY: clean | |||
| clean: | |||
| rm -f plugin plugin.o | |||
| .PHONY: all | |||
| all: | |||
| make -f makefile.linux clean | |||
| make -f makefile.linux bin | |||
| cp debug_lglw.so /home/cameron/.local/share/VST/ | |||
| @@ -0,0 +1,458 @@ | |||
| #include <aeffect.h> | |||
| #include <aeffectx.h> | |||
| #include <stdio.h> | |||
| #if defined(_WIN32) || defined(_WIN64) | |||
| #include <windows.h> | |||
| #include <wingdi.h> | |||
| #define VST_EXPORT extern "C" __declspec(dllexport) | |||
| #else | |||
| #include <GL/gl.h> | |||
| #include <X11/Xlib.h> | |||
| #include <X11/Xutil.h> | |||
| #include <X11/Xos.h> | |||
| #define VST_EXPORT extern | |||
| #endif | |||
| #include "lglw.h" | |||
| #define EDITWIN_X 20 | |||
| #define EDITWIN_Y 20 | |||
| #define EDITWIN_W 640 | |||
| #define EDITWIN_H 480 | |||
| /* | |||
| * I find the naming a bit confusing so I decided to use more meaningful names instead. | |||
| */ | |||
| /** | |||
| * The VSTHostCallback is a function pointer so that the plugin can communicate with the host (not used in this small example) | |||
| */ | |||
| typedef audioMasterCallback VSTHostCallback; | |||
| /** | |||
| * The VSTPlugin structure (AEffect) contains information about the plugin (like version, number of inputs, ...) and | |||
| * callbacks so that the host can call the plugin to do its work. The primary callback will be `processReplacing` for | |||
| * single precision (float) sample processing (or `processDoubleReplacing` for double precision (double)). | |||
| */ | |||
| typedef AEffect VSTPlugin; | |||
| // Since the host is expecting a very specific API we need to make sure it has C linkage (not C++) | |||
| extern "C" { | |||
| /* | |||
| * This is the main entry point to the VST plugin. | |||
| * | |||
| * The host (DAW like Maschine, Ableton Live, Reason, ...) will look for this function with this exact API. | |||
| * | |||
| * It is the equivalent to `int main(int argc, char *argv[])` for a C executable. | |||
| * | |||
| * @param vstHostCallback is a callback so that the plugin can communicate with the host (not used in this small example) | |||
| * @return a pointer to the AEffect structure | |||
| */ | |||
| VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback); | |||
| // note this looks like this without the type aliases (and is obviously 100% equivalent) | |||
| // extern AEffect *VSTPluginMain(audioMasterCallback audioMaster); | |||
| } | |||
| /* | |||
| * Constant for the version of the plugin. For example 1100 for version 1.1.0.0 | |||
| */ | |||
| const VstInt32 PLUGIN_VERSION = 1000; | |||
| // extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
| void loc_mouse_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _buttonState, uint32_t _changedButtonState) { | |||
| printf("xxx lglw_mouse_cbk: lglw=%p p=(%d; %d) bt=0x%08x changedBt=0x%08x\n", _lglw, _x, _y, _buttonState, _changedButtonState); | |||
| if(LGLW_IS_MOUSE_LBUTTON_DOWN()) | |||
| { | |||
| // lglw_mouse_grab(_lglw, LGLW_MOUSE_GRAB_CAPTURE); | |||
| lglw_mouse_grab(_lglw, LGLW_MOUSE_GRAB_WARP); | |||
| } | |||
| else if(LGLW_IS_MOUSE_LBUTTON_UP()) | |||
| { | |||
| lglw_mouse_ungrab(_lglw); | |||
| } | |||
| } | |||
| void loc_focus_cbk(lglw_t _lglw, uint32_t _focusState, uint32_t _changedFocusState) { | |||
| printf("xxx lglw_focus_cbk: lglw=%p focusState=0x%08x changedFocusState=0x%08x\n", _lglw, _focusState, _changedFocusState); | |||
| } | |||
| lglw_bool_t loc_keyboard_cbk(lglw_t _lglw, uint32_t _vkey, uint32_t _kmod, lglw_bool_t _bPressed) { | |||
| printf("xxx lglw_keyboard_cbk: lglw=%p vkey=0x%08x (\'%c\') kmod=0x%08x bPressed=%d\n", _lglw, _vkey, _vkey, _kmod, _bPressed); | |||
| return LGLW_FALSE; | |||
| } | |||
| void loc_timer_cbk(lglw_t _lglw) { | |||
| printf("xxx lglw_timer_cbk: tick\n"); | |||
| } | |||
| /** | |||
| * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the | |||
| * host (VSTPlugin). This class will be stored in the `VSTPlugin.object` field (circular reference) so that it can | |||
| * be accessed when the host calls the plugin back (for example in `processDoubleReplacing`). | |||
| */ | |||
| class VSTPluginWrapper | |||
| { | |||
| public: | |||
| ERect editor_rect; | |||
| lglw_t lglw; | |||
| float clear_color = 0.0f; | |||
| static VSTPluginWrapper *window_to_wrapper; | |||
| public: | |||
| VSTPluginWrapper(VSTHostCallback vstHostCallback, | |||
| VstInt32 vendorUniqueID, | |||
| VstInt32 vendorVersion, | |||
| VstInt32 numParams, | |||
| VstInt32 numPrograms, | |||
| VstInt32 numInputs, | |||
| VstInt32 numOutputs); | |||
| ~VSTPluginWrapper(); | |||
| inline VSTPlugin *getVSTPlugin() | |||
| { | |||
| return &_vstPlugin; | |||
| } | |||
| inline VstInt32 getNumInputs() const | |||
| { | |||
| return _vstPlugin.numInputs; | |||
| } | |||
| inline VstInt32 getNumOutputs() const | |||
| { | |||
| return _vstPlugin.numOutputs; | |||
| } | |||
| void openEditor(void *_hwnd) { | |||
| (void)lglw_window_open(lglw, _hwnd, 0/*x*/, 0/*y*/, EDITWIN_W, EDITWIN_H); | |||
| lglw_mouse_callback_set(lglw, &loc_mouse_cbk); | |||
| lglw_focus_callback_set(lglw, &loc_focus_cbk); | |||
| lglw_keyboard_callback_set(lglw, &loc_keyboard_cbk); | |||
| lglw_timer_callback_set(lglw, &loc_timer_cbk); | |||
| lglw_timer_start(lglw, 200); | |||
| window_to_wrapper = this; | |||
| } | |||
| void closeEditor(void) { | |||
| if(NULL != window_to_wrapper) | |||
| { | |||
| lglw_window_close(lglw); | |||
| window_to_wrapper = NULL; | |||
| } | |||
| } | |||
| static VSTPluginWrapper *FindWrapperByWindow(Window hwnd) { | |||
| return window_to_wrapper; | |||
| } | |||
| void redrawWindow(void) { | |||
| // Save host GL context | |||
| lglw_glcontext_push(lglw); | |||
| // Draw something | |||
| ::glClearColor(0.0, 1.0, clear_color, 1.0); | |||
| clear_color += 0.05f; | |||
| if(clear_color >= 1.0f) | |||
| clear_color -= 1.0f; | |||
| ::glClear(GL_COLOR_BUFFER_BIT); | |||
| ::glFlush(); | |||
| lglw_swap_buffers(lglw); | |||
| // Restore host GL context | |||
| lglw_glcontext_pop(lglw); | |||
| } | |||
| private: | |||
| // the host callback (a function pointer) | |||
| VSTHostCallback _vstHostCallback; | |||
| // the actual structure required by the host | |||
| VSTPlugin _vstPlugin; | |||
| }; | |||
| VSTPluginWrapper *VSTPluginWrapper::window_to_wrapper = NULL; | |||
| /******************************************* | |||
| * Callbacks: Host -> Plugin | |||
| * | |||
| * Defined here because they are used in the rest of the code later | |||
| */ | |||
| /** | |||
| * This is the callback that will be called to process the samples in the case of single precision. This is where the | |||
| * meat of the logic happens! | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @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] | |||
| * @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] | |||
| * @param sampleFrames the number of samples (second dimension in both arrays) | |||
| */ | |||
| void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float **outputs, VstInt32 sampleFrames) | |||
| { | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the | |||
| // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound) | |||
| for(int i = 0; i < wrapper->getNumInputs(); i++) | |||
| { | |||
| auto inputSamples = inputs[i]; | |||
| auto outputSamples = outputs[i]; | |||
| for(int j = 0; j < sampleFrames; j++) | |||
| { | |||
| outputSamples[j] = inputSamples[j] * 0.5f; | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * This is the callback that will be called to process the samples in the case of double precision. This is where the | |||
| * meat of the logic happens! | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @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] | |||
| * @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] | |||
| * @param sampleFrames the number of samples (second dimension in both arrays) | |||
| */ | |||
| void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, double **outputs, VstInt32 sampleFrames) | |||
| { | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the | |||
| // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound) | |||
| for(int i = 0; i < wrapper->getNumInputs(); i++) | |||
| { | |||
| auto inputSamples = inputs[i]; | |||
| auto outputSamples = outputs[i]; | |||
| for(int j = 0; j < sampleFrames; j++) | |||
| { | |||
| outputSamples[j] = inputSamples[j] * 0.5; | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the | |||
| * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...) | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand | |||
| * total of 79 of them! Only a few of them are implemented in this small plugin. | |||
| * @param index depend on the opcode | |||
| * @param value depend on the opcode | |||
| * @param ptr depend on the opcode | |||
| * @param opt depend on the opcode | |||
| * @return depend on the opcode (0 is ok when you don't implement an opcode...) | |||
| */ | |||
| VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt) | |||
| { | |||
| // printf("vstgltest: called VSTPluginDispatcher(%d)\n", opCode); | |||
| VstIntPtr r = 0; | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them | |||
| switch(opCode) | |||
| { | |||
| // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed | |||
| // to generating sound) | |||
| case effGetPlugCategory: | |||
| return kPlugCategEffect; | |||
| // called by the host when the plugin was called... time to reclaim memory! | |||
| case effClose: | |||
| delete wrapper; | |||
| break; | |||
| case effGetEffectName: | |||
| ::strncpy((char*)ptr, "VST GL Test", kVstMaxEffectNameLen); | |||
| r = 1; | |||
| break; | |||
| // request for the vendor string (usually used in the UI for plugin grouping) | |||
| case effGetVendorString: | |||
| strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen); | |||
| r = 1; | |||
| break; | |||
| // request for the version | |||
| case effGetVendorVersion: | |||
| return PLUGIN_VERSION; | |||
| case effGetParamName: | |||
| strncpy(static_cast<char *>(ptr), "myparam", kVstMaxParamStrLen); | |||
| r = 1; | |||
| break; | |||
| case effEditIdle: | |||
| printf("xxx vstgltest: redraw window\n"); | |||
| // (void)::RedrawWindow(wrapper->hwnd, NULL, NULL, RDW_INTERNALPAINT); | |||
| //(void)::UpdateWindow(wrapper->hwnd); | |||
| if(lglw_window_is_visible(wrapper->lglw)) | |||
| { | |||
| wrapper->redrawWindow(); | |||
| } | |||
| break; | |||
| case effEditGetRect: | |||
| // Query editor window geometry | |||
| // ptr: ERect* (on Windows) | |||
| if(NULL != ptr) // yeah, this should never be NULL | |||
| { | |||
| // ... | |||
| wrapper->editor_rect.left = EDITWIN_X; | |||
| wrapper->editor_rect.top = EDITWIN_Y; | |||
| wrapper->editor_rect.right = EDITWIN_X + EDITWIN_W; | |||
| wrapper->editor_rect.bottom = EDITWIN_Y + EDITWIN_H; | |||
| *(void**)ptr = (void*) &wrapper->editor_rect; | |||
| r = 1; | |||
| } | |||
| else | |||
| { | |||
| r = 0; | |||
| } | |||
| break; | |||
| #if 0 | |||
| case effEditTop: | |||
| // deprecated in vst2.4 | |||
| r = 0; | |||
| break; | |||
| #endif | |||
| case effEditOpen: | |||
| // Show editor window | |||
| // ptr: native window handle (hWnd on Windows) | |||
| wrapper->openEditor(ptr); | |||
| r = 1; | |||
| break; | |||
| case effEditClose: | |||
| // Hide editor window | |||
| wrapper->closeEditor(); | |||
| r = 1; | |||
| break; | |||
| // ignoring all other opcodes | |||
| default: | |||
| // printf("Unknown opCode %d [ignored] \n", opCode); | |||
| break; | |||
| } | |||
| return r; | |||
| } | |||
| /** | |||
| * Used for parameter setting (not used by this plugin) | |||
| */ | |||
| void VSTPluginSetParameter(VSTPlugin *vstPlugin, VstInt32 index, float parameter) | |||
| { | |||
| printf("called VSTPluginSetParameter(%d, %f)\n", index, parameter); | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| } | |||
| /** | |||
| * Used for parameter (not used by this plugin) | |||
| */ | |||
| float VSTPluginGetParameter(VSTPlugin *vstPlugin, VstInt32 index) | |||
| { | |||
| printf("called VSTPluginGetParameter(%d)\n", index); | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| return 0; | |||
| } | |||
| /** | |||
| * Main constructor for our C++ class | |||
| */ | |||
| VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback, | |||
| VstInt32 vendorUniqueID, | |||
| VstInt32 vendorVersion, | |||
| VstInt32 numParams, | |||
| VstInt32 numPrograms, | |||
| VstInt32 numInputs, | |||
| VstInt32 numOutputs) : | |||
| _vstHostCallback(vstHostCallback) | |||
| { | |||
| // Make sure that the memory is properly initialized | |||
| memset(&_vstPlugin, 0, sizeof(_vstPlugin)); | |||
| // this field must be set with this constant... | |||
| _vstPlugin.magic = kEffectMagic; | |||
| // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use) | |||
| _vstPlugin.object = this; | |||
| // specifying that we handle both single and double precision (there are other flags see aeffect.h/VstAEffectFlags) | |||
| _vstPlugin.flags = effFlagsCanReplacing | effFlagsCanDoubleReplacing | effFlagsHasEditor; | |||
| // initializing the plugin with the various values | |||
| _vstPlugin.uniqueID = vendorUniqueID; | |||
| _vstPlugin.version = vendorVersion; | |||
| _vstPlugin.numParams = numParams; | |||
| _vstPlugin.numPrograms = numPrograms; | |||
| _vstPlugin.numInputs = numInputs; | |||
| _vstPlugin.numOutputs = numOutputs; | |||
| // setting the callbacks to the previously defined functions | |||
| _vstPlugin.dispatcher = VSTPluginDispatcher; | |||
| _vstPlugin.getParameter = VSTPluginGetParameter; | |||
| _vstPlugin.setParameter = VSTPluginSetParameter; | |||
| _vstPlugin.processReplacing = VSTPluginProcessSamplesFloat32; | |||
| _vstPlugin.processDoubleReplacing = VSTPluginProcessSamplesFloat64; | |||
| lglw = lglw_init(EDITWIN_W, EDITWIN_H); | |||
| } | |||
| /** | |||
| * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin | |||
| * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a | |||
| * memory leak which may end up slowing down and/or crashing the host | |||
| */ | |||
| VSTPluginWrapper::~VSTPluginWrapper() { | |||
| lglw_exit(lglw); | |||
| } | |||
| /** | |||
| * Implementation of the main entry point of the plugin | |||
| */ | |||
| VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback) | |||
| { | |||
| printf("called VSTPluginMain... \n"); | |||
| // simply create our plugin C++ class | |||
| VSTPluginWrapper *plugin = | |||
| new VSTPluginWrapper(vstHostCallback, | |||
| CCONST('u', 's', 'a', '§'), // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm) | |||
| PLUGIN_VERSION, // version | |||
| 2, // no params | |||
| 0, // no programs | |||
| 2, // 2 inputs | |||
| 2); // 2 outputs | |||
| // return the plugin per the contract of the API | |||
| return plugin->getVSTPlugin(); | |||
| } | |||
| @@ -0,0 +1,615 @@ | |||
| #include <aeffect.h> | |||
| #include <aeffectx.h> | |||
| #include <stdio.h> | |||
| #if defined(_WIN32) || defined(_WIN64) | |||
| #include <windows.h> | |||
| #include <wingdi.h> | |||
| #define VST_EXPORT extern "C" __declspec(dllexport) | |||
| #else | |||
| #define VST_EXPORT extern | |||
| #endif | |||
| #include <GL/GL.h> | |||
| #define EDITWIN_X 20 | |||
| #define EDITWIN_Y 20 | |||
| #define EDITWIN_W 640 | |||
| #define EDITWIN_H 480 | |||
| #define EDITWIN_CLASS_NAME "VeeSeeVSTWindow" | |||
| #define EDITWIN_HIDDEN_CLASS_NAME "hidden_VeeSeeVSTWindow" | |||
| /* | |||
| * I find the naming a bit confusing so I decided to use more meaningful names instead. | |||
| */ | |||
| /** | |||
| * The VSTHostCallback is a function pointer so that the plugin can communicate with the host (not used in this small example) | |||
| */ | |||
| typedef audioMasterCallback VSTHostCallback; | |||
| /** | |||
| * The VSTPlugin structure (AEffect) contains information about the plugin (like version, number of inputs, ...) and | |||
| * callbacks so that the host can call the plugin to do its work. The primary callback will be `processReplacing` for | |||
| * single precision (float) sample processing (or `processDoubleReplacing` for double precision (double)). | |||
| */ | |||
| typedef AEffect VSTPlugin; | |||
| // Since the host is expecting a very specific API we need to make sure it has C linkage (not C++) | |||
| extern "C" { | |||
| /* | |||
| * This is the main entry point to the VST plugin. | |||
| * | |||
| * The host (DAW like Maschine, Ableton Live, Reason, ...) will look for this function with this exact API. | |||
| * | |||
| * It is the equivalent to `int main(int argc, char *argv[])` for a C executable. | |||
| * | |||
| * @param vstHostCallback is a callback so that the plugin can communicate with the host (not used in this small example) | |||
| * @return a pointer to the AEffect structure | |||
| */ | |||
| VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback); | |||
| // note this looks like this without the type aliases (and is obviously 100% equivalent) | |||
| // extern AEffect *VSTPluginMain(audioMasterCallback audioMaster); | |||
| } | |||
| /* | |||
| * Constant for the version of the plugin. For example 1100 for version 1.1.0.0 | |||
| */ | |||
| const VstInt32 PLUGIN_VERSION = 1000; | |||
| extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
| /** | |||
| * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the | |||
| * host (VSTPlugin). This class will be stored in the `VSTPlugin.object` field (circular reference) so that it can | |||
| * be accessed when the host calls the plugin back (for example in `processDoubleReplacing`). | |||
| */ | |||
| class VSTPluginWrapper | |||
| { | |||
| public: | |||
| ERect editor_rect; | |||
| HWND parent_hwnd; // created by host | |||
| struct { | |||
| HWND hwnd; | |||
| HDC hdc; | |||
| } hidden; | |||
| struct { | |||
| HWND hwnd; | |||
| HDC hdc; | |||
| } editor; | |||
| HGLRC hglrc; | |||
| float clear_color = 0.0f; | |||
| static const char *registered_class_name; | |||
| static VSTPluginWrapper *window_to_wrapper; | |||
| public: | |||
| VSTPluginWrapper(VSTHostCallback vstHostCallback, | |||
| VstInt32 vendorUniqueID, | |||
| VstInt32 vendorVersion, | |||
| VstInt32 numParams, | |||
| VstInt32 numPrograms, | |||
| VstInt32 numInputs, | |||
| VstInt32 numOutputs); | |||
| ~VSTPluginWrapper(); | |||
| inline VSTPlugin *getVSTPlugin() | |||
| { | |||
| return &_vstPlugin; | |||
| } | |||
| inline VstInt32 getNumInputs() const | |||
| { | |||
| return _vstPlugin.numInputs; | |||
| } | |||
| inline VstInt32 getNumOutputs() const | |||
| { | |||
| return _vstPlugin.numOutputs; | |||
| } | |||
| void createHiddenWindow(void) { | |||
| WNDCLASS wc; | |||
| ::ZeroMemory(&wc, sizeof(wc)); | |||
| wc.style = CS_OWNDC; | |||
| wc.lpfnWndProc = (WNDPROC) WndProc; | |||
| wc.hInstance = ::GetModuleHandle(NULL); | |||
| wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); | |||
| wc.lpszClassName = EDITWIN_HIDDEN_CLASS_NAME; | |||
| if(!::RegisterClass(&wc)) | |||
| { | |||
| // something went terribly wrong | |||
| printf("[---] vstgltest: failed to register hidden window class\n"); | |||
| return; | |||
| } | |||
| DWORD dwExStyle = 0; | |||
| DWORD dwStyle = 0; | |||
| hidden.hwnd = ::CreateWindowEx(dwExStyle, | |||
| EDITWIN_HIDDEN_CLASS_NAME, | |||
| "Test", | |||
| dwStyle, | |||
| 0/*xpos*/, 0/*ypos*/, | |||
| EDITWIN_W, EDITWIN_H, | |||
| NULL/*parentHWND*/, | |||
| NULL, // window menu | |||
| ::GetModuleHandle(NULL), | |||
| NULL | |||
| ); | |||
| PIXELFORMATDESCRIPTOR pfd = { | |||
| sizeof(PIXELFORMATDESCRIPTOR), | |||
| 1, | |||
| PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags | |||
| PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. | |||
| 32, // Colordepth of the framebuffer. | |||
| 0, 0, 0, 0, 0, 0, | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 0, 0, 0, 0, | |||
| 24, // Number of bits for the depthbuffer | |||
| 8, // Number of bits for the stencilbuffer | |||
| 0, // Number of Aux buffers in the framebuffer. | |||
| PFD_MAIN_PLANE, | |||
| 0, | |||
| 0, 0, 0 | |||
| }; | |||
| hidden.hdc = ::GetDC(hidden.hwnd); | |||
| printf("xxx vstgltest: hidden hdc=%p\n", hidden.hdc); | |||
| int pfmt = ::ChoosePixelFormat(hidden.hdc, &pfd); | |||
| printf("xxx vstgltest: hidden pfmt=%d\n", pfmt); | |||
| ::SetPixelFormat(hidden.hdc, pfmt, &pfd); | |||
| hglrc = ::wglCreateContext(hidden.hdc); | |||
| printf("xxx vstgltest: hidden hglrc=%p\n", hglrc); | |||
| } | |||
| void destroyHiddenWindow(void) { | |||
| if(NULL != hidden.hwnd) | |||
| { | |||
| ::wglDeleteContext(hglrc); | |||
| ::DestroyWindow(hidden.hwnd); | |||
| ::UnregisterClass(EDITWIN_HIDDEN_CLASS_NAME, ::GetModuleHandle(NULL)); | |||
| } | |||
| } | |||
| void openEditor(HWND _hwnd) { | |||
| parent_hwnd = _hwnd; | |||
| if(NULL == registered_class_name) | |||
| { | |||
| WNDCLASS wc; | |||
| ::ZeroMemory(&wc, sizeof(wc)); | |||
| wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; | |||
| wc.lpfnWndProc = (WNDPROC) WndProc; | |||
| wc.hInstance = ::GetModuleHandle(NULL); | |||
| wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); | |||
| wc.lpszClassName = EDITWIN_CLASS_NAME; | |||
| if(!::RegisterClass(&wc)) | |||
| { | |||
| // something went terribly wrong | |||
| printf("[---] vstgltest: failed to register window class\n"); | |||
| return; | |||
| } | |||
| registered_class_name = EDITWIN_CLASS_NAME; | |||
| } | |||
| DWORD dwExStyle = 0; | |||
| DWORD dwStyle = WS_CHILD | WS_VISIBLE; | |||
| editor.hwnd = ::CreateWindowEx(dwExStyle, | |||
| EDITWIN_CLASS_NAME, | |||
| "Test", | |||
| dwStyle, | |||
| 0/*xpos*/, 0/*ypos*/, | |||
| EDITWIN_W, EDITWIN_H, | |||
| parent_hwnd, | |||
| NULL, // window menu | |||
| ::GetModuleHandle(NULL), | |||
| NULL | |||
| ); | |||
| window_to_wrapper = this; | |||
| PIXELFORMATDESCRIPTOR pfd = { | |||
| sizeof(PIXELFORMATDESCRIPTOR), | |||
| 1, | |||
| PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags | |||
| PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. | |||
| 32, // Colordepth of the framebuffer. | |||
| 0, 0, 0, 0, 0, 0, | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 0, 0, 0, 0, | |||
| 24, // Number of bits for the depthbuffer | |||
| 8, // Number of bits for the stencilbuffer | |||
| 0, // Number of Aux buffers in the framebuffer. | |||
| PFD_MAIN_PLANE, | |||
| 0, | |||
| 0, 0, 0 | |||
| }; | |||
| editor.hdc = ::GetDC(editor.hwnd); | |||
| int pfmt = ::ChoosePixelFormat(editor.hdc, &pfd); | |||
| printf("xxx vstgltest: editor pfmt=%d\n", pfmt); | |||
| ::SetPixelFormat(editor.hdc, pfmt, &pfd); | |||
| // // hglrc = ::wglCreateContext(hdc); | |||
| } | |||
| void closeEditor(void) { | |||
| if(NULL != editor.hwnd) | |||
| { | |||
| ::DestroyWindow(editor.hwnd); | |||
| editor.hwnd = NULL; | |||
| // (todo) unregister when last window is closed | |||
| ::UnregisterClass(EDITWIN_CLASS_NAME, ::GetModuleHandle(NULL)); | |||
| registered_class_name = NULL; | |||
| window_to_wrapper = NULL; | |||
| } | |||
| } | |||
| static VSTPluginWrapper *FindWrapperByWindow(HWND hwnd) { | |||
| return window_to_wrapper; | |||
| } | |||
| void redrawWindow(void) { | |||
| // Save host GL context | |||
| HDC hostHDC = ::wglGetCurrentDC(); | |||
| HGLRC hostHGLRC = ::wglGetCurrentContext(); | |||
| printf("xxx vstgltest: WM_PAINT: hostHDC=%p hostGLRC=%p\n", hostHDC, hostHGLRC); | |||
| // Bind plugin GL context | |||
| printf("xxx vstgltest: WM_PAINT: pluginHDC=%p pluginGLRC=%p\n", editor.hdc, hglrc); | |||
| ::wglMakeCurrent(editor.hdc, hglrc); | |||
| // Draw something | |||
| ::glClearColor(0.0, 1.0, clear_color, 1.0); | |||
| clear_color += 0.05f; | |||
| if(clear_color >= 1.0f) | |||
| clear_color -= 1.0f; | |||
| ::glClear(GL_COLOR_BUFFER_BIT); | |||
| ::glFlush(); | |||
| ::wglSwapLayerBuffers(editor.hdc, WGL_SWAP_MAIN_PLANE); | |||
| // Restore host GL context | |||
| ::wglMakeCurrent(hostHDC, hostHGLRC); | |||
| } | |||
| private: | |||
| // the host callback (a function pointer) | |||
| VSTHostCallback _vstHostCallback; | |||
| // the actual structure required by the host | |||
| VSTPlugin _vstPlugin; | |||
| }; | |||
| const char *VSTPluginWrapper::registered_class_name = NULL; | |||
| VSTPluginWrapper *VSTPluginWrapper::window_to_wrapper = NULL; | |||
| extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { | |||
| switch(message) | |||
| { | |||
| case WM_CREATE: | |||
| printf("xxx vstgltest: WM_CREATE\n"); | |||
| break; | |||
| case WM_DESTROY: | |||
| printf("xxx vstgltest: WM_DESTROY\n"); | |||
| break; | |||
| case WM_LBUTTONDOWN: | |||
| printf("xxx vstgltest: WM_LBUTTONDOWN\n"); | |||
| break; | |||
| case WM_LBUTTONUP: | |||
| printf("xxx vstgltest: WM_LBUTTONUP\n"); | |||
| break; | |||
| case WM_PAINT: | |||
| // https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-redrawwindow | |||
| printf("xxx vstgltest: WM_PAINT\n"); | |||
| VSTPluginWrapper *wrapper = VSTPluginWrapper::FindWrapperByWindow(hWnd); | |||
| wrapper->redrawWindow(); | |||
| break; | |||
| } | |||
| return DefWindowProc(hWnd, message, wParam, lParam); | |||
| } | |||
| /******************************************* | |||
| * Callbacks: Host -> Plugin | |||
| * | |||
| * Defined here because they are used in the rest of the code later | |||
| */ | |||
| /** | |||
| * This is the callback that will be called to process the samples in the case of single precision. This is where the | |||
| * meat of the logic happens! | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @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] | |||
| * @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] | |||
| * @param sampleFrames the number of samples (second dimension in both arrays) | |||
| */ | |||
| void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float **outputs, VstInt32 sampleFrames) | |||
| { | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the | |||
| // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound) | |||
| for(int i = 0; i < wrapper->getNumInputs(); i++) | |||
| { | |||
| auto inputSamples = inputs[i]; | |||
| auto outputSamples = outputs[i]; | |||
| for(int j = 0; j < sampleFrames; j++) | |||
| { | |||
| outputSamples[j] = inputSamples[j] * 0.5f; | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * This is the callback that will be called to process the samples in the case of double precision. This is where the | |||
| * meat of the logic happens! | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @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] | |||
| * @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] | |||
| * @param sampleFrames the number of samples (second dimension in both arrays) | |||
| */ | |||
| void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, double **outputs, VstInt32 sampleFrames) | |||
| { | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the | |||
| // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound) | |||
| for(int i = 0; i < wrapper->getNumInputs(); i++) | |||
| { | |||
| auto inputSamples = inputs[i]; | |||
| auto outputSamples = outputs[i]; | |||
| for(int j = 0; j < sampleFrames; j++) | |||
| { | |||
| outputSamples[j] = inputSamples[j] * 0.5; | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the | |||
| * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...) | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand | |||
| * total of 79 of them! Only a few of them are implemented in this small plugin. | |||
| * @param index depend on the opcode | |||
| * @param value depend on the opcode | |||
| * @param ptr depend on the opcode | |||
| * @param opt depend on the opcode | |||
| * @return depend on the opcode (0 is ok when you don't implement an opcode...) | |||
| */ | |||
| VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt) | |||
| { | |||
| printf("vstgltest: called VSTPluginDispatcher(%d)\n", opCode); | |||
| VstIntPtr r = 0; | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them | |||
| switch(opCode) | |||
| { | |||
| // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed | |||
| // to generating sound) | |||
| case effGetPlugCategory: | |||
| return kPlugCategEffect; | |||
| // called by the host when the plugin was called... time to reclaim memory! | |||
| case effClose: | |||
| delete wrapper; | |||
| break; | |||
| case effGetEffectName: | |||
| ::strncpy((char*)ptr, "VST GL Test", kVstMaxEffectNameLen); | |||
| r = 1; | |||
| break; | |||
| // request for the vendor string (usually used in the UI for plugin grouping) | |||
| case effGetVendorString: | |||
| strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen); | |||
| r = 1; | |||
| break; | |||
| // request for the version | |||
| case effGetVendorVersion: | |||
| return PLUGIN_VERSION; | |||
| case effEditIdle: | |||
| if(NULL != wrapper->editor.hwnd) | |||
| { | |||
| printf("xxx vstgltest: redraw window\n"); | |||
| // (void)::RedrawWindow(wrapper->hwnd, NULL, NULL, RDW_INTERNALPAINT); | |||
| //(void)::UpdateWindow(wrapper->hwnd); | |||
| if(::IsWindowVisible(wrapper->editor.hwnd)) | |||
| { | |||
| wrapper->redrawWindow(); | |||
| } | |||
| } | |||
| break; | |||
| case effEditGetRect: | |||
| // Query editor window geometry | |||
| // ptr: ERect* (on Windows) | |||
| if(NULL != ptr) // yeah, this should never be NULL | |||
| { | |||
| // ... | |||
| wrapper->editor_rect.left = EDITWIN_X; | |||
| wrapper->editor_rect.top = EDITWIN_Y; | |||
| wrapper->editor_rect.right = EDITWIN_X + EDITWIN_W; | |||
| wrapper->editor_rect.bottom = EDITWIN_Y + EDITWIN_H; | |||
| *(void**)ptr = (void*) &wrapper->editor_rect; | |||
| r = 1; | |||
| } | |||
| else | |||
| { | |||
| r = 0; | |||
| } | |||
| break; | |||
| #if 0 | |||
| case effEditTop: | |||
| // deprecated in vst2.4 | |||
| r = 0; | |||
| break; | |||
| #endif | |||
| case effEditOpen: | |||
| // Show editor window | |||
| // ptr: native window handle (hWnd on Windows) | |||
| wrapper->openEditor((HWND)ptr); | |||
| r = 1; | |||
| break; | |||
| case effEditClose: | |||
| // Hide editor window | |||
| wrapper->closeEditor(); | |||
| r = 1; | |||
| break; | |||
| // ignoring all other opcodes | |||
| default: | |||
| printf("Unknown opCode %d [ignored] \n", opCode); | |||
| break; | |||
| } | |||
| return r; | |||
| } | |||
| /** | |||
| * Used for parameter setting (not used by this plugin) | |||
| */ | |||
| void VSTPluginSetParameter(VSTPlugin *vstPlugin, VstInt32 index, float parameter) | |||
| { | |||
| printf("called VSTPluginSetParameter(%d, %f)\n", index, parameter); | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| } | |||
| /** | |||
| * Used for parameter (not used by this plugin) | |||
| */ | |||
| float VSTPluginGetParameter(VSTPlugin *vstPlugin, VstInt32 index) | |||
| { | |||
| printf("called VSTPluginGetParameter(%d)\n", index); | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| return 0; | |||
| } | |||
| /** | |||
| * Main constructor for our C++ class | |||
| */ | |||
| VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback, | |||
| VstInt32 vendorUniqueID, | |||
| VstInt32 vendorVersion, | |||
| VstInt32 numParams, | |||
| VstInt32 numPrograms, | |||
| VstInt32 numInputs, | |||
| VstInt32 numOutputs) : | |||
| _vstHostCallback(vstHostCallback) | |||
| { | |||
| // Make sure that the memory is properly initialized | |||
| memset(&_vstPlugin, 0, sizeof(_vstPlugin)); | |||
| // this field must be set with this constant... | |||
| _vstPlugin.magic = kEffectMagic; | |||
| // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use) | |||
| _vstPlugin.object = this; | |||
| // specifying that we handle both single and double precision (there are other flags see aeffect.h/VstAEffectFlags) | |||
| _vstPlugin.flags = effFlagsCanReplacing | effFlagsCanDoubleReplacing | effFlagsHasEditor; | |||
| // initializing the plugin with the various values | |||
| _vstPlugin.uniqueID = vendorUniqueID; | |||
| _vstPlugin.version = vendorVersion; | |||
| _vstPlugin.numParams = numParams; | |||
| _vstPlugin.numPrograms = numPrograms; | |||
| _vstPlugin.numInputs = numInputs; | |||
| _vstPlugin.numOutputs = numOutputs; | |||
| // setting the callbacks to the previously defined functions | |||
| _vstPlugin.dispatcher = VSTPluginDispatcher; | |||
| _vstPlugin.getParameter = VSTPluginGetParameter; | |||
| _vstPlugin.setParameter = VSTPluginSetParameter; | |||
| _vstPlugin.processReplacing = VSTPluginProcessSamplesFloat32; | |||
| _vstPlugin.processDoubleReplacing = VSTPluginProcessSamplesFloat64; | |||
| parent_hwnd = NULL; | |||
| hidden.hwnd = NULL; | |||
| hidden.hdc = NULL; | |||
| editor.hwnd = NULL; | |||
| editor.hdc = NULL; | |||
| hglrc = NULL; | |||
| createHiddenWindow(); | |||
| } | |||
| /** | |||
| * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin | |||
| * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a | |||
| * memory leak which may end up slowing down and/or crashing the host | |||
| */ | |||
| VSTPluginWrapper::~VSTPluginWrapper() { | |||
| destroyHiddenWindow(); | |||
| } | |||
| /** | |||
| * Implementation of the main entry point of the plugin | |||
| */ | |||
| VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback) | |||
| { | |||
| printf("called VSTPluginMain... \n"); | |||
| // simply create our plugin C++ class | |||
| VSTPluginWrapper *plugin = | |||
| new VSTPluginWrapper(vstHostCallback, | |||
| CCONST('u', 's', 'a', '§'), // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm) | |||
| PLUGIN_VERSION, // version | |||
| 0, // no params | |||
| 0, // no programs | |||
| 2, // 2 inputs | |||
| 2); // 2 outputs | |||
| // return the plugin per the contract of the API | |||
| return plugin->getVSTPlugin(); | |||
| } | |||
| @@ -0,0 +1,527 @@ | |||
| #include <aeffect.h> | |||
| #include <aeffectx.h> | |||
| #include <stdio.h> | |||
| #if defined(_WIN32) || defined(_WIN64) | |||
| #include <windows.h> | |||
| #include <wingdi.h> | |||
| #define VST_EXPORT extern "C" __declspec(dllexport) | |||
| #else | |||
| #define VST_EXPORT extern | |||
| #endif | |||
| #include <GL/GL.h> | |||
| #define EDITWIN_X 20 | |||
| #define EDITWIN_Y 20 | |||
| #define EDITWIN_W 640 | |||
| #define EDITWIN_H 480 | |||
| #define EDITWIN_CLASS_NAME "VeeSeeVSTWindow" | |||
| /* | |||
| * I find the naming a bit confusing so I decided to use more meaningful names instead. | |||
| */ | |||
| /** | |||
| * The VSTHostCallback is a function pointer so that the plugin can communicate with the host (not used in this small example) | |||
| */ | |||
| typedef audioMasterCallback VSTHostCallback; | |||
| /** | |||
| * The VSTPlugin structure (AEffect) contains information about the plugin (like version, number of inputs, ...) and | |||
| * callbacks so that the host can call the plugin to do its work. The primary callback will be `processReplacing` for | |||
| * single precision (float) sample processing (or `processDoubleReplacing` for double precision (double)). | |||
| */ | |||
| typedef AEffect VSTPlugin; | |||
| // Since the host is expecting a very specific API we need to make sure it has C linkage (not C++) | |||
| extern "C" { | |||
| /* | |||
| * This is the main entry point to the VST plugin. | |||
| * | |||
| * The host (DAW like Maschine, Ableton Live, Reason, ...) will look for this function with this exact API. | |||
| * | |||
| * It is the equivalent to `int main(int argc, char *argv[])` for a C executable. | |||
| * | |||
| * @param vstHostCallback is a callback so that the plugin can communicate with the host (not used in this small example) | |||
| * @return a pointer to the AEffect structure | |||
| */ | |||
| VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback); | |||
| // note this looks like this without the type aliases (and is obviously 100% equivalent) | |||
| // extern AEffect *VSTPluginMain(audioMasterCallback audioMaster); | |||
| } | |||
| /* | |||
| * Constant for the version of the plugin. For example 1100 for version 1.1.0.0 | |||
| */ | |||
| const VstInt32 PLUGIN_VERSION = 1000; | |||
| extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
| /** | |||
| * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the | |||
| * host (VSTPlugin). This class will be stored in the `VSTPlugin.object` field (circular reference) so that it can | |||
| * be accessed when the host calls the plugin back (for example in `processDoubleReplacing`). | |||
| */ | |||
| class VSTPluginWrapper | |||
| { | |||
| public: | |||
| ERect editor_rect; | |||
| HWND parent_hwnd; // created by host | |||
| HWND hwnd; | |||
| HDC hdc; | |||
| HGLRC hglrc; | |||
| float clear_color = 0.0f; | |||
| static const char *registered_class_name; | |||
| static VSTPluginWrapper *window_to_wrapper; | |||
| public: | |||
| VSTPluginWrapper(VSTHostCallback vstHostCallback, | |||
| VstInt32 vendorUniqueID, | |||
| VstInt32 vendorVersion, | |||
| VstInt32 numParams, | |||
| VstInt32 numPrograms, | |||
| VstInt32 numInputs, | |||
| VstInt32 numOutputs); | |||
| ~VSTPluginWrapper(); | |||
| inline VSTPlugin *getVSTPlugin() | |||
| { | |||
| return &_vstPlugin; | |||
| } | |||
| inline VstInt32 getNumInputs() const | |||
| { | |||
| return _vstPlugin.numInputs; | |||
| } | |||
| inline VstInt32 getNumOutputs() const | |||
| { | |||
| return _vstPlugin.numOutputs; | |||
| } | |||
| void openEditor(HWND _hwnd) { | |||
| parent_hwnd = _hwnd; | |||
| if(NULL == registered_class_name) | |||
| { | |||
| WNDCLASS wc; | |||
| ::ZeroMemory(&wc, sizeof(wc)); | |||
| wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; | |||
| wc.lpfnWndProc = (WNDPROC) WndProc; | |||
| wc.hInstance = ::GetModuleHandle(NULL); | |||
| wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); | |||
| wc.lpszClassName = EDITWIN_CLASS_NAME; | |||
| if(!::RegisterClass(&wc)) | |||
| { | |||
| // something went terribly wrong | |||
| printf("[---] vstgltest: failed to register window class\n"); | |||
| return; | |||
| } | |||
| registered_class_name = EDITWIN_CLASS_NAME; | |||
| } | |||
| DWORD dwExStyle = 0; | |||
| DWORD dwStyle = WS_CHILD | WS_VISIBLE; | |||
| hwnd = ::CreateWindowEx(dwExStyle, | |||
| EDITWIN_CLASS_NAME, | |||
| "Test", | |||
| dwStyle, | |||
| 0/*xpos*/, 0/*ypos*/, | |||
| EDITWIN_W, EDITWIN_H, | |||
| parent_hwnd, | |||
| NULL, // window menu | |||
| ::GetModuleHandle(NULL), | |||
| NULL | |||
| ); | |||
| window_to_wrapper = this; | |||
| PIXELFORMATDESCRIPTOR pfd = { | |||
| sizeof(PIXELFORMATDESCRIPTOR), | |||
| 1, | |||
| PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags | |||
| PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. | |||
| 32, // Colordepth of the framebuffer. | |||
| 0, 0, 0, 0, 0, 0, | |||
| 0, | |||
| 0, | |||
| 0, | |||
| 0, 0, 0, 0, | |||
| 24, // Number of bits for the depthbuffer | |||
| 8, // Number of bits for the stencilbuffer | |||
| 0, // Number of Aux buffers in the framebuffer. | |||
| PFD_MAIN_PLANE, | |||
| 0, | |||
| 0, 0, 0 | |||
| }; | |||
| hdc = ::GetDC(hwnd); | |||
| int pfmt = ::ChoosePixelFormat(hdc, &pfd); | |||
| printf("xxx vstgltest: pfmt=%d", pfmt); | |||
| ::SetPixelFormat(hdc, pfmt, &pfd); | |||
| hglrc = ::wglCreateContext(hdc); | |||
| } | |||
| void closeEditor(void) { | |||
| if(NULL != hwnd) | |||
| { | |||
| ::wglDeleteContext(hglrc); | |||
| ::DestroyWindow(hwnd); | |||
| // (todo) unregister when last window is closed | |||
| ::UnregisterClass(EDITWIN_CLASS_NAME, ::GetModuleHandle(NULL)); | |||
| registered_class_name = NULL; | |||
| window_to_wrapper = NULL; | |||
| } | |||
| } | |||
| static VSTPluginWrapper *FindWrapperByWindow(HWND hwnd) { | |||
| return window_to_wrapper; | |||
| } | |||
| void redrawWindow(void) { | |||
| // Save host GL context | |||
| HDC hostHDC = ::wglGetCurrentDC(); | |||
| HGLRC hostHGLRC = ::wglGetCurrentContext(); | |||
| printf("xxx vstgltest: WM_PAINT: hostHDC=%p hostGLRC=%p\n", hostHDC, hostHGLRC); | |||
| // Bind plugin GL context | |||
| printf("xxx vstgltest: WM_PAINT: pluginHDC=%p pluginGLRC=%p\n", hdc, hglrc); | |||
| ::wglMakeCurrent(hdc, hglrc); | |||
| // Draw something | |||
| ::glClearColor(0.0, 1.0, clear_color, 1.0); | |||
| clear_color += 0.05f; | |||
| if(clear_color >= 1.0f) | |||
| clear_color -= 1.0f; | |||
| ::glClear(GL_COLOR_BUFFER_BIT); | |||
| ::glFlush(); | |||
| ::wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE); | |||
| // Restore host GL context | |||
| ::wglMakeCurrent(hostHDC, hostHGLRC); | |||
| } | |||
| private: | |||
| // the host callback (a function pointer) | |||
| VSTHostCallback _vstHostCallback; | |||
| // the actual structure required by the host | |||
| VSTPlugin _vstPlugin; | |||
| }; | |||
| const char *VSTPluginWrapper::registered_class_name = NULL; | |||
| VSTPluginWrapper *VSTPluginWrapper::window_to_wrapper = NULL; | |||
| extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { | |||
| switch(message) | |||
| { | |||
| case WM_CREATE: | |||
| printf("xxx vstgltest: WM_CREATE\n"); | |||
| break; | |||
| case WM_DESTROY: | |||
| printf("xxx vstgltest: WM_DESTROY\n"); | |||
| break; | |||
| case WM_LBUTTONDOWN: | |||
| printf("xxx vstgltest: WM_LBUTTONDOWN\n"); | |||
| break; | |||
| case WM_LBUTTONUP: | |||
| printf("xxx vstgltest: WM_LBUTTONUP\n"); | |||
| break; | |||
| case WM_PAINT: | |||
| // https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-redrawwindow | |||
| printf("xxx vstgltest: WM_PAINT\n"); | |||
| VSTPluginWrapper *wrapper = VSTPluginWrapper::FindWrapperByWindow(hWnd); | |||
| wrapper->redrawWindow(); | |||
| break; | |||
| } | |||
| return DefWindowProc(hWnd, message, wParam, lParam); | |||
| } | |||
| /******************************************* | |||
| * Callbacks: Host -> Plugin | |||
| * | |||
| * Defined here because they are used in the rest of the code later | |||
| */ | |||
| /** | |||
| * This is the callback that will be called to process the samples in the case of single precision. This is where the | |||
| * meat of the logic happens! | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @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] | |||
| * @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] | |||
| * @param sampleFrames the number of samples (second dimension in both arrays) | |||
| */ | |||
| void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float **outputs, VstInt32 sampleFrames) | |||
| { | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the | |||
| // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound) | |||
| for(int i = 0; i < wrapper->getNumInputs(); i++) | |||
| { | |||
| auto inputSamples = inputs[i]; | |||
| auto outputSamples = outputs[i]; | |||
| for(int j = 0; j < sampleFrames; j++) | |||
| { | |||
| outputSamples[j] = inputSamples[j] * 0.5f; | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * This is the callback that will be called to process the samples in the case of double precision. This is where the | |||
| * meat of the logic happens! | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @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] | |||
| * @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] | |||
| * @param sampleFrames the number of samples (second dimension in both arrays) | |||
| */ | |||
| void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, double **outputs, VstInt32 sampleFrames) | |||
| { | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // code speaks for itself: for each input (2 when stereo input), iterating over every sample and writing the | |||
| // result in the outputs array after multiplying by 0.5 (which result in a 3dB attenuation of the sound) | |||
| for(int i = 0; i < wrapper->getNumInputs(); i++) | |||
| { | |||
| auto inputSamples = inputs[i]; | |||
| auto outputSamples = outputs[i]; | |||
| for(int j = 0; j < sampleFrames; j++) | |||
| { | |||
| outputSamples[j] = inputSamples[j] * 0.5; | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the | |||
| * vendor string, the plugin category...) or communicate state/changes (like open/close, frame rate...) | |||
| * | |||
| * @param vstPlugin the object returned by VSTPluginMain | |||
| * @param opCode defined in aeffect.h/AEffectOpcodes and which continues in aeffectx.h/AEffectXOpcodes for a grand | |||
| * total of 79 of them! Only a few of them are implemented in this small plugin. | |||
| * @param index depend on the opcode | |||
| * @param value depend on the opcode | |||
| * @param ptr depend on the opcode | |||
| * @param opt depend on the opcode | |||
| * @return depend on the opcode (0 is ok when you don't implement an opcode...) | |||
| */ | |||
| VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt) | |||
| { | |||
| printf("vstgltest: called VSTPluginDispatcher(%d)\n", opCode); | |||
| VstIntPtr r = 0; | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them | |||
| switch(opCode) | |||
| { | |||
| // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed | |||
| // to generating sound) | |||
| case effGetPlugCategory: | |||
| return kPlugCategEffect; | |||
| // called by the host when the plugin was called... time to reclaim memory! | |||
| case effClose: | |||
| delete wrapper; | |||
| break; | |||
| case effGetEffectName: | |||
| ::strncpy((char*)ptr, "VST GL Test", kVstMaxEffectNameLen); | |||
| r = 1; | |||
| break; | |||
| // request for the vendor string (usually used in the UI for plugin grouping) | |||
| case effGetVendorString: | |||
| strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen); | |||
| r = 1; | |||
| break; | |||
| // request for the version | |||
| case effGetVendorVersion: | |||
| return PLUGIN_VERSION; | |||
| case effEditIdle: | |||
| if(NULL != wrapper->hwnd) | |||
| { | |||
| printf("xxx vstgltest: redraw window\n"); | |||
| // (void)::RedrawWindow(wrapper->hwnd, NULL, NULL, RDW_INTERNALPAINT); | |||
| //(void)::UpdateWindow(wrapper->hwnd); | |||
| if(::IsWindowVisible(wrapper->hwnd)) | |||
| { | |||
| wrapper->redrawWindow(); | |||
| } | |||
| } | |||
| break; | |||
| case effEditGetRect: | |||
| // Query editor window geometry | |||
| // ptr: ERect* (on Windows) | |||
| if(NULL != ptr) // yeah, this should never be NULL | |||
| { | |||
| // ... | |||
| wrapper->editor_rect.left = EDITWIN_X; | |||
| wrapper->editor_rect.top = EDITWIN_Y; | |||
| wrapper->editor_rect.right = EDITWIN_X + EDITWIN_W; | |||
| wrapper->editor_rect.bottom = EDITWIN_Y + EDITWIN_H; | |||
| *(void**)ptr = (void*) &wrapper->editor_rect; | |||
| r = 1; | |||
| } | |||
| else | |||
| { | |||
| r = 0; | |||
| } | |||
| break; | |||
| #if 0 | |||
| case effEditTop: | |||
| // deprecated in vst2.4 | |||
| r = 0; | |||
| break; | |||
| #endif | |||
| case effEditOpen: | |||
| // Show editor window | |||
| // ptr: native window handle (hWnd on Windows) | |||
| wrapper->openEditor((HWND)ptr); | |||
| r = 1; | |||
| break; | |||
| case effEditClose: | |||
| // Hide editor window | |||
| wrapper->closeEditor(); | |||
| r = 1; | |||
| break; | |||
| // ignoring all other opcodes | |||
| default: | |||
| printf("Unknown opCode %d [ignored] \n", opCode); | |||
| break; | |||
| } | |||
| return r; | |||
| } | |||
| /** | |||
| * Used for parameter setting (not used by this plugin) | |||
| */ | |||
| void VSTPluginSetParameter(VSTPlugin *vstPlugin, VstInt32 index, float parameter) | |||
| { | |||
| printf("called VSTPluginSetParameter(%d, %f)\n", index, parameter); | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| } | |||
| /** | |||
| * Used for parameter (not used by this plugin) | |||
| */ | |||
| float VSTPluginGetParameter(VSTPlugin *vstPlugin, VstInt32 index) | |||
| { | |||
| printf("called VSTPluginGetParameter(%d)\n", index); | |||
| // we can get a hold to our C++ class since we stored it in the `object` field (see constructor) | |||
| VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); | |||
| return 0; | |||
| } | |||
| /** | |||
| * Main constructor for our C++ class | |||
| */ | |||
| VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback, | |||
| VstInt32 vendorUniqueID, | |||
| VstInt32 vendorVersion, | |||
| VstInt32 numParams, | |||
| VstInt32 numPrograms, | |||
| VstInt32 numInputs, | |||
| VstInt32 numOutputs) : | |||
| _vstHostCallback(vstHostCallback) | |||
| { | |||
| // Make sure that the memory is properly initialized | |||
| memset(&_vstPlugin, 0, sizeof(_vstPlugin)); | |||
| // this field must be set with this constant... | |||
| _vstPlugin.magic = kEffectMagic; | |||
| // storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use) | |||
| _vstPlugin.object = this; | |||
| // specifying that we handle both single and double precision (there are other flags see aeffect.h/VstAEffectFlags) | |||
| _vstPlugin.flags = effFlagsCanReplacing | effFlagsCanDoubleReplacing | effFlagsHasEditor; | |||
| // initializing the plugin with the various values | |||
| _vstPlugin.uniqueID = vendorUniqueID; | |||
| _vstPlugin.version = vendorVersion; | |||
| _vstPlugin.numParams = numParams; | |||
| _vstPlugin.numPrograms = numPrograms; | |||
| _vstPlugin.numInputs = numInputs; | |||
| _vstPlugin.numOutputs = numOutputs; | |||
| // setting the callbacks to the previously defined functions | |||
| _vstPlugin.dispatcher = VSTPluginDispatcher; | |||
| _vstPlugin.getParameter = VSTPluginGetParameter; | |||
| _vstPlugin.setParameter = VSTPluginSetParameter; | |||
| _vstPlugin.processReplacing = VSTPluginProcessSamplesFloat32; | |||
| _vstPlugin.processDoubleReplacing = VSTPluginProcessSamplesFloat64; | |||
| parent_hwnd = NULL; | |||
| hwnd = NULL; | |||
| hdc = NULL; | |||
| hglrc = NULL; | |||
| } | |||
| /** | |||
| * Destructor called when the plugin is closed (see VSTPluginDispatcher with effClose opCode). In this very simply plugin | |||
| * there is nothing to do but in general the memory that gets allocated MUST be freed here otherwise there might be a | |||
| * memory leak which may end up slowing down and/or crashing the host | |||
| */ | |||
| VSTPluginWrapper::~VSTPluginWrapper() | |||
| { | |||
| } | |||
| /** | |||
| * Implementation of the main entry point of the plugin | |||
| */ | |||
| VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback) | |||
| { | |||
| printf("called VSTPluginMain... \n"); | |||
| // simply create our plugin C++ class | |||
| VSTPluginWrapper *plugin = | |||
| new VSTPluginWrapper(vstHostCallback, | |||
| CCONST('u', 's', 'a', '§'), // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm) | |||
| PLUGIN_VERSION, // version | |||
| 0, // no params | |||
| 0, // no programs | |||
| 2, // 2 inputs | |||
| 2); // 2 outputs | |||
| // return the plugin per the contract of the API | |||
| return plugin->getVSTPlugin(); | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| | |||
| Microsoft Visual Studio Solution File, Format Version 12.00 | |||
| # Visual Studio 15 | |||
| VisualStudioVersion = 15.0.27130.2024 | |||
| MinimumVisualStudioVersion = 10.0.40219.1 | |||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "m3db", "m3db\m3db.vcxproj", "{ADEB3292-4BBF-4B20-9A11-3D8076423F16}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|x64 = Debug|x64 | |||
| Debug|x86 = Debug|x86 | |||
| Release|x64 = Release|x64 | |||
| Release|x86 = Release|x86 | |||
| EndGlobalSection | |||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Debug|x64.ActiveCfg = Debug|x64 | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Debug|x64.Build.0 = Debug|x64 | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Debug|x86.ActiveCfg = Debug|Win32 | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Debug|x86.Build.0 = Debug|Win32 | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Release|x64.ActiveCfg = Release|x64 | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Release|x64.Build.0 = Release|x64 | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Release|x86.ActiveCfg = Release|Win32 | |||
| {ADEB3292-4BBF-4B20-9A11-3D8076423F16}.Release|x86.Build.0 = Release|Win32 | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| EndGlobalSection | |||
| GlobalSection(ExtensibilityGlobals) = postSolution | |||
| SolutionGuid = {A1B75069-8782-4AB1-A661-12FED71250D4} | |||
| EndGlobalSection | |||
| EndGlobal | |||
| @@ -0,0 +1,167 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <ItemGroup Label="ProjectConfigurations"> | |||
| <ProjectConfiguration Include="Debug|Win32"> | |||
| <Configuration>Debug</Configuration> | |||
| <Platform>Win32</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Release|Win32"> | |||
| <Configuration>Release</Configuration> | |||
| <Platform>Win32</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Debug|x64"> | |||
| <Configuration>Debug</Configuration> | |||
| <Platform>x64</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Release|x64"> | |||
| <Configuration>Release</Configuration> | |||
| <Platform>x64</Platform> | |||
| </ProjectConfiguration> | |||
| </ItemGroup> | |||
| <PropertyGroup Label="Globals"> | |||
| <VCProjectVersion>15.0</VCProjectVersion> | |||
| <ProjectGuid>{ADEB3292-4BBF-4B20-9A11-3D8076423F16}</ProjectGuid> | |||
| <Keyword>Win32Proj</Keyword> | |||
| <RootNamespace>m3db</RootNamespace> | |||
| <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> | |||
| </PropertyGroup> | |||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> | |||
| <ConfigurationType>DynamicLibrary</ConfigurationType> | |||
| <UseDebugLibraries>true</UseDebugLibraries> | |||
| <PlatformToolset>v141</PlatformToolset> | |||
| <CharacterSet>Unicode</CharacterSet> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> | |||
| <ConfigurationType>DynamicLibrary</ConfigurationType> | |||
| <UseDebugLibraries>false</UseDebugLibraries> | |||
| <PlatformToolset>v141</PlatformToolset> | |||
| <WholeProgramOptimization>true</WholeProgramOptimization> | |||
| <CharacterSet>Unicode</CharacterSet> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> | |||
| <ConfigurationType>DynamicLibrary</ConfigurationType> | |||
| <UseDebugLibraries>true</UseDebugLibraries> | |||
| <PlatformToolset>v141</PlatformToolset> | |||
| <CharacterSet>Unicode</CharacterSet> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> | |||
| <ConfigurationType>DynamicLibrary</ConfigurationType> | |||
| <UseDebugLibraries>false</UseDebugLibraries> | |||
| <PlatformToolset>v141</PlatformToolset> | |||
| <WholeProgramOptimization>true</WholeProgramOptimization> | |||
| </PropertyGroup> | |||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | |||
| <ImportGroup Label="ExtensionSettings"> | |||
| </ImportGroup> | |||
| <ImportGroup Label="Shared"> | |||
| </ImportGroup> | |||
| <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | |||
| <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
| </ImportGroup> | |||
| <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | |||
| <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
| </ImportGroup> | |||
| <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
| <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
| </ImportGroup> | |||
| <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
| <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
| </ImportGroup> | |||
| <PropertyGroup Label="UserMacros" /> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | |||
| <LinkIncremental>true</LinkIncremental> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
| <LinkIncremental>true</LinkIncremental> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | |||
| <LinkIncremental>false</LinkIncremental> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
| <LinkIncremental>false</LinkIncremental> | |||
| </PropertyGroup> | |||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | |||
| <ClCompile> | |||
| <PrecompiledHeader>NotUsing</PrecompiledHeader> | |||
| <WarningLevel>Level3</WarningLevel> | |||
| <Optimization>Disabled</Optimization> | |||
| <SDLCheck>true</SDLCheck> | |||
| <PreprocessorDefinitions>WIN32;_DEBUG;M3DB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <ConformanceMode>true</ConformanceMode> | |||
| <AdditionalIncludeDirectories>f:\dev\vstsdk2.4</AdditionalIncludeDirectories> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Windows</SubSystem> | |||
| <GenerateDebugInformation>true</GenerateDebugInformation> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
| <ClCompile> | |||
| <PrecompiledHeader>NotUsing</PrecompiledHeader> | |||
| <WarningLevel>Level3</WarningLevel> | |||
| <Optimization>Disabled</Optimization> | |||
| <SDLCheck>true</SDLCheck> | |||
| <PreprocessorDefinitions>_DEBUG;M3DB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <ConformanceMode>true</ConformanceMode> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Windows</SubSystem> | |||
| <GenerateDebugInformation>true</GenerateDebugInformation> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | |||
| <ClCompile> | |||
| <PrecompiledHeader>NotUsing</PrecompiledHeader> | |||
| <WarningLevel>Level3</WarningLevel> | |||
| <Optimization>MaxSpeed</Optimization> | |||
| <FunctionLevelLinking>true</FunctionLevelLinking> | |||
| <IntrinsicFunctions>true</IntrinsicFunctions> | |||
| <SDLCheck>true</SDLCheck> | |||
| <PreprocessorDefinitions>WIN32;NDEBUG;M3DB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <ConformanceMode>true</ConformanceMode> | |||
| <AdditionalIncludeDirectories>f:\dev\vstsdk2.4</AdditionalIncludeDirectories> | |||
| <DebugInformationFormat>None</DebugInformationFormat> | |||
| <PrecompiledHeaderFile /> | |||
| <PrecompiledHeaderOutputFile /> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Windows</SubSystem> | |||
| <EnableCOMDATFolding>true</EnableCOMDATFolding> | |||
| <OptimizeReferences>true</OptimizeReferences> | |||
| <GenerateDebugInformation>false</GenerateDebugInformation> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
| <ClCompile> | |||
| <PrecompiledHeader>NotUsing</PrecompiledHeader> | |||
| <WarningLevel>Level3</WarningLevel> | |||
| <Optimization>MaxSpeed</Optimization> | |||
| <FunctionLevelLinking>true</FunctionLevelLinking> | |||
| <IntrinsicFunctions>true</IntrinsicFunctions> | |||
| <SDLCheck>true</SDLCheck> | |||
| <PreprocessorDefinitions>NDEBUG;M3DB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_DEPRECATE</PreprocessorDefinitions> | |||
| <ConformanceMode>true</ConformanceMode> | |||
| <AdditionalIncludeDirectories>F:\git\VeeSeeVSTRack\dep\lglw;f:\dev\vstsdk2.4\pluginterfaces\vst2.x\</AdditionalIncludeDirectories> | |||
| <DebugInformationFormat>None</DebugInformationFormat> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Windows</SubSystem> | |||
| <EnableCOMDATFolding>true</EnableCOMDATFolding> | |||
| <OptimizeReferences>true</OptimizeReferences> | |||
| <GenerateDebugInformation>false</GenerateDebugInformation> | |||
| <AdditionalDependencies>opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemGroup> | |||
| <ClCompile Include="..\..\..\..\..\..\..\git\VeeSeeVSTRack\dep\lglw\lglw_windows.c" /> | |||
| <ClCompile Include="..\..\..\..\..\..\..\git\VeeSeeVSTRack\dep\lglw\lglw_windows_cpp.cpp" /> | |||
| <ClCompile Include="..\..\..\plugin.cpp" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClInclude Include="..\..\..\..\..\..\..\git\VeeSeeVSTRack\dep\lglw\lglw.h" /> | |||
| </ItemGroup> | |||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | |||
| <ImportGroup Label="ExtensionTargets"> | |||
| </ImportGroup> | |||
| </Project> | |||
| @@ -0,0 +1,33 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <ItemGroup> | |||
| <Filter Include="Source Files"> | |||
| <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> | |||
| <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> | |||
| </Filter> | |||
| <Filter Include="Header Files"> | |||
| <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> | |||
| <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> | |||
| </Filter> | |||
| <Filter Include="Resource Files"> | |||
| <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> | |||
| <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | |||
| </Filter> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClCompile Include="..\..\..\plugin.cpp"> | |||
| <Filter>Source Files</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\..\..\..\git\VeeSeeVSTRack\dep\lglw\lglw_windows.c"> | |||
| <Filter>Source Files</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\..\..\..\git\VeeSeeVSTRack\dep\lglw\lglw_windows_cpp.cpp"> | |||
| <Filter>Source Files</Filter> | |||
| </ClCompile> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClInclude Include="..\..\..\..\..\..\..\git\VeeSeeVSTRack\dep\lglw\lglw.h"> | |||
| <Filter>Source Files</Filter> | |||
| </ClInclude> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -0,0 +1,4 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup /> | |||
| </Project> | |||
| @@ -0,0 +1,5 @@ | |||
| f:\projects\research\pongasoft_vst24-hello-world-master\m3db\vs2017\m3db\m3db\x64\debug\vc141.pdb | |||
| f:\projects\research\pongasoft_vst24-hello-world-master\m3db\vs2017\m3db\m3db\x64\debug\vc141.idb | |||
| f:\projects\research\pongasoft_vst24-hello-world-master\m3db\vs2017\m3db\m3db\x64\debug\m3db.tlog\cl.command.1.tlog | |||
| f:\projects\research\pongasoft_vst24-hello-world-master\m3db\vs2017\m3db\m3db\x64\debug\m3db.tlog\cl.read.1.tlog | |||
| f:\projects\research\pongasoft_vst24-hello-world-master\m3db\vs2017\m3db\m3db\x64\debug\m3db.tlog\cl.write.1.tlog | |||
| @@ -0,0 +1,2 @@ | |||
| plugin.cpp | |||
| f:\projects\research\pongasoft_vst24-hello-world-master\m3db\plugin.cpp(3): fatal error C1083: Cannot open include file: 'aeffect.h': No such file or directory | |||
| @@ -0,0 +1,2 @@ | |||
| #TargetFrameworkVersion=v4.0:PlatformToolSet=v141:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit:WindowsTargetPlatformVersion=10.0.16299.0 | |||
| Debug|x64|F:\Projects\research\pongasoft_vst24-hello-world-master\M3dB\vs2017\m3db\| | |||
| @@ -0,0 +1,15 @@ | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\plugin.obj | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\lglw_windows.obj | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\lglw_windows_cpp.obj | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\x64\release\m3db.dll | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\x64\release\m3db.lib | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\x64\release\m3db.exp | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\x64\release\m3db.ipdb | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\x64\release\m3db.iobj | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\m3db.tlog\cl.command.1.tlog | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\m3db.tlog\cl.read.1.tlog | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\m3db.tlog\cl.write.1.tlog | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\m3db.tlog\link.command.1.tlog | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\m3db.tlog\link.read.1.tlog | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\m3db.tlog\link.write.1.tlog | |||
| f:\projects\research\vst24_gl_test\m3db\vs2017\m3db\m3db\x64\release\m3db.tlog\m3db.write.1u.tlog | |||
| @@ -0,0 +1,8 @@ | |||
| lglw_windows.c | |||
| lglw_windows_cpp.cpp | |||
| plugin.cpp | |||
| Creating library F:\Projects\research\vst24_gl_test\M3dB\vs2017\m3db\x64\Release\m3db.lib and object F:\Projects\research\vst24_gl_test\M3dB\vs2017\m3db\x64\Release\m3db.exp | |||
| Generating code | |||
| All 57 functions were compiled because no usable IPDB/IOBJ from previous compilation was found. | |||
| Finished generating code | |||
| m3db.vcxproj -> F:\Projects\research\vst24_gl_test\M3dB\vs2017\m3db\x64\Release\m3db.dll | |||
| @@ -0,0 +1,2 @@ | |||
| #TargetFrameworkVersion=v4.0:PlatformToolSet=v141:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit:WindowsTargetPlatformVersion=10.0.16299.0 | |||
| Release|x64|F:\Projects\research\vst24_gl_test\M3dB\vs2017\m3db\| | |||
| @@ -56,7 +56,11 @@ static void loggerLogVa(LoggerLevel level, const char *file, int line, const cha | |||
| if (global->logger.logFile == LOG_STDFILE) | |||
| fprintf(global->logger.logFile, "\x1B[0m"); | |||
| vfprintf(global->logger.logFile, format, args); | |||
| vprintf(format, args); // xxx | |||
| // TODO | |||
| // At least for me, this will cause a complete crash from __strlen_avx512 | |||
| // vprintf(format, args); // xxx | |||
| printf("\n"); // xxx | |||
| fprintf(global->logger.logFile, "\n"); | |||
| fflush(global->logger.logFile); | |||