| @@ -213,13 +213,15 @@ DGL_SYSTEM_LIBS += -framework Cocoa | |||
| endif | |||
| ifeq ($(WINDOWS),true) | |||
| DGL_SYSTEM_LIBS += -lgdi32 | |||
| DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 | |||
| endif | |||
| ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||
| ifeq ($(HAVE_X11),true) | |||
| DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) | |||
| DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) | |||
| endif | |||
| endif | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| # Set Cairo specific stuff | |||
| @@ -30,6 +30,11 @@ List of plugins made with DPF:<br/> | |||
| - [DragonFly-Reverb](https://github.com/michaelwillis/dragonfly-reverb) | |||
| - [Wolf-Shaper](https://github.com/pdesaulniers/wolf-shaper) and [Wolf-Spectrum](https://github.com/pdesaulniers/wolf-spectrum) | |||
| - [YK Chorus](https://github.com/SpotlightKid/ykchorus) | |||
| - [Ninjas2](https://github.com/rghvdberg/ninjas2) | |||
| - [String-machine](https://github.com/jpcima/string-machine) | |||
| - [Rezonateur](https://github.com/jpcima/rezonateur) | |||
| - [QuadraFuzz](https://github.com/jpcima/quadrafuzz) | |||
| - [Shiru Plugins](https://github.com/linuxmao-org/shiru-plugins) | |||
| Plugin examples are available in the `example/` folder inside this repo.<br/> | |||
| @@ -14,13 +14,98 @@ | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "../Base.hpp" | |||
| #include "../Geometry.hpp" | |||
| #include "../Cairo.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| // nothing here yet | |||
| static void notImplemented(const char *name) | |||
| { | |||
| d_stderr2("cairo function not implemented: %s", name); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Line | |||
| template<typename T> | |||
| void Line<T>::draw() | |||
| { | |||
| notImplemented("Line::draw"); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Circle | |||
| template<typename T> | |||
| void Circle<T>::_draw(const bool outline) | |||
| { | |||
| notImplemented("Circle::draw"); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Triangle | |||
| template<typename T> | |||
| void Triangle<T>::_draw(const bool outline) | |||
| { | |||
| notImplemented("Triangle::draw"); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Rectangle | |||
| template<typename T> | |||
| void Rectangle<T>::_draw(const bool outline) | |||
| { | |||
| notImplemented("Rectangle::draw"); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Possible template data types | |||
| template class Point<double>; | |||
| template class Point<float>; | |||
| template class Point<int>; | |||
| template class Point<uint>; | |||
| template class Point<short>; | |||
| template class Point<ushort>; | |||
| template class Size<double>; | |||
| template class Size<float>; | |||
| template class Size<int>; | |||
| template class Size<uint>; | |||
| template class Size<short>; | |||
| template class Size<ushort>; | |||
| template class Line<double>; | |||
| template class Line<float>; | |||
| template class Line<int>; | |||
| template class Line<uint>; | |||
| template class Line<short>; | |||
| template class Line<ushort>; | |||
| template class Circle<double>; | |||
| template class Circle<float>; | |||
| template class Circle<int>; | |||
| template class Circle<uint>; | |||
| template class Circle<short>; | |||
| template class Circle<ushort>; | |||
| template class Triangle<double>; | |||
| template class Triangle<float>; | |||
| template class Triangle<int>; | |||
| template class Triangle<uint>; | |||
| template class Triangle<short>; | |||
| template class Triangle<ushort>; | |||
| template class Rectangle<double>; | |||
| template class Rectangle<float>; | |||
| template class Rectangle<int>; | |||
| template class Rectangle<uint>; | |||
| template class Rectangle<short>; | |||
| template class Rectangle<ushort>; | |||
| // ----------------------------------------------------------------------- | |||
| @@ -81,11 +81,8 @@ static NVGcontext* nvgCreateGL_helper(int flags) | |||
| { | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| static bool needsInit = true; | |||
| if (needsInit) | |||
| { | |||
| needsInit = false; | |||
| # define DGL_EXT(PROC, func) \ | |||
| func = (PROC) wglGetProcAddress ( #func ); \ | |||
| if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | |||
| DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); | |||
| DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) | |||
| DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) | |||
| @@ -115,7 +112,7 @@ DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | |||
| DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | |||
| DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||
| # undef DGL_EXT | |||
| } | |||
| needsInit = false; | |||
| #endif | |||
| return nvgCreateGL(flags); | |||
| } | |||
| @@ -918,6 +915,8 @@ int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWi | |||
| #ifndef DGL_NO_SHARED_RESOURCES | |||
| void NanoVG::loadSharedResources() | |||
| { | |||
| if (fContext == nullptr) return; | |||
| if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) | |||
| return; | |||
| @@ -1,6 +1,8 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2019 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
| * Copyright (C) 2019 Robin Gareus <robin@gareus.org> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -105,6 +107,10 @@ struct Window::PrivateData { | |||
| mView(nullptr), | |||
| mWindow(nullptr), | |||
| mParentWindow(nullptr) | |||
| # ifndef DGL_FILE_BROWSER_DISABLED | |||
| , fOpenFilePanel(nullptr), | |||
| fFilePanelDelegate(nullptr) | |||
| # endif | |||
| #else | |||
| xDisplay(nullptr), | |||
| xWindow(0) | |||
| @@ -137,6 +143,10 @@ struct Window::PrivateData { | |||
| mView(nullptr), | |||
| mWindow(nullptr), | |||
| mParentWindow(nullptr) | |||
| # ifndef DGL_FILE_BROWSER_DISABLED | |||
| , fOpenFilePanel(nullptr), | |||
| fFilePanelDelegate(nullptr) | |||
| # endif | |||
| #else | |||
| xDisplay(nullptr), | |||
| xWindow(0) | |||
| @@ -181,6 +191,10 @@ struct Window::PrivateData { | |||
| mView(nullptr), | |||
| mWindow(nullptr), | |||
| mParentWindow(nullptr) | |||
| # ifndef DGL_FILE_BROWSER_DISABLED | |||
| , fOpenFilePanel(nullptr), | |||
| fFilePanelDelegate(nullptr) | |||
| # endif | |||
| #else | |||
| xDisplay(nullptr), | |||
| xWindow(0) | |||
| @@ -323,6 +337,19 @@ struct Window::PrivateData { | |||
| xWindow = 0; | |||
| #endif | |||
| #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) | |||
| if (fOpenFilePanel) | |||
| { | |||
| [fOpenFilePanel release]; | |||
| fOpenFilePanel = nullptr; | |||
| } | |||
| if (fFilePanelDelegate) | |||
| { | |||
| [fFilePanelDelegate release]; | |||
| fFilePanelDelegate = nullptr; | |||
| } | |||
| #endif | |||
| DBG("Success!\n"); | |||
| } | |||
| @@ -687,6 +714,9 @@ struct Window::PrivateData { | |||
| } | |||
| #else | |||
| XStoreName(xDisplay, xWindow, title); | |||
| Atom netWmName = XInternAtom(xDisplay, "_NET_WM_NAME", False); | |||
| Atom utf8String = XInternAtom(xDisplay, "UTF8_STRING", False); | |||
| XChangeProperty(xDisplay, xWindow, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title)); | |||
| #endif | |||
| } | |||
| @@ -774,6 +804,15 @@ struct Window::PrivateData { | |||
| } | |||
| #endif | |||
| #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) | |||
| if (fSelectedFile.isNotEmpty()) | |||
| { | |||
| char* const buffer = fSelectedFile.getAndReleaseBuffer(); | |||
| fView->fileSelectedFunc(fView, buffer); | |||
| std::free(buffer); | |||
| } | |||
| #endif | |||
| if (fModal.enabled && fModal.parent != nullptr) | |||
| fModal.parent->idle(); | |||
| } | |||
| @@ -1050,6 +1089,39 @@ struct Window::PrivateData { | |||
| return false; | |||
| } | |||
| #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) | |||
| static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData) | |||
| { | |||
| PrivateData* pData = (PrivateData*)userData; | |||
| if (returnCode == NSOKButton) | |||
| { | |||
| NSArray* urls = [panel URLs]; | |||
| NSURL* fileUrl = nullptr; | |||
| for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i) | |||
| { | |||
| NSURL* url = (NSURL*)[urls objectAtIndex:i]; | |||
| if ([url isFileURL]) | |||
| fileUrl = url; | |||
| } | |||
| if (fileUrl) | |||
| { | |||
| PuglView* view = pData->fView; | |||
| if (view->fileSelectedFunc) | |||
| { | |||
| const char* fileName = [fileUrl.path UTF8String]; | |||
| view->fileSelectedFunc(view, fileName); | |||
| } | |||
| } | |||
| } | |||
| [pData->fOpenFilePanel release]; | |||
| pData->fOpenFilePanel = nullptr; | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| Application& fApp; | |||
| @@ -1095,11 +1167,18 @@ struct Window::PrivateData { | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| HWND hwnd; | |||
| HWND hwndParent; | |||
| # ifndef DGL_FILE_BROWSER_DISABLED | |||
| String fSelectedFile; | |||
| # endif | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| bool fNeedsIdle; | |||
| NSView<PuglGenericView>* mView; | |||
| id mWindow; | |||
| id mParentWindow; | |||
| # ifndef DGL_FILE_BROWSER_DISABLED | |||
| NSOpenPanel* fOpenFilePanel; | |||
| id fFilePanelDelegate; | |||
| # endif | |||
| #else | |||
| Display* xDisplay; | |||
| ::Window xWindow; | |||
| @@ -1216,6 +1295,20 @@ void Window::repaint() noexcept | |||
| // } | |||
| #ifndef DGL_FILE_BROWSER_DISABLED | |||
| #ifdef DISTRHO_OS_MAC | |||
| END_NAMESPACE_DGL | |||
| @interface FilePanelDelegate : NSObject | |||
| { | |||
| void (*fCallback)(NSOpenPanel*, int, void*); | |||
| void* fUserData; | |||
| } | |||
| -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void*)userData; | |||
| -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; | |||
| @end | |||
| START_NAMESPACE_DGL | |||
| #endif | |||
| bool Window::openFileBrowser(const FileBrowserOptions& options) | |||
| { | |||
| # ifdef SOFD_HAVE_X11 | |||
| @@ -1278,6 +1371,93 @@ bool Window::openFileBrowser(const FileBrowserOptions& options) | |||
| // show | |||
| return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0); | |||
| # elif defined(DISTRHO_OS_WINDOWS) | |||
| // the old and compatible dialog API | |||
| OPENFILENAMEW ofn; | |||
| memset(&ofn, 0, sizeof(ofn)); | |||
| ofn.lStructSize = sizeof(ofn); | |||
| ofn.hwndOwner = pData->hwnd; | |||
| // set initial directory in UTF-16 coding | |||
| std::vector<WCHAR> startDirW; | |||
| if (options.startDir) | |||
| { | |||
| startDirW.resize(strlen(options.startDir) + 1); | |||
| if (MultiByteToWideChar(CP_UTF8, 0, options.startDir, -1, startDirW.data(), startDirW.size())) | |||
| ofn.lpstrInitialDir = startDirW.data(); | |||
| } | |||
| // set title in UTF-16 coding | |||
| std::vector<WCHAR> titleW; | |||
| if (options.title) | |||
| { | |||
| titleW.resize(strlen(options.title) + 1); | |||
| if (MultiByteToWideChar(CP_UTF8, 0, options.title, -1, titleW.data(), titleW.size())) | |||
| ofn.lpstrTitle = titleW.data(); | |||
| } | |||
| // prepare a buffer to receive the result | |||
| std::vector<WCHAR> fileNameW(32768); // the Unicode maximum | |||
| ofn.lpstrFile = fileNameW.data(); | |||
| ofn.nMaxFile = (DWORD)fileNameW.size(); | |||
| // TODO synchronous only, can't do better with WinAPI native dialogs. | |||
| // threading might work, if someone is motivated to risk it. | |||
| if (GetOpenFileNameW(&ofn)) | |||
| { | |||
| // back to UTF-8 | |||
| std::vector<char> fileNameA(4 * 32768); | |||
| if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr)) | |||
| { | |||
| // handle it during the next idle cycle (fake async) | |||
| pData->fSelectedFile = fileNameA.data(); | |||
| } | |||
| } | |||
| return true; | |||
| # elif defined(DISTRHO_OS_MAC) | |||
| if (pData->fOpenFilePanel) // permit one dialog at most | |||
| { | |||
| [pData->fOpenFilePanel makeKeyAndOrderFront:nil]; | |||
| return false; | |||
| } | |||
| NSOpenPanel* panel = [NSOpenPanel openPanel]; | |||
| pData->fOpenFilePanel = [panel retain]; | |||
| [panel setCanChooseFiles:YES]; | |||
| [panel setCanChooseDirectories:NO]; | |||
| [panel setAllowsMultipleSelection:NO]; | |||
| if (options.startDir) | |||
| [panel setDirectory:[NSString stringWithUTF8String:options.startDir]]; | |||
| if (options.title) | |||
| { | |||
| NSString* titleString = [[NSString alloc] | |||
| initWithBytes:options.title | |||
| length:strlen(options.title) | |||
| encoding:NSUTF8StringEncoding]; | |||
| [panel setTitle:titleString]; | |||
| } | |||
| id delegate = pData->fFilePanelDelegate; | |||
| if (!delegate) | |||
| { | |||
| delegate = [[FilePanelDelegate alloc] initWithCallback:&PrivateData::openPanelDidEnd | |||
| userData:pData]; | |||
| pData->fFilePanelDelegate = [delegate retain]; | |||
| } | |||
| [panel beginSheetForDirectory:nullptr | |||
| file:nullptr | |||
| modalForWindow:nullptr | |||
| modalDelegate:delegate | |||
| didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) | |||
| contextInfo:nullptr]; | |||
| return true; | |||
| # else | |||
| // not implemented | |||
| return false; | |||
| @@ -1286,8 +1466,29 @@ bool Window::openFileBrowser(const FileBrowserOptions& options) | |||
| (void)options; | |||
| # endif | |||
| } | |||
| #ifdef DISTRHO_OS_MAC | |||
| END_NAMESPACE_DGL | |||
| @implementation FilePanelDelegate | |||
| -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void *)userData | |||
| { | |||
| [super init]; | |||
| self->fCallback = callback; | |||
| self->fUserData = userData; | |||
| return self; | |||
| } | |||
| -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo | |||
| { | |||
| self->fCallback(sheet, returnCode, self->fUserData); | |||
| (void)contextInfo; | |||
| } | |||
| @end | |||
| START_NAMESPACE_DGL | |||
| #endif | |||
| #endif // !defined(DGL_FILE_BROWSER_DISABLED) | |||
| bool Window::isEmbed() const noexcept | |||
| { | |||
| return pData->fUsingEmbed; | |||
| @@ -274,6 +274,9 @@ puglCreateWindow(PuglView* view, const char* title) | |||
| if (title) { | |||
| XStoreName(impl->display, impl->win, title); | |||
| Atom netWmName = XInternAtom(impl->display, "_NET_WM_NAME", False); | |||
| Atom utf8String = XInternAtom(impl->display, "UTF8_STRING", False); | |||
| XChangeProperty(impl->display, impl->win, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title)); | |||
| } | |||
| if (view->transient_parent > 0) { | |||
| @@ -1257,7 +1257,8 @@ static int fib_opendir (Display *dpy, const char* path, const char *sel) { | |||
| } else { | |||
| int i; | |||
| struct dirent *de; | |||
| strcpy (_cur_path, path); | |||
| if (path != _cur_path) | |||
| strcpy (_cur_path, path); | |||
| if (_cur_path[strlen (_cur_path) -1] != '/') | |||
| strcat (_cur_path, "/"); | |||
| @@ -384,6 +384,13 @@ struct Parameter { | |||
| */ | |||
| String name; | |||
| /** | |||
| The short name of this parameter.@n | |||
| Used when displaying the parameter name in a very limited space. | |||
| @note This value is optional, the full name is used when the short one is missing. | |||
| */ | |||
| String shortName; | |||
| /** | |||
| The symbol of this parameter.@n | |||
| A parameter symbol is a short restricted name used as a machine and human readable identifier.@n | |||
| @@ -399,6 +406,12 @@ struct Parameter { | |||
| */ | |||
| String unit; | |||
| /** | |||
| An extensive description/comment about the parameter. | |||
| @note This value is optional and only used for LV2. | |||
| */ | |||
| String description; | |||
| /** | |||
| Ranges of this parameter.@n | |||
| The ranges describe the default, minimum and maximum values. | |||
| @@ -430,6 +443,7 @@ struct Parameter { | |||
| Parameter() noexcept | |||
| : hints(0x0), | |||
| name(), | |||
| shortName(), | |||
| symbol(), | |||
| unit(), | |||
| ranges(), | |||
| @@ -443,6 +457,7 @@ struct Parameter { | |||
| Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept | |||
| : hints(h), | |||
| name(n), | |||
| shortName(), | |||
| symbol(s), | |||
| unit(u), | |||
| ranges(def, min, max), | |||
| @@ -462,11 +477,12 @@ struct Parameter { | |||
| case kParameterDesignationNull: | |||
| break; | |||
| case kParameterDesignationBypass: | |||
| hints = kParameterIsAutomable|kParameterIsBoolean|kParameterIsInteger; | |||
| name = "Bypass"; | |||
| symbol = "dpf_bypass"; | |||
| unit = ""; | |||
| midiCC = 0; | |||
| hints = kParameterIsAutomable|kParameterIsBoolean|kParameterIsInteger; | |||
| name = "Bypass"; | |||
| shortName = "Bypass"; | |||
| symbol = "dpf_bypass"; | |||
| unit = ""; | |||
| midiCC = 0; | |||
| ranges.def = 0.0f; | |||
| ranges.min = 0.0f; | |||
| ranges.max = 1.0f; | |||
| @@ -156,6 +156,46 @@ void d_safe_assert(const char* const assertion, const char* const file, const in | |||
| d_stderr2("assertion failure: \"%s\" in file %s, line %i", assertion, file, line); | |||
| } | |||
| /* | |||
| * Print a safe assertion error message, with 1 extra signed integer value. | |||
| */ | |||
| static inline | |||
| void d_safe_assert_int(const char* const assertion, const char* const file, | |||
| const int line, const int value) noexcept | |||
| { | |||
| d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %i", assertion, file, line, value); | |||
| } | |||
| /* | |||
| * Print a safe assertion error message, with 1 extra unsigned integer value. | |||
| */ | |||
| static inline | |||
| void d_safe_assert_uint(const char* const assertion, const char* const file, | |||
| const int line, const uint value) noexcept | |||
| { | |||
| d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %u", assertion, file, line, value); | |||
| } | |||
| /* | |||
| * Print a safe assertion error message, with 2 extra signed integer values. | |||
| */ | |||
| static inline | |||
| void d_safe_assert_int2(const char* const assertion, const char* const file, | |||
| const int line, const int v1, const int v2) noexcept | |||
| { | |||
| d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %i, v2 %i", assertion, file, line, v1, v2); | |||
| } | |||
| /* | |||
| * Print a safe assertion error message, with 2 extra unsigned integer values. | |||
| */ | |||
| static inline | |||
| void d_safe_assert_uint2(const char* const assertion, const char* const file, | |||
| const int line, const uint v1, const uint v2) noexcept | |||
| { | |||
| d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %u, v2 %u", assertion, file, line, v1, v2); | |||
| } | |||
| /* | |||
| * Print a safe exception error message. | |||
| */ | |||
| @@ -568,6 +568,18 @@ public: | |||
| return fBuffer; | |||
| } | |||
| /* | |||
| * Get and release the string buffer, while also clearing this string. | |||
| * Result must be freed. | |||
| */ | |||
| char* getAndReleaseBuffer() noexcept | |||
| { | |||
| char* const ret = fBuffer; | |||
| fBuffer = _null(); | |||
| fBufferLen = 0; | |||
| return ret; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||
| // Copyright (C) 2004-2008 René Nyffenegger | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -81,6 +81,22 @@ | |||
| #define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); continue; } | |||
| #define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; } | |||
| #define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value); break; } | |||
| #define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); continue; } | |||
| #define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); return ret; } | |||
| #define DISTRHO_SAFE_ASSERT_INT2_BREAK(cond, v1, v2) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); break; } | |||
| #define DISTRHO_SAFE_ASSERT_INT2_CONTINUE(cond, v1, v2) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); continue; } | |||
| #define DISTRHO_SAFE_ASSERT_INT2_RETURN(cond, v1, v2, ret) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); return ret; } | |||
| #define DISTRHO_SAFE_ASSERT_UINT_BREAK(cond, value) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value); break; } | |||
| #define DISTRHO_SAFE_ASSERT_UINT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); continue; } | |||
| #define DISTRHO_SAFE_ASSERT_UINT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); return ret; } | |||
| #define DISTRHO_SAFE_ASSERT_UINT2_BREAK(cond, v1, v2) if (! (cond)) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); break; } | |||
| #define DISTRHO_SAFE_ASSERT_UINT2_CONTINUE(cond, v1, v2) if (! (cond)) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); continue; } | |||
| #define DISTRHO_SAFE_ASSERT_UINT2_RETURN(cond, v1, v2, ret) if (! (cond)) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); return ret; } | |||
| /* Define DISTRHO_SAFE_EXCEPTION */ | |||
| #define DISTRHO_SAFE_EXCEPTION(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); } | |||
| #define DISTRHO_SAFE_EXCEPTION_BREAK(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); break; } | |||
| @@ -375,6 +375,13 @@ public: | |||
| return fData->parameters[index].name; | |||
| } | |||
| const String& getParameterShortName(const uint32_t index) const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||
| return fData->parameters[index].shortName; | |||
| } | |||
| const String& getParameterSymbol(const uint32_t index) const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||
| @@ -389,6 +396,13 @@ public: | |||
| return fData->parameters[index].unit; | |||
| } | |||
| const String& getParameterDescription(const uint32_t index) const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||
| return fData->parameters[index].description; | |||
| } | |||
| const ParameterEnumerationValues& getParameterEnumValues(const uint32_t index) const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackEnumValues); | |||
| @@ -846,6 +846,11 @@ public: | |||
| return LV2_WORKER_SUCCESS; | |||
| } | |||
| LV2_Worker_Status lv2_work_response(uint32_t, const void*) | |||
| { | |||
| return LV2_WORKER_SUCCESS; | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| @@ -1243,6 +1248,11 @@ LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function, LV2 | |||
| { | |||
| return instancePtr->lv2_work(data); | |||
| } | |||
| LV2_Worker_Status lv2_work_response(LV2_Handle instance, uint32_t size, const void* body) | |||
| { | |||
| return instancePtr->lv2_work_response(size, body); | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| @@ -1272,7 +1282,7 @@ static const void* lv2_extension_data(const char* uri) | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| static const LV2_State_Interface state = { lv2_save, lv2_restore }; | |||
| static const LV2_Worker_Interface worker = { lv2_work, nullptr, nullptr }; | |||
| static const LV2_Worker_Interface worker = { lv2_work, lv2_work_response, nullptr }; | |||
| if (std::strcmp(uri, LV2_STATE__interface) == 0) | |||
| return &state; | |||
| @@ -70,6 +70,140 @@ | |||
| #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||
| #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||
| // ----------------------------------------------------------------------- | |||
| static const char* const lv2ManifestPluginExtensionData[] = | |||
| { | |||
| "opts:interface", | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| LV2_STATE__interface, | |||
| LV2_WORKER__interface, | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| LV2_PROGRAMS__Interface, | |||
| #endif | |||
| #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
| MOD_LICENSE__interface, | |||
| #endif | |||
| nullptr | |||
| }; | |||
| static const char* const lv2ManifestPluginOptionalFeatures[] = | |||
| { | |||
| #if DISTRHO_PLUGIN_IS_RT_SAFE | |||
| LV2_CORE__hardRTCapable, | |||
| #endif | |||
| LV2_BUF_SIZE__boundedBlockLength, | |||
| nullptr | |||
| }; | |||
| static const char* const lv2ManifestPluginRequiredFeatures[] = | |||
| { | |||
| "opts:options", | |||
| LV2_URID__map, | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| LV2_WORKER__schedule, | |||
| #endif | |||
| #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
| MOD_LICENSE__feature, | |||
| #endif | |||
| nullptr | |||
| }; | |||
| static const char* const lv2ManifestPluginSupportedOptions[] = | |||
| { | |||
| LV2_BUF_SIZE__nominalBlockLength, | |||
| LV2_BUF_SIZE__maxBlockLength, | |||
| LV2_PARAMETERS__sampleRate, | |||
| nullptr | |||
| }; | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| static const char* const lv2ManifestUiExtensionData[] = | |||
| { | |||
| "opts:interface", | |||
| "ui:idleInterface", | |||
| "ui:showInterface", | |||
| "ui:resize", | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| LV2_PROGRAMS__UIInterface, | |||
| #endif | |||
| nullptr | |||
| }; | |||
| static const char* const lv2ManifestUiOptionalFeatures[] = | |||
| { | |||
| #if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
| # if !DISTRHO_UI_USER_RESIZABLE | |||
| "ui:noUserResize", | |||
| # endif | |||
| "ui:resize", | |||
| "ui:touch", | |||
| #endif | |||
| nullptr | |||
| }; | |||
| static const char* const lv2ManifestUiRequiredFeatures[] = | |||
| { | |||
| "opts:options", | |||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| LV2_DATA_ACCESS_URI, | |||
| LV2_INSTANCE_ACCESS_URI, | |||
| #endif | |||
| LV2_URID__map, | |||
| nullptr | |||
| }; | |||
| static const char* const lv2ManifestUiSupportedOptions[] = | |||
| { | |||
| LV2_PARAMETERS__sampleRate, | |||
| nullptr | |||
| }; | |||
| #endif // DISTRHO_PLUGIN_HAS_UI | |||
| static void addAttribute(String& text, | |||
| const char* const attribute, | |||
| const char* const values[], | |||
| const uint indent, | |||
| const bool endInDot = false) | |||
| { | |||
| if (values[0] == nullptr) | |||
| { | |||
| if (endInDot) | |||
| { | |||
| bool found; | |||
| const size_t index = text.rfind(';', &found); | |||
| if (found) text[index] = '.'; | |||
| } | |||
| return; | |||
| } | |||
| const size_t attributeLength = std::strlen(attribute); | |||
| for (uint i = 0; values[i] != nullptr; ++i) | |||
| { | |||
| for (uint j = 0; j < indent; ++j) | |||
| text += " "; | |||
| if (i == 0) | |||
| { | |||
| text += attribute; | |||
| } | |||
| else | |||
| { | |||
| for (uint j = 0; j < attributeLength; ++j) | |||
| text += " "; | |||
| } | |||
| text += " "; | |||
| const bool isUrl = std::strstr(values[i], "://") != nullptr || std::strncmp(values[i], "urn:", 4) == 0; | |||
| if (isUrl) text += "<"; | |||
| text += values[i]; | |||
| if (isUrl) text += ">"; | |||
| text += values[i + 1] ? " ,\n" : (endInDot ? " .\n\n" : " ;\n\n"); | |||
| } | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| DISTRHO_PLUGIN_EXPORT | |||
| @@ -132,32 +266,10 @@ void lv2_generate_ttl(const char* const basename) | |||
| manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n"; | |||
| manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |||
| # if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| manifestString += "\n"; | |||
| manifestString += " lv2:extensionData ui:idleInterface ,\n"; | |||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| manifestString += " ui:showInterface ,\n"; | |||
| manifestString += " <" LV2_PROGRAMS__Interface "> ;\n"; | |||
| # else | |||
| manifestString += " ui:showInterface ;\n"; | |||
| # endif | |||
| manifestString += "\n"; | |||
| # if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
| # if DISTRHO_UI_USER_RESIZABLE | |||
| manifestString += " lv2:optionalFeature ui:resize ,\n"; | |||
| manifestString += " ui:touch ;\n"; | |||
| manifestString += "\n"; | |||
| # else // DISTRHO_UI_USER_RESIZABLE | |||
| manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||
| manifestString += " ui:resize ,\n"; | |||
| manifestString += " ui:touch ;\n"; | |||
| manifestString += "\n"; | |||
| # endif // DISTRHO_UI_USER_RESIZABLE | |||
| # endif // DISTRHO_PLUGIN_HAS_EMBED_UI | |||
| manifestString += " lv2:requiredFeature <" LV2_DATA_ACCESS_URI "> ,\n"; | |||
| manifestString += " <" LV2_INSTANCE_ACCESS_URI "> ,\n"; | |||
| manifestString += " <" LV2_OPTIONS__options "> ,\n"; | |||
| manifestString += " <" LV2_URID__map "> ;\n"; | |||
| manifestString += " opts:supportedOption <" LV2_PARAMETERS__sampleRate "> .\n"; | |||
| addAttribute(manifestString, "lv2:extensionData", lv2ManifestUiExtensionData, 4); | |||
| addAttribute(manifestString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4); | |||
| addAttribute(manifestString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4); | |||
| addAttribute(manifestString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); | |||
| # else // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | |||
| # endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| @@ -214,7 +326,9 @@ void lv2_generate_ttl(const char* const basename) | |||
| pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |||
| pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; | |||
| pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
| #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |||
| pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
| #endif | |||
| @@ -232,45 +346,10 @@ void lv2_generate_ttl(const char* const basename) | |||
| #endif | |||
| pluginString += "\n"; | |||
| // extensionData | |||
| pluginString += " lv2:extensionData <" LV2_STATE__interface "> "; | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| pluginString += ",\n <" LV2_OPTIONS__interface "> "; | |||
| pluginString += ",\n <" LV2_WORKER__interface "> "; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| pluginString += ",\n <" LV2_PROGRAMS__Interface "> "; | |||
| #endif | |||
| #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
| pluginString += ",\n <" MOD_LICENSE__interface "> "; | |||
| #endif | |||
| pluginString += ";\n\n"; | |||
| // optionalFeatures | |||
| #if DISTRHO_PLUGIN_IS_RT_SAFE | |||
| pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; | |||
| pluginString += " <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; | |||
| #else | |||
| pluginString += " lv2:optionalFeature <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; | |||
| #endif | |||
| pluginString += "\n"; | |||
| // requiredFeatures | |||
| pluginString += " lv2:requiredFeature <" LV2_OPTIONS__options "> "; | |||
| pluginString += ",\n <" LV2_URID__map "> "; | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| pluginString += ",\n <" LV2_WORKER__schedule "> "; | |||
| #endif | |||
| #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
| pluginString += ",\n <" MOD_LICENSE__feature "> "; | |||
| #endif | |||
| pluginString += ";\n\n"; | |||
| // supportedOptions | |||
| pluginString += " opts:supportedOption <" LV2_BUF_SIZE__nominalBlockLength "> ,\n"; | |||
| pluginString += " <" LV2_BUF_SIZE__maxBlockLength "> ,\n"; | |||
| pluginString += " <" LV2_PARAMETERS__sampleRate "> ;\n"; | |||
| pluginString += "\n"; | |||
| addAttribute(pluginString, "lv2:extensionData", lv2ManifestPluginExtensionData, 4); | |||
| addAttribute(pluginString, "lv2:optionalFeature", lv2ManifestPluginOptionalFeatures, 4); | |||
| addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4); | |||
| addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4); | |||
| // UI | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| @@ -440,6 +519,12 @@ void lv2_generate_ttl(const char* const basename) | |||
| pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | |||
| // short name | |||
| const String& shortName(plugin.getParameterShortName(i)); | |||
| if (shortName.isNotEmpty()) | |||
| pluginString += " lv2:shortName \"\"\"" + shortName + "\"\"\" ;\n"; | |||
| // ranges | |||
| const ParameterRanges& ranges(plugin.getParameterRanges(i)); | |||
| @@ -528,6 +613,12 @@ void lv2_generate_ttl(const char* const basename) | |||
| } | |||
| } | |||
| // comment | |||
| const String& comment(plugin.getParameterDescription(i)); | |||
| if (comment.isNotEmpty()) | |||
| pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n"; | |||
| // hints | |||
| const uint32_t hints(plugin.getParameterHints(i)); | |||
| @@ -560,7 +651,7 @@ void lv2_generate_ttl(const char* const basename) | |||
| const String comment(plugin.getDescription()); | |||
| if (comment.isNotEmpty()) | |||
| pluginString += " rdfs:comment \"\"\"\n" + comment + "\n\"\"\" ;\n\n"; | |||
| pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n\n"; | |||
| } | |||
| #ifdef DISTRHO_PLUGIN_BRAND | |||
| @@ -629,30 +720,11 @@ void lv2_generate_ttl(const char* const basename) | |||
| uiString += "\n"; | |||
| uiString += "<" DISTRHO_UI_URI ">\n"; | |||
| uiString += " lv2:extensionData ui:idleInterface ,\n"; | |||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| uiString += " ui:showInterface ,\n"; | |||
| uiString += " <" LV2_PROGRAMS__Interface "> ;\n"; | |||
| # else | |||
| uiString += " ui:showInterface ;\n"; | |||
| # endif | |||
| uiString += "\n"; | |||
| # if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
| # if DISTRHO_UI_USER_RESIZABLE | |||
| uiString += " lv2:optionalFeature ui:resize ,\n"; | |||
| uiString += " ui:touch ;\n"; | |||
| uiString += "\n"; | |||
| # else // DISTRHO_UI_USER_RESIZABLE | |||
| uiString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||
| uiString += " ui:resize ,\n"; | |||
| uiString += " ui:touch ;\n"; | |||
| uiString += "\n"; | |||
| # endif // DISTRHO_UI_USER_RESIZABLE | |||
| # endif // DISTRHO_PLUGIN_HAS_EMBED_UI | |||
| uiString += " lv2:requiredFeature <" LV2_OPTIONS__options "> ,\n"; | |||
| uiString += " <" LV2_URID__map "> ;\n"; | |||
| uiString += " opts:supportedOption <" LV2_PARAMETERS__sampleRate "> .\n"; | |||
| addAttribute(uiString, "lv2:extensionData", lv2ManifestUiExtensionData, 4); | |||
| addAttribute(uiString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4); | |||
| addAttribute(uiString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4); | |||
| addAttribute(uiString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); | |||
| uiFile << uiString << std::endl; | |||
| uiFile.close(); | |||
| @@ -1294,7 +1294,11 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||
| case effGetParamName: | |||
| if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) | |||
| { | |||
| DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterName(index), 16); | |||
| const String& shortName(plugin.getParameterShortName(index)); | |||
| if (shortName.isNotEmpty()) | |||
| DISTRHO_NAMESPACE::strncpy((char*)ptr, shortName, 16); | |||
| else | |||
| DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterName(index), 16); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| @@ -9,19 +9,18 @@ else | |||
| exit | |||
| fi | |||
| PWD=`pwd` | |||
| DPF_DIR=$(dirname $0)/.. | |||
| PLUGINS=$(ls | grep vst.dylib) | |||
| rm -rf *.vst/ | |||
| PLUGINS=`ls | grep vst.dylib` | |||
| for i in $PLUGINS; do | |||
| FILE=`echo $i | awk 'sub("-vst.dylib","")'` | |||
| cp -r ../dpf/utils/plugin.vst/ $FILE.vst | |||
| mv $i $FILE.vst/Contents/MacOS/$FILE | |||
| rm -f $FILE.vst/Contents/MacOS/deleteme | |||
| sed -i -e "s/X-PROJECTNAME-X/$FILE/" $FILE.vst/Contents/Info.plist | |||
| rm -f $FILE.vst/Contents/Info.plist-e | |||
| BUNDLE=$(echo ${i} | awk 'sub("-vst.dylib","")') | |||
| cp -r ${DPF_DIR}/utils/plugin.vst/ ${BUNDLE}.vst | |||
| mv ${i} ${BUNDLE}.vst/Contents/MacOS/${BUNDLE} | |||
| rm -f ${BUNDLE}.vst/Contents/MacOS/deleteme | |||
| sed -i -e "s/X-PROJECTNAME-X/${BUNDLE}/" ${BUNDLE}.vst/Contents/Info.plist | |||
| rm -f ${BUNDLE}.vst/Contents/Info.plist-e | |||
| done | |||
| cd .. | |||
| @@ -15,6 +15,7 @@ | |||
| */ | |||
| #include "DistrhoPluginKars.hpp" | |||
| #include "DistrhoPluginUtils.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| @@ -117,136 +118,6 @@ void DistrhoPluginKars::activate() | |||
| } | |||
| } | |||
| /** | |||
| Handy class to help keep audio buffer in sync with incoming MIDI events. | |||
| To use it, create a local variable (on the stack) and call nextEvent() until it returns false. | |||
| @code | |||
| for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();) | |||
| { | |||
| float* const outL = amsh.outputs[0]; | |||
| float* const outR = amsh.outputs[1]; | |||
| for (uint32_t i=0; i<amsh.midiEventCount; ++i) | |||
| { | |||
| const MidiEvent& ev(amsh.midiEvents[i]); | |||
| // ... do something with the midi event | |||
| } | |||
| renderSynth(outL, outR, amsh.frames); | |||
| } | |||
| @endcode | |||
| Some important notes when using this class: | |||
| 1. MidiEvent::frame retains its original value, but it is useless, do not use it. | |||
| 2. The class variables names are be the same as the default ones in the run function. | |||
| Keep that in mind and try to avoid typos. :) | |||
| */ | |||
| class AudioMidiSyncHelper { | |||
| public: | |||
| /** Parameters from the run function, adjusted for event sync */ | |||
| float** outputs; | |||
| uint32_t frames; | |||
| const MidiEvent* midiEvents; | |||
| uint32_t midiEventCount; | |||
| /** | |||
| Constructor, using values from the run function. | |||
| */ | |||
| AudioMidiSyncHelper(float** const o, uint32_t f, const MidiEvent* m, uint32_t mc) | |||
| : outputs(o), | |||
| frames(0), | |||
| midiEvents(m), | |||
| midiEventCount(0), | |||
| remainingFrames(f), | |||
| remainingMidiEventCount(mc), | |||
| totalFramesUsed(0) {} | |||
| /** | |||
| Process a batch of events untill no more are available. | |||
| You must not read any more values from this class after this function returns false. | |||
| */ | |||
| bool nextEvent() | |||
| { | |||
| // nothing else to do | |||
| if (remainingFrames == 0) | |||
| return false; | |||
| // initial setup, need to find first MIDI event | |||
| if (totalFramesUsed == 0) | |||
| { | |||
| // no MIDI events at all in this process cycle | |||
| if (remainingMidiEventCount == 0) | |||
| { | |||
| frames = remainingFrames; | |||
| remainingFrames = 0; | |||
| totalFramesUsed += frames; | |||
| return true; | |||
| } | |||
| // render audio until first midi event, if needed | |||
| if (const uint32_t firstEventFrame = midiEvents[0].frame) | |||
| { | |||
| frames = midiEvents[0].frame; | |||
| remainingFrames -= frames; | |||
| totalFramesUsed += frames; | |||
| return true; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
| outputs[i] += frames; | |||
| } | |||
| // no more MIDI events available | |||
| if (remainingMidiEventCount == 0) | |||
| { | |||
| frames = remainingFrames; | |||
| midiEvents = nullptr; | |||
| midiEventCount = 0; | |||
| remainingFrames = 0; | |||
| totalFramesUsed += frames; | |||
| return true; | |||
| } | |||
| // if there were midi events before, increment pointer | |||
| if (midiEventCount != 0) | |||
| midiEvents += midiEventCount; | |||
| const uint32_t firstEventFrame = midiEvents[0].frame; | |||
| DISTRHO_SAFE_ASSERT_RETURN((firstEventFrame - frames) < remainingFrames, false); | |||
| midiEventCount = 1; | |||
| while (midiEventCount < remainingMidiEventCount) | |||
| { | |||
| if (midiEvents[midiEventCount].frame == firstEventFrame) | |||
| ++midiEventCount; | |||
| else | |||
| break; | |||
| } | |||
| if (totalFramesUsed != 0) | |||
| { | |||
| // need to modify timestamp of midi events | |||
| MidiEvent* const rwEvents = const_cast<MidiEvent*>(midiEvents); | |||
| for (uint32_t i=0; i < midiEventCount; ++i) | |||
| rwEvents[i].frame -= totalFramesUsed; | |||
| } | |||
| frames = remainingFrames - firstEventFrame; | |||
| remainingFrames -= frames; | |||
| remainingMidiEventCount -= midiEventCount; | |||
| totalFramesUsed += frames; | |||
| return true; | |||
| } | |||
| private: | |||
| /** @internal */ | |||
| uint32_t remainingFrames; | |||
| uint32_t remainingMidiEventCount; | |||
| uint32_t totalFramesUsed; | |||
| }; | |||
| void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) | |||
| { | |||
| uint8_t note, velo; | |||
| @@ -270,7 +141,7 @@ void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, con | |||
| DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | |||
| if (velo > 0) | |||
| { | |||
| fNotes[note].on = fBlockStart + amsh.midiEvents[i].frame; | |||
| fNotes[note].on = fBlockStart; | |||
| fNotes[note].off = kNoteNull; | |||
| fNotes[note].velocity = velo; | |||
| break; | |||
| @@ -279,7 +150,7 @@ void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, con | |||
| case 0x80: | |||
| note = data[1]; | |||
| DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | |||
| fNotes[note].off = fBlockStart + amsh.midiEvents[i].frame; | |||
| fNotes[note].off = fBlockStart; | |||
| break; | |||
| } | |||
| } | |||