@@ -213,13 +213,15 @@ DGL_SYSTEM_LIBS += -framework Cocoa | |||||
endif | endif | ||||
ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
DGL_SYSTEM_LIBS += -lgdi32 | |||||
DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 | |||||
endif | endif | ||||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | ||||
ifeq ($(HAVE_X11),true) | |||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) | DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) | ||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) | DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) | ||||
endif | endif | ||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set Cairo specific stuff | # Set Cairo specific stuff | ||||
@@ -30,6 +30,11 @@ List of plugins made with DPF:<br/> | |||||
- [DragonFly-Reverb](https://github.com/michaelwillis/dragonfly-reverb) | - [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) | - [Wolf-Shaper](https://github.com/pdesaulniers/wolf-shaper) and [Wolf-Spectrum](https://github.com/pdesaulniers/wolf-spectrum) | ||||
- [YK Chorus](https://github.com/SpotlightKid/ykchorus) | - [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/> | 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. | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#include "../Base.hpp" | |||||
#include "../Geometry.hpp" | |||||
#include "../Cairo.hpp" | |||||
START_NAMESPACE_DGL | 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) | #if defined(DISTRHO_OS_WINDOWS) | ||||
static bool needsInit = true; | static bool needsInit = true; | ||||
if (needsInit) | |||||
{ | |||||
needsInit = false; | |||||
# define DGL_EXT(PROC, func) \ | # define DGL_EXT(PROC, func) \ | ||||
func = (PROC) wglGetProcAddress ( #func ); \ | |||||
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | |||||
DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); | DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); | ||||
DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) | DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) | ||||
DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) | DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) | ||||
@@ -115,7 +112,7 @@ DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | |||||
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | ||||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
# undef DGL_EXT | # undef DGL_EXT | ||||
} | |||||
needsInit = false; | |||||
#endif | #endif | ||||
return nvgCreateGL(flags); | return nvgCreateGL(flags); | ||||
} | } | ||||
@@ -918,6 +915,8 @@ int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWi | |||||
#ifndef DGL_NO_SHARED_RESOURCES | #ifndef DGL_NO_SHARED_RESOURCES | ||||
void NanoVG::loadSharedResources() | void NanoVG::loadSharedResources() | ||||
{ | { | ||||
if (fContext == nullptr) return; | |||||
if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) | if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) | ||||
return; | return; | ||||
@@ -1,6 +1,8 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -105,6 +107,10 @@ struct Window::PrivateData { | |||||
mView(nullptr), | mView(nullptr), | ||||
mWindow(nullptr), | mWindow(nullptr), | ||||
mParentWindow(nullptr) | mParentWindow(nullptr) | ||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
, fOpenFilePanel(nullptr), | |||||
fFilePanelDelegate(nullptr) | |||||
# endif | |||||
#else | #else | ||||
xDisplay(nullptr), | xDisplay(nullptr), | ||||
xWindow(0) | xWindow(0) | ||||
@@ -137,6 +143,10 @@ struct Window::PrivateData { | |||||
mView(nullptr), | mView(nullptr), | ||||
mWindow(nullptr), | mWindow(nullptr), | ||||
mParentWindow(nullptr) | mParentWindow(nullptr) | ||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
, fOpenFilePanel(nullptr), | |||||
fFilePanelDelegate(nullptr) | |||||
# endif | |||||
#else | #else | ||||
xDisplay(nullptr), | xDisplay(nullptr), | ||||
xWindow(0) | xWindow(0) | ||||
@@ -181,6 +191,10 @@ struct Window::PrivateData { | |||||
mView(nullptr), | mView(nullptr), | ||||
mWindow(nullptr), | mWindow(nullptr), | ||||
mParentWindow(nullptr) | mParentWindow(nullptr) | ||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
, fOpenFilePanel(nullptr), | |||||
fFilePanelDelegate(nullptr) | |||||
# endif | |||||
#else | #else | ||||
xDisplay(nullptr), | xDisplay(nullptr), | ||||
xWindow(0) | xWindow(0) | ||||
@@ -323,6 +337,19 @@ struct Window::PrivateData { | |||||
xWindow = 0; | xWindow = 0; | ||||
#endif | #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"); | DBG("Success!\n"); | ||||
} | } | ||||
@@ -687,6 +714,9 @@ struct Window::PrivateData { | |||||
} | } | ||||
#else | #else | ||||
XStoreName(xDisplay, xWindow, title); | 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 | #endif | ||||
} | } | ||||
@@ -774,6 +804,15 @@ struct Window::PrivateData { | |||||
} | } | ||||
#endif | #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) | if (fModal.enabled && fModal.parent != nullptr) | ||||
fModal.parent->idle(); | fModal.parent->idle(); | ||||
} | } | ||||
@@ -1050,6 +1089,39 @@ struct Window::PrivateData { | |||||
return false; | 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; | Application& fApp; | ||||
@@ -1095,11 +1167,18 @@ struct Window::PrivateData { | |||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
HWND hwnd; | HWND hwnd; | ||||
HWND hwndParent; | HWND hwndParent; | ||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
String fSelectedFile; | |||||
# endif | |||||
#elif defined(DISTRHO_OS_MAC) | #elif defined(DISTRHO_OS_MAC) | ||||
bool fNeedsIdle; | bool fNeedsIdle; | ||||
NSView<PuglGenericView>* mView; | NSView<PuglGenericView>* mView; | ||||
id mWindow; | id mWindow; | ||||
id mParentWindow; | id mParentWindow; | ||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
NSOpenPanel* fOpenFilePanel; | |||||
id fFilePanelDelegate; | |||||
# endif | |||||
#else | #else | ||||
Display* xDisplay; | Display* xDisplay; | ||||
::Window xWindow; | ::Window xWindow; | ||||
@@ -1216,6 +1295,20 @@ void Window::repaint() noexcept | |||||
// } | // } | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #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) | bool Window::openFileBrowser(const FileBrowserOptions& options) | ||||
{ | { | ||||
# ifdef SOFD_HAVE_X11 | # ifdef SOFD_HAVE_X11 | ||||
@@ -1278,6 +1371,93 @@ bool Window::openFileBrowser(const FileBrowserOptions& options) | |||||
// show | // show | ||||
return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0); | 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 | # else | ||||
// not implemented | // not implemented | ||||
return false; | return false; | ||||
@@ -1286,8 +1466,29 @@ bool Window::openFileBrowser(const FileBrowserOptions& options) | |||||
(void)options; | (void)options; | ||||
# endif | # 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 | ||||
#endif // !defined(DGL_FILE_BROWSER_DISABLED) | |||||
bool Window::isEmbed() const noexcept | bool Window::isEmbed() const noexcept | ||||
{ | { | ||||
return pData->fUsingEmbed; | return pData->fUsingEmbed; | ||||
@@ -274,6 +274,9 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
if (title) { | if (title) { | ||||
XStoreName(impl->display, impl->win, 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) { | if (view->transient_parent > 0) { | ||||
@@ -1257,7 +1257,8 @@ static int fib_opendir (Display *dpy, const char* path, const char *sel) { | |||||
} else { | } else { | ||||
int i; | int i; | ||||
struct dirent *de; | struct dirent *de; | ||||
strcpy (_cur_path, path); | |||||
if (path != _cur_path) | |||||
strcpy (_cur_path, path); | |||||
if (_cur_path[strlen (_cur_path) -1] != '/') | if (_cur_path[strlen (_cur_path) -1] != '/') | ||||
strcat (_cur_path, "/"); | strcat (_cur_path, "/"); | ||||
@@ -384,6 +384,13 @@ struct Parameter { | |||||
*/ | */ | ||||
String name; | 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 | The symbol of this parameter.@n | ||||
A parameter symbol is a short restricted name used as a machine and human readable identifier.@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; | 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 | Ranges of this parameter.@n | ||||
The ranges describe the default, minimum and maximum values. | The ranges describe the default, minimum and maximum values. | ||||
@@ -430,6 +443,7 @@ struct Parameter { | |||||
Parameter() noexcept | Parameter() noexcept | ||||
: hints(0x0), | : hints(0x0), | ||||
name(), | name(), | ||||
shortName(), | |||||
symbol(), | symbol(), | ||||
unit(), | unit(), | ||||
ranges(), | 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 | Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept | ||||
: hints(h), | : hints(h), | ||||
name(n), | name(n), | ||||
shortName(), | |||||
symbol(s), | symbol(s), | ||||
unit(u), | unit(u), | ||||
ranges(def, min, max), | ranges(def, min, max), | ||||
@@ -462,11 +477,12 @@ struct Parameter { | |||||
case kParameterDesignationNull: | case kParameterDesignationNull: | ||||
break; | break; | ||||
case kParameterDesignationBypass: | 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.def = 0.0f; | ||||
ranges.min = 0.0f; | ranges.min = 0.0f; | ||||
ranges.max = 1.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); | 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. | * Print a safe exception error message. | ||||
*/ | */ | ||||
@@ -568,6 +568,18 @@ public: | |||||
return fBuffer; | 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 | // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | ||||
// Copyright (C) 2004-2008 René Nyffenegger | // Copyright (C) 2004-2008 René Nyffenegger | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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_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_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 */ | ||||
#define DISTRHO_SAFE_EXCEPTION(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); } | #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; } | #define DISTRHO_SAFE_EXCEPTION_BREAK(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); break; } | ||||
@@ -375,6 +375,13 @@ public: | |||||
return fData->parameters[index].name; | 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 | const String& getParameterSymbol(const uint32_t index) const noexcept | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | ||||
@@ -389,6 +396,13 @@ public: | |||||
return fData->parameters[index].unit; | 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 | const ParameterEnumerationValues& getParameterEnumValues(const uint32_t index) const noexcept | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackEnumValues); | DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackEnumValues); | ||||
@@ -846,6 +846,11 @@ public: | |||||
return LV2_WORKER_SUCCESS; | return LV2_WORKER_SUCCESS; | ||||
} | } | ||||
LV2_Worker_Status lv2_work_response(uint32_t, const void*) | |||||
{ | |||||
return LV2_WORKER_SUCCESS; | |||||
} | |||||
#endif | #endif | ||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
@@ -1243,6 +1248,11 @@ LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function, LV2 | |||||
{ | { | ||||
return instancePtr->lv2_work(data); | 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 | #endif | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1272,7 +1282,7 @@ static const void* lv2_extension_data(const char* uri) | |||||
#if DISTRHO_PLUGIN_WANT_STATE | #if DISTRHO_PLUGIN_WANT_STATE | ||||
static const LV2_State_Interface state = { lv2_save, lv2_restore }; | 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) | if (std::strcmp(uri, LV2_STATE__interface) == 0) | ||||
return &state; | 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_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)) | #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 | DISTRHO_PLUGIN_EXPORT | ||||
@@ -132,32 +266,10 @@ void lv2_generate_ttl(const char* const basename) | |||||
manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n"; | manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n"; | ||||
manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n"; | manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n"; | ||||
# if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | # 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 | # else // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | ||||
manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | ||||
# endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | # 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 opts: <" LV2_OPTIONS_PREFIX "> .\n"; | ||||
pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\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"; | 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"; | pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | ||||
#endif | |||||
#if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | ||||
#endif | #endif | ||||
@@ -232,45 +346,10 @@ void lv2_generate_ttl(const char* const basename) | |||||
#endif | #endif | ||||
pluginString += "\n"; | 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 | // UI | ||||
#if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
@@ -440,6 +519,12 @@ void lv2_generate_ttl(const char* const basename) | |||||
pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | ||||
// short name | |||||
const String& shortName(plugin.getParameterShortName(i)); | |||||
if (shortName.isNotEmpty()) | |||||
pluginString += " lv2:shortName \"\"\"" + shortName + "\"\"\" ;\n"; | |||||
// ranges | // ranges | ||||
const ParameterRanges& ranges(plugin.getParameterRanges(i)); | 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 | // hints | ||||
const uint32_t hints(plugin.getParameterHints(i)); | const uint32_t hints(plugin.getParameterHints(i)); | ||||
@@ -560,7 +651,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
const String comment(plugin.getDescription()); | const String comment(plugin.getDescription()); | ||||
if (comment.isNotEmpty()) | if (comment.isNotEmpty()) | ||||
pluginString += " rdfs:comment \"\"\"\n" + comment + "\n\"\"\" ;\n\n"; | |||||
pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n\n"; | |||||
} | } | ||||
#ifdef DISTRHO_PLUGIN_BRAND | #ifdef DISTRHO_PLUGIN_BRAND | ||||
@@ -629,30 +720,11 @@ void lv2_generate_ttl(const char* const basename) | |||||
uiString += "\n"; | uiString += "\n"; | ||||
uiString += "<" DISTRHO_UI_URI ">\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 << uiString << std::endl; | ||||
uiFile.close(); | uiFile.close(); | ||||
@@ -1294,7 +1294,11 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||||
case effGetParamName: | case effGetParamName: | ||||
if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) | 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 1; | ||||
} | } | ||||
return 0; | return 0; | ||||
@@ -9,19 +9,18 @@ else | |||||
exit | exit | ||||
fi | fi | ||||
PWD=`pwd` | |||||
DPF_DIR=$(dirname $0)/.. | |||||
PLUGINS=$(ls | grep vst.dylib) | |||||
rm -rf *.vst/ | rm -rf *.vst/ | ||||
PLUGINS=`ls | grep vst.dylib` | |||||
for i in $PLUGINS; do | 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 | done | ||||
cd .. | cd .. |
@@ -15,6 +15,7 @@ | |||||
*/ | */ | ||||
#include "DistrhoPluginKars.hpp" | #include "DistrhoPluginKars.hpp" | ||||
#include "DistrhoPluginUtils.hpp" | |||||
START_NAMESPACE_DISTRHO | 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) | void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) | ||||
{ | { | ||||
uint8_t note, velo; | 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 | DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | ||||
if (velo > 0) | if (velo > 0) | ||||
{ | { | ||||
fNotes[note].on = fBlockStart + amsh.midiEvents[i].frame; | |||||
fNotes[note].on = fBlockStart; | |||||
fNotes[note].off = kNoteNull; | fNotes[note].off = kNoteNull; | ||||
fNotes[note].velocity = velo; | fNotes[note].velocity = velo; | ||||
break; | break; | ||||
@@ -279,7 +150,7 @@ void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, con | |||||
case 0x80: | case 0x80: | ||||
note = data[1]; | note = data[1]; | ||||
DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | ||||
fNotes[note].off = fBlockStart + amsh.midiEvents[i].frame; | |||||
fNotes[note].off = fBlockStart; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||