DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit f8670208e8281ee02d59b49f173096bbc8bd1031
Author: falkTX <falktx@gmail.com>
Date:   Tue, 24 Dec 2013 04:23:30 +0000

Initial commit

Diffstat:
Adgl/App.hpp | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/Base.hpp | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/CairoWidget.hpp | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/Geometry.hpp | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/Image.hpp | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/ImageAboutWindow.hpp | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/ImageButton.hpp | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/ImageKnob.hpp | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/ImageSlider.hpp | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/Makefile | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/Makefile.mk | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/StandaloneWindow.hpp | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/Widget.hpp | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/Window.hpp | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/App.cpp | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/Base.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/Geometry.cpp | 416+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/Image.cpp | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/ImageAboutWindow.cpp | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/ImageButton.cpp | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/ImageKnob.cpp | 340+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/ImageSlider.cpp | 316+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/Widget.cpp | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/Window.cpp | 805+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/pugl/pugl.h | 353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/pugl/pugl_internal.h | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/pugl/pugl_osx.m | 418+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/pugl/pugl_osx_extended.h | 29+++++++++++++++++++++++++++++
Adgl/src/pugl/pugl_osx_extended.m | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/pugl/pugl_win.cpp | 374+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/pugl/pugl_x11.c | 414+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/DistrhoPlugin.hpp | 239+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/DistrhoPluginMain.cpp | 26++++++++++++++++++++++++++
Adistrho/DistrhoUI.hpp | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/DistrhoUIMain.cpp | 25+++++++++++++++++++++++++
Adistrho/DistrhoUtils.hpp | 684+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoDefines.h | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoPlugin.cpp | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoPluginInternal.hpp | 380+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoPluginLADSPA+DSSI.cpp | 702+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoPluginLV2.cpp | 714+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoPluginLV2export.cpp | 367+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoPluginVST.cpp | 963+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoUI.cpp | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoUIDSSI.cpp | 489+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoUIInternal.hpp | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/DistrhoUILV2.cpp | 333+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/dssi/dssi.h | 441+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/dssi/seq_event-compat.h | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/ladspa/ladspa.h | 603+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/atom-forge.h | 619+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/atom-helpers.h | 255+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/atom-util.h | 401+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/atom.h | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/buf-size.h | 30++++++++++++++++++++++++++++++
Adistrho/src/lv2/data-access.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/dynmanifest.h | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/event-helpers.h | 263+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/event.h | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/instance-access.h | 37+++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/log.h | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/logger.h | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/lv2-midifunctions.h | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/lv2-miditype.h | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/lv2.h | 454+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/lv2_external_ui.h | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/lv2_programs.h | 174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/lv2_rtmempool.h | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/midi.h | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/morph.h | 34++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/options.h | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/parameters.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/patch.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/port-groups.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/port-props.h | 42++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/presets.h | 34++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/resize-port.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/state.h | 352+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/time.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/ui.h | 407+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/units.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/uri-map.h | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/urid.h | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/lv2/worker.h | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/src/vestige/aeffectx.h | 279+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alv2-ttl-generator/GNUmakefile | 16++++++++++++++++
Alv2-ttl-generator/lv2_ttl_generator.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
87 files changed, 18657 insertions(+), 0 deletions(-)

diff --git a/dgl/App.hpp b/dgl/App.hpp @@ -0,0 +1,54 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_APP_HPP_INCLUDED +#define DGL_APP_HPP_INCLUDED + +#include "Base.hpp" + +START_NAMESPACE_DGL + +class Window; + +// ----------------------------------------------------------------------- + +class App +{ +public: + App(); + ~App(); + + void idle(); + void exec(); + void quit(); + bool isQuiting() const; + +private: + struct PrivateData; + PrivateData* const pData; + friend class Window; + + void addWindow(Window* const window); + void removeWindow(Window* const window); + void oneShown(); + void oneHidden(); +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_APP_HPP_INCLUDED diff --git a/dgl/Base.hpp b/dgl/Base.hpp @@ -0,0 +1,143 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_BASE_HPP_INCLUDED +#define DGL_BASE_HPP_INCLUDED + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define DGL_OS_WINDOWS 1 +#elif defined(__APPLE__) +# define DGL_OS_MAC 1 +#elif defined(__HAIKU__) +# define DGL_OS_HAIKU 1 +#elif defined(__linux__) +# define DGL_OS_LINUX 1 +#endif + +#if defined(HAVE_CPP11_SUPPORT) +# define PROPER_CPP11_SUPPORT +#elif defined(__GNUC__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +# define PROPER_CPP11_SUPPORT +# if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 +# define override // gcc4.7+ only +# endif +# endif +#endif + +#ifndef PROPER_CPP11_SUPPORT +# ifndef __clang__ +# define noexcept throw() +# endif +# define override +# define nullptr (0) +#endif + +#ifndef DGL_NAMESPACE +# define DGL_NAMESPACE DGL +#endif + +#define START_NAMESPACE_DGL namespace DGL_NAMESPACE { +#define END_NAMESPACE_DGL } +#define USE_NAMESPACE_DGL using namespace DGL_NAMESPACE; + +#if DGL_OS_MAC +# include <OpenGL/gl.h> +#else +# include <GL/gl.h> +#endif + +#if defined(GL_BGR_EXT) && ! defined(GL_BGR) +# define GL_BGR GL_BGR_EXT +#endif + +#if defined(GL_BGRA_EXT) && ! defined(GL_BGRA) +# define GL_BGRA GL_BGRA_EXT +#endif + +#ifndef GL_CLAMP_TO_BORDER +# define GL_CLAMP_TO_BORDER 0x812D +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +/* + * Convenience symbols for ASCII control characters. + */ +enum Char { + CHAR_BACKSPACE = 0x08, + CHAR_ESCAPE = 0x1B, + CHAR_DELETE = 0x7F +}; + +/* + * Special (non-Unicode) keyboard keys. + */ +enum Key { + KEY_F1 = 1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_LEFT, + KEY_UP, + KEY_RIGHT, + KEY_DOWN, + KEY_PAGE_UP, + KEY_PAGE_DOWN, + KEY_HOME, + KEY_END, + KEY_INSERT, + KEY_SHIFT, + KEY_CTRL, + KEY_ALT, + KEY_SUPER +}; + +/* + * Keyboard modifier flags. + */ +enum Modifier { + MODIFIER_SHIFT = 1 << 0, /**< Shift key */ + MODIFIER_CTRL = 1 << 1, /**< Control key */ + MODIFIER_ALT = 1 << 2, /**< Alt/Option key */ + MODIFIER_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ +}; + +/* + * Cross-platform sleep function. + */ +void sleep(unsigned int secs); + +/* + * Cross-platform msleep function. + */ +void msleep(unsigned int msecs); + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_BASE_HPP_INCLUDED diff --git a/dgl/CairoWidget.hpp b/dgl/CairoWidget.hpp @@ -0,0 +1,142 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_CAIRO_WIDGET_HPP_INCLUDED +#define DGL_CAIRO_WIDGET_HPP_INCLUDED + +#include "Widget.hpp" + +#include <cairo.h> + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class CairoWidget : public Widget +{ +public: + CairoWidget(Window& parent) + : Widget(parent), + fContext(nullptr), + fSurface(nullptr), + fTextureId(0) + { + } + +protected: + virtual void cairoDisplay(cairo_t* const context) = 0; + +private: + void onReshape(int width, int height) override + { + // handle resize + setSize(width, height); + Widget::onReshape(width, height); + + // free previous if needed + onClose(); + + // create new + fSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); + fContext = cairo_create(fSurface); + + glGenTextures(1, &fTextureId); + } + + void onClose() override + { + if (fContext != nullptr) + { + cairo_destroy(fContext); + fContext = nullptr; + } + + if (fSurface != nullptr) + { + cairo_surface_destroy(fSurface); + fSurface = nullptr; + } + + if (fTextureId != 0) + { + glDeleteTextures(1, &fTextureId); + fTextureId = 0; + } + } + + void onDisplay() override + { + // wait for first resize + if (fSurface == nullptr || fContext == nullptr) + { + glClear(GL_COLOR_BUFFER_BIT); + return; + } + + const int width = getWidth(); + const int height = getHeight(); + + // draw cairo stuff + cairoDisplay(fContext); + + // get cairo surface data (RGB24) + unsigned char* const surfaceData = cairo_image_surface_get_data(fSurface); + + // enable GL texture + glEnable(GL_TEXTURE_RECTANGLE_ARB); + + // set texture params + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // bind texture to surface data + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fTextureId); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, surfaceData); + + // draw the texture + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glBegin(GL_QUADS); + glTexCoord2i(0, height); + glVertex2i(0, height); + + glTexCoord2i(width, height); + glVertex2i(width, height); + + glTexCoord2i(width, 0); + glVertex2i(width, 0); + + glTexCoord2i(0, 0); + glVertex2i(0, 0); + glEnd(); + + // cleanup + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + glDisable(GL_TEXTURE_RECTANGLE_ARB); + } + +private: + cairo_t* fContext; + cairo_surface_t* fSurface; + GLuint fTextureId; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_CAIRO_WIDGET_HPP_INCLUDED diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp @@ -0,0 +1,133 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_GEOMETRY_HPP_INCLUDED +#define DGL_GEOMETRY_HPP_INCLUDED + +#include "Base.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +template<typename T> +class Point +{ +public: + Point() noexcept; + Point(T x, T y) noexcept; + Point(const Point<T>& pos) noexcept; + + T getX() const noexcept; + T getY() const noexcept; + + void setX(T x) noexcept; + void setY(T y) noexcept; + + void move(T x, T y) noexcept; + void move(const Point<T>& pos) noexcept; + + Point<T>& operator=(const Point<T>& pos) noexcept; + Point<T>& operator+=(const Point<T>& pos) noexcept; + Point<T>& operator-=(const Point<T>& pos) noexcept; + bool operator==(const Point<T>& pos) const noexcept; + bool operator!=(const Point<T>& pos) const noexcept; + +private: + T fX, fY; + template<typename> friend class Rectangle; +}; + +// ----------------------------------------------------------------------- + +template<typename T> +class Size +{ +public: + Size() noexcept; + Size(T width, T height) noexcept; + Size(const Size<T>& size) noexcept; + + T getWidth() const noexcept; + T getHeight() const noexcept; + + void setWidth(T width) noexcept; + void setHeight(T height) noexcept; + + Size<T>& operator=(const Size<T>& size) noexcept; + Size<T>& operator+=(const Size<T>& size) noexcept; + Size<T>& operator-=(const Size<T>& size) noexcept; + Size<T>& operator*=(T m) noexcept; + Size<T>& operator/=(T d) noexcept; + bool operator==(const Size<T>& size) const noexcept; + bool operator!=(const Size<T>& size) const noexcept; + +private: + T fWidth, fHeight; + template<typename> friend class Rectangle; +}; + +// ----------------------------------------------------------------------- + +template<typename T> +class Rectangle +{ +public: + Rectangle() noexcept; + Rectangle(T x, T y, T width, T height) noexcept; + Rectangle(T x, T y, const Size<T>& size) noexcept; + Rectangle(const Point<T>& pos, T width, T height) noexcept; + Rectangle(const Point<T>& pos, const Size<T>& size) noexcept; + Rectangle(const Rectangle<T>& rect) noexcept; + + T getX() const noexcept; + T getY() const noexcept; + T getWidth() const noexcept; + T getHeight() const noexcept; + + const Point<T>& getPos() const noexcept; + const Size<T>& getSize() const noexcept; + + bool contains(T x, T y) const noexcept; + bool contains(const Point<T>& pos) const noexcept; + bool containsX(T x) const noexcept; + bool containsY(T y) const noexcept; + + void setX(T x) noexcept; + void setY(T y) noexcept; + void setPos(T x, T y) noexcept; + void setPos(const Point<T>& pos) noexcept; + + void move(T x, T y) noexcept; + void move(const Point<T>& pos) noexcept; + + void setWidth(T width) noexcept; + void setHeight(T height) noexcept; + void setSize(T width, T height) noexcept; + void setSize(const Size<T>& size) noexcept; + + Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept; + +private: + Point<T> fPos; + Size<T> fSize; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_GEOMETRY_HPP_INCLUDED diff --git a/dgl/Image.hpp b/dgl/Image.hpp @@ -0,0 +1,66 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_IMAGE_HPP_INCLUDED +#define DGL_IMAGE_HPP_INCLUDED + +#include "Geometry.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class Image +{ +public: + Image() noexcept; + Image(const char* rawData, int width, int height, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; + Image(const char* rawData, const Size<int>& size, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; + Image(const Image& image) noexcept; + + void loadFromMemory(const char* rawData, int width, int height, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; + void loadFromMemory(const char* rawData, const Size<int>& size, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; + + bool isValid() const noexcept; + + int getWidth() const noexcept; + int getHeight() const noexcept; + const Size<int>& getSize() const noexcept; + + const char* getRawData() const noexcept; + GLenum getFormat() const noexcept; + GLenum getType() const noexcept; + + void draw() const; + void draw(int x, int y) const; + void draw(const Point<int>& pos) const; + + Image& operator=(const Image& image) noexcept; + bool operator==(const Image& image) const noexcept; + bool operator!=(const Image& image) const noexcept; + +private: + const char* fRawData; + Size<int> fSize; + GLenum fFormat; + GLenum fType; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_IMAGE_HPP_INCLUDED diff --git a/dgl/ImageAboutWindow.hpp b/dgl/ImageAboutWindow.hpp @@ -0,0 +1,56 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_IMAGE_ABOUT_WINDOW_HPP_INCLUDED +#define DGL_IMAGE_ABOUT_WINDOW_HPP_INCLUDED + +#include "Image.hpp" +#include "Widget.hpp" +#include "Window.hpp" + +#ifdef PROPER_CPP11_SUPPORT +# include <cstdint> +#else +# include <stdint.h> +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class ImageAboutWindow : public Window, + public Widget +{ +public: + ImageAboutWindow(App& app, Window& parent, const Image& image = Image()); + ImageAboutWindow(Widget* widget, const Image& image = Image()); + + void setImage(const Image& image); + +protected: + void onDisplay() override; + bool onMouse(int button, bool press, int x, int y) override; + bool onKeyboard(bool press, uint32_t key) override; + +private: + Image fImgBackground; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_IMAGE_ABOUT_WINDOW_HPP_INCLUDED diff --git a/dgl/ImageButton.hpp b/dgl/ImageButton.hpp @@ -0,0 +1,64 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_IMAGE_BUTTON_HPP_INCLUDED +#define DGL_IMAGE_BUTTON_HPP_INCLUDED + +#include "Image.hpp" +#include "Widget.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class ImageButton : public Widget +{ +public: + class Callback + { + public: + virtual ~Callback() {} + virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0; + }; + + ImageButton(Window& parent, const Image& image); + ImageButton(Widget* widget, const Image& image); + ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown); + ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); + ImageButton(const ImageButton& imageButton); + + void setCallback(Callback* callback); + +protected: + void onDisplay() override; + bool onMouse(int button, bool press, int x, int y) override; + bool onMotion(int x, int y) override; + +private: + Image fImageNormal; + Image fImageHover; + Image fImageDown; + Image* fCurImage; + int fCurButton; + + Callback* fCallback; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_IMAGE_BUTTON_HPP_INCLUDED diff --git a/dgl/ImageKnob.hpp b/dgl/ImageKnob.hpp @@ -0,0 +1,89 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_IMAGE_KNOB_HPP_INCLUDED +#define DGL_IMAGE_KNOB_HPP_INCLUDED + +#include "Image.hpp" +#include "Widget.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class ImageKnob : public Widget +{ +public: + enum Orientation { + Horizontal, + Vertical + }; + + class Callback + { + public: + virtual ~Callback() {} + virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0; + virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0; + virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0; + }; + + ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical); + ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical); + ImageKnob(const ImageKnob& imageKnob); + + float getValue() const; + + void setOrientation(Orientation orientation); + void setRange(float min, float max); + void setValue(float value, bool sendCallback = false); + void setRotationAngle(int angle); + + void setCallback(Callback* callback); + +protected: + void onDisplay() override; + bool onMouse(int button, bool press, int x, int y) override; + bool onMotion(int x, int y) override; + void onReshape(int width, int height) override; + void onClose() override; + +private: + Image fImage; + float fMinimum; + float fMaximum; + float fValue; + Orientation fOrientation; + + int fRotationAngle; + bool fDragging; + int fLastX; + int fLastY; + + Callback* fCallback; + + bool fIsImgVertical; + int fImgLayerSize; + int fImgLayerCount; + Rectangle<int> fKnobArea; + GLuint fTextureId; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_IMAGE_KNOB_HPP_INCLUDED diff --git a/dgl/ImageSlider.hpp b/dgl/ImageSlider.hpp @@ -0,0 +1,85 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_IMAGE_SLIDER_HPP_INCLUDED +#define DGL_IMAGE_SLIDER_HPP_INCLUDED + +#include "Image.hpp" +#include "Widget.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class ImageSlider : public Widget +{ +public: + class Callback + { + public: + virtual ~Callback() {} + virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0; + virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0; + virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0; + }; + + ImageSlider(Window& parent, const Image& image); + ImageSlider(Widget* widget, const Image& image); + ImageSlider(const ImageSlider& imageSlider); + + float getValue() const; + + void setStartPos(const Point<int>& startPos); + void setStartPos(int x, int y); + void setEndPos(const Point<int>& endPos); + void setEndPos(int x, int y); + + void setRange(float min, float max); + void setValue(float value, bool sendCallback = false); + void setIsSwitch(bool yesNo); + + void setCallback(Callback* callback); + +protected: + void onDisplay() override; + bool onMouse(int button, bool press, int x, int y) override; + bool onMotion(int x, int y) override; + +private: + Image fImage; + float fMinimum; + float fMaximum; + float fValue; + + bool fIsSwitch; + bool fDragging; + int fStartedX; + int fStartedY; + + Callback* fCallback; + + Point<int> fStartPos; + Point<int> fEndPos; + Rectangle<int> fSliderArea; + + void _recheckArea(); +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_IMAGE_SLIDER_HPP_INCLUDED diff --git a/dgl/Makefile b/dgl/Makefile @@ -0,0 +1,67 @@ +#!/usr/bin/make -f +# Makefile for dgl # +# ---------------- # +# Created by falkTX +# + +include Makefile.mk + +# -------------------------------------------------------------- + +BUILD_C_FLAGS += $(DGL_FLAGS) -I. +BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. +LINK_FLAGS += $(DGL_LIBS) + +# -------------------------------------------------------------- + +OBJS = \ + src/App.cpp.o \ + src/Base.cpp.o \ + src/Image.cpp.o \ + src/ImageAboutWindow.cpp.o \ + src/ImageButton.cpp.o \ + src/ImageKnob.cpp.o \ + src/ImageSlider.cpp.o \ + src/Geometry.cpp.o \ + src/Widget.cpp.o \ + src/Window.cpp.o + +ifeq ($(MACOS),true) +OBJS += src/pugl/pugl_osx_extended.m.o +endif + +TARGET = ../libdgl.a + +# -------------------------------------------------------------- + +all: $(TARGET) + +# -------------------------------------------------------------- + +../libdgl.a: $(OBJS) + $(RM) $@ + $(AR) crs $@ $^ + +../libdgl.dll: $(OBJS) + $(CXX) $^ -shared $(LINK_FLAGS) -o $@ + +../libdgl.dylib: $(OBJS) + $(CXX) $^ -dynamiclib $(LINK_FLAGS) -o $@ + +../libdgl.so: $(OBJS) + $(CXX) $^ -shared $(LINK_FLAGS) -o $@ + +# -------------------------------------------------------------- + +%.cpp.o: %.cpp + $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ + +# -------------------------------------------------------------- + +clean: + $(RM) src/*.o src/pugl/*.o ../libdgl.* + +debug: + $(MAKE) DEBUG=true + +# -------------------------------------------------------------- diff --git a/dgl/Makefile.mk b/dgl/Makefile.mk @@ -0,0 +1,79 @@ +#!/usr/bin/make -f +# Makefile for dgl # +# ---------------- # +# Created by falkTX +# + +AR ?= ar +RM ?= rm -f + +CC ?= gcc +CXX ?= g++ + +# -------------------------------------------------------------- +# Fallback to Linux if no other OS defined + +ifneq ($(MACOS),true) +ifneq ($(WIN32),true) +LINUX=true +endif +endif + +# -------------------------------------------------------------- +# Common build and link flags + +BASE_FLAGS = -Wall -Wextra -fPIC -DPIC -pipe -DREAL_BUILD +BASE_OPTS = -O3 -ffast-math -mtune=generic -msse -msse2 -mfpmath=sse + +ifeq ($(RASPPI),true) +# Raspberry-Pi optimization flags +BASE_OPTS = -O3 -ffast-math -march=armv6 -mfpu=vfp -mfloat-abi=hard +endif + +ifeq ($(DEBUG),true) +BASE_FLAGS += -DDEBUG -O0 -g +else +BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden +CXXFLAGS += -fvisibility-inlines-hidden +LINK_OPTS += -Wl,--strip-all +endif + +BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) +BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=gnu++0x $(CXXFLAGS) +LINK_FLAGS = $(LINK_OPTS) -Wl,--no-undefined $(LDFLAGS) + +ifeq ($(MACOS),true) +# Get rid of most options for old gcc4.2 +BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) +LINK_FLAGS = $(LDFLAGS) +endif + +# -------------------------------------------------------------- +# Check for required libs + +ifeq ($(LINUX),true) +ifneq ($(shell pkg-config --exists gl && echo true),true) +$(error OpenGL missing, cannot continue) +endif +ifneq ($(shell pkg-config --exists x11 && echo true),true) +$(error X11 missing, cannot continue) +endif +endif + +# -------------------------------------------------------------- +# Set libs stuff + +ifeq ($(LINUX),true) +DGL_FLAGS = $(shell pkg-config --cflags gl x11) +DGL_LIBS = $(shell pkg-config --libs gl x11) +endif + +ifeq ($(MACOS),true) +DGL_LIBS = -framework OpenGL -framework Cocoa +endif + +ifeq ($(WIN32),true) +DGL_LIBS = -lopengl32 -lgdi32 +endif + +# -------------------------------------------------------------- diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp @@ -0,0 +1,79 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED +#define DGL_STANDALONE_WINDOW_HPP_INCLUDED + +#include "App.hpp" +#include "Window.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class StandaloneWindow +{ +public: + StandaloneWindow() + : fApp(), + fWindow(fApp) + { + } + + App& getApp() noexcept + { + return fApp; + } + + Window& getWindow() noexcept + { + return fWindow; + } + + void exec() + { + fWindow.show(); + fApp.exec(); + } + + // ------------------------------------------------------------------- + // helpers + + void setResizable(bool yesNo) + { + fWindow.setResizable(yesNo); + } + + void setSize(unsigned int width, unsigned int height) + { + fWindow.setSize(width, height); + } + + void setTitle(const char* title) + { + fWindow.setTitle(title); + } + +private: + App fApp; + Window fWindow; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_STANDALONE_WINDOW_HPP_INCLUDED diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp @@ -0,0 +1,100 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_WIDGET_HPP_INCLUDED +#define DGL_WIDGET_HPP_INCLUDED + +#include "Geometry.hpp" + +#ifdef PROPER_CPP11_SUPPORT +# include <cstdint> +#else +# include <stdint.h> +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class App; +class Window; + +class Widget +{ +public: + Widget(Window& parent); + virtual ~Widget(); + + bool isVisible() const noexcept; + void setVisible(bool yesNo); + + void show(); + void hide(); + + int getX() const noexcept; + int getY() const noexcept; + const Point<int>& getPos() const noexcept; + + void setX(int x); + void setY(int y); + void setPos(int x, int y); + void setPos(const Point<int>& pos); + + void move(int x, int y); + void move(const Point<int>& pos); + + int getWidth() const noexcept; + int getHeight() const noexcept; + const Size<int>& getSize() const noexcept; + + void setWidth(int width); + void setHeight(int height); + void setSize(int width, int height); + void setSize(const Size<int>& size); + + const Rectangle<int>& getArea() const noexcept; + + uint32_t getEventTimestamp(); + int getModifiers(); + + App& getParentApp() const noexcept; + Window& getParentWindow() const noexcept; + + void repaint(); + +protected: + virtual void onDisplay() = 0; + virtual bool onKeyboard(bool press, uint32_t key); + virtual bool onMouse(int button, bool press, int x, int y); + virtual bool onMotion(int x, int y); + virtual bool onScroll(float dx, float dy); + virtual bool onSpecial(bool press, Key key); + virtual void onReshape(int width, int height); + virtual void onClose(); + +private: + Window& fParent; + bool fVisible; + Rectangle<int> fArea; + + friend class Window; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_WIDGET_HPP_INCLUDED diff --git a/dgl/Window.hpp b/dgl/Window.hpp @@ -0,0 +1,85 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_WINDOW_HPP_INCLUDED +#define DGL_WINDOW_HPP_INCLUDED + +#include "Geometry.hpp" + +#ifdef PROPER_CPP11_SUPPORT +# include <cstdint> +#else +# include <stdint.h> +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class App; +class Widget; + +class Window +{ +public: + Window(App& app); + Window(App& app, Window& parent); + Window(App& app, intptr_t parentId); + virtual ~Window(); + + void show(); + void hide(); + void close(); + void exec(bool lockWait = false); + + void focus(); + void idle(); + void repaint(); + + bool isVisible() const noexcept; + void setVisible(bool yesNo); + + bool isResizable() const noexcept; + void setResizable(bool yesNo); + +#ifndef DGL_OS_MAC + int getWidth() const noexcept; + int getHeight() const noexcept; + Size<int> getSize() const noexcept; +#endif + void setSize(unsigned int width, unsigned int height); + + void setTitle(const char* title); + + App& getApp() const noexcept; + uint32_t getEventTimestamp() const; + int getModifiers() const; + intptr_t getWindowId() const; + +private: + class PrivateData; + PrivateData* const pData; + friend class Widget; + + void addWidget(Widget* const widget); + void removeWidget(Widget* const widget); +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_WINDOW_HPP_INCLUDED diff --git a/dgl/src/App.cpp b/dgl/src/App.cpp @@ -0,0 +1,111 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../App.hpp" +#include "../Window.hpp" + +#include <list> + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +struct App::PrivateData { + bool doLoop; + unsigned visibleWindows; + std::list<Window*> windows; + + PrivateData() + : doLoop(false), + visibleWindows(0) {} +}; + +// ----------------------------------------------------------------------- + +App::App() + : pData(new PrivateData()) +{ +} + +App::~App() +{ + pData->windows.clear(); + delete pData; +} + +void App::idle() +{ + for (std::list<Window*>::iterator it = pData->windows.begin(); it != pData->windows.end(); ++it) + { + Window* const window(*it); + window->idle(); + } +} + +void App::exec() +{ + while (pData->doLoop) + { + idle(); + msleep(10); + } +} + +void App::quit() +{ + pData->doLoop = false; + + for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(); rit != pData->windows.rend(); ++rit) + { + Window* const window(*rit); + window->close(); + } +} + +bool App::isQuiting() const +{ + return !pData->doLoop; +} + +// ----------------------------------------------------------------------- + +void App::addWindow(Window* const window) +{ + if (window != nullptr) + pData->windows.push_back(window); +} + +void App::removeWindow(Window* const window) +{ + if (window != nullptr) + pData->windows.remove(window); +} + +void App::oneShown() +{ + if (++pData->visibleWindows == 1) + pData->doLoop = true; +} + +void App::oneHidden() +{ + if (--pData->visibleWindows == 0) + pData->doLoop = false; +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/Base.cpp b/dgl/src/Base.cpp @@ -0,0 +1,49 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../Base.hpp" + +#if DGL_OS_WINDOWS +# include <windows.h> +#else +# include <unistd.h> +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +void sleep(unsigned int secs) +{ +#ifdef DGL_OS_WINDOWS + ::Sleep(secs * 1000); +#else + ::sleep(secs); +#endif +} + +void msleep(unsigned int msecs) +{ +#ifdef DGL_OS_WINDOWS + ::Sleep(msecs); +#else + ::usleep(msecs * 1000); +#endif +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp @@ -0,0 +1,416 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../Geometry.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- +// Point + +template<typename T> +Point<T>::Point() noexcept + : fX(0), + fY(0) +{ +} + +template<typename T> +Point<T>::Point(T x, T y) noexcept + : fX(x), + fY(y) +{ +} + +template<typename T> +Point<T>::Point(const Point& pos) noexcept + : fX(pos.fX), + fY(pos.fY) +{ +} + +template<typename T> +T Point<T>::getX() const noexcept +{ + return fX; +} + +template<typename T> +T Point<T>::getY() const noexcept +{ + return fY; +} + +template<typename T> +void Point<T>::setX(T x) noexcept +{ + fX = x; +} + +template<typename T> +void Point<T>::setY(T y) noexcept +{ + fY = y; +} + +template<typename T> +void Point<T>::move(T x, T y) noexcept +{ + fX += x; + fY += y; +} + +template<typename T> +void Point<T>::move(const Point& pos) noexcept +{ + fX += pos.fX; + fY += pos.fY; +} + +template<typename T> +Point<T>& Point<T>::operator=(const Point<T>& pos) noexcept +{ + fX = pos.fX; + fY = pos.fY; + return *this; +} + +template<typename T> +Point<T>& Point<T>::operator+=(const Point<T>& pos) noexcept +{ + fX += pos.fX; + fY += pos.fY; + return *this; +} + +template<typename T> +Point<T>& Point<T>::operator-=(const Point<T>& pos) noexcept +{ + fX -= pos.fX; + fY -= pos.fY; + return *this; +} + +template<typename T> +bool Point<T>::operator==(const Point<T>& pos) const noexcept +{ + return (fX == pos.fX && fY== pos.fY); +} + +template<typename T> +bool Point<T>::operator!=(const Point<T>& pos) const noexcept +{ + return !operator==(pos); +} + +// ----------------------------------------------------------------------- +// Size + +template<typename T> +Size<T>::Size() noexcept + : fWidth(0), + fHeight(0) +{ +} + +template<typename T> +Size<T>::Size(T width, T height) noexcept + : fWidth(width), + fHeight(height) +{ +} + +template<typename T> +Size<T>::Size(const Size<T>& size) noexcept + : fWidth(size.fWidth), + fHeight(size.fHeight) +{ +} + +template<typename T> +T Size<T>::getWidth() const noexcept +{ + return fWidth; +} + +template<typename T> +T Size<T>::getHeight() const noexcept +{ + return fHeight; +} + +template<typename T> +void Size<T>::setWidth(T width) noexcept +{ + fWidth = width; +} + +template<typename T> +void Size<T>::setHeight(T height) noexcept +{ + fHeight = height; +} + +template<typename T> +Size<T>& Size<T>::operator=(const Size<T>& size) noexcept +{ + fWidth = size.fWidth; + fHeight = size.fHeight; + return *this; +} + +template<typename T> +Size<T>& Size<T>::operator+=(const Size<T>& size) noexcept +{ + fWidth += size.fWidth; + fHeight += size.fHeight; + return *this; +} + +template<typename T> +Size<T>& Size<T>::operator-=(const Size<T>& size) noexcept +{ + fWidth -= size.fWidth; + fHeight -= size.fHeight; + return *this; +} + +template<typename T> +Size<T>& Size<T>::operator*=(T m) noexcept +{ + fWidth *= m; + fHeight *= m; + return *this; +} + +template<typename T> +Size<T>& Size<T>::operator/=(T d) noexcept +{ + fWidth /= d; + fHeight /= d; + return *this; +} + +template<typename T> +bool Size<T>::operator==(const Size<T>& size) const noexcept +{ + return (fWidth == size.fWidth && fHeight == size.fHeight); +} + +template<typename T> +bool Size<T>::operator!=(const Size<T>& size) const noexcept +{ + return !operator==(size); +} + +// ----------------------------------------------------------------------- +// Rectangle + +template<typename T> +Rectangle<T>::Rectangle() noexcept + : fPos(0, 0), + fSize(0, 0) +{ +} + +template<typename T> +Rectangle<T>::Rectangle(T x, T y, T width, T height) noexcept + : fPos(x, y), + fSize(width, height) +{ +} + +template<typename T> +Rectangle<T>::Rectangle(T x, T y, const Size<T>& size) noexcept + : fPos(x, y), + fSize(size) +{ +} + +template<typename T> +Rectangle<T>::Rectangle(const Point<T>& pos, T width, T height) noexcept + : fPos(pos), + fSize(width, height) +{ +} + +template<typename T> +Rectangle<T>::Rectangle(const Point<T>& pos, const Size<T>& size) noexcept + : fPos(pos), + fSize(size) +{ +} + +template<typename T> +Rectangle<T>::Rectangle(const Rectangle<T>& rect) noexcept + : fPos(rect.fPos), + fSize(rect.fSize) +{ +} + +template<typename T> +T Rectangle<T>::getX() const noexcept +{ + return fPos.fX; +} + +template<typename T> +T Rectangle<T>::getY() const noexcept +{ + return fPos.fY; +} + +template<typename T> +T Rectangle<T>::getWidth() const noexcept +{ + return fSize.fWidth; +} + +template<typename T> +T Rectangle<T>::getHeight() const noexcept +{ + return fSize.fHeight; +} + +template<typename T> +const Point<T>& Rectangle<T>::getPos() const noexcept +{ + return fPos; +} + +template<typename T> +const Size<T>& Rectangle<T>::getSize() const noexcept +{ + return fSize; +} + +template<typename T> +bool Rectangle<T>::contains(T x, T y) const noexcept +{ + return (x >= fPos.fX && y >= fPos.fY && x <= fPos.fX+fSize.fWidth && y <= fPos.fY+fSize.fHeight); +} + +template<typename T> +bool Rectangle<T>::contains(const Point<T>& pos) const noexcept +{ + return contains(pos.fX, pos.fY); +} + +template<typename T> +bool Rectangle<T>::containsX(T x) const noexcept +{ + return (x >= fPos.fX && x <= fPos.fX + fSize.fWidth); +} + +template<typename T> +bool Rectangle<T>::containsY(T y) const noexcept +{ + return (y >= fPos.fY && y <= fPos.fY + fSize.fHeight); +} + +template<typename T> +void Rectangle<T>::setX(T x) noexcept +{ + fPos.fX = x; +} + +template<typename T> +void Rectangle<T>::setY(T y) noexcept +{ + fPos.fY = y; +} + +template<typename T> +void Rectangle<T>::setPos(T x, T y) noexcept +{ + fPos.fX = x; + fPos.fY = y; +} + +template<typename T> +void Rectangle<T>::setPos(const Point<T>& pos) noexcept +{ + fPos = pos; +} + +template<typename T> +void Rectangle<T>::move(T x, T y) noexcept +{ + fPos.fX += x; + fPos.fY += y; +} + +template<typename T> +void Rectangle<T>::move(const Point<T>& pos) noexcept +{ + fPos += pos; +} + +template<typename T> +void Rectangle<T>::setWidth(T width) noexcept +{ + fSize.fWidth = width; +} + +template<typename T> +void Rectangle<T>::setHeight(T height) noexcept +{ + fSize.fHeight = height; +} + +template<typename T> +void Rectangle<T>::setSize(T width, T height) noexcept +{ + fSize.fWidth = width; + fSize.fHeight = height; +} + +template<typename T> +void Rectangle<T>::setSize(const Size<T>& size) noexcept +{ + fSize = size; +} + +template<typename T> +Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept +{ + fPos = rect.fPos; + fSize = rect.fSize; + return *this; +} + +// ----------------------------------------------------------------------- +// Possible template data types + +template class Point<int>; +template class Point<long>; +template class Point<float>; +template class Point<double>; + +template class Size<int>; +template class Size<long>; +template class Size<float>; +template class Size<double>; + +template class Rectangle<int>; +template class Rectangle<long>; +template class Rectangle<float>; +template class Rectangle<double>; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + diff --git a/dgl/src/Image.cpp b/dgl/src/Image.cpp @@ -0,0 +1,145 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../Image.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +Image::Image() noexcept + : fRawData(nullptr), + fSize(0, 0), + fFormat(0), + fType(0) +{ +} + +Image::Image(const char* rawData, int width, int height, GLenum format, GLenum type) noexcept + : fRawData(rawData), + fSize(width, height), + fFormat(format), + fType(type) +{ +} + +Image::Image(const char* rawData, const Size<int>& size, GLenum format, GLenum type) noexcept + : fRawData(rawData), + fSize(size), + fFormat(format), + fType(type) +{ +} + +Image::Image(const Image& image) noexcept + : fRawData(image.fRawData), + fSize(image.fSize), + fFormat(image.fFormat), + fType(image.fType) +{ +} + +void Image::loadFromMemory(const char* rawData, int width, int height, GLenum format, GLenum type) noexcept +{ + loadFromMemory(rawData, Size<int>(width, height), format, type); +} + +void Image::loadFromMemory(const char* rawData, const Size<int>& size, GLenum format, GLenum type) noexcept +{ + fRawData = rawData; + fSize = size; + fFormat = format; + fType = type; +} + +bool Image::isValid() const noexcept +{ + return (fRawData != nullptr && getWidth() > 0 && getHeight() > 0); +} + +int Image::getWidth() const noexcept +{ + return fSize.getWidth(); +} + +int Image::getHeight() const noexcept +{ + return fSize.getHeight(); +} + +const Size<int>& Image::getSize() const noexcept +{ + return fSize; +} + +const char* Image::getRawData() const noexcept +{ + return fRawData; +} + +GLenum Image::getFormat() const noexcept +{ + return fFormat; +} + +GLenum Image::getType() const noexcept +{ + return fType; +} + +void Image::draw() const +{ + draw(0, 0); +} + +void Image::draw(int x, int y) const +{ + if (! isValid()) + return; + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glRasterPos2i(x, fSize.getHeight()+y); + glDrawPixels(fSize.getWidth(), fSize.getHeight(), fFormat, fType, fRawData); +} + +void Image::draw(const Point<int>& pos) const +{ + draw(pos.getX(), pos.getY()); +} + +Image& Image::operator=(const Image& image) noexcept +{ + fRawData = image.fRawData; + fSize = image.fSize; + fFormat = image.fFormat; + fType = image.fType; + return *this; +} + +bool Image::operator==(const Image& image) const noexcept +{ + return (fRawData == image.fRawData); +} + +bool Image::operator!=(const Image& image) const noexcept +{ + return (fRawData != image.fRawData); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/ImageAboutWindow.cpp b/dgl/src/ImageAboutWindow.cpp @@ -0,0 +1,83 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../ImageAboutWindow.hpp" + +// FIXME: 32bit hack +#if ! (defined (__LP64__) || defined (_LP64) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) +# define PAD_SIZE +1 +#else +# define PAD_SIZE +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +ImageAboutWindow::ImageAboutWindow(App& app, Window& parent, const Image& image) + : Window(app, parent), + Widget((Window&)*this), + fImgBackground(image) +{ + Window::setSize(image.getWidth(), image.getHeight() PAD_SIZE); + Window::setTitle("About"); +} + +ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image) + : Window(widget->getParentApp(), widget->getParentWindow()), + Widget((Window&)*this), + fImgBackground(image) +{ + Window::setSize(image.getWidth(), image.getHeight() PAD_SIZE); + Window::setTitle("About"); +} + +void ImageAboutWindow::setImage(const Image& image) +{ + fImgBackground = image; + Window::setSize(image.getWidth(), image.getHeight() PAD_SIZE); +} + +void ImageAboutWindow::onDisplay() +{ + fImgBackground.draw(); +} + +bool ImageAboutWindow::onMouse(int, bool press, int, int) +{ + if (press) + { + Window::close(); + return true; + } + + return false; +} + +bool ImageAboutWindow::onKeyboard(bool press, uint32_t key) +{ + if (press && key == CHAR_ESCAPE) + { + Window::close(); + return true; + } + + return false; +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/ImageButton.cpp b/dgl/src/ImageButton.cpp @@ -0,0 +1,173 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../ImageButton.hpp" + +#include <cassert> + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +ImageButton::ImageButton(Window& parent, const Image& image) + : Widget(parent), + fImageNormal(image), + fImageHover(image), + fImageDown(image), + fCurImage(&fImageNormal), + fCurButton(-1), + fCallback(nullptr) +{ +} + +ImageButton::ImageButton(Widget* widget, const Image& image) + : Widget(widget->getParentWindow()), + fImageNormal(image), + fImageHover(image), + fImageDown(image), + fCurImage(&fImageNormal), + fCurButton(-1), + fCallback(nullptr) +{ +} + +ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) + : Widget(parent), + fImageNormal(imageNormal), + fImageHover(imageHover), + fImageDown(imageDown), + fCurImage(&fImageNormal), + fCurButton(-1), + fCallback(nullptr) +{ + assert(fImageNormal.getSize() == fImageHover.getSize() && fImageHover.getSize() == fImageDown.getSize()); + + setSize(fCurImage->getSize()); +} + +ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) + : Widget(widget->getParentWindow()), + fImageNormal(imageNormal), + fImageHover(imageHover), + fImageDown(imageDown), + fCurImage(&fImageNormal), + fCurButton(-1), + fCallback(nullptr) +{ + assert(fImageNormal.getSize() == fImageHover.getSize() && fImageHover.getSize() == fImageDown.getSize()); + + setSize(fCurImage->getSize()); +} + +ImageButton::ImageButton(const ImageButton& imageButton) + : Widget(imageButton.getParentWindow()), + fImageNormal(imageButton.fImageNormal), + fImageHover(imageButton.fImageHover), + fImageDown(imageButton.fImageDown), + fCurImage(&fImageNormal), + fCurButton(-1), + fCallback(imageButton.fCallback) +{ + assert(fImageNormal.getSize() == fImageHover.getSize() && fImageHover.getSize() == fImageDown.getSize()); + + setSize(fCurImage->getSize()); +} + +void ImageButton::setCallback(Callback* callback) +{ + fCallback = callback; +} + +void ImageButton::onDisplay() +{ + fCurImage->draw(getPos()); +} + +bool ImageButton::onMouse(int button, bool press, int x, int y) +{ + if (fCurButton != -1 && ! press) + { + if (fCurImage != &fImageNormal) + { + fCurImage = &fImageNormal; + repaint(); + } + + if (! getArea().contains(x, y)) + { + fCurButton = -1; + return false; + } + + if (fCallback != nullptr) + fCallback->imageButtonClicked(this, fCurButton); + + //if (getArea().contains(x, y)) + //{ + // fCurImage = &fImageHover; + // repaint(); + //} + + fCurButton = -1; + + return true; + } + + if (press && getArea().contains(x, y)) + { + if (fCurImage != &fImageDown) + { + fCurImage = &fImageDown; + repaint(); + } + + fCurButton = button; + return true; + } + + return false; +} + +bool ImageButton::onMotion(int x, int y) +{ + if (fCurButton != -1) + return true; + + if (getArea().contains(x, y)) + { + if (fCurImage != &fImageHover) + { + fCurImage = &fImageHover; + repaint(); + } + + return true; + } + else + { + if (fCurImage != &fImageNormal) + { + fCurImage = &fImageNormal; + repaint(); + } + + return false; + } +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/ImageKnob.cpp b/dgl/src/ImageKnob.cpp @@ -0,0 +1,340 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../ImageKnob.hpp" + +#include <cassert> +#include <cstdio> + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation) + : Widget(parent), + fImage(image), + fMinimum(0.0f), + fMaximum(1.0f), + fValue(0.5f), + fOrientation(orientation), + fRotationAngle(0), + fDragging(false), + fLastX(0), + fLastY(0), + fCallback(nullptr), + fIsImgVertical(image.getHeight() > image.getWidth()), + fImgLayerSize(fIsImgVertical ? image.getWidth() : image.getHeight()), + fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerSize : image.getWidth()/fImgLayerSize), + fKnobArea(0, 0, fImgLayerSize, fImgLayerSize), + fTextureId(0) +{ + setSize(fImgLayerSize, fImgLayerSize); +} + +ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation) + : Widget(widget->getParentWindow()), + fImage(image), + fMinimum(0.0f), + fMaximum(1.0f), + fValue(0.5f), + fOrientation(orientation), + fRotationAngle(0), + fDragging(false), + fLastX(0), + fLastY(0), + fCallback(nullptr), + fIsImgVertical(image.getHeight() > image.getWidth()), + fImgLayerSize(fIsImgVertical ? image.getWidth() : image.getHeight()), + fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerSize : image.getWidth()/fImgLayerSize), + fKnobArea(0, 0, fImgLayerSize, fImgLayerSize), + fTextureId(0) +{ + setSize(fImgLayerSize, fImgLayerSize); +} + +ImageKnob::ImageKnob(const ImageKnob& imageKnob) + : Widget(imageKnob.getParentWindow()), + fImage(imageKnob.fImage), + fMinimum(imageKnob.fMinimum), + fMaximum(imageKnob.fMaximum), + fValue(imageKnob.fValue), + fOrientation(imageKnob.fOrientation), + fRotationAngle(imageKnob.fRotationAngle), + fDragging(false), + fLastX(0), + fLastY(0), + fCallback(imageKnob.fCallback), + fIsImgVertical(imageKnob.fIsImgVertical), + fImgLayerSize(imageKnob.fImgLayerSize), + fImgLayerCount(imageKnob.fImgLayerCount), + fKnobArea(imageKnob.fKnobArea), + fTextureId(0) +{ + setSize(fImgLayerSize, fImgLayerSize); + + if (fRotationAngle != 0) + { + // force new texture creation + fRotationAngle = 0; + setRotationAngle(imageKnob.fRotationAngle); + } +} + +float ImageKnob::getValue() const +{ + return fValue; +} + +void ImageKnob::setOrientation(Orientation orientation) +{ + if (fOrientation == orientation) + return; + + fOrientation = orientation; +} + +void ImageKnob::setRange(float min, float max) +{ + if (fValue < min) + { + fValue = min; + repaint(); + + if (fCallback != nullptr) + fCallback->imageKnobValueChanged(this, fValue); + } + else if (fValue > max) + { + fValue = max; + repaint(); + + if (fCallback != nullptr) + fCallback->imageKnobValueChanged(this, fValue); + } + + fMinimum = min; + fMaximum = max; +} + +void ImageKnob::setValue(float value, bool sendCallback) +{ + if (fValue == value) + return; + + fValue = value; + repaint(); + + if (sendCallback && fCallback != nullptr) + fCallback->imageKnobValueChanged(this, fValue); +} + +void ImageKnob::setRotationAngle(int angle) +{ + if (fRotationAngle == angle) + return; + + if (fRotationAngle != 0) + { + // delete old texture + glDeleteTextures(1, &fTextureId); + fTextureId = 0; + } + + fRotationAngle = angle; + + if (angle != 0) + { + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &fTextureId); + glBindTexture(GL_TEXTURE_2D, fTextureId); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, fImage.getFormat(), fImage.getType(), fImage.getRawData()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + } +} + +void ImageKnob::setCallback(Callback* callback) +{ + fCallback = callback; +} + +void ImageKnob::onDisplay() +{ + const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); + + if (fRotationAngle != 0) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, fTextureId); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, fImage.getFormat(), fImage.getType(), fImage.getRawData()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glPushMatrix(); + + const GLint w2 = getWidth()/2; + const GLint h2 = getHeight()/2; + + glTranslatef(getX()+w2, getY()+h2, 0.0f); + glRotatef(normValue*fRotationAngle, 0.0f, 0.0f, 1.0f); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 1.0f); + glVertex2i(-w2, -h2); + + glTexCoord2f(1.0f, 1.0f); + glVertex2i(getWidth()-w2, -h2); + + glTexCoord2f(1.0f, 0.0f); + glVertex2i(getWidth()-w2, getHeight()-h2); + + glTexCoord2f(0.0f, 0.0f); + glVertex2i(-w2, getHeight()-h2); + glEnd(); + + glPopMatrix(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + } + else + { + const int layerDataSize = fImgLayerSize * fImgLayerSize * ((fImage.getFormat() == GL_BGRA || fImage.getFormat() == GL_RGBA) ? 4 : 3); + const int imageDataSize = layerDataSize * fImgLayerCount; + const int imageDataOffset = imageDataSize - layerDataSize - (layerDataSize * int(normValue * float(fImgLayerCount-1))); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glRasterPos2i(getX(), getY()+getHeight()); + glDrawPixels(fImgLayerSize, fImgLayerSize, fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset); + } +} + +bool ImageKnob::onMouse(int button, bool press, int x, int y) +{ + if (button != 1) + return false; + + if (press) + { + if (! getArea().contains(x, y)) + return false; + + fDragging = true; + fLastX = x; + fLastY = y; + + if (fCallback != nullptr) + fCallback->imageKnobDragStarted(this); + + return true; + } + else if (fDragging) + { + if (fCallback != nullptr) + fCallback->imageKnobDragFinished(this); + + fDragging = false; + return true; + } + + return false; +} + +bool ImageKnob::onMotion(int x, int y) +{ + if (! fDragging) + return false; + + if (fOrientation == ImageKnob::Horizontal) + { + int movX = x - fLastX; + + if (movX != 0) + { + float d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; + float value = fValue + (float(fMaximum - fMinimum) / d * float(movX)); + + if (value < fMinimum) + value = fMinimum; + else if (value > fMaximum) + value = fMaximum; + + setValue(value, true); + } + } + else if (fOrientation == ImageKnob::Vertical) + { + int movY = fLastY - y; + + if (movY != 0) + { + float d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; + float value = fValue + (float(fMaximum - fMinimum) / d * float(movY)); + + if (value < fMinimum) + value = fMinimum; + else if (value > fMaximum) + value = fMaximum; + + setValue(value, true); + } + } + + fLastX = x; + fLastY = y; + + return true; +} + +void ImageKnob::onReshape(int width, int height) +{ +// if (fRotationAngle != 0) +// glEnable(GL_TEXTURE_2D); + + Widget::onReshape(width, height); +} + +void ImageKnob::onClose() +{ + // delete old texture + setRotationAngle(0); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/ImageSlider.cpp b/dgl/src/ImageSlider.cpp @@ -0,0 +1,316 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../ImageSlider.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +ImageSlider::ImageSlider(Window& parent, const Image& image) + : Widget(parent), + fImage(image), + fMinimum(0.0f), + fMaximum(1.0f), + fValue(0.5f), + fIsSwitch(false), + fDragging(false), + fStartedX(0), + fStartedY(0), + fCallback(nullptr) +{ + setSize(fImage.getSize()); +} + +ImageSlider::ImageSlider(Widget* widget, const Image& image) + : Widget(widget->getParentWindow()), + fImage(image), + fMinimum(0.0f), + fMaximum(1.0f), + fValue(0.5f), + fIsSwitch(false), + fDragging(false), + fStartedX(0), + fStartedY(0), + fCallback(nullptr) +{ + setSize(fImage.getSize()); +} + +ImageSlider::ImageSlider(const ImageSlider& imageSlider) + : Widget(imageSlider.getParentWindow()), + fImage(imageSlider.fImage), + fMinimum(imageSlider.fMinimum), + fMaximum(imageSlider.fMaximum), + fValue(imageSlider.fValue), + fIsSwitch(imageSlider.fIsSwitch), + fDragging(false), + fStartedX(0), + fStartedY(0), + fCallback(imageSlider.fCallback), + fStartPos(imageSlider.fStartPos), + fEndPos(imageSlider.fEndPos), + fSliderArea(imageSlider.fSliderArea) +{ + setSize(fImage.getSize()); +} + +float ImageSlider::getValue() const +{ + return fValue; +} + +void ImageSlider::setStartPos(const Point<int>& startPos) +{ + fStartPos = startPos; + _recheckArea(); +} + +void ImageSlider::setStartPos(int x, int y) +{ + setStartPos(Point<int>(x, y)); +} + +void ImageSlider::setEndPos(const Point<int>& endPos) +{ + fEndPos = endPos; + _recheckArea(); +} + +void ImageSlider::setEndPos(int x, int y) +{ + setEndPos(Point<int>(x, y)); +} + +void ImageSlider::setRange(float min, float max) +{ + if (fValue < min) + { + fValue = min; + repaint(); + + if (fCallback != nullptr) + fCallback->imageSliderValueChanged(this, fValue); + } + else if (fValue > max) + { + fValue = max; + repaint(); + + if (fCallback != nullptr) + fCallback->imageSliderValueChanged(this, fValue); + } + + fMinimum = min; + fMaximum = max; +} + +void ImageSlider::setValue(float value, bool sendCallback) +{ + if (fValue == value) + return; + + fValue = value; + repaint(); + + if (sendCallback && fCallback != nullptr) + fCallback->imageSliderValueChanged(this, fValue); +} + +void ImageSlider::setIsSwitch(bool yesNo) +{ + if (fIsSwitch == yesNo) + return; + + fIsSwitch = yesNo; + repaint(); +} + +void ImageSlider::setCallback(Callback* callback) +{ + fCallback = callback; +} + +void ImageSlider::onDisplay() +{ +#if 0 // DEBUG, paints slider area + glColor3f(0.4f, 0.5f, 0.1f); + glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight()); +#endif + + float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); + + int x, y; + + if (fStartPos.getX() == fEndPos.getX()) + { + x = fStartPos.getX(); + y = fEndPos.getY() - normValue*(fEndPos.getY()-fStartPos.getY()); + } + else if (fStartPos.getY() == fEndPos.getY()) + { + x = fEndPos.getX() - normValue*(fEndPos.getX()-fStartPos.getX()); + y = fStartPos.getY(); + } + else + return; + + fImage.draw(x, y); +} + +bool ImageSlider::onMouse(int button, bool press, int x, int y) +{ + if (button != 1) + return false; + + if (press) + { + if (! fSliderArea.contains(x, y)) + return false; + + float vper; + + if (fStartPos.getX() == fEndPos.getX()) + { + // vertical + vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); + } + else if (fStartPos.getY() == fEndPos.getY()) + { + // horizontal + vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); + } + else + return false; + + float value; + + if (fIsSwitch) + { + if (vper < 0.5f) + value = fMaximum; + else + value = fMinimum; + } + else + { + value = fMaximum - vper * (fMaximum - fMinimum); + + if (value < fMinimum) + value = fMinimum; + else if (value > fMaximum) + value = fMaximum; + } + + fDragging = true; + fStartedX = x; + fStartedY = y; + + if (fCallback != nullptr) + fCallback->imageSliderDragStarted(this); + + setValue(value, true); + + return true; + } + else if (fDragging) + { + if (fCallback != nullptr) + fCallback->imageSliderDragFinished(this); + + fDragging = false; + return true; + } + + return false; +} + +bool ImageSlider::onMotion(int x, int y) +{ + if (! fDragging) + return false; + + bool horizontal = fStartPos.getY() == fEndPos.getY(); + + if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal)) + { + float vper; + + if (horizontal) + { + // horizontal + vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); + } + else + { + // vertical + vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); + } + + float value; + + if (fIsSwitch) + { + if (vper < 0.5f) + value = fMaximum; + else + value = fMinimum; + } + else + { + value = fMaximum - vper * (fMaximum - fMinimum); + + if (value < fMinimum) + value = fMinimum; + else if (value > fMaximum) + value = fMaximum; + } + + setValue(value, true); + } + else if (y < fSliderArea.getY()) + { + setValue(fMaximum, true); + } + else + { + setValue(fMinimum, true); + } + + return true; +} + +void ImageSlider::_recheckArea() +{ + if (fStartPos.getX() == fEndPos.getX()) + { + fSliderArea = Rectangle<int>(fStartPos.getX(), + fStartPos.getY(), + fImage.getWidth(), + fEndPos.getY() + fImage.getHeight() - fStartPos.getY()); + } + else if (fStartPos.getY() == fEndPos.getY()) + { + fSliderArea = Rectangle<int>(fStartPos.getX(), + fStartPos.getY(), + fEndPos.getX() + fImage.getWidth() - fStartPos.getX(), + fImage.getHeight()); + } +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp @@ -0,0 +1,243 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../App.hpp" +#include "../Widget.hpp" +#include "../Window.hpp" + +#include <cassert> + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- +// Widget + +Widget::Widget(Window& parent) + : fParent(parent), + fVisible(true) +{ + fParent.addWidget(this); +} + +Widget::~Widget() +{ + fParent.removeWidget(this); +} + +bool Widget::isVisible() const noexcept +{ + return fVisible; +} + +void Widget::setVisible(bool yesNo) +{ + if (fVisible == yesNo) + return; + + fVisible = yesNo; + fParent.repaint(); +} + +void Widget::show() +{ + setVisible(true); +} + +void Widget::hide() +{ + setVisible(false); +} + +int Widget::getX() const noexcept +{ + return fArea.getX(); +} + +int Widget::getY() const noexcept +{ + return fArea.getY(); +} + +const Point<int>& Widget::getPos() const noexcept +{ + return fArea.getPos(); +} + +void Widget::setX(int x) +{ + if (fArea.getX() == x) + return; + + fArea.setX(x); + fParent.repaint(); +} + +void Widget::setY(int y) +{ + if (fArea.getY() == y) + return; + + fArea.setY(y); + fParent.repaint(); +} + +void Widget::setPos(int x, int y) +{ + setPos(Point<int>(x, y)); +} + +void Widget::setPos(const Point<int>& pos) +{ + if (fArea.getPos() == pos) + return; + + fArea.setPos(pos); + fParent.repaint(); +} + +void Widget::move(int x, int y) +{ + fArea.move(x, y); + fParent.repaint(); +} + +void Widget::move(const Point<int>& pos) +{ + fArea.move(pos); + fParent.repaint(); +} + +int Widget::getWidth() const noexcept +{ + return fArea.getWidth(); +} + +int Widget::getHeight() const noexcept +{ + return fArea.getHeight(); +} + +const Size<int>& Widget::getSize() const noexcept +{ + return fArea.getSize(); +} + +void Widget::setWidth(int width) +{ + if (fArea.getWidth() == width) + return; + + fArea.setWidth(width); + fParent.repaint(); +} + +void Widget::setHeight(int height) +{ + if (fArea.getHeight() == height) + return; + + fArea.setHeight(height); + fParent.repaint(); +} + +void Widget::setSize(int width, int height) +{ + setSize(Size<int>(width, height)); +} + +void Widget::setSize(const Size<int>& size) +{ + if (fArea.getSize() == size) + return; + + fArea.setSize(size); + fParent.repaint(); +} + +const Rectangle<int>& Widget::getArea() const noexcept +{ + return fArea; +} + +uint32_t Widget::getEventTimestamp() +{ + return fParent.getEventTimestamp(); +} + +int Widget::getModifiers() +{ + return fParent.getModifiers(); +} + +App& Widget::getParentApp() const noexcept +{ + return fParent.getApp(); +} + +Window& Widget::getParentWindow() const noexcept +{ + return fParent; +} + +void Widget::repaint() +{ + fParent.repaint(); +} + +bool Widget::onKeyboard(bool, uint32_t) +{ + return false; +} + +bool Widget::onMouse(int, bool, int, int) +{ + return false; +} + +bool Widget::onMotion(int, int) +{ + return false; +} + +bool Widget::onScroll(float, float) +{ + return false; +} + +bool Widget::onSpecial(bool, Key) +{ + return false; +} + +void Widget::onReshape(int width, int height) +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, 0.0f, 1.0f); + glViewport(0, 0, width, height); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void Widget::onClose() +{ +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp @@ -0,0 +1,805 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../App.hpp" +#include "../Widget.hpp" +#include "../Window.hpp" + +#include <cassert> +#include <list> + +#include "pugl/pugl.h" + +#if DGL_OS_WINDOWS +# include "pugl/pugl_win.cpp" +#elif DGL_OS_MAC +extern "C" { +# include "pugl/pugl_osx_extended.h" +} +#elif DGL_OS_LINUX +extern "C" { +# include "pugl/pugl_x11.c" +} +#else +# error Unsupported platform +#endif + +#define FOR_EACH_WIDGET(it) \ + for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) + +#define FOR_EACH_WIDGET_INV(rit) \ + for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) + +START_NAMESPACE_DGL + +Window* dgl_lastUiParent = nullptr; + +// ----------------------------------------------------------------------- +// Window Private + +class Window::PrivateData +{ +public: + PrivateData(App& app, Window* const self) + : fApp(app), + fSelf(self), + fView(puglCreate(0, "Window", 100, 100, true, false)), + fFirstInit(true), + fVisible(false), + fResizable(true), +#if DGL_OS_WINDOWS + hwnd(0) +#elif DGL_OS_LINUX + xDisplay(nullptr), + xWindow(0) +#else + _dummy('\0') +#endif + { + init(); + } + + PrivateData(App& app, Window* const self, Window& parent) + : fApp(app), + fSelf(self), + fView(puglCreate(0, "Window", 100, 100, true, false)), + fFirstInit(true), + fVisible(false), + fResizable(true), + fModal(parent.pData), +#if DGL_OS_WINDOWS + hwnd(0) +#elif DGL_OS_LINUX + xDisplay(nullptr), + xWindow(0) +#else + _dummy('\0') +#endif + { + init(); + +#if DGL_OS_LINUX + PuglInternals* const parentImpl = parent.pData->fView->impl; + + XSetTransientForHint(xDisplay, xWindow, parentImpl->win); +#endif + } + + PrivateData(App& app, Window* const self, const intptr_t parentId) + : fApp(app), + fSelf(self), + fView(puglCreate(parentId, "Window", 100, 100, true, true)), + fFirstInit(true), + fVisible(true), + fResizable(false), +#if DGL_OS_WINDOWS + hwnd(0) +#elif DGL_OS_LINUX + xDisplay(nullptr), + xWindow(0) +#else + _dummy('\0') +#endif + { + init(); + + // starts visible + fApp.oneShown(); + fFirstInit = false; + } + + void init() + { + if (fView == nullptr) + return; + + dgl_lastUiParent = fSelf; + + puglSetHandle(fView, this); + puglSetDisplayFunc(fView, onDisplayCallback); + puglSetKeyboardFunc(fView, onKeyboardCallback); + puglSetMotionFunc(fView, onMotionCallback); + puglSetMouseFunc(fView, onMouseCallback); + puglSetScrollFunc(fView, onScrollCallback); + puglSetSpecialFunc(fView, onSpecialCallback); + puglSetReshapeFunc(fView, onReshapeCallback); + puglSetCloseFunc(fView, onCloseCallback); + +#if DGL_OS_WINDOWS + PuglInternals* impl = fView->impl; + hwnd = impl->hwnd; +#elif DGL_OS_LINUX + PuglInternals* impl = fView->impl; + xDisplay = impl->display; + xWindow = impl->win; +#endif + + fApp.addWindow(fSelf); + } + + ~PrivateData() + { + //fOnModal = false; + fWidgets.clear(); + + if (fView != nullptr) + { + fApp.removeWindow(fSelf); + puglDestroy(fView); + } + } + + // ------------------------------------------------------------------- + + void close() + { + setVisible(false); + + if (! fFirstInit) + { + fApp.oneHidden(); + fFirstInit = true; + } + } + + void exec(const bool lockWait) + { + exec_init(); + + if (lockWait) + { + while (fVisible && fModal.enabled) + { + // idle() + puglProcessEvents(fView); + + if (fModal.parent != nullptr) + fModal.parent->idle(); + + msleep(10); + } + + exec_fini(); + } + else + { + idle(); + } + } + + // ------------------------------------------------------------------- + + void focus() + { +#if DGL_OS_WINDOWS + SetForegroundWindow(hwnd); + SetActiveWindow(hwnd); + SetFocus(hwnd); +#elif DGL_OS_MAC + puglImplFocus(fView); +#elif DGL_OS_LINUX + XRaiseWindow(xDisplay, xWindow); + XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime); +#endif + } + + void idle() + { + puglProcessEvents(fView); + + if (fVisible && fModal.enabled && fModal.parent != nullptr) + fModal.parent->idle(); + } + + void repaint() + { + puglPostRedisplay(fView); + } + + void flush() + { +#if DGL_OS_WINDOWS + UpdateWindow(hwnd); +#elif DGL_OS_MAC +#elif DGL_OS_LINUX + XFlush(xDisplay); +#endif + } + + // ------------------------------------------------------------------- + + bool isVisible() const noexcept + { + return fVisible; + } + + void setVisible(const bool yesNo) + { + if (fVisible == yesNo) + return; + + fVisible = yesNo; + +#ifndef DGL_OS_MAC + if (yesNo && fFirstInit) + setSize(fView->width, fView->height, true); +#endif + +#if DGL_OS_WINDOWS + if (yesNo) + { + ShowWindow(hwnd, WS_VISIBLE); + + if (! fFirstInit) + ShowWindow(hwnd, SW_RESTORE); + } + else + { + ShowWindow(hwnd, SW_HIDE); + } +#elif DGL_OS_MAC + puglImplSetVisible(fView, yesNo); +#elif DGL_OS_LINUX + if (yesNo) + XMapRaised(xDisplay, xWindow); + else + XUnmapWindow(xDisplay, xWindow); +#endif + + if (yesNo) + { + if (fFirstInit) + { + fApp.oneShown(); + fFirstInit = false; + } + } + else if (fModal.enabled) + exec_fini(); + } + + // ------------------------------------------------------------------- + + bool isResizable() const noexcept + { + return fResizable; + } + + void setResizable(const bool yesNo) + { + if (fResizable == yesNo) + return; + + fResizable = yesNo; + +#ifndef DGL_OS_MAC + setSize(fView->width, fView->height, true); +#endif + } + + // ------------------------------------------------------------------- + +#ifndef DGL_OS_MAC + int getWidth() const noexcept + { + return fView->width; + } + + int getHeight() const noexcept + { + return fView->height; + } + + Size<int> getSize() const noexcept + { + return Size<int>(fView->width, fView->height); + } +#endif + + void setSize(unsigned int width, unsigned int height, const bool forced = false) + { + if (width == 0) + width = 1; + if (height == 0) + height = 1; + +#ifndef DGL_OS_MAC + if (fView->width == (int)width && fView->height == (int)height && ! forced) + return; + + fView->width = width; + fView->height = height; +#endif + +#if DGL_OS_WINDOWS + int winFlags = WS_POPUPWINDOW | WS_CAPTION; + + if (fResizable) + winFlags |= WS_SIZEBOX; + + RECT wr = { 0, 0, (long)width, (long)height }; + AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); + + SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); +#elif DGL_OS_MAC + puglImplSetSize(fView, width, height); +#elif DGL_OS_LINUX + XResizeWindow(xDisplay, xWindow, width, height); + + if (! fResizable) + { + XSizeHints sizeHints; + memset(&sizeHints, 0, sizeof(sizeHints)); + + sizeHints.flags = PMinSize|PMaxSize; + sizeHints.min_width = width; + sizeHints.min_height = height; + sizeHints.max_width = width; + sizeHints.max_height = height; + + XSetNormalHints(xDisplay, xWindow, &sizeHints); + } +#endif + + repaint(); + } + + // ------------------------------------------------------------------- + + void setTitle(const char* const title) + { +#if DGL_OS_WINDOWS + SetWindowTextA(hwnd, title); +#elif DGL_OS_MAC + puglImplSetTitle(fView, title); +#elif DGL_OS_LINUX + XStoreName(xDisplay, xWindow, title); +#endif + } + + App& getApp() const noexcept + { + return fApp; + } + + int getModifiers() const + { + return puglGetModifiers(fView); + } + + uint32_t getEventTimestamp() const + { + return puglGetEventTimestamp(fView); + } + + intptr_t getWindowId() const + { + return puglGetNativeWindow(fView); + } + + // ------------------------------------------------------------------- + + void addWidget(Widget* const widget) + { + fWidgets.push_back(widget); + } + + void removeWidget(Widget* const widget) + { + fWidgets.remove(widget); + } + + // ------------------------------------------------------------------- + + void exec_init() + { + fModal.enabled = true; + assert(fModal.parent != nullptr); + + if (fModal.parent == nullptr) + return setVisible(true); + + fModal.parent->fModal.childFocus = this; + +#if DGL_OS_WINDOWS + // Center this window + PuglInternals* const parentImpl = fParent->fView->impl; + + RECT curRect; + RECT parentRect; + GetWindowRect(hwnd, &curRect); + GetWindowRect(parentImpl->hwnd, &parentRect); + + int x = parentRect.left+(parentRect.right-curRect.right)/2; + int y = parentRect.top +(parentRect.bottom-curRect.bottom)/2; + + SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER); + UpdateWindow(hwnd); +#endif + + fModal.parent->setVisible(true); + setVisible(true); + } + + void exec_fini() + { + fModal.enabled = false; + + if (fModal.parent != nullptr) + fModal.parent->fModal.childFocus = nullptr; + } + + // ------------------------------------------------------------------- + +protected: + void onDisplay() + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + + if (widget->isVisible()) + widget->onDisplay(); + } + } + + void onKeyboard(const bool press, const uint32_t key) + { + if (fModal.childFocus != nullptr) + return fModal.childFocus->focus(); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onKeyboard(press, key)) + break; + } + } + + void onMouse(const int button, const bool press, const int x, const int y) + { + if (fModal.childFocus != nullptr) + return fModal.childFocus->focus(); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onMouse(button, press, x, y)) + break; + } + } + + void onMotion(const int x, const int y) + { + if (fModal.childFocus != nullptr) + return; + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onMotion(x, y)) + break; + } + } + + void onScroll(const float dx, const float dy) + { + if (fModal.childFocus != nullptr) + return; + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onScroll(dx, dy)) + break; + } + } + + void onSpecial(const bool press, const Key key) + { + if (fModal.childFocus != nullptr) + return; + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onSpecial(press, key)) + break; + } + } + + void onReshape(const int width, const int height) + { + printf("resized: %i:%i\n", width, height); + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + widget->onReshape(width, height); + } + } + + void onClose() + { + fModal.enabled = false; + + if (fModal.childFocus != nullptr) + fModal.childFocus->onClose(); + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + widget->onClose(); + } + + close(); + } + + // ------------------------------------------------------------------- + +private: + App& fApp; + Window* const fSelf; + PuglView* const fView; + + bool fFirstInit; + bool fVisible; + bool fResizable; + std::list<Widget*> fWidgets; + + struct Modal { + bool enabled; + PrivateData* parent; + PrivateData* childFocus; + + Modal() + : enabled(false), + parent(nullptr), + childFocus(nullptr) {} + + Modal(PrivateData* const p) + : enabled(false), + parent(p), + childFocus(nullptr) {} + + ~Modal() + { + assert(! enabled); + assert(childFocus == nullptr); + } + } fModal; + +#if DGL_OS_WINDOWS + HWND hwnd; +#elif DGL_OS_LINUX + Display* xDisplay; + ::Window xWindow; +#else + char _dummy; +#endif + + // ------------------------------------------------------------------- + // Callbacks + + #define handlePtr ((PrivateData*)puglGetHandle(view)) + + static void onDisplayCallback(PuglView* view) + { + handlePtr->onDisplay(); + } + + static void onKeyboardCallback(PuglView* view, bool press, uint32_t key) + { + handlePtr->onKeyboard(press, key); + } + + static void onMouseCallback(PuglView* view, int button, bool press, int x, int y) + { + handlePtr->onMouse(button, press, x, y); + } + + static void onMotionCallback(PuglView* view, int x, int y) + { + handlePtr->onMotion(x, y); + } + + static void onScrollCallback(PuglView* view, float dx, float dy) + { + handlePtr->onScroll(dx, dy); + } + + static void onSpecialCallback(PuglView* view, bool press, PuglKey key) + { + handlePtr->onSpecial(press, static_cast<Key>(key)); + } + + static void onReshapeCallback(PuglView* view, int width, int height) + { + handlePtr->onReshape(width, height); + } + + static void onCloseCallback(PuglView* view) + { + handlePtr->onClose(); + } + + #undef handlePtr +}; + +// ----------------------------------------------------------------------- +// Window + +Window::Window(App& app) + : pData(new PrivateData(app, this)) +{ +} + +Window::Window(App& app, Window& parent) + : pData(new PrivateData(app, this, parent)) +{ +} + +Window::Window(App& app, intptr_t parentId) + : pData(new PrivateData(app, this, parentId)) +{ +} + +Window::~Window() +{ + delete pData; +} + +void Window::show() +{ + pData->setVisible(true); +} + +void Window::hide() +{ + pData->setVisible(false); +} + +void Window::close() +{ + pData->close(); +} + +void Window::exec(bool lockWait) +{ + pData->exec(lockWait); +} + +void Window::focus() +{ + pData->focus(); +} + +void Window::idle() +{ + pData->idle(); +} + +void Window::repaint() +{ + pData->repaint(); +} + +bool Window::isVisible() const noexcept +{ + return pData->isVisible(); +} + +void Window::setVisible(bool yesNo) +{ + pData->setVisible(yesNo); +} + +bool Window::isResizable() const noexcept +{ + return pData->isResizable(); +} + +void Window::setResizable(bool yesNo) +{ + pData->setResizable(yesNo); +} + +#ifndef DGL_OS_MAC +int Window::getWidth() const noexcept +{ + return pData->getWidth(); +} + +int Window::getHeight() const noexcept +{ + return pData->getHeight(); +} + +Size<int> Window::getSize() const noexcept +{ + return pData->getSize(); +} +#endif + +void Window::setSize(unsigned int width, unsigned int height) +{ + pData->setSize(width, height); +} + +void Window::setTitle(const char* title) +{ + pData->setTitle(title); +} + +App& Window::getApp() const noexcept +{ + return pData->getApp(); +} + +int Window::getModifiers() const +{ + return pData->getModifiers(); +} + +uint32_t Window::getEventTimestamp() const +{ + return pData->getEventTimestamp(); +} + +intptr_t Window::getWindowId() const +{ + return pData->getWindowId(); +} + +void Window::addWidget(Widget* const widget) +{ + pData->addWidget(widget); +} + +void Window::removeWidget(Widget* const widget) +{ + pData->removeWidget(widget); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/pugl/pugl.h b/dgl/src/pugl/pugl.h @@ -0,0 +1,353 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl.h API for Pugl, a minimal portable API for OpenGL. +*/ + +#ifndef PUGL_H_INCLUDED +#define PUGL_H_INCLUDED + +#include <stdint.h> + +/* + This API is pure portable C and contains no platform specific elements, or + even a GL dependency. However, unfortunately GL includes vary across + platforms so they are included here to allow for pure portable programs. +*/ +#ifdef __APPLE__ +# include "OpenGL/gl.h" +#else +# ifdef _WIN32 +# include <windows.h> /* Broken Windows GL headers require this */ +# endif +# include "GL/gl.h" +#endif + +#ifdef PUGL_SHARED +# ifdef _WIN32 +# define PUGL_LIB_IMPORT __declspec(dllimport) +# define PUGL_LIB_EXPORT __declspec(dllexport) +# else +# define PUGL_LIB_IMPORT __attribute__((visibility("default"))) +# define PUGL_LIB_EXPORT __attribute__((visibility("default"))) +# endif +# ifdef PUGL_INTERNAL +# define PUGL_API PUGL_LIB_EXPORT +# else +# define PUGL_API PUGL_LIB_IMPORT +# endif +#else +# define PUGL_API +#endif + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +/** + @defgroup pugl Pugl + A minimal portable API for OpenGL. + @{ +*/ + +/** + An OpenGL view. +*/ +typedef struct PuglViewImpl PuglView; + +/** + A native window handle. + + On X11, this is a Window. + On OSX, this is an NSView*. + On Windows, this is a HWND. +*/ +typedef intptr_t PuglNativeWindow; + +/** + Return status code. +*/ +typedef enum { + PUGL_SUCCESS = 0 +} PuglStatus; + +/** + Convenience symbols for ASCII control characters. +*/ +typedef enum { + PUGL_CHAR_BACKSPACE = 0x08, + PUGL_CHAR_ESCAPE = 0x1B, + PUGL_CHAR_DELETE = 0x7F +} PuglChar; + +/** + Special (non-Unicode) keyboard keys. +*/ +typedef enum { + PUGL_KEY_F1 = 1, + PUGL_KEY_F2, + PUGL_KEY_F3, + PUGL_KEY_F4, + PUGL_KEY_F5, + PUGL_KEY_F6, + PUGL_KEY_F7, + PUGL_KEY_F8, + PUGL_KEY_F9, + PUGL_KEY_F10, + PUGL_KEY_F11, + PUGL_KEY_F12, + PUGL_KEY_LEFT, + PUGL_KEY_UP, + PUGL_KEY_RIGHT, + PUGL_KEY_DOWN, + PUGL_KEY_PAGE_UP, + PUGL_KEY_PAGE_DOWN, + PUGL_KEY_HOME, + PUGL_KEY_END, + PUGL_KEY_INSERT, + PUGL_KEY_SHIFT, + PUGL_KEY_CTRL, + PUGL_KEY_ALT, + PUGL_KEY_SUPER +} PuglKey; + +/** + Keyboard modifier flags. +*/ +typedef enum { + PUGL_MOD_SHIFT = 1, /**< Shift key */ + PUGL_MOD_CTRL = 1 << 1, /**< Control key */ + PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ + PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ +} PuglMod; + +/** + Handle for opaque user data. +*/ +typedef void* PuglHandle; + +/** + A function called when the window is closed. +*/ +typedef void (*PuglCloseFunc)(PuglView* view); + +/** + A function called to draw the view contents with OpenGL. +*/ +typedef void (*PuglDisplayFunc)(PuglView* view); + +/** + A function called when a key is pressed or released. + @param view The view the event occured in. + @param press True if the key was pressed, false if released. + @param key Unicode point of the key pressed. +*/ +typedef void (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key); + +/** + A function called when the pointer moves. + @param view The view the event occured in. + @param x The window-relative x coordinate of the pointer. + @param y The window-relative y coordinate of the pointer. +*/ +typedef void (*PuglMotionFunc)(PuglView* view, int x, int y); + +/** + A function called when a mouse button is pressed or released. + @param view The view the event occured in. + @param button The button number (1 = left, 2 = middle, 3 = right). + @param press True if the key was pressed, false if released. + @param x The window-relative x coordinate of the pointer. + @param y The window-relative y coordinate of the pointer. +*/ +typedef void (*PuglMouseFunc)( + PuglView* view, int button, bool press, int x, int y); + +/** + A function called when the view is resized. + @param view The view being resized. + @param width The new view width. + @param height The new view height. +*/ +typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); + +/** + A function called on scrolling (e.g. mouse wheel or track pad). + + The distances used here are in "lines", a single tick of a clicking mouse + wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and + devices support finer resolution and/or higher values for fast scrolls, + so programs should handle any value gracefully. + + @param view The view being scrolled. + @param dx The scroll x distance. + @param dx The scroll y distance. +*/ +typedef void (*PuglScrollFunc)(PuglView* view, float dx, float dy); + +/** + A function called when a special key is pressed or released. + + This callback allows the use of keys that do not have unicode points. Note + that some non-printable keys + @param view The view the event occured in. + @param press True if the key was pressed, false if released. + @param key The key pressed. +*/ +typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); + +/** + Create a new GL window. + @param parent Parent window, or 0 for top level. + @param title Window title, or NULL. + @param width Window width in pixels. + @param height Window height in pixels. + @param resizable Whether window should be user resizable. + @param visible Whether window should be initially visible. +*/ +PUGL_API PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible); + +/** + Set the handle to be passed to all callbacks. + + This is generally a pointer to a struct which contains all necessary state. + Everything needed in callbacks should be here, not in static variables. + + Note the lack of this facility makes GLUT unsuitable for plugins or + non-trivial programs; this mistake is largely why Pugl exists. +*/ +PUGL_API void +puglSetHandle(PuglView* view, PuglHandle handle); + +/** + Get the handle to be passed to all callbacks. +*/ +PUGL_API PuglHandle +puglGetHandle(PuglView* view); + +/** + Return the timestamp (if any) of the currently-processing event. +*/ +PUGL_API uint32_t +puglGetEventTimestamp(PuglView* view); + +/** + Get the currently active modifiers (PuglMod flags). + + This should only be called from an event handler. +*/ +PUGL_API int +puglGetModifiers(PuglView* view); + +/** + Ignore synthetic repeated key events. +*/ +PUGL_API void +puglIgnoreKeyRepeat(PuglView* view, bool ignore); + +/** + Set the function to call when the window is closed. +*/ +PUGL_API void +puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); + +/** + Set the display function which should draw the UI using GL. +*/ +PUGL_API void +puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); + +/** + Set the function to call on keyboard events. +*/ +PUGL_API void +puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); + +/** + Set the function to call on mouse motion. +*/ +PUGL_API void +puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); + +/** + Set the function to call on mouse button events. +*/ +PUGL_API void +puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); + +/** + Set the function to call on scroll events. +*/ +PUGL_API void +puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); + +/** + Set the function to call on special events. +*/ +PUGL_API void +puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); + +/** + Set the function to call when the window size changes. +*/ +PUGL_API void +puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); + +/** + Return the native window handle. +*/ +PUGL_API PuglNativeWindow +puglGetNativeWindow(PuglView* view); + +/** + Process all pending window events. + + This handles input events as well as rendering, so it should be called + regularly and rapidly enough to keep the UI responsive. +*/ +PUGL_API PuglStatus +puglProcessEvents(PuglView* view); + +/** + Request a redisplay on the next call to puglProcessEvents(). +*/ +PUGL_API void +puglPostRedisplay(PuglView* view); + +/** + Destroy a GL window. +*/ +PUGL_API void +puglDestroy(PuglView* view); + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PUGL_H_INCLUDED */ diff --git a/dgl/src/pugl/pugl_internal.h b/dgl/src/pugl/pugl_internal.h @@ -0,0 +1,143 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_internal.h Private platform-independent definitions. + + Note this file contains function definitions, so it must be compiled into + the final binary exactly once. Each platform specific implementation file + including it once should achieve this. +*/ + +#include "pugl.h" + +typedef struct PuglInternalsImpl PuglInternals; + +struct PuglViewImpl { + PuglHandle handle; + PuglCloseFunc closeFunc; + PuglDisplayFunc displayFunc; + PuglKeyboardFunc keyboardFunc; + PuglMotionFunc motionFunc; + PuglMouseFunc mouseFunc; + PuglReshapeFunc reshapeFunc; + PuglScrollFunc scrollFunc; + PuglSpecialFunc specialFunc; + + PuglInternals* impl; + + int width; + int height; + int mods; + bool mouse_in_view; + bool ignoreKeyRepeat; + bool redisplay; + uint32_t event_timestamp_ms; +}; + +void +puglSetHandle(PuglView* view, PuglHandle handle) +{ + view->handle = handle; +} + +PuglHandle +puglGetHandle(PuglView* view) +{ + return view->handle; +} + +uint32_t +puglGetEventTimestamp(PuglView* view) +{ + return view->event_timestamp_ms; +} + +int +puglGetModifiers(PuglView* view) +{ + return view->mods; +} + +void +puglDefaultReshape(PuglView* view, int width, int height) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, 0, 1); + glViewport(0, 0, width, height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + return; + + // unused + (void)view; +} + +void +puglIgnoreKeyRepeat(PuglView* view, bool ignore) +{ + view->ignoreKeyRepeat = ignore; +} + +void +puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc) +{ + view->closeFunc = closeFunc; +} + +void +puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc) +{ + view->displayFunc = displayFunc; +} + +void +puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc) +{ + view->keyboardFunc = keyboardFunc; +} + +void +puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc) +{ + view->motionFunc = motionFunc; +} + +void +puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc) +{ + view->mouseFunc = mouseFunc; +} + +void +puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) +{ + view->reshapeFunc = reshapeFunc; +} + +void +puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) +{ + view->scrollFunc = scrollFunc; +} + +void +puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) +{ + view->specialFunc = specialFunc; +} diff --git a/dgl/src/pugl/pugl_osx.m b/dgl/src/pugl/pugl_osx.m @@ -0,0 +1,418 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_osx.m OSX/Cocoa Pugl Implementation. +*/ + +#include <stdlib.h> + +#import <Cocoa/Cocoa.h> + +#include "pugl_internal.h" + +@interface PuglWindow : NSWindow +{ +@public + PuglView* puglview; +} + +- (id) initWithContentRect:(NSRect)contentRect + styleMask:(unsigned int)aStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)flag; +- (void) setPuglview:(PuglView*)view; +- (BOOL) windowShouldClose:(id)sender; +@end + +@implementation PuglWindow + +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(unsigned int)aStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)flag +{ + NSWindow* result = [super initWithContentRect:contentRect + styleMask:(NSClosableWindowMask | + NSTitledWindowMask | + NSResizableWindowMask) + backing:NSBackingStoreBuffered defer:NO]; + + [result setAcceptsMouseMovedEvents:YES]; + [result setLevel: CGShieldingWindowLevel() + 1]; + + return result; +} + +- (void)setPuglview:(PuglView*)view +{ + puglview = view; + [self setContentSize:NSMakeSize(view->width, view->height) ]; +} + +- (BOOL)windowShouldClose:(id)sender +{ + if (puglview->closeFunc) + puglview->closeFunc(puglview); + return YES; +} + +@end + +void +puglDisplay(PuglView* view) +{ + if (view->displayFunc) { + view->displayFunc(view); + } +} + +@interface PuglOpenGLView : NSOpenGLView +{ + int colorBits; + int depthBits; +@public + PuglView* puglview; + + NSTrackingArea* trackingArea; +} + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits; +- (void) reshape; +- (void) drawRect:(NSRect)rect; +- (void) mouseMoved:(NSEvent*)event; +- (void) mouseDragged:(NSEvent*)event; +- (void) mouseDown:(NSEvent*)event; +- (void) mouseUp:(NSEvent*)event; +- (void) rightMouseDown:(NSEvent*)event; +- (void) rightMouseUp:(NSEvent*)event; +- (void) keyDown:(NSEvent*)event; +- (void) keyUp:(NSEvent*)event; +- (void) flagsChanged:(NSEvent*)event; + +@end + +@implementation PuglOpenGLView + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits +{ + colorBits = numColorBits; + depthBits = numDepthBits; + + NSOpenGLPixelFormatAttribute pixelAttribs[16] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorSize, + colorBits, + NSOpenGLPFADepthSize, + depthBits, + 0 + }; + + NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] + initWithAttributes:pixelAttribs]; + + if (pixelFormat) { + self = [super initWithFrame:frame pixelFormat:pixelFormat]; + [pixelFormat release]; + if (self) { + [[self openGLContext] makeCurrentContext]; + [self reshape]; + } + } else { + self = nil; + } + + return self; +} + +- (void) reshape +{ + [[self openGLContext] update]; + + NSRect bounds = [self bounds]; + int width = bounds.size.width; + int height = bounds.size.height; + + if (puglview) { + /* NOTE: Apparently reshape gets called when the GC gets around to + deleting the view (?), so we must have reset puglview to NULL when + this comes around. + */ + if (puglview->reshapeFunc) { + puglview->reshapeFunc(puglview, width, height); + } else { + puglDefaultReshape(puglview, width, height); + } + + puglview->width = width; + puglview->height = height; + } +} + +- (void) drawRect:(NSRect)rect +{ + puglDisplay(puglview); + glFlush(); + glSwapAPPLE(); +} + +static unsigned +getModifiers(PuglView* view, NSEvent* ev) +{ + const unsigned modifierFlags = [ev modifierFlags]; + + view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); + + unsigned mods = 0; + mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; + mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; + mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0; + mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0; + return mods; +} + +-(void)updateTrackingAreas +{ + if (trackingArea != nil) { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + const int opts = (NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingActiveAlways); + trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] + options:opts + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; +} + +- (void)mouseEntered:(NSEvent*)theEvent +{ + [self updateTrackingAreas]; +} + +- (void)mouseExited:(NSEvent*)theEvent +{ +} + +- (void) mouseMoved:(NSEvent*)event +{ + if (puglview->motionFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseDragged:(NSEvent*)event +{ + if (puglview->motionFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseDown:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseUp:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y); + } + [self updateTrackingAreas]; +} + +- (void) rightMouseDown:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y); + } +} + +- (void) rightMouseUp:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y); + } +} + +- (void) scrollWheel:(NSEvent*)event +{ + if (puglview->scrollFunc) { + puglview->mods = getModifiers(puglview, event); + puglview->scrollFunc(puglview, [event deltaX], [event deltaY]); + } + [self updateTrackingAreas]; +} + +- (void) keyDown:(NSEvent*)event +{ + if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { + NSString* chars = [event characters]; + puglview->mods = getModifiers(puglview, event); + puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); + } +} + +- (void) keyUp:(NSEvent*)event +{ + if (puglview->keyboardFunc) { + NSString* chars = [event characters]; + puglview->mods = getModifiers(puglview, event); + puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); + } +} + +- (void) flagsChanged:(NSEvent*)event +{ + if (puglview->specialFunc) { + const unsigned mods = getModifiers(puglview, event); + if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); + } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); + } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); + } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); + } + puglview->mods = mods; + } +} + +@end + +struct PuglInternalsImpl { + PuglOpenGLView* glview; + id window; +}; + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + [NSAutoreleasePool new]; + [NSApplication sharedApplication]; + + NSString* titleString = [[NSString alloc] + initWithBytes:title + length:strlen(title) + encoding:NSUTF8StringEncoding]; + + id window = [[PuglWindow new]retain]; + + [window setPuglview:view]; + [window setTitle:titleString]; + + impl->glview = [PuglOpenGLView new]; + impl->window = window; + impl->glview->puglview = view; + + [window setContentView:impl->glview]; + [NSApp activateIgnoringOtherApps:YES]; + [window makeFirstResponder:impl->glview]; + [window makeKeyAndOrderFront:window]; + + if (! visible) { + [window setIsVisible:NO]; + } + + return view; +} + +void +puglDestroy(PuglView* view) +{ + view->impl->glview->puglview = NULL; + [view->impl->window close]; + [view->impl->glview release]; + [view->impl->window release]; + free(view->impl); + free(view); +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + [view->impl->glview setNeedsDisplay: YES]; + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSEvent* event; + + for (;;) { + event = [view->impl->window + nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + + if (event == nil) + break; + + [view->impl->window sendEvent: event]; + } + + [pool release]; + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->glview; +} diff --git a/dgl/src/pugl/pugl_osx_extended.h b/dgl/src/pugl/pugl_osx_extended.h @@ -0,0 +1,29 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + Copyright 2013 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_osx_extended.h Extended OSX/Cocoa Pugl Implementation. +*/ + +#include <stdbool.h> + +#include "pugl.h" + +void puglImplFocus(PuglView* view); +void puglImplSetSize(PuglView* view, unsigned int width, unsigned int height); +void puglImplSetTitle(PuglView* view, const char* title); +void puglImplSetVisible(PuglView* view, bool yesNo); diff --git a/dgl/src/pugl/pugl_osx_extended.m b/dgl/src/pugl/pugl_osx_extended.m @@ -0,0 +1,64 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + Copyright 2013 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_osx_extended.m Extended OSX/Cocoa Pugl Implementation. +*/ + +#import "pugl_osx.m" + +#include "pugl_osx_extended.h" + +void puglImplFocus(PuglView* view) +{ + // TODO +} + +void puglImplSetSize(PuglView* view, unsigned int width, unsigned int height) +{ + //id window = view->impl->window; + + // TODO + //NSRect frame = [window frame]; + //frame.size.width = width; + //frame.size.height = height; + + // display:NO ? + //[window setFrame:frame display:YES animate:NO]; +} + +void puglImplSetTitle(PuglView* view, const char* title) +{ + id window = view->impl->window; + + NSString* titleString = [[NSString alloc] + initWithBytes:title + length:strlen(title) + encoding:NSUTF8StringEncoding]; + + [window setTitle:titleString]; +} + +void puglImplSetVisible(PuglView* view, bool yesNo) +{ + id window = view->impl->window; + + if (yesNo) + [window setIsVisible:YES]; + else + [window setIsVisible:NO]; +} diff --git a/dgl/src/pugl/pugl_win.cpp b/dgl/src/pugl/pugl_win.cpp @@ -0,0 +1,374 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_win.cpp Windows/WGL Pugl Implementation. +*/ + +#include <windows.h> +#include <windowsx.h> +#include <GL/gl.h> + +#include <stdio.h> +#include <stdlib.h> + +#include "pugl_internal.h" + +#ifndef WM_MOUSEWHEEL +# define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WM_MOUSEHWHEEL +# define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WHEEL_DELTA +# define WHEEL_DELTA 120 +#endif + +const int LOCAL_CLOSE_MSG = WM_USER + 50; + +struct PuglInternalsImpl { + HWND hwnd; + HDC hdc; + HGLRC hglrc; + WNDCLASS wc; +}; + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + // FIXME: This is nasty, and pugl should not have static anything. + // Should class be a parameter? Does this make sense on other platforms? + static int wc_count = 0; + char classNameBuf[256]; + _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++); + + impl->wc.style = CS_OWNDC; + impl->wc.lpfnWndProc = wndProc; + impl->wc.cbClsExtra = 0; + impl->wc.cbWndExtra = 0; + impl->wc.hInstance = 0; + impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW); + impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + impl->wc.lpszMenuName = NULL; + impl->wc.lpszClassName = classNameBuf; + RegisterClass(&impl->wc); + + int winFlags = WS_POPUPWINDOW | WS_CAPTION; + if (resizable) { + winFlags |= WS_SIZEBOX; + } + + // Adjust the overall window size to accomodate our requested client size + RECT wr = { 0, 0, width, height }; + AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); + + impl->hwnd = CreateWindowEx( + WS_EX_TOPMOST, + classNameBuf, title, + (visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags), + 0, 0, wr.right-wr.left, wr.bottom-wr.top, + (HWND)parent, NULL, NULL, NULL); + + if (!impl->hwnd) { + free(impl); + free(view); + return NULL; + } + + SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view); + + impl->hdc = GetDC(impl->hwnd); + + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + + int format = ChoosePixelFormat(impl->hdc, &pfd); + SetPixelFormat(impl->hdc, format, &pfd); + + impl->hglrc = wglCreateContext(impl->hdc); + wglMakeCurrent(impl->hdc, impl->hglrc); + + view->width = width; + view->height = height; + + return view; +} + +void +puglDestroy(PuglView* view) +{ + wglMakeCurrent(NULL, NULL); + wglDeleteContext(view->impl->hglrc); + ReleaseDC(view->impl->hwnd, view->impl->hdc); + DestroyWindow(view->impl->hwnd); + UnregisterClass(view->impl->wc.lpszClassName, NULL); + free(view->impl); + free(view); +} + +static void +puglReshape(PuglView* view, int width, int height) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + + if (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +void +puglDisplay(PuglView* view) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + SwapBuffers(view->impl->hdc); + view->redisplay = false; +} + +static PuglKey +keySymToSpecial(int sym) +{ + switch (sym) { + case VK_F1: return PUGL_KEY_F1; + case VK_F2: return PUGL_KEY_F2; + case VK_F3: return PUGL_KEY_F3; + case VK_F4: return PUGL_KEY_F4; + case VK_F5: return PUGL_KEY_F5; + case VK_F6: return PUGL_KEY_F6; + case VK_F7: return PUGL_KEY_F7; + case VK_F8: return PUGL_KEY_F8; + case VK_F9: return PUGL_KEY_F9; + case VK_F10: return PUGL_KEY_F10; + case VK_F11: return PUGL_KEY_F11; + case VK_F12: return PUGL_KEY_F12; + case VK_LEFT: return PUGL_KEY_LEFT; + case VK_UP: return PUGL_KEY_UP; + case VK_RIGHT: return PUGL_KEY_RIGHT; + case VK_DOWN: return PUGL_KEY_DOWN; + case VK_PRIOR: return PUGL_KEY_PAGE_UP; + case VK_NEXT: return PUGL_KEY_PAGE_DOWN; + case VK_HOME: return PUGL_KEY_HOME; + case VK_END: return PUGL_KEY_END; + case VK_INSERT: return PUGL_KEY_INSERT; + case VK_SHIFT: return PUGL_KEY_SHIFT; + case VK_CONTROL: return PUGL_KEY_CTRL; + case VK_MENU: return PUGL_KEY_ALT; + case VK_LWIN: return PUGL_KEY_SUPER; + case VK_RWIN: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static void +processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) +{ + view->event_timestamp_ms = GetMessageTime(); + if (press) { + SetCapture(view->impl->hwnd); + } else { + ReleaseCapture(); + } + + if (view->mouseFunc) { + view->mouseFunc(view, button, press, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam)); + } +} + +static void +setModifiers(PuglView* view) +{ + view->mods = 0; + view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; + view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; + view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; + view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; + view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; +} + +static LRESULT +handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + PuglKey key; + + setModifiers(view); + switch (message) { + case WM_CREATE: + case WM_SHOWWINDOW: + case WM_SIZE: + RECT rect; + GetClientRect(view->impl->hwnd, &rect); + puglReshape(view, rect.right, rect.bottom); + view->width = rect.right; + view->height = rect.bottom; + break; + case WM_PAINT: + BeginPaint(view->impl->hwnd, &ps); + puglDisplay(view); + EndPaint(view->impl->hwnd, &ps); + break; + case WM_MOUSEMOVE: + if (view->motionFunc) { + view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + break; + case WM_LBUTTONDOWN: + processMouseEvent(view, 1, true, lParam); + break; + case WM_MBUTTONDOWN: + processMouseEvent(view, 2, true, lParam); + break; + case WM_RBUTTONDOWN: + processMouseEvent(view, 3, true, lParam); + break; + case WM_LBUTTONUP: + processMouseEvent(view, 1, false, lParam); + break; + case WM_MBUTTONUP: + processMouseEvent(view, 2, false, lParam); + break; + case WM_RBUTTONUP: + processMouseEvent(view, 3, false, lParam); + break; + case WM_MOUSEWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, 0, (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA); + } + break; + case WM_MOUSEHWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0); + } + break; + case WM_KEYDOWN: + view->event_timestamp_ms = (GetMessageTime()); + if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { + break; + } // else nobreak + case WM_KEYUP: + if ((key = keySymToSpecial(wParam))) { + if (view->specialFunc) { + view->specialFunc(view, message == WM_KEYDOWN, key); + } + } else if (view->keyboardFunc) { + view->keyboardFunc(view, message == WM_KEYDOWN, wParam); + } + break; + case WM_QUIT: + case LOCAL_CLOSE_MSG: + if (view->closeFunc) { + view->closeFunc(view); + } + break; + default: + return DefWindowProc( + view->impl->hwnd, message, wParam, lParam); + } + + return 0; +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + MSG msg; + while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { + handleMessage(view, msg.message, msg.wParam, msg.lParam); + } + + + if (view->redisplay) { + InvalidateRect(view->impl->hwnd, NULL, FALSE); + } + + return PUGL_SUCCESS; +} + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA); + switch (message) { + case WM_CREATE: + PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); + return 0; + case WM_CLOSE: + PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); + return 0; + case WM_DESTROY: + return 0; + default: + if (view) { + return handleMessage(view, message, wParam, lParam); + } else { + return DefWindowProc(hwnd, message, wParam, lParam); + } + } +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->hwnd; +} diff --git a/dgl/src/pugl/pugl_x11.c b/dgl/src/pugl/pugl_x11.c @@ -0,0 +1,414 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + Copyright 2011-2012 Ben Loftis, Harrison Consoles + Copyright 2013 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_x11.c X11 Pugl Implementation. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <GL/gl.h> +#include <GL/glx.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/keysym.h> + +#include "pugl_internal.h" + +/* work around buggy re-parent & focus issues on some systems + * where no keyboard events are passed through even if the + * app has mouse-focus and all other events are working. + */ +//#define XKEYFOCUSGRAB + +/* show messages during initalization + */ +//#define VERBOSE_PUGL + +struct PuglInternalsImpl { + Display* display; + int screen; + Window win; + GLXContext ctx; + Bool doubleBuffered; +}; + +/** + Attributes for single-buffered RGBA with at least + 4 bits per color and a 16 bit depth buffer. +*/ +static int attrListSgl[] = { + GLX_RGBA, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +/** + Attributes for double-buffered RGBA with at least + 4 bits per color and a 16 bit depth buffer. +*/ +static int attrListDbl[] = { + GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + impl->display = XOpenDisplay(0); + impl->screen = DefaultScreen(impl->display); + + XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); + if (!vi) { + vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); + impl->doubleBuffered = False; +#ifdef VERBOSE_PUGL + printf("puGL: singlebuffered rendering will be used, no doublebuffering available\n"); +#endif + } else { + impl->doubleBuffered = True; +#ifdef VERBOSE_PUGL + printf("puGL: doublebuffered rendering available\n"); +#endif + } + + int glxMajor, glxMinor; + glXQueryVersion(impl->display, &glxMajor, &glxMinor); +#ifdef VERBOSE_PUGL + printf("puGL: GLX-Version : %d.%d\n", glxMajor, glxMinor); +#endif + + impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); + + Window xParent = parent + ? (Window)parent + : RootWindow(impl->display, impl->screen); + + Colormap cmap = XCreateColormap( + impl->display, xParent, vi->visual, AllocNone); + + XSetWindowAttributes attr; + memset(&attr, 0, sizeof(XSetWindowAttributes)); + attr.colormap = cmap; + attr.border_pixel = 0; + + attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask + | ButtonPressMask | ButtonReleaseMask +#ifdef XKEYFOCUSGRAB + | EnterWindowMask +#endif + | PointerMotionMask | StructureNotifyMask; + + impl->win = XCreateWindow( + impl->display, xParent, + 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &attr); + + XSizeHints sizeHints; + memset(&sizeHints, 0, sizeof(sizeHints)); + if (!resizable) { + sizeHints.flags = PMinSize|PMaxSize; + sizeHints.min_width = width; + sizeHints.min_height = height; + sizeHints.max_width = width; + sizeHints.max_height = height; + XSetNormalHints(impl->display, impl->win, &sizeHints); + } + + if (title) { + XStoreName(impl->display, impl->win, title); + } + + if (!parent) { + Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); + } + + if (visible) { + XMapRaised(impl->display, impl->win); + } + + if (glXIsDirect(impl->display, impl->ctx)) { +#ifdef VERBOSE_PUGL + printf("puGL: DRI enabled\n"); +#endif + } else { +#ifdef VERBOSE_PUGL + printf("puGL: No DRI available\n"); +#endif + } + + XFree(vi); + + return view; +} + +void +puglDestroy(PuglView* view) +{ + if (!view) { + return; + } + + glXDestroyContext(view->impl->display, view->impl->ctx); + XDestroyWindow(view->impl->display, view->impl->win); + XCloseDisplay(view->impl->display); + free(view->impl); + free(view); +} + +static void +puglReshape(PuglView* view, int width, int height) +{ + glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); + + if (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +static void +puglDisplay(PuglView* view) +{ + glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + if (view->impl->doubleBuffered) { + glXSwapBuffers(view->impl->display, view->impl->win); + } + + view->redisplay = false; +} + +static PuglKey +keySymToSpecial(KeySym sym) +{ + switch (sym) { + case XK_F1: return PUGL_KEY_F1; + case XK_F2: return PUGL_KEY_F2; + case XK_F3: return PUGL_KEY_F3; + case XK_F4: return PUGL_KEY_F4; + case XK_F5: return PUGL_KEY_F5; + case XK_F6: return PUGL_KEY_F6; + case XK_F7: return PUGL_KEY_F7; + case XK_F8: return PUGL_KEY_F8; + case XK_F9: return PUGL_KEY_F9; + case XK_F10: return PUGL_KEY_F10; + case XK_F11: return PUGL_KEY_F11; + case XK_F12: return PUGL_KEY_F12; + case XK_Left: return PUGL_KEY_LEFT; + case XK_Up: return PUGL_KEY_UP; + case XK_Right: return PUGL_KEY_RIGHT; + case XK_Down: return PUGL_KEY_DOWN; + case XK_Page_Up: return PUGL_KEY_PAGE_UP; + case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; + case XK_Home: return PUGL_KEY_HOME; + case XK_End: return PUGL_KEY_END; + case XK_Insert: return PUGL_KEY_INSERT; + case XK_Shift_L: return PUGL_KEY_SHIFT; + case XK_Shift_R: return PUGL_KEY_SHIFT; + case XK_Control_L: return PUGL_KEY_CTRL; + case XK_Control_R: return PUGL_KEY_CTRL; + case XK_Alt_L: return PUGL_KEY_ALT; + case XK_Alt_R: return PUGL_KEY_ALT; + case XK_Super_L: return PUGL_KEY_SUPER; + case XK_Super_R: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static void +setModifiers(PuglView* view, unsigned xstate, unsigned xtime) +{ + view->event_timestamp_ms = xtime; + + view->mods = 0; + view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; + view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; + view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; + view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + XEvent event; + while (XPending(view->impl->display) > 0) { + XNextEvent(view->impl->display, &event); + switch (event.type) { + case MapNotify: + puglReshape(view, view->width, view->height); + break; + case ConfigureNotify: + if ((event.xconfigure.width != view->width) || + (event.xconfigure.height != view->height)) { + puglReshape(view, + event.xconfigure.width, + event.xconfigure.height); + } + break; + case Expose: + if (event.xexpose.count != 0) { + break; + } + puglDisplay(view); + break; + case MotionNotify: + setModifiers(view, event.xmotion.state, event.xmotion.time); + if (view->motionFunc) { + view->motionFunc(view, event.xmotion.x, event.xmotion.y); + } + break; + case ButtonPress: + setModifiers(view, event.xbutton.state, event.xbutton.time); + if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { + if (view->scrollFunc) { + float dx = 0, dy = 0; + switch (event.xbutton.button) { + case 4: dy = 1.0f; break; + case 5: dy = -1.0f; break; + case 6: dx = -1.0f; break; + case 7: dx = 1.0f; break; + } + view->scrollFunc(view, dx, dy); + } + break; + } + // nobreak + case ButtonRelease: + setModifiers(view, event.xbutton.state, event.xbutton.time); + if (view->mouseFunc && + (event.xbutton.button < 4 || event.xbutton.button > 7)) { + view->mouseFunc(view, + event.xbutton.button, event.type == ButtonPress, + event.xbutton.x, event.xbutton.y); + } + break; + case KeyPress: { + setModifiers(view, event.xkey.state, event.xkey.time); + KeySym sym; + char str[5]; + int n = XLookupString(&event.xkey, str, 4, &sym, NULL); + PuglKey key = keySymToSpecial(sym); + if (!key && view->keyboardFunc) { + if (n == 1) { + view->keyboardFunc(view, true, str[0]); + } else { + fprintf(stderr, "warning: Unknown key %X\n", (int)sym); + } + } else if (view->specialFunc) { + view->specialFunc(view, true, key); + } + } break; + case KeyRelease: { + setModifiers(view, event.xkey.state, event.xkey.time); + bool repeated = false; + if (view->ignoreKeyRepeat && + XEventsQueued(view->impl->display, QueuedAfterReading)) { + XEvent next; + XPeekEvent(view->impl->display, &next); + if (next.type == KeyPress && + next.xkey.time == event.xkey.time && + next.xkey.keycode == event.xkey.keycode) { + XNextEvent(view->impl->display, &event); + repeated = true; + } + } + + if (!repeated && view->keyboardFunc) { + KeySym sym = XLookupKeysym(&event.xkey, 0); + PuglKey special = keySymToSpecial(sym); + if (!special) { + view->keyboardFunc(view, false, sym); + } else if (view->specialFunc) { + view->specialFunc(view, false, special); + } + } + } break; + case ClientMessage: + if (!strcmp(XGetAtomName(view->impl->display, + event.xclient.message_type), + "WM_PROTOCOLS")) { + if (view->closeFunc) { + view->closeFunc(view); + } + } + break; +#ifdef XKEYFOCUSGRAB + case EnterNotify: + XSetInputFocus(view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime); + break; +#endif + default: + break; + } + } + + if (view->redisplay) { + puglDisplay(view); + } + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return view->impl->win; +} diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp @@ -0,0 +1,239 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_PLUGIN_HPP_INCLUDED +#define DISTRHO_PLUGIN_HPP_INCLUDED + +#include "DistrhoUtils.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Parameter Hints + +const uint32_t PARAMETER_IS_AUTOMABLE = 1 << 0; +const uint32_t PARAMETER_IS_BOOLEAN = 1 << 1; +const uint32_t PARAMETER_IS_INTEGER = 1 << 2; +const uint32_t PARAMETER_IS_LOGARITHMIC = 1 << 3; +const uint32_t PARAMETER_IS_OUTPUT = 1 << 4; + +// ----------------------------------------------------------------------- +// Parameter Ranges + +struct ParameterRanges { + float def; + float min; + float max; + + ParameterRanges() noexcept + : def(0.0f), + min(0.0f), + max(1.0f) {} + + ParameterRanges(float def, float min, float max) noexcept + { + this->def = def; + this->min = min; + this->max = max; + } + + void clear() noexcept + { + def = 0.0f; + min = 0.0f; + max = 1.0f; + } + + void fixValue(float& value) const noexcept + { + if (value < min) + value = min; + else if (value > max) + value = max; + } + + float getFixedValue(const float& value) const noexcept + { + if (value < min) + return min; + else if (value > max) + return max; + return value; + } + + float getNormalizedValue(const float& value) const noexcept + { + const float newValue((value - min) / (max - min)); + + if (newValue <= 0.0f) + return 0.0f; + if (newValue >= 1.0f) + return 1.0f; + return newValue; + } + + float getUnnormalizedValue(const float& value) const noexcept + { + return value * (max - min) + min; + } +}; + +// ----------------------------------------------------------------------- +// Parameter + +struct Parameter { + uint32_t hints; + d_string name; + d_string symbol; + d_string unit; + ParameterRanges ranges; + + Parameter() + : hints(0x0) {} + + void clear() noexcept + { + hints = 0x0; + name = ""; + symbol = ""; + unit = ""; + ranges.clear(); + } +}; + +// ----------------------------------------------------------------------- +// MidiEvent + +struct MidiEvent { + uint32_t frame; + uint8_t size; + uint8_t buf[4]; + + void clear() noexcept + { + frame = 0; + size = 0; + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + } +}; + +// ----------------------------------------------------------------------- +// TimePos + +struct TimePos { + bool playing; + uint64_t frame; + double bpm; + + TimePos() noexcept + : playing(false), + frame(0), + bpm(120.0) {} +}; + +// ----------------------------------------------------------------------- +// Plugin + +class Plugin +{ +public: + Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount); + virtual ~Plugin(); + + // ------------------------------------------------------------------- + // Host state + + uint32_t d_getBufferSize() const noexcept; + double d_getSampleRate() const noexcept; +#if DISTRHO_PLUGIN_WANT_TIMEPOS + const TimePos& d_getTimePos() const noexcept; +#endif +#if DISTRHO_PLUGIN_WANT_LATENCY + void d_setLatency(uint32_t frames) noexcept; +#endif + +protected: + // ------------------------------------------------------------------- + // Information + + virtual const char* d_getName() const noexcept { return DISTRHO_PLUGIN_NAME; } + virtual const char* d_getLabel() const noexcept = 0; + virtual const char* d_getMaker() const noexcept = 0; + virtual const char* d_getLicense() const noexcept = 0; + virtual uint32_t d_getVersion() const noexcept = 0; + virtual long d_getUniqueId() const noexcept = 0; + + // ------------------------------------------------------------------- + // Init + + virtual void d_initParameter(uint32_t index, Parameter& parameter) = 0; +#if DISTRHO_PLUGIN_WANT_PROGRAMS + virtual void d_initProgramName(uint32_t index, d_string& programName) = 0; +#endif +#if DISTRHO_PLUGIN_WANT_STATE + virtual void d_initStateKey(uint32_t index, d_string& stateKey) = 0; +#endif + + // ------------------------------------------------------------------- + // Internal data + + virtual float d_getParameterValue(uint32_t index) const = 0; + virtual void d_setParameterValue(uint32_t index, float value) = 0; +#if DISTRHO_PLUGIN_WANT_PROGRAMS + virtual void d_setProgram(uint32_t index) = 0; +#endif +#if DISTRHO_PLUGIN_WANT_STATE + virtual void d_setState(const char* key, const char* value) = 0; +#endif + + // ------------------------------------------------------------------- + // Process + + virtual void d_activate() {} + virtual void d_deactivate() {} +#if DISTRHO_PLUGIN_IS_SYNTH + virtual void d_run(float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) = 0; +#else + virtual void d_run(float** inputs, float** outputs, uint32_t frames) = 0; +#endif + + // ------------------------------------------------------------------- + // Callbacks (optional) + + virtual void d_bufferSizeChanged(uint32_t newBufferSize); + virtual void d_sampleRateChanged(double newSampleRate); + + // ------------------------------------------------------------------- + +private: + struct PrivateData; + PrivateData* const pData; + friend class PluginExporter; +}; + +// ----------------------------------------------------------------------- +// Create plugin, entry point + +extern Plugin* createPlugin(); + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_PLUGIN_HPP_INCLUDED diff --git a/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp @@ -0,0 +1,26 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "src/DistrhoPlugin.cpp" + +#if (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI)) +# include "src/DistrhoPluginLADSPA+DSSI.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_LV2) +# include "src/DistrhoPluginLV2.cpp" +# include "src/DistrhoPluginLV2export.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_VST) +# include "src/DistrhoPluginVST.cpp" +#endif diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp @@ -0,0 +1,94 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_UI_HPP_INCLUDED +#define DISTRHO_UI_HPP_INCLUDED + +#include "DistrhoUtils.hpp" + +#include "../dgl/Widget.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// UI + +class UI : public DGL::Widget +{ +public: + UI(); + virtual ~UI(); + + // ------------------------------------------------------------------- + // Host DSP State + + double d_getSampleRate() const noexcept; + void d_editParameter(uint32_t index, bool started); + void d_setParameterValue(uint32_t index, float value); +#if DISTRHO_PLUGIN_WANT_STATE + void d_setState(const char* key, const char* value); +#endif +#if DISTRHO_PLUGIN_IS_SYNTH + void d_sendNote(uint8_t channel, uint8_t note, uint8_t velocity); +#endif + + // ------------------------------------------------------------------- + // Host UI State + + void d_uiResize(unsigned int width, unsigned int height); + +protected: + // ------------------------------------------------------------------- + // Basic Information + + virtual const char* d_getName() const noexcept { return DISTRHO_PLUGIN_NAME; } + virtual unsigned int d_getWidth() const noexcept = 0; + virtual unsigned int d_getHeight() const noexcept = 0; + + // ------------------------------------------------------------------- + // DSP Callbacks + + virtual void d_parameterChanged(uint32_t index, float value) = 0; +#if DISTRHO_PLUGIN_WANT_PROGRAMS + virtual void d_programChanged(uint32_t index) = 0; +#endif +#if DISTRHO_PLUGIN_WANT_STATE + virtual void d_stateChanged(const char* key, const char* value) = 0; +#endif + + // ------------------------------------------------------------------- + // UI Callbacks (optional) + + virtual void d_uiIdle() {} + + // ------------------------------------------------------------------- + +private: + struct PrivateData; + PrivateData* const pData; + friend class UIExporter; +}; + +// ----------------------------------------------------------------------- +// Create UI, entry point + +extern UI* createUI(); + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_HPP_INCLUDED diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp @@ -0,0 +1,25 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "src/DistrhoUI.cpp" + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) +# include "src/DistrhoUIDSSI.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_LV2) +# include "src/DistrhoUILV2.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_VST) +// nothing +#endif diff --git a/distrho/DistrhoUtils.hpp b/distrho/DistrhoUtils.hpp @@ -0,0 +1,684 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_UTILS_HPP_INCLUDED +#define DISTRHO_UTILS_HPP_INCLUDED + +#include "src/DistrhoDefines.h" + +#include <cassert> +#include <cstdarg> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#ifdef PROPER_CPP11_SUPPORT +# include <cstdint> +#else +# include <stdint.h> +#endif + +#ifdef DISTRHO_OS_WINDOWS +# include <windows.h> +#else +# include <unistd.h> +#endif + +#if defined(DISTRHO_OS_MAC) && ! defined(CARLA_OS_MAC) +namespace std { +inline float + fmin(float __x, float __y) + { return __builtin_fminf(__x, __y); } +inline float + fmax(float __x, float __y) + { return __builtin_fmaxf(__x, __y); } +inline float + rint(float __x) + { return __builtin_rintf(__x); } +} +#endif + +// ----------------------------------------------------------------------- +// misc functions + +static inline +long d_cconst(int a, int b, int c, int d) noexcept +{ + return (a << 24) | (b << 16) | (c << 8) | (d << 0); +} + +// ----------------------------------------------------------------------- +// string print functions + +#ifndef DEBUG +# define d_debug(...) +#else +static inline +void d_debug(const char* const fmt, ...) +{ + va_list args; + va_start(args, fmt); + std::fprintf(stdout, "\x1b[30;1m"); + std::vfprintf(stdout, fmt, args); + std::fprintf(stdout, "\x1b[0m\n"); + va_end(args); +} +#endif + +static inline +void d_stdout(const char* const fmt, ...) +{ + va_list args; + va_start(args, fmt); + std::vfprintf(stdout, fmt, args); + std::fprintf(stdout, "\n"); + va_end(args); +} + +static inline +void d_stderr(const char* const fmt, ...) +{ + va_list args; + va_start(args, fmt); + std::vfprintf(stderr, fmt, args); + std::fprintf(stderr, "\n"); + va_end(args); +} + +static inline +void d_stderr2(const char* const fmt, ...) +{ + va_list args; + va_start(args, fmt); + std::fprintf(stderr, "\x1b[31m"); + std::vfprintf(stderr, fmt, args); + std::fprintf(stderr, "\x1b[0m\n"); + va_end(args); +} + +// ----------------------------------------------------------------------- +// d_*sleep + +static inline +void d_sleep(unsigned int secs) +{ +#ifdef DISTRHO_OS_WINDOWS + Sleep(secs * 1000); +#else + sleep(secs); +#endif +} + +static inline +void d_msleep(unsigned int msecs) +{ +#ifdef DISTRHO_OS_WINDOWS + Sleep(msecs); +#else + usleep(msecs * 1000); +#endif +} + +// ----------------------------------------------------------------------- +// d_string class + +class d_string +{ +public: + // ------------------------------------------------------------------- + // constructors (no explicit conversions allowed) + + /* + * Empty string. + */ + explicit d_string() + { + _init(); + _dup(nullptr); + } + + /* + * Simple character. + */ + explicit d_string(const char c) + { + char ch[2]; + ch[0] = c; + ch[1] = '\0'; + + _init(); + _dup(ch); + } + + /* + * Simple char string. + */ + explicit d_string(char* const strBuf) + { + _init(); + _dup(strBuf); + } + + /* + * Simple const char string. + */ + explicit d_string(const char* const strBuf) + { + _init(); + _dup(strBuf); + } + + /* + * Integer. + */ + explicit d_string(const int value) + { + char strBuf[0xff+1]; + std::memset(strBuf, 0, (0xff+1)*sizeof(char)); + std::snprintf(strBuf, 0xff, "%d", value); + + _init(); + _dup(strBuf); + } + + /* + * Unsigned integer, possibly in hexadecimal. + */ + explicit d_string(const unsigned int value, const bool hexadecimal = false) + { + char strBuf[0xff+1]; + std::memset(strBuf, 0, (0xff+1)*sizeof(char)); + std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); + + _init(); + _dup(strBuf); + } + + /* + * Long integer. + */ + explicit d_string(const long int value) + { + char strBuf[0xff+1]; + std::memset(strBuf, 0, (0xff+1)*sizeof(char)); + std::snprintf(strBuf, 0xff, "%ld", value); + + _init(); + _dup(strBuf); + } + + /* + * Long unsigned integer, possibly hexadecimal. + */ + explicit d_string(const unsigned long int value, const bool hexadecimal = false) + { + char strBuf[0xff+1]; + std::memset(strBuf, 0, (0xff+1)*sizeof(char)); + std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); + + _init(); + _dup(strBuf); + } + + /* + * Single-precision floating point number. + */ + explicit d_string(const float value) + { + char strBuf[0xff+1]; + std::memset(strBuf, 0, (0xff+1)*sizeof(char)); + std::snprintf(strBuf, 0xff, "%f", value); + + _init(); + _dup(strBuf); + } + + /* + * Double-precision floating point number. + */ + explicit d_string(const double value) + { + char strBuf[0xff+1]; + std::memset(strBuf, 0, (0xff+1)*sizeof(char)); + std::snprintf(strBuf, 0xff, "%g", value); + + _init(); + _dup(strBuf); + } + + // ------------------------------------------------------------------- + // non-explicit constructor + + /* + * Create string from another string. + */ + d_string(const d_string& str) + { + _init(); + _dup(str.fBuffer); + } + + // ------------------------------------------------------------------- + // destructor + + /* + * Destructor. + */ + ~d_string() + { + assert(fBuffer != nullptr); + + delete[] fBuffer; + fBuffer = nullptr; + } + + // ------------------------------------------------------------------- + // public methods + + /* + * Get length of the string. + */ + size_t length() const noexcept + { + return fBufferLen; + } + + /* + * Check if the string is empty. + */ + bool isEmpty() const noexcept + { + return (fBufferLen == 0); + } + + /* + * Check if the string is not empty. + */ + bool isNotEmpty() const noexcept + { + return (fBufferLen != 0); + } + + /* + * Check if the string contains another string, optionally ignoring case. + */ + bool contains(const char* const strBuf, const bool ignoreCase = false) const + { + if (strBuf == nullptr) + return false; + + if (ignoreCase) + { +#ifdef __USE_GNU + return (strcasestr(fBuffer, strBuf) != nullptr); +#else + d_string tmp1(fBuffer), tmp2(strBuf); + tmp1.toLower(); + tmp2.toLower(); + return (std::strstr((const char*)tmp1, (const char*)tmp2) != nullptr); +#endif + } + + return (std::strstr(fBuffer, strBuf) != nullptr); + } + + /* + * Overloaded function. + */ + bool contains(const d_string& str, const bool ignoreCase = false) const + { + return contains(str.fBuffer, ignoreCase); + } + + /* + * Check if character at 'pos' is a digit. + */ + bool isDigit(const size_t pos) const noexcept + { + if (pos >= fBufferLen) + return false; + + return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); + } + + /* + * Check if the string starts with the character 'c'. + */ + bool startsWith(const char c) const + { + if (c == '\0') + return false; + + return (fBufferLen > 0 && fBuffer[0] == c); + } + + /* + * Check if the string starts with the string 'prefix'. + */ + bool startsWith(const char* const prefix) const + { + if (prefix == nullptr) + return false; + + const size_t prefixLen(std::strlen(prefix)); + + if (fBufferLen < prefixLen) + return false; + + return (std::strncmp(fBuffer + (fBufferLen-prefixLen), prefix, prefixLen) == 0); + } + + /* + * Check if the string ends with the character 'c'. + */ + bool endsWith(const char c) const + { + if (c == '\0') + return false; + + return (fBufferLen > 0 && fBuffer[fBufferLen] == c); + } + + /* + * Check if the string ends with the string 'suffix'. + */ + bool endsWith(const char* const suffix) const + { + if (suffix == nullptr) + return false; + + const size_t suffixLen(std::strlen(suffix)); + + if (fBufferLen < suffixLen) + return false; + + return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); + } + + /* + * Clear the string. + */ + void clear() noexcept + { + truncate(0); + } + + /* + * Replace all occurrences of character 'before' with character 'after'. + */ + void replace(const char before, const char after) noexcept + { + if (before == '\0' || after == '\0') + return; + + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] == before) + fBuffer[i] = after; + else if (fBuffer[i] == '\0') + break; + } + } + + /* + * Truncate the string to size 'n'. + */ + void truncate(const size_t n) noexcept + { + if (n >= fBufferLen) + return; + + for (size_t i=n; i < fBufferLen; ++i) + fBuffer[i] = '\0'; + + fBufferLen = n; + } + + /* + * Convert all non-basic characters to '_'. + */ + void toBasic() noexcept + { + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] >= '0' && fBuffer[i] <= '9') + continue; + if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') + continue; + if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') + continue; + if (fBuffer[i] == '_') + continue; + + fBuffer[i] = '_'; + } + } + + /* + * Convert to all ascii characters to lowercase. + */ + void toLower() noexcept + { + static const char kCharDiff('a' - 'A'); + + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') + fBuffer[i] += kCharDiff; + } + } + + /* + * Convert to all ascii characters to uppercase. + */ + void toUpper() noexcept + { + static const char kCharDiff('a' - 'A'); + + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') + fBuffer[i] -= kCharDiff; + } + } + + // ------------------------------------------------------------------- + // public operators + + operator const char*() const noexcept + { + return fBuffer; + } + + char& operator[](const size_t pos) const noexcept + { + return fBuffer[pos]; + } + + bool operator==(const char* const strBuf) const + { + return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0); + } + + bool operator==(const d_string& str) const + { + return operator==(str.fBuffer); + } + + bool operator!=(const char* const strBuf) const + { + return !operator==(strBuf); + } + + bool operator!=(const d_string& str) const + { + return !operator==(str.fBuffer); + } + + d_string& operator=(const char* const strBuf) + { + _dup(strBuf); + + return *this; + } + + d_string& operator=(const d_string& str) + { + return operator=(str.fBuffer); + } + + d_string& operator+=(const char* const strBuf) + { + if (strBuf == nullptr) + return *this; + + const size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, fBuffer); + std::strcat(newBuf, strBuf); + + _dup(newBuf, newBufSize-1); + + return *this; + } + + d_string& operator+=(const d_string& str) + { + return operator+=(str.fBuffer); + } + + d_string operator+(const char* const strBuf) + { + const size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, fBuffer); + + if (strBuf != nullptr) + std::strcat(newBuf, strBuf); + + return d_string(newBuf); + } + + d_string operator+(const d_string& str) + { + return operator+(str.fBuffer); + } + + // ------------------------------------------------------------------- + +private: + char* fBuffer; // the actual string buffer + size_t fBufferLen; // string length + bool fFirstInit; // true when first initiated + + /* + * Shared init function. + * Called on all constructors. + */ + void _init() noexcept + { + fBuffer = nullptr; + fBufferLen = 0; + fFirstInit = true; + } + + /* + * Helper function. + * Called whenever the string needs to be allocated. + * + * Notes: + * - Allocates string only if first initiated, or if 'strBuf' is not null and new string contents are different + * - If 'strBuf' is null 'size' must be 0 + */ + void _dup(const char* const strBuf, const size_t size = 0) + { + if (strBuf != nullptr) + { + // don't recreate string if contents match + if (fFirstInit || std::strcmp(fBuffer, strBuf) != 0) + { + if (! fFirstInit) + { + assert(fBuffer != nullptr); + delete[] fBuffer; + } + + fBufferLen = (size > 0) ? size : std::strlen(strBuf); + fBuffer = new char[fBufferLen+1]; + + std::strcpy(fBuffer, strBuf); + + fBuffer[fBufferLen] = '\0'; + + fFirstInit = false; + } + } + else + { + assert(size == 0); + + // don't recreate null string + if (fFirstInit || fBufferLen != 0) + { + if (! fFirstInit) + { + assert(fBuffer != nullptr); + delete[] fBuffer; + } + + fBufferLen = 0; + fBuffer = new char[1]; + fBuffer[0] = '\0'; + + fFirstInit = false; + } + } + } +}; + +// ----------------------------------------------------------------------- + +static inline +d_string operator+(const d_string& strBefore, const char* const strBufAfter) +{ + const char* const strBufBefore = (const char*)strBefore; + const size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, strBufBefore); + std::strcat(newBuf, strBufAfter); + + return d_string(newBuf); +} + +static inline +d_string operator+(const char* const strBufBefore, const d_string& strAfter) +{ + const char* const strBufAfter = (const char*)strAfter; + const size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, strBufBefore); + std::strcat(newBuf, strBufAfter); + + return d_string(newBuf); +} + +// ----------------------------------------------------------------------- + +#endif // DISTRHO_UTILS_HPP_INCLUDED diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h @@ -0,0 +1,114 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_DEFINES_H_INCLUDED +#define DISTRHO_DEFINES_H_INCLUDED + +#include "DistrhoPluginInfo.h" + +#ifndef DISTRHO_PLUGIN_NAME +# error DISTRHO_PLUGIN_NAME undefined! +#endif + +#ifndef DISTRHO_PLUGIN_HAS_UI +# error DISTRHO_PLUGIN_HAS_UI undefined! +#endif + +#ifndef DISTRHO_PLUGIN_IS_SYNTH +# error DISTRHO_PLUGIN_IS_SYNTH undefined! +#endif + +#ifndef DISTRHO_PLUGIN_NUM_INPUTS +# error DISTRHO_PLUGIN_NUM_INPUTS undefined! +#endif + +#ifndef DISTRHO_PLUGIN_NUM_OUTPUTS +# error DISTRHO_PLUGIN_NUM_OUTPUTS undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_LATENCY +# error DISTRHO_PLUGIN_WANT_LATENCY undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_PROGRAMS +# error DISTRHO_PLUGIN_WANT_PROGRAMS undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_STATE +# error DISTRHO_PLUGIN_WANT_STATE undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_TIMEPOS +# error DISTRHO_PLUGIN_WANT_TIMEPOS undefined! +#endif + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define DISTRHO_PLUGIN_EXPORT extern "C" __declspec (dllexport) +# define DISTRHO_OS_WINDOWS 1 +# define DISTRHO_DLL_EXTENSION "dll" +#else +# define DISTRHO_PLUGIN_EXPORT extern "C" __attribute__ ((visibility("default"))) +# if defined(__APPLE__) +# define DISTRHO_OS_MAC 1 +# define DISTRHO_DLL_EXTENSION "dylib" +# elif defined(__HAIKU__) +# define DISTRHO_OS_HAIKU 1 +# define DISTRHO_DLL_EXTENSION "so" +# elif defined(__linux__) +# define DISTRHO_OS_LINUX 1 +# define DISTRHO_DLL_EXTENSION "so" +# endif +#endif + +#ifndef DISTRHO_DLL_EXTENSION +# define DISTRHO_DLL_EXTENSION "so" +#endif + +#if defined(HAVE_CPP11_SUPPORT) +# define PROPER_CPP11_SUPPORT +#elif defined(__GNUC__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +# define PROPER_CPP11_SUPPORT +# if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 +# define override // gcc4.7+ only +# endif +# endif +#endif + +#ifndef PROPER_CPP11_SUPPORT +# ifndef __clang__ +# define noexcept throw() +# endif +# define override +# define nullptr (0) +#endif + +#ifndef DISTRHO_NO_NAMESPACE +# ifndef DISTRHO_NAMESPACE +# define DISTRHO_NAMESPACE DISTRHO +# endif +# define START_NAMESPACE_DISTRHO namespace DISTRHO_NAMESPACE { +# define END_NAMESPACE_DISTRHO } +# define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE; +#else +# define START_NAMESPACE_DISTRHO +# define END_NAMESPACE_DISTRHO +# define USE_NAMESPACE_DISTRHO +#endif + +#define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" + +#endif // DISTRHO_DEFINES_H_INCLUDED diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp @@ -0,0 +1,107 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPluginInternal.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Static data, see DistrhoPluginInternal.hpp + +uint32_t d_lastBufferSize = 0; +double d_lastSampleRate = 0.0; + +// ----------------------------------------------------------------------- +// Static fallback data, see DistrhoPluginInternal.hpp + +const d_string PluginExporter::sFallbackString; +const ParameterRanges PluginExporter::sFallbackRanges; + +// ----------------------------------------------------------------------- +// Plugin + +Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) + : pData(new PrivateData()) +{ + if (parameterCount > 0) + { + pData->parameterCount = parameterCount; + pData->parameters = new Parameter[parameterCount]; + } + + if (programCount > 0) + { +#if DISTRHO_PLUGIN_WANT_PROGRAMS + pData->programCount = programCount; + pData->programNames = new d_string[programCount]; +#endif + } + + if (stateCount > 0) + { +#if DISTRHO_PLUGIN_WANT_STATE + pData->stateCount = stateCount; + pData->stateKeys = new d_string[stateCount]; +#endif + } +} + +Plugin::~Plugin() +{ + delete pData; +} + +// ----------------------------------------------------------------------- +// Host state + +uint32_t Plugin::d_getBufferSize() const noexcept +{ + return pData->bufferSize; +} + +double Plugin::d_getSampleRate() const noexcept +{ + return pData->sampleRate; +} + +#if DISTRHO_PLUGIN_WANT_TIMEPOS +const TimePos& Plugin::d_getTimePos() const noexcept +{ + return pData->timePos; +} +#endif + +#if DISTRHO_PLUGIN_WANT_LATENCY +void Plugin::d_setLatency(uint32_t frames) noexcept +{ + pData->latency = frames; +} +#endif + +// ----------------------------------------------------------------------- +// Callbacks (optional) + +void Plugin::d_bufferSizeChanged(uint32_t) +{ +} + +void Plugin::d_sampleRateChanged(double) +{ +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp @@ -0,0 +1,380 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED +#define DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED + +#include "../DistrhoPlugin.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Maxmimum values + +static const uint32_t kMaxMidiEvents = 512; + +// ----------------------------------------------------------------------- +// Static data, see DistrhoPlugin.cpp + +extern uint32_t d_lastBufferSize; +extern double d_lastSampleRate; + +// ----------------------------------------------------------------------- +// Plugin private data + +struct Plugin::PrivateData { + uint32_t parameterCount; + Parameter* parameters; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + uint32_t programCount; + d_string* programNames; +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + uint32_t stateCount; + d_string* stateKeys; +#endif + +#if DISTRHO_PLUGIN_WANT_LATENCY + uint32_t latency; +#endif + +#if DISTRHO_PLUGIN_WANT_TIMEPOS + TimePos timePos; +#endif + + uint32_t bufferSize; + double sampleRate; + + PrivateData() noexcept + : parameterCount(0), + parameters(nullptr), +#if DISTRHO_PLUGIN_WANT_PROGRAMS + programCount(0), + programNames(nullptr), +#endif +#if DISTRHO_PLUGIN_WANT_STATE + stateCount(0), + stateKeys(nullptr), +#endif +#if DISTRHO_PLUGIN_WANT_LATENCY + latency(0), +#endif + bufferSize(d_lastBufferSize), + sampleRate(d_lastSampleRate) + { + assert(bufferSize != 0); + assert(sampleRate != 0.0); + } + + ~PrivateData() + { + if (parameters != nullptr) + { + delete[] parameters; + parameters = nullptr; + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (programNames != nullptr) + { + delete[] programNames; + programNames = nullptr; + } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + if (stateKeys != nullptr) + { + delete[] stateKeys; + stateKeys = nullptr; + } +#endif + } +}; + +// ----------------------------------------------------------------------- +// Plugin exporter class + +class PluginExporter +{ +public: + PluginExporter() + : fPlugin(createPlugin()), + fData((fPlugin != nullptr) ? fPlugin->pData : nullptr) + { + assert(fPlugin != nullptr); + + if (fPlugin == nullptr) + return; + + for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) + fPlugin->d_initParameter(i, fData->parameters[i]); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + for (uint32_t i=0, count=fData->programCount; i < count; ++i) + fPlugin->d_initProgramName(i, fData->programNames[i]); +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + for (uint32_t i=0, count=fData->stateCount; i < count; ++i) + fPlugin->d_initStateKey(i, fData->stateKeys[i]); +#endif + } + + ~PluginExporter() + { + delete fPlugin; + } + + // ------------------------------------------------------------------- + + const char* getName() const noexcept + { + return (fPlugin != nullptr) ? fPlugin->d_getName() : ""; + } + + const char* getLabel() const noexcept + { + return (fPlugin != nullptr) ? fPlugin->d_getLabel() : ""; + } + + const char* getMaker() const noexcept + { + return (fPlugin != nullptr) ? fPlugin->d_getMaker() : ""; + } + + const char* getLicense() const noexcept + { + return (fPlugin != nullptr) ? fPlugin->d_getLicense() : ""; + } + + uint32_t getVersion() const noexcept + { + return (fPlugin != nullptr) ? fPlugin->d_getVersion() : 1000; + } + + long getUniqueId() const noexcept + { + return (fPlugin != nullptr) ? fPlugin->d_getUniqueId() : 0; + } + + // ------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_LATENCY + uint32_t getLatency() const noexcept + { + return (fData != nullptr) ? fData->latency : 0; + } +#endif + + uint32_t getParameterCount() const noexcept + { + return (fData != nullptr) ? fData->parameterCount : 0; + } + + uint32_t getParameterHints(const uint32_t index) const noexcept + { + assert(index < fData->parameterCount); + return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].hints : 0x0; + } + + bool isParameterOutput(const uint32_t index) const noexcept + { + return (getParameterHints(index) & PARAMETER_IS_OUTPUT); + } + + const d_string& getParameterName(const uint32_t index) const noexcept + { + assert(index < fData->parameterCount); + return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].name : sFallbackString; + } + + const d_string& getParameterSymbol(const uint32_t index) const noexcept + { + assert(index < fData->parameterCount); + return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].symbol : sFallbackString; + } + + const d_string& getParameterUnit(const uint32_t index) const noexcept + { + assert(index < fData->parameterCount); + return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].unit : sFallbackString; + } + + const ParameterRanges& getParameterRanges(const uint32_t index) const noexcept + { + assert(index < fData->parameterCount); + return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].ranges : sFallbackRanges; + } + + float getParameterValue(const uint32_t index) const noexcept + { + assert(index < fData->parameterCount); + return (fPlugin != nullptr && index < fData->parameterCount) ? fPlugin->d_getParameterValue(index) : 0.0f; + } + + void setParameterValue(const uint32_t index, const float value) + { + assert(index < fData->parameterCount); + + if (fPlugin != nullptr && index < fData->parameterCount) + fPlugin->d_setParameterValue(index, value); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + uint32_t getProgramCount() const noexcept + { + return (fData != nullptr) ? fData->programCount : 0; + } + + const d_string& getProgramName(const uint32_t index) const noexcept + { + assert(index < fData->programCount); + return (fData != nullptr && index < fData->programCount) ? fData->programNames[index] : sFallbackString; + } + + void setProgram(const uint32_t index) + { + assert(index < fData->programCount); + + if (fPlugin != nullptr && index < fData->programCount) + fPlugin->d_setProgram(index); + } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + uint32_t getStateCount() const noexcept + { + return fData != nullptr ? fData->stateCount : 0; + } + + const d_string& getStateKey(const uint32_t index) const noexcept + { + assert(index < fData->stateCount); + return (fData != nullptr && index < fData->stateCount) ? fData->stateKeys[index] : sFallbackString; + } + + void setState(const char* const key, const char* const value) + { + assert(key != nullptr && value != nullptr); + + if (fPlugin != nullptr && key != nullptr && value != nullptr) + fPlugin->d_setState(key, value); + } +#endif + +#if DISTRHO_PLUGIN_WANT_TIMEPOS + void setTimePos(const bool playing, const uint64_t frame, const double bpm) + { + if (fData != nullptr) + { + fData->timePos.playing = playing; + fData->timePos.frame = frame; + fData->timePos.bpm = bpm; + } + } +#endif + + // ------------------------------------------------------------------- + + void activate() + { + if (fPlugin != nullptr) + fPlugin->d_activate(); + } + + void deactivate() + { + if (fPlugin != nullptr) + fPlugin->d_deactivate(); + } + +#if DISTRHO_PLUGIN_IS_SYNTH + void run(float** const inputs, float** const outputs, const uint32_t frames, const MidiEvent* const midiEvents, const uint32_t midiEventCount) + { + if (fPlugin != nullptr) + fPlugin->d_run(inputs, outputs, frames, midiEvents, midiEventCount); + } +#else + void run(float** const inputs, float** const outputs, const uint32_t frames) + { + if (fPlugin != nullptr) + fPlugin->d_run(inputs, outputs, frames); + } +#endif + // ------------------------------------------------------------------- + + void setBufferSize(const uint32_t bufferSize, bool doCallback = false) + { + assert(bufferSize >= 2); + + if (fData != nullptr) + { + if (doCallback && fData->bufferSize == bufferSize) + doCallback = false; + + fData->bufferSize = bufferSize; + } + + if (fPlugin != nullptr && doCallback) + { + fPlugin->d_deactivate(); + fPlugin->d_bufferSizeChanged(bufferSize); + fPlugin->d_activate(); + } + } + + void setSampleRate(const double sampleRate, bool doCallback = false) + { + assert(sampleRate > 0.0); + + if (fData != nullptr) + { + if (doCallback && fData->sampleRate == sampleRate) + doCallback = false; + + fData->sampleRate = sampleRate; + } + + if (fPlugin != nullptr && doCallback) + { + fPlugin->d_deactivate(); + fPlugin->d_sampleRateChanged(sampleRate); + fPlugin->d_activate(); + } + } + +private: + // ------------------------------------------------------------------- + // private members accessed by DistrhoPlugin class + + Plugin* const fPlugin; + Plugin::PrivateData* const fData; + + // ------------------------------------------------------------------- + // Static fallback data, see DistrhoPlugin.cpp + + static const d_string sFallbackString; + static const ParameterRanges sFallbackRanges; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED diff --git a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp @@ -0,0 +1,702 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPluginInternal.hpp" + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +# include "dssi/dssi.h" +#else +# include "ladspa/ladspa.h" +# if DISTRHO_PLUGIN_IS_SYNTH +# error Cannot build synth plugin with LADSPA +# endif +# if DISTRHO_PLUGIN_WANT_STATE +# warning LADSPA cannot handle states +# endif +#endif + +#if DISTRHO_PLUGIN_WANT_TIMEPOS +# warning LADSPA/DSSI does not support TimePos +#endif + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +class PluginLadspaDssi +{ +public: + PluginLadspaDssi() + : fPortControls(nullptr), + fLastControlValues(nullptr) + { +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) + fPortAudioIns[i] = nullptr; +#else + fPortAudioIns = nullptr; +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + fPortAudioOuts[i] = nullptr; +#else + fPortAudioOuts = nullptr; +#endif + + { + const uint32_t count(fPlugin.getParameterCount()); + + fPortControls = new LADSPA_Data*[count]; + fLastControlValues = new LADSPA_Data[count]; + + for (uint32_t i=0; i < count; ++i) + { + fPortControls[i] = nullptr; + fLastControlValues[i] = fPlugin.getParameterValue(i); + } + } + +#if DISTRHO_PLUGIN_WANT_LATENCY + fPortLatency = nullptr; +#endif + } + + ~PluginLadspaDssi() + { + if (fPortControls != nullptr) + { + delete[] fPortControls; + fPortControls = nullptr; + } + + if (fLastControlValues) + { + delete[] fLastControlValues; + fLastControlValues = nullptr; + } + } + + // ------------------------------------------------------------------- + + void ladspa_activate() + { + fPlugin.activate(); + } + + void ladspa_deactivate() + { + fPlugin.deactivate(); + } + + // ------------------------------------------------------------------- + + void ladspa_connect_port(const unsigned long port, LADSPA_Data* const dataLocation) + { + unsigned long index = 0; + +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (unsigned long i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) + { + if (port == index++) + { + fPortAudioIns[i] = dataLocation; + return; + } + } +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + for (unsigned long i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + { + if (port == index++) + { + fPortAudioOuts[i] = dataLocation; + return; + } + } +#endif + +#if DISTRHO_PLUGIN_WANT_LATENCY + if (port == index++) + { + fPortLatency = dataLocation; + return; + } +#endif + + for (unsigned long i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (port == index++) + { + fPortControls[i] = dataLocation; + return; + } + } + } + + // ------------------------------------------------------------------- + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI + void ladspa_run(const unsigned long sampleCount) + { + dssi_run_synth(sampleCount, nullptr, 0); + } + + void dssi_run_synth(const unsigned long sampleCount, snd_seq_event_t* const events, const unsigned long eventCount) +#else + void ladspa_run(const unsigned long sampleCount) +#endif + { + // pre-roll + if (sampleCount == 0) + return updateParameterOutputs(); + + // Check for updated parameters + float curValue; + + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (fPortControls[i] == nullptr) + continue; + + curValue = *fPortControls[i]; + + if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) + { + fLastControlValues[i] = curValue; + fPlugin.setParameterValue(i, curValue); + } + } + +#if DISTRHO_PLUGIN_IS_SYNTH + // Get MIDI Events + uint32_t midiEventCount = 0; + MidiEvent midiEvents[eventCount]; + + for (uint32_t i=0, j; i < eventCount; ++i) + { + const snd_seq_event_t& seqEvent(events[i]); + + if (seqEvent.data.note.channel > 0xF || seqEvent.data.control.channel > 0xF) + continue; + + switch (seqEvent.type) + { + case SND_SEQ_EVENT_NOTEOFF: + j = midiEventCount++; + midiEvents[j].frame = seqEvent.time.tick; + midiEvents[j].size = 3; + midiEvents[j].buf[0] = 0x80 + seqEvent.data.note.channel; + midiEvents[j].buf[1] = seqEvent.data.note.note; + midiEvents[j].buf[2] = 0; + midiEvents[j].buf[3] = 0; + break; + case SND_SEQ_EVENT_NOTEON: + j = midiEventCount++; + midiEvents[j].frame = seqEvent.time.tick; + midiEvents[j].size = 3; + midiEvents[j].buf[0] = 0x90 + seqEvent.data.note.channel; + midiEvents[j].buf[1] = seqEvent.data.note.note; + midiEvents[j].buf[2] = seqEvent.data.note.velocity; + midiEvents[j].buf[3] = 0; + break; + case SND_SEQ_EVENT_KEYPRESS: + j = midiEventCount++; + midiEvents[j].frame = seqEvent.time.tick; + midiEvents[j].size = 3; + midiEvents[j].buf[0] = 0xA0 + seqEvent.data.note.channel; + midiEvents[j].buf[1] = seqEvent.data.note.note; + midiEvents[j].buf[2] = seqEvent.data.note.velocity; + midiEvents[j].buf[3] = 0; + break; + case SND_SEQ_EVENT_CONTROLLER: + j = midiEventCount++; + midiEvents[j].frame = seqEvent.time.tick; + midiEvents[j].size = 3; + midiEvents[j].buf[0] = 0xB0 + seqEvent.data.control.channel; + midiEvents[j].buf[1] = seqEvent.data.control.param; + midiEvents[j].buf[2] = seqEvent.data.control.value; + midiEvents[j].buf[3] = 0; + break; + case SND_SEQ_EVENT_CHANPRESS: + j = midiEventCount++; + midiEvents[j].frame = seqEvent.time.tick; + midiEvents[j].size = 2; + midiEvents[j].buf[0] = 0xD0 + seqEvent.data.control.channel; + midiEvents[j].buf[1] = seqEvent.data.control.value; + midiEvents[j].buf[2] = 0; + midiEvents[j].buf[3] = 0; + break; +#if 0 // TODO + case SND_SEQ_EVENT_PITCHBEND: + j = midiEventCount++; + midiEvents[j].frame = seqEvent.time.tick; + midiEvents[j].size = 3; + midiEvents[j].buf[0] = 0xE0 + seqEvent.data.control.channel; + midiEvents[j].buf[1] = 0; + midiEvents[j].buf[2] = 0; + midiEvents[j].buf[3] = 0; + break; +#endif + } + } + + fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, midiEvents, midiEventCount); +#else + fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); +#endif + + updateParameterOutputs(); + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && ! DISTRHO_PLUGIN_IS_SYNTH + return; // unused + (void)events; + (void)eventCount; +#endif + } + + // ------------------------------------------------------------------- + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +# if DISTRHO_PLUGIN_WANT_STATE + char* dssi_configure(const char* const key, const char* const value) + { + if (std::strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, std::strlen(DSSI_RESERVED_CONFIGURE_PREFIX) == 0)) + return nullptr; + if (std::strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, std::strlen(DSSI_GLOBAL_CONFIGURE_PREFIX) == 0)) + return nullptr; + + fPlugin.setState(key, value); + return nullptr; + } +# endif + +# if DISTRHO_PLUGIN_WANT_PROGRAMS + const DSSI_Program_Descriptor* dssi_get_program(const unsigned long index) + { + if (index >= fPlugin.getProgramCount()) + return nullptr; + + static DSSI_Program_Descriptor desc; + + desc.Bank = index / 128; + desc.Program = index % 128; + desc.Name = fPlugin.getProgramName(index); + + return &desc; + } + + void dssi_select_program(const unsigned long bank, const unsigned long program) + { + const unsigned long realProgram(bank * 128 + program); + + if (realProgram >= fPlugin.getProgramCount()) + return; + + fPlugin.setProgram(realProgram); + + // Update control inputs + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (fPlugin.isParameterOutput(i)) + continue; + + fLastControlValues[i] = fPlugin.getParameterValue(i); + + if (fPortControls[i] != nullptr) + *fPortControls[i] = fLastControlValues[i]; + } + } +# endif +#endif + + // ------------------------------------------------------------------- + +private: + PluginExporter fPlugin; + + // LADSPA ports +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + LADSPA_Data* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; +#else + LADSPA_Data** fPortAudioIns; +#endif +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + LADSPA_Data* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; +#else + LADSPA_Data** fPortAudioOuts; +#endif + LADSPA_Data** fPortControls; +#if DISTRHO_PLUGIN_WANT_LATENCY + LADSPA_Data* fPortLatency; +#endif + + // Temporary data + LADSPA_Data* fLastControlValues; + + // ------------------------------------------------------------------- + + void updateParameterOutputs() + { + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (! fPlugin.isParameterOutput(i)) + continue; + + fLastControlValues[i] = fPlugin.getParameterValue(i); + + if (fPortControls[i] != nullptr) + *fPortControls[i] = fLastControlValues[i]; + } + +#if DISTRHO_PLUGIN_WANT_LATENCY + if (fPortLatency != nullptr) + *fPortLatency = fPlugin.getLatency(); +#endif + } +}; + +// ----------------------------------------------------------------------- + +static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, unsigned long sampleRate) +{ + if (d_lastBufferSize == 0) + d_lastBufferSize = 2048; + d_lastSampleRate = sampleRate; + + return new PluginLadspaDssi(); +} + +#define instancePtr ((PluginLadspaDssi*)instance) + +static void ladspa_connect_port(LADSPA_Handle instance, unsigned long port, LADSPA_Data* dataLocation) +{ + instancePtr->ladspa_connect_port(port, dataLocation); +} + +static void ladspa_activate(LADSPA_Handle instance) +{ + instancePtr->ladspa_activate(); +} + +static void ladspa_run(LADSPA_Handle instance, unsigned long sampleCount) +{ + instancePtr->ladspa_run(sampleCount); +} + +static void ladspa_deactivate(LADSPA_Handle instance) +{ + instancePtr->ladspa_deactivate(); +} + +static void ladspa_cleanup(LADSPA_Handle instance) +{ + delete instancePtr; +} + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +# if DISTRHO_PLUGIN_WANT_STATE +static char* dssi_configure(LADSPA_Handle instance, const char* key, const char* value) +{ + return instancePtr->dssi_configure(key, value); +} +# endif + +# if DISTRHO_PLUGIN_WANT_PROGRAMS +static const DSSI_Program_Descriptor* dssi_get_program(LADSPA_Handle instance, unsigned long index) +{ + return instancePtr->dssi_get_program(index); +} + +static void dssi_select_program(LADSPA_Handle instance, unsigned long bank, unsigned long program) +{ + instancePtr->dssi_select_program(bank, program); +} +# endif + +# if DISTRHO_PLUGIN_IS_SYNTH +static void dssi_run_synth(LADSPA_Handle instance, unsigned long sampleCount, snd_seq_event_t* events, unsigned long eventCount) +{ + instancePtr->dssi_run_synth(sampleCount, events, eventCount); +} +# endif +#endif + +#undef instancePtr + +// ----------------------------------------------------------------------- + +static LADSPA_Descriptor sLadspaDescriptor = { + /* UniqueID */ 0, + /* Label */ nullptr, + /* Properties */ LADSPA_PROPERTY_REALTIME | LADSPA_PROPERTY_HARD_RT_CAPABLE, + /* Name */ nullptr, + /* Maker */ nullptr, + /* Copyright */ nullptr, + /* PortCount */ 0, + /* PortDescriptors */ nullptr, + /* PortNames */ nullptr, + /* PortRangeHints */ nullptr, + /* ImplementationData */ nullptr, + ladspa_instantiate, + ladspa_connect_port, + ladspa_activate, + ladspa_run, + /* run_adding */ nullptr, + /* set_run_adding_gain */ nullptr, + ladspa_deactivate, + ladspa_cleanup +}; + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +static DSSI_Descriptor sDssiDescriptor = { + 1, + &sLadspaDescriptor, +# if DISTRHO_PLUGIN_WANT_STATE + dssi_configure, +# else + /* configure */ nullptr, +# endif +# if DISTRHO_PLUGIN_WANT_PROGRAMS + dssi_get_program, + dssi_select_program, +# else + /* get_program */ nullptr, + /* select_program */ nullptr, +# endif + /* get_midi_controller_for_port */ nullptr, +# if DISTRHO_PLUGIN_IS_SYNTH + dssi_run_synth, +# else + /* run_synth */ nullptr, +# endif + /* run_synth_adding */ nullptr, + /* run_multiple_synths */ nullptr, + /* run_multiple_synths_adding */ nullptr, + nullptr, nullptr +}; +#endif + +// ----------------------------------------------------------------------- + +class DescriptorInitializer +{ +public: + DescriptorInitializer() + { + // Create dummy plugin to get data from + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + PluginExporter plugin; + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + // Get port count, init + unsigned long port = 0; + unsigned long portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.getParameterCount(); +#if DISTRHO_PLUGIN_WANT_LATENCY + portCount += 1; +#endif + const char** const portNames = new const char*[portCount]; + LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor[portCount]; + LADSPA_PortRangeHint* portRangeHints = new LADSPA_PortRangeHint [portCount]; + + // Set ports +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (unsigned long i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++port) + { + char portName[24] = { '\0' }; + std::sprintf(portName, "Audio Input %lu", i+1); + + portNames[port] = strdup(portName); + portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT; + + portRangeHints[port].HintDescriptor = 0x0; + portRangeHints[port].LowerBound = 0.0f; + portRangeHints[port].UpperBound = 1.0f; + } +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + for (unsigned long i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++port) + { + char portName[24] = { '\0' }; + std::sprintf(portName, "Audio Output %lu", i+1); + + portNames[port] = strdup(portName); + portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT; + + portRangeHints[port].HintDescriptor = 0x0; + portRangeHints[port].LowerBound = 0.0f; + portRangeHints[port].UpperBound = 1.0f; + } +#endif + +#if DISTRHO_PLUGIN_WANT_LATENCY + // Set latency port + portNames[port] = strdup("_latency"); + portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT; + portRangeHints[port].HintDescriptor = LADSPA_HINT_SAMPLE_RATE; + portRangeHints[port].LowerBound = 0.0f; + portRangeHints[port].UpperBound = 1.0f; + ++port; +#endif + + for (unsigned long i=0, count=plugin.getParameterCount(); i < count; ++i, ++port) + { + portNames[port] = strdup((const char*)plugin.getParameterName(i)); + portDescriptors[port] = LADSPA_PORT_CONTROL; + + if (plugin.isParameterOutput(i)) + portDescriptors[port] |= LADSPA_PORT_OUTPUT; + else + portDescriptors[port] |= LADSPA_PORT_INPUT; + + { + const ParameterRanges& ranges(plugin.getParameterRanges(i)); + const float defValue(ranges.def); + + portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; + portRangeHints[port].LowerBound = ranges.min; + portRangeHints[port].UpperBound = ranges.max; + + if (defValue == 0.0f) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; + else if (defValue == 1.0f) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1; + else if (defValue == 100.0f) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100; + else if (defValue == 440.0f) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440; + else if (ranges.min == defValue) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM; + else if (ranges.max == defValue) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM; + else + { + const float middleValue = ranges.min/2.0f + ranges.max/2.0f; + const float middleLow = (ranges.min/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; + const float middleHigh = (ranges.max/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; + + if (defValue < middleLow) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW; + else if (defValue > middleHigh) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH; + else + portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE; + } + } + + { + const uint32_t hints(plugin.getParameterHints(i)); + + if (hints & PARAMETER_IS_BOOLEAN) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; + if (hints & PARAMETER_IS_INTEGER) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_INTEGER; + if (hints & PARAMETER_IS_LOGARITHMIC) + portRangeHints[port].HintDescriptor |= LADSPA_HINT_LOGARITHMIC; + } + } + + // Set data + sLadspaDescriptor.UniqueID = plugin.getUniqueId(); + sLadspaDescriptor.Label = strdup(plugin.getLabel()); + sLadspaDescriptor.Name = strdup(plugin.getName()); + sLadspaDescriptor.Maker = strdup(plugin.getMaker()); + sLadspaDescriptor.Copyright = strdup(plugin.getLicense()); + sLadspaDescriptor.PortCount = portCount; + sLadspaDescriptor.PortNames = portNames; + sLadspaDescriptor.PortDescriptors = portDescriptors; + sLadspaDescriptor.PortRangeHints = portRangeHints; + } + + ~DescriptorInitializer() + { + if (sLadspaDescriptor.Label != nullptr) + { + std::free((void*)sLadspaDescriptor.Label); + sLadspaDescriptor.Label = nullptr; + } + + if (sLadspaDescriptor.Name != nullptr) + { + std::free((void*)sLadspaDescriptor.Name); + sLadspaDescriptor.Name = nullptr; + } + + if (sLadspaDescriptor.Maker != nullptr) + { + std::free((void*)sLadspaDescriptor.Maker); + sLadspaDescriptor.Maker = nullptr; + } + + if (sLadspaDescriptor.Copyright != nullptr) + { + std::free((void*)sLadspaDescriptor.Copyright); + sLadspaDescriptor.Copyright = nullptr; + } + + if (sLadspaDescriptor.PortDescriptors != nullptr) + { + delete[] sLadspaDescriptor.PortDescriptors; + sLadspaDescriptor.PortDescriptors = nullptr; + } + + if (sLadspaDescriptor.PortRangeHints != nullptr) + { + delete[] sLadspaDescriptor.PortRangeHints; + sLadspaDescriptor.PortRangeHints = nullptr; + } + + if (sLadspaDescriptor.PortNames != nullptr) + { + for (unsigned long i=0; i < sLadspaDescriptor.PortCount; ++i) + { + if (sLadspaDescriptor.PortNames[i] != nullptr) + std::free((void*)sLadspaDescriptor.PortNames[i]); + } + + delete[] sLadspaDescriptor.PortNames; + sLadspaDescriptor.PortNames = nullptr; + } + } +}; + +static DescriptorInitializer sDescInit; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +DISTRHO_PLUGIN_EXPORT +const LADSPA_Descriptor* ladspa_descriptor(unsigned long index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &sLadspaDescriptor : nullptr; +} + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +DISTRHO_PLUGIN_EXPORT +const DSSI_Descriptor* dssi_descriptor(unsigned long index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &sDssiDescriptor : nullptr; +} +#endif + +// ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp @@ -0,0 +1,714 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPluginInternal.hpp" + +#include "lv2/atom.h" +#include "lv2/atom-util.h" +#include "lv2/buf-size.h" +#include "lv2/midi.h" +#include "lv2/options.h" +#include "lv2/state.h" +#include "lv2/time.h" +#include "lv2/urid.h" +#include "lv2/worker.h" +#include "lv2/lv2_programs.h" + +#ifdef noexcept +# undef noexcept +#endif + +#include <map> + +#ifndef DISTRHO_PLUGIN_URI +# error DISTRHO_PLUGIN_URI undefined! +#endif + +#if DISTRHO_PLUGIN_WANT_STATE +# warning LV2 State still TODO +#endif +#if DISTRHO_PLUGIN_WANT_TIMEPOS +# warning LV2 TimePos still TODO +#endif + +#define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) +#define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) + +typedef std::map<d_string,d_string> StringMap; + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +class PluginLv2 +{ +public: + PluginLv2(const LV2_URID_Map* const uridMap, const LV2_Worker_Schedule* const worker) + : fPortControls(nullptr), + fLastControlValues(nullptr), +#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT + fURIDs(uridMap), +#endif + fUridMap(uridMap), + fWorker(worker) + { +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) + fPortAudioIns[i] = nullptr; +#else + fPortAudioIns = nullptr; +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + fPortAudioOuts[i] = nullptr; +#else + fPortAudioOuts = nullptr; +#endif + + { + const uint32_t count(fPlugin.getParameterCount()); + + fPortControls = new float*[count]; + fLastControlValues = new float[count]; + + for (uint32_t i=0; i < count; ++i) + { + fPortControls[i] = nullptr; + fLastControlValues[i] = fPlugin.getParameterValue(i); + } + } + +#if DISTRHO_LV2_USE_EVENTS_IN + fPortEventsIn = nullptr; +#endif +#if DISTRHO_LV2_USE_EVENTS_OUT + fPortEventsOut = nullptr; +#endif +#if DISTRHO_PLUGIN_WANT_LATENCY + fPortLatency = nullptr; +#endif + } + + ~PluginLv2() + { + if (fPortControls != nullptr) + { + delete[] fPortControls; + fPortControls = nullptr; + } + + if (fLastControlValues) + { + delete[] fLastControlValues; + fLastControlValues = nullptr; + } + +#if DISTRHO_PLUGIN_WANT_STATE + fStateMap.clear(); +#endif + } + + // ------------------------------------------------------------------- + + void lv2_activate() + { + fPlugin.activate(); + } + + void lv2_deactivate() + { + fPlugin.deactivate(); + } + + // ------------------------------------------------------------------- + + void lv2_connect_port(const uint32_t port, void* const dataLocation) + { + uint32_t index = 0; + +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) + { + if (port == index++) + { + fPortAudioIns[i] = (float*)dataLocation; + return; + } + } +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + { + if (port == index++) + { + fPortAudioOuts[i] = (float*)dataLocation; + return; + } + } +#endif + +#if DISTRHO_LV2_USE_EVENTS_IN + if (port == index++) + { + fPortEventsIn = (LV2_Atom_Sequence*)dataLocation; + return; + } +#endif + +#if DISTRHO_LV2_USE_EVENTS_OUT + if (port == index++) + { + fPortEventsOut = (LV2_Atom_Sequence*)dataLocation; + return; + } +#endif + +#if DISTRHO_PLUGIN_WANT_LATENCY + if (port == index++) + { + fPortLatency = (float*)dataLocation; + return; + } +#endif + + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (port == index++) + { + fPortControls[i] = (float*)dataLocation; + return; + } + } + } + + // ------------------------------------------------------------------- + + void lv2_run(const uint32_t sampleCount) + { + // pre-roll + if (sampleCount == 0) + return updateParameterOutputs(); + + // Check for updated parameters + float curValue; + + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (fPortControls[i] == nullptr) + continue; + + curValue = *fPortControls[i]; + + if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) + { + fLastControlValues[i] = curValue; + fPlugin.setParameterValue(i, curValue); + } + } + +#if DISTRHO_LV2_USE_EVENTS_IN +# if DISTRHO_PLUGIN_IS_SYNTH + uint32_t midiEventCount = 0; +# endif + LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) + { + if (event == nullptr) + break; + +# if DISTRHO_PLUGIN_IS_SYNTH + if (event->body.type == fURIDs.midiEvent) + { + if (event->body.size > 4 || midiEventCount >= kMaxMidiEvents) + continue; + + const uint8_t* const data((const uint8_t*)(event + 1)); + + MidiEvent& midiEvent(fMidiEvents[midiEventCount]); + + midiEvent.frame = event->time.frames; + midiEvent.size = event->body.size; + + uint8_t i; + for (i=0; i < midiEvent.size; ++i) + midiEvent.buf[i] = data[i]; + for (; i < 4; ++i) + midiEvent.buf[i] = 0; + + ++midiEventCount; + continue; + } +# endif +# if DISTRHO_PLUGIN_WANT_TIMEPOS + if (event->body.type == fURIDs.timePosition) + { + // TODO + } +# endif +# if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) + if (event->body.type == fURIDs.distrhoState && fWorker != nullptr) + { + const void* const data((const void*)(event + 1)); + fWorker->schedule_work(fWorker->handle, event->body.size, data); + } +# endif + } +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount); +#else + fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); +#endif + +#if DISTRHO_LV2_USE_EVENTS_OUT +#endif + + updateParameterOutputs(); + } + + // ------------------------------------------------------------------- + + uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) + { + // currently unused + return LV2_OPTIONS_ERR_UNKNOWN; + } + + uint32_t lv2_set_options(const LV2_Options_Option* const options) + { + for (int i=0; options[i].key != 0; ++i) + { + if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__maxBlockLength)) + { + if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Int)) + { + const int bufferSize(*(const int*)options[i].value); + fPlugin.setBufferSize(bufferSize); + return LV2_OPTIONS_SUCCESS; + } + else + { + d_stderr("Host changed maxBlockLength but with wrong value type"); + return LV2_OPTIONS_ERR_BAD_VALUE; + } + } + else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_CORE__sampleRate)) + { + if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Double)) + { + const double sampleRate(*(const double*)options[i].value); + fPlugin.setSampleRate(sampleRate); + return LV2_OPTIONS_SUCCESS; + } + else + { + d_stderr("Host changed sampleRate but with wrong value type"); + return LV2_OPTIONS_ERR_BAD_VALUE; + } + } + } + + return LV2_OPTIONS_ERR_BAD_KEY; + } + + // ------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) + { + if (index >= fPlugin.getProgramCount()) + return nullptr; + + static LV2_Program_Descriptor desc; + + desc.bank = index / 128; + desc.program = index % 128; + desc.name = fPlugin.getProgramName(index); + + return &desc; + } + + void lv2_select_program(const uint32_t bank, const uint32_t program) + { + const uint32_t realProgram(bank * 128 + program); + + if (realProgram >= fPlugin.getProgramCount()) + return; + + fPlugin.setProgram(realProgram); + + // Update control inputs + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (fPlugin.isParameterOutput(i)) + continue; + + fLastControlValues[i] = fPlugin.getParameterValue(i); + + if (fPortControls[i] != nullptr) + *fPortControls[i] = fLastControlValues[i]; + } + } +#endif + + // ------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_STATE + LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle, const uint32_t flags) + { + //flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; + + for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) + { + const d_string& key = it->first; + const d_string& value = it->second; + + store(handle, fUridMap->map(fUridMap->handle, (const char*)key), (const char*)value, value.length()+1, fURIDs.atomString, flags); + } + + return LV2_STATE_SUCCESS; + } + + LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, const LV2_State_Handle handle) + { + size_t size = 0; + uint32_t type = 0; + uint32_t flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; + + for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) + { + const d_string& key = fPlugin.getStateKey(i); + + const void* data = retrieve(handle, fUridMap->map(fUridMap->handle, (const char*)key), &size, &type, &flags); + + if (size == 0) + continue; + if (data == nullptr) + continue; + if (type != fURIDs.atomString) + continue; + + const char* const value((const char*)data); + + if (std::strlen(value) != size) + continue; + + setState(key, value); + } + + return LV2_STATE_SUCCESS; + } + + // ------------------------------------------------------------------- + + LV2_Worker_Status lv2_work(const void* const data) + { + const char* const stateKey((const char*)data); + const char* const stateValue(stateKey+std::strlen(stateKey)+1); + + setState(stateKey, stateValue); + + return LV2_WORKER_SUCCESS; + } +#endif + + // ------------------------------------------------------------------- + +private: + PluginExporter fPlugin; + + // LV2 ports +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; +#else + float** fPortAudioIns; +#endif +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; +#else + float** fPortAudioOuts; +#endif + float** fPortControls; +#if DISTRHO_LV2_USE_EVENTS_IN + LV2_Atom_Sequence* fPortEventsIn; +#endif +#if DISTRHO_LV2_USE_EVENTS_OUT + LV2_Atom_Sequence* fPortEventsOut; +#endif +#if DISTRHO_PLUGIN_WANT_LATENCY + float* fPortLatency; +#endif + + // Temporary data + float* fLastControlValues; +#if DISTRHO_PLUGIN_IS_SYNTH + MidiEvent fMidiEvents[kMaxMidiEvents]; +#endif + + // LV2 URIDs +#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT + struct URIDs { + LV2_URID atomString; + LV2_URID distrhoState; + LV2_URID midiEvent; + LV2_URID timePosition; + + URIDs(const LV2_URID_Map* const uridMap) + : atomString(uridMap->map(uridMap->handle, LV2_ATOM__String)), + distrhoState(uridMap->map(uridMap->handle, "urn:distrho:keyValueState")), + midiEvent(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), + timePosition(uridMap->map(uridMap->handle, LV2_TIME__Position)) {} + } fURIDs; +#endif + + // LV2 features + const LV2_URID_Map* const fUridMap; + const LV2_Worker_Schedule* const fWorker; + +#if DISTRHO_PLUGIN_WANT_STATE + StringMap fStateMap; + + void setState(const char* const key, const char* const newValue) + { + fPlugin.setState(key, newValue); + + // check if key already exists + for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) + { + const d_string& d_key = it->first; + + if (d_key == key) + { + it->second = newValue; + return; + } + } + + // add a new one then + d_string d_key(key); + fStateMap[d_key] = newValue; + } +#endif + + void updateParameterOutputs() + { + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (! fPlugin.isParameterOutput(i)) + continue; + + fLastControlValues[i] = fPlugin.getParameterValue(i); + + if (fPortControls[i] != nullptr) + *fPortControls[i] = fLastControlValues[i]; + } + +#if DISTRHO_PLUGIN_WANT_LATENCY + if (fPortLatency != nullptr) + *fPortLatency = fPlugin.getLatency(); +#endif + } +}; + +// ----------------------------------------------------------------------- + +static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features) +{ + const LV2_Options_Option* options = nullptr; + const LV2_URID_Map* uridMap = nullptr; + const LV2_Worker_Schedule* worker = nullptr; + + for (int i=0; features[i] != nullptr; ++i) + { + if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) + options = (const LV2_Options_Option*)features[i]->data; + else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) + uridMap = (const LV2_URID_Map*)features[i]->data; + else if (std::strcmp(features[i]->URI, LV2_WORKER__schedule) == 0) + worker = (const LV2_Worker_Schedule*)features[i]->data; + } + + if (options == nullptr) + { + d_stderr("Options feature missing, cannot continue!"); + return nullptr; + } + + if (uridMap == nullptr) + { + d_stderr("URID Map feature missing, cannot continue!"); + return nullptr; + } + +#if DISTRHO_PLUGIN_WANT_STATE + if (worker == nullptr) + { + d_stderr("Worker feature missing, cannot continue!"); + return nullptr; + } +#endif + + for (int i=0; options[i].key != 0; ++i) + { + if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength)) + { + if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int)) + d_lastBufferSize = *(const int*)options[i].value; + else + d_stderr("Host provides maxBlockLength but has wrong value type"); + + break; + } + } + + if (d_lastBufferSize == 0) + d_lastBufferSize = 2048; + + d_lastSampleRate = sampleRate; + + return new PluginLv2(uridMap, worker); +} + +#define instancePtr ((PluginLv2*)instance) + +static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation) +{ + instancePtr->lv2_connect_port(port, dataLocation); +} + +static void lv2_activate(LV2_Handle instance) +{ + instancePtr->lv2_activate(); +} + +static void lv2_run(LV2_Handle instance, uint32_t sampleCount) +{ + instancePtr->lv2_run(sampleCount); +} + +static void lv2_deactivate(LV2_Handle instance) +{ + instancePtr->lv2_deactivate(); +} + +static void lv2_cleanup(LV2_Handle instance) +{ + delete instancePtr; +} + +// ----------------------------------------------------------------------- + +static uint32_t lv2_get_options(LV2_Handle instance, LV2_Options_Option* options) +{ + return instancePtr->lv2_get_options(options); +} + +static uint32_t lv2_set_options(LV2_Handle instance, const LV2_Options_Option* options) +{ + return instancePtr->lv2_set_options(options); +} + +// ----------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_PROGRAMS +static const LV2_Program_Descriptor* lv2_get_program(LV2_Handle instance, uint32_t index) +{ + return instancePtr->lv2_get_program(index); +} + +static void lv2_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) +{ + instancePtr->lv2_select_program(bank, program); +} +#endif + +// ----------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_STATE +static LV2_State_Status lv2_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const*) +{ + return instancePtr->lv2_save(store, handle, flags); +} + +static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t, const LV2_Feature* const*) +{ + return instancePtr->lv2_restore(retrieve, handle); +} + +LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function, LV2_Worker_Respond_Handle, uint32_t, const void* data) +{ + return instancePtr->lv2_work(data); +} +#endif + +// ----------------------------------------------------------------------- + +static const void* lv2_extension_data(const char* uri) +{ + static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options }; + + if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) + return &options; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program }; + + if (std::strcmp(uri, LV2_PROGRAMS__Interface) == 0) + return &programs; +#endif + +#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 }; + + if (std::strcmp(uri, LV2_STATE__interface) == 0) + return &state; + if (std::strcmp(uri, LV2_WORKER__interface) == 0) + return &worker; +#endif + + return nullptr; +} + +#undef instancePtr + +// ----------------------------------------------------------------------- + +static const LV2_Descriptor sLv2Descriptor = { + DISTRHO_PLUGIN_URI, + lv2_instantiate, + lv2_connect_port, + lv2_activate, + lv2_run, + lv2_deactivate, + lv2_cleanup, + lv2_extension_data +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +DISTRHO_PLUGIN_EXPORT +const LV2_Descriptor* lv2_descriptor(uint32_t index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &sLv2Descriptor : nullptr; +} + +// ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp @@ -0,0 +1,367 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPluginInternal.hpp" + +#include "lv2/atom.h" +#include "lv2/buf-size.h" +#include "lv2/midi.h" +#include "lv2/options.h" +#include "lv2/state.h" +#include "lv2/time.h" +#include "lv2/ui.h" +#include "lv2/units.h" +#include "lv2/urid.h" +#include "lv2/worker.h" +#include "lv2/lv2_programs.h" + +#include <fstream> +#include <iostream> + +#ifndef DISTRHO_PLUGIN_URI +# error DISTRHO_PLUGIN_URI undefined! +#endif + +#define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) +#define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) + +// ----------------------------------------------------------------------- + +DISTRHO_PLUGIN_EXPORT +void lv2_generate_ttl(const char* const basename) +{ + USE_NAMESPACE_DISTRHO + + // Dummy plugin to get data from + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + PluginExporter plugin; + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + d_string pluginLabel(basename); + d_string pluginTTL(pluginLabel + ".ttl"); + + // --------------------------------------------- + + { + std::cout << "Writing manifest.ttl..."; std::cout.flush(); + std::fstream manifestFile("manifest.ttl", std::ios::out); + + d_string manifestString; + manifestString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; + manifestString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; +#if DISTRHO_PLUGIN_HAS_UI + manifestString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; +#endif + manifestString += "\n"; + + manifestString += "<" DISTRHO_PLUGIN_URI ">\n"; + manifestString += " a lv2:Plugin ;\n"; + manifestString += " lv2:binary <" + pluginLabel + "." DISTRHO_DLL_EXTENSION "> ;\n"; + manifestString += " rdfs:seeAlso <" + pluginTTL + "> .\n"; + manifestString += "\n"; + +#if DISTRHO_PLUGIN_HAS_UI + manifestString += "<" DISTRHO_UI_URI ">\n"; +# if DISTRHO_OS_HAIKU + manifestString += " a ui:BeUI ;\n"; +# elif DISTRHO_OS_MACOS + manifestString += " a ui:CocoaUI ;\n"; +# elif DISTRHO_OS_WINDOWS + manifestString += " a ui:WindowsUI ;\n"; +# else + manifestString += " a ui:X11UI ;\n"; +# endif + manifestString += " ui:binary <" + pluginLabel + "_ui." DISTRHO_DLL_EXTENSION "> ;\n"; +#if DISTRHO_PLUGIN_WANT_PROGRAMS + manifestString += " lv2:extensionData ui:idleInterface ,\n"; + manifestString += " <" LV2_PROGRAMS__Interface "> ;\n"; +#else + manifestString += " lv2:extensionData ui:idleInterface ;\n"; +#endif + manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; + manifestString += " ui:touch ;\n"; + manifestString += " lv2:requiredFeature ui:resize ,\n"; + manifestString += " <" LV2_OPTIONS__options "> ,\n"; + manifestString += " <" LV2_URID__map "> .\n"; +#endif + + manifestFile << manifestString << std::endl; + manifestFile.close(); + std::cout << " done!" << std::endl; + } + + // --------------------------------------------- + + { + std::cout << "Writing " << pluginTTL << "..."; std::cout.flush(); + std::fstream pluginFile(pluginTTL, std::ios::out); + + d_string pluginString; + + // header +#if DISTRHO_LV2_USE_EVENTS_IN + pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; +#endif + pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; + pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; + pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; +#if DISTRHO_PLUGIN_HAS_UI + pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; +#endif + pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; + pluginString += "\n"; + + // plugin + pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; +#if DISTRHO_PLUGIN_IS_SYNTH + pluginString += " a lv2:InstrumentPlugin, lv2:Plugin ;\n"; +#else + pluginString += " a lv2:Plugin ;\n"; +#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 + pluginString += ";\n\n"; + + // optionalFeatures + pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; + pluginString += " <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; + 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 + pluginString += ";\n\n"; + + // UI +#if DISTRHO_PLUGIN_HAS_UI + pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; + pluginString += "\n"; +#endif + + { + uint32_t portIndex = 0; + +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++portIndex) + { + if (i == 0) + pluginString += " lv2:port [\n"; + else + pluginString += " [\n"; + + pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; + pluginString += " lv2:symbol \"lv2_audio_in_" + d_string(i+1) + "\" ;\n"; + pluginString += " lv2:name \"Audio Input " + d_string(i+1) + "\" ;\n"; + + if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS) + pluginString += " ] ;\n\n"; + else + pluginString += " ] ,\n"; + } + pluginString += "\n"; +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++portIndex) + { + if (i == 0) + pluginString += " lv2:port [\n"; + else + pluginString += " [\n"; + + pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; + pluginString += " lv2:symbol \"lv2_audio_out_" + d_string(i+1) + "\" ;\n"; + pluginString += " lv2:name \"Audio Output " + d_string(i+1) + "\" ;\n"; + + if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS) + pluginString += " ] ;\n\n"; + else + pluginString += " ] ,\n"; + } + pluginString += "\n"; +#endif + +#if DISTRHO_LV2_USE_EVENTS_IN + pluginString += " lv2:port [\n"; + pluginString += " a lv2:InputPort, atom:AtomPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; + pluginString += " lv2:name \"Events Input\" ;\n"; + pluginString += " lv2:symbol \"lv2_events_in\" ;\n"; + pluginString += " atom:bufferType atom:Sequence ;\n"; +# if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) + pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; +# endif +# if DISTRHO_PLUGIN_WANT_TIMEPOS + pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; +# endif +# if DISTRHO_PLUGIN_IS_SYNTH + pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; +# endif + + pluginString += " ] ;\n\n"; + ++portIndex; +#endif + +#if DISTRHO_LV2_USE_EVENTS_OUT + pluginString += " lv2:port [\n"; + pluginString += " a lv2:OutputPort, atom:AtomPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; + pluginString += " lv2:name \"Events Output\" ;\n"; + pluginString += " lv2:symbol \"lv2_events_out\" ;\n"; + pluginString += " atom:bufferType atom:Sequence ;\n"; + pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; + pluginString += " ] ;\n\n"; + ++portIndex; +#endif + +#if DISTRHO_PLUGIN_WANT_LATENCY + pluginString += " lv2:port [\n"; + pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; + pluginString += " lv2:name \"Latency\" ;\n"; + pluginString += " lv2:symbol \"lv2_latency\" ;\n"; + pluginString += " lv2:designation lv2:latency ;\n"; + pluginString += " lv2:portProperty lv2:reportsLatency ;\n"; + pluginString += " ] ;\n\n"; + ++portIndex; +#endif + + for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i, ++portIndex) + { + if (i == 0) + pluginString += " lv2:port [\n"; + else + pluginString += " [\n"; + + if (plugin.isParameterOutput(i)) + pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; + else + pluginString += " a lv2:InputPort, lv2:ControlPort ;\n"; + + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; + pluginString += " lv2:name \"" + plugin.getParameterName(i) + "\" ;\n"; + + // symbol + { + d_string symbol(plugin.getParameterSymbol(i)); + + if (symbol.isEmpty()) + symbol = "lv2_port_" + d_string(portIndex-1); + + pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; + } + + // ranges + { + const ParameterRanges& ranges(plugin.getParameterRanges(i)); + + if (plugin.getParameterHints(i) & PARAMETER_IS_INTEGER) + { + pluginString += " lv2:default " + d_string(int(plugin.getParameterValue(i))) + " ;\n"; + pluginString += " lv2:minimum " + d_string(int(ranges.min)) + " ;\n"; + pluginString += " lv2:maximum " + d_string(int(ranges.max)) + " ;\n"; + } + else + { + pluginString += " lv2:default " + d_string(plugin.getParameterValue(i)) + " ;\n"; + pluginString += " lv2:minimum " + d_string(ranges.min) + " ;\n"; + pluginString += " lv2:maximum " + d_string(ranges.max) + " ;\n"; + } + } + + // unit + { + const d_string& unit(plugin.getParameterUnit(i)); + + if (! unit.isEmpty()) + { + if (unit == "db" || unit == "dB") + { + pluginString += " unit:unit unit:db ;\n"; + } + else if (unit == "hz" || unit == "Hz") + { + pluginString += " unit:unit unit:hz ;\n"; + } + else if (unit == "khz" || unit == "kHz") + { + pluginString += " unit:unit unit:khz ;\n"; + } + else if (unit == "mhz" || unit == "mHz") + { + pluginString += " unit:unit unit:mhz ;\n"; + } + else if (unit == "%") + { + pluginString += " unit:unit unit:pc ;\n"; + } + else + { + pluginString += " unit:unit [\n"; + pluginString += " a unit:Unit ;\n"; + pluginString += " unit:name \"" + unit + "\" ;\n"; + pluginString += " unit:symbol \"" + unit + "\" ;\n"; + pluginString += " unit:render \"%f " + unit + "\" ;\n"; + pluginString += " ] ;\n"; + } + } + } + + // hints + { + const uint32_t hints(plugin.getParameterHints(i)); + + if (hints & PARAMETER_IS_BOOLEAN) + pluginString += " lv2:portProperty lv2:toggled ;\n"; + if (hints & PARAMETER_IS_INTEGER) + pluginString += " lv2:portProperty lv2:integer ;\n"; + if (hints & PARAMETER_IS_LOGARITHMIC) + pluginString += " lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ;\n"; + if ((hints & PARAMETER_IS_AUTOMABLE) == 0 && ! plugin.isParameterOutput(i)) + pluginString += " lv2:portProperty <http://lv2plug.in/ns/ext/port-props#expensive> ;\n"; + } + + if (i+1 == count) + pluginString += " ] ;\n\n"; + else + pluginString += " ] ,\n"; + } + } + + pluginString += " doap:name \"" + d_string(plugin.getName()) + "\" ;\n"; + pluginString += " doap:maintainer [ foaf:name \"" + d_string(plugin.getMaker()) + "\" ] .\n"; + + pluginFile << pluginString << std::endl; + pluginFile.close(); + std::cout << " done!" << std::endl; + } +} diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp @@ -0,0 +1,963 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPluginInternal.hpp" + +#if DISTRHO_PLUGIN_HAS_UI +# include "DistrhoUIInternal.hpp" +#endif + +#ifndef __cdecl +# define __cdecl +#endif + +// has some conflicts +#ifdef noexcept +# undef noexcept +#endif + +#define VESTIGE_HEADER +#define VST_FORCE_DEPRECATED 0 + +#include <map> +#include <string> + +#ifdef VESTIGE_HEADER +# include "vestige/aeffectx.h" +#define effFlagsProgramChunks (1 << 5) +#define effGetParamLabel 6 +#define effGetParamDisplay 7 +#define effGetChunk 23 +#define effSetChunk 24 +#define effCanBeAutomated 26 +#define effGetProgramNameIndexed 29 +#define effGetPlugCategory 35 +#define kPlugCategEffect 1 +#define kPlugCategSynth 2 +#define kVstVersion 2400 +struct ERect { + int16_t top, left, bottom, right; +}; +#else +# include "vst/aeffectx.h" +#endif + +typedef std::map<d_string,d_string> StringMap; + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +void strncpy(char* const dst, const char* const src, const size_t size) +{ + std::strncpy(dst, src, size); + dst[size] = '\0'; +} + +// ----------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_STATE +class StateHelper +{ +public: + virtual ~StateHelper() {} + virtual void setSharedState(const char* const newKey, const char* const newValue) = 0; +}; +#else +typedef void StateHelper; +#endif + +// ----------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_HAS_UI +class UIVst +{ +public: + UIVst(const audioMasterCallback audioMaster, AEffect* const effect, PluginExporter* const plugin, StateHelper* const stateHelper, const intptr_t winId) + : fAudioMaster(audioMaster), + fEffect(effect), + fPlugin(plugin), + fStateHelper(stateHelper), + fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback), + fParameterChecks(nullptr), + fParameterValues(nullptr) + { + const uint32_t paramCount(plugin->getParameterCount()); + + if (paramCount > 0) + { + fParameterChecks = new bool[paramCount]; + fParameterValues = new float[paramCount]; + + for (uint32_t i=0; i < paramCount; ++i) + { + fParameterChecks[i] = false; + fParameterValues[i] = 0.0f; + } + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + fNextProgram = -1; +#endif + } + + ~UIVst() + { + if (fParameterChecks != nullptr) + { + delete[] fParameterChecks; + fParameterChecks = nullptr; + } + + if (fParameterValues != nullptr) + { + delete[] fParameterValues; + fParameterValues = nullptr; + } + } + + // ------------------------------------------------------------------- + + void idle() + { +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (fNextProgram != -1) + { + fUI.programChanged(fNextProgram); + fNextProgram = -1; + } +#endif + + for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) + { + if (fParameterChecks[i]) + { + fParameterChecks[i] = false; + fUI.parameterChanged(i, fParameterValues[i]); + } + } + + fUI.idle(); + } + + int16_t getWidth() const + { + return fUI.getWidth(); + } + + int16_t getHeight() const + { + return fUI.getHeight(); + } + + // ------------------------------------------------------------------- + // functions called from the plugin side, RT no block + + void setParameterValueFromPlugin(const uint32_t index, const float perValue) + { + fParameterChecks[index] = true; + fParameterValues[index] = perValue; + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void setProgramFromPlugin(const uint32_t index) + { + fNextProgram = index; + + // set previous parameters invalid + for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) + fParameterChecks[i] = false; + } +#endif + + // ------------------------------------------------------------------- + // functions called from the plugin side, block + +#if DISTRHO_PLUGIN_WANT_STATE + void setStateFromPlugin(const char* const key, const char* const value) + { + fUI.stateChanged(key, value); + } +#endif + + // ------------------------------------------------------------------- + +protected: + intptr_t hostCallback(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) + { + return fAudioMaster(fEffect, opcode, index, value, ptr, opt); + } + + void editParameter(const uint32_t index, const bool started) + { + if (started) + hostCallback(audioMasterBeginEdit, index, 0, nullptr, 0.0f); + else + hostCallback(audioMasterEndEdit, index, 0, nullptr, 0.0f); + } + + void setParameterValue(const uint32_t index, const float realValue) + { + const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); + const float perValue(ranges.getNormalizedValue(realValue)); + + fPlugin->setParameterValue(index, realValue); + hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); + } + + void setState(const char* const key, const char* const value) + { +#if DISTRHO_PLUGIN_WANT_STATE + fStateHelper->setSharedState(key, value); +#else + return; // unused + (void)key; + (void)value; +#endif + } + + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { +#if 0 //DISTRHO_PLUGIN_IS_SYNTH + // TODO +#else + return; // unused + (void)channel; + (void)note; + (void)velocity; +#endif + } + + void uiResize(const unsigned int width, const unsigned int height) + { + fUI.setSize(width, height); + hostCallback(audioMasterSizeWindow, width, height, nullptr, 0.0f); + } + +private: + // Vst stuff + const audioMasterCallback fAudioMaster; + AEffect* const fEffect; + PluginExporter* const fPlugin; + StateHelper* const fStateHelper; + + // Plugin UI + UIExporter fUI; + + // Temporary data + bool* fParameterChecks; + float* fParameterValues; +#if DISTRHO_PLUGIN_WANT_PROGRAMS + int32_t fNextProgram; +#endif + + // ------------------------------------------------------------------- + // Callbacks + + #define handlePtr ((UIVst*)ptr) + + static void editParameterCallback(void* ptr, uint32_t index, bool started) + { + handlePtr->editParameter(index, started); + } + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + handlePtr->setParameterValue(rindex, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { + handlePtr->setState(key, value); + } + + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + handlePtr->sendNote(channel, note, velocity); + } + + static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) + { + handlePtr->uiResize(width, height); + } + + #undef handlePtr +}; +#endif + +// ----------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_STATE +class PluginVst : public StateHelper +#else +class PluginVst +#endif +{ +public: + PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) + : fAudioMaster(audioMaster), + fEffect(effect) + { +#if DISTRHO_PLUGIN_WANT_PROGRAMS + fCurProgram = -1; +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + fMidiEventCount = 0; +#endif + +#if DISTRHO_PLUGIN_HAS_UI + fVstUi = nullptr; + fVstRect.top = 0; + fVstRect.left = 0; + fVstRect.bottom = 0; + fVstRect.right = 0; +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + fStateChunk = nullptr; +#endif + } + +#if DISTRHO_PLUGIN_WANT_STATE + ~PluginVst() + { + if (fStateChunk != nullptr) + { + delete[] fStateChunk; + fStateChunk = nullptr; + } + + fStateMap.clear(); + } +#endif + + intptr_t vst_dispatcher(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) + { + int32_t ret = 0; + + switch (opcode) + { +#if DISTRHO_PLUGIN_WANT_PROGRAMS + case effSetProgram: + if (value >= 0 && value < static_cast<intptr_t>(fPlugin.getProgramCount())) + { + fCurProgram = value; + fPlugin.setProgram(fCurProgram); + +#if DISTRHO_PLUGIN_HAS_UI + if (fVstUi != nullptr) + fVstUi->setProgramFromPlugin(fCurProgram); +#endif + + ret = 1; + } + break; + + case effGetProgram: + ret = fCurProgram; + break; + + //case effSetProgramName: + // unsupported + // break; + + case effGetProgramName: + if (ptr != nullptr && fCurProgram >= 0 && fCurProgram < static_cast<int32_t>(fPlugin.getProgramCount())) + { + DISTRHO::strncpy((char*)ptr, fPlugin.getProgramName(fCurProgram), 24); + ret = 1; + } + break; +#endif + + case effGetParamDisplay: + if (ptr != nullptr && index < static_cast<int32_t>(fPlugin.getParameterCount())) + { + char* buf = (char*)ptr; + std::snprintf((char*)ptr, 8, "%f", fPlugin.getParameterValue(index)); + buf[8] = '\0'; + ret = 1; + } + break; + + case effSetSampleRate: + fPlugin.setSampleRate(opt, true); + break; + + case effSetBlockSize: + fPlugin.setBufferSize(value, true); + break; + + case effMainsChanged: + if (value != 0) + { + fPlugin.activate(); +#if DISTRHO_PLUGIN_IS_SYNTH + fMidiEventCount = 0; +#endif + } + else + { + fPlugin.deactivate(); + } + break; + +#if DISTRHO_PLUGIN_HAS_UI + case effEditGetRect: + if (fVstUi != nullptr) + { + fVstRect.right = fVstUi->getWidth(); + fVstRect.bottom = fVstUi->getHeight(); + } + else + { + d_lastUiSampleRate = fAudioMaster(fEffect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); + UIExporter tmpUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr); + fVstRect.right = tmpUI.getWidth(); + fVstRect.bottom = tmpUI.getHeight(); + } + *(ERect**)ptr = &fVstRect; + ret = 1; + break; + + case effEditOpen: + if (fVstUi == nullptr) + { + d_lastUiSampleRate = fAudioMaster(fEffect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); + + fVstUi = new UIVst(fAudioMaster, fEffect, &fPlugin, this, (intptr_t)ptr); + +# if DISTRHO_PLUGIN_WANT_PROGRAMS + if (fCurProgram >= 0) + fVstUi->setProgramFromPlugin(fCurProgram); +# endif + for (uint32_t i=0, count = fPlugin.getParameterCount(); i < count; ++i) + fVstUi->setParameterValueFromPlugin(i, fPlugin.getParameterValue(i)); + + fVstUi->idle(); + +#if DISTRHO_PLUGIN_WANT_STATE + if (fStateMap.size() > 0) + { + for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) + { + const d_string& key = it->first; + const d_string& value = it->second; + + fVstUi->setStateFromPlugin((const char*)key, (const char*)value); + } + + fVstUi->idle(); + } +#endif + ret = 1; + } + break; + + case effEditClose: + if (fVstUi != nullptr) + { + delete fVstUi; + fVstUi = nullptr; + ret = 1; + } + break; + + case effEditIdle: + if (fVstUi != nullptr) + fVstUi->idle(); + break; +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + case effGetChunk: + if (ptr == nullptr) + return 0; + + if (fStateChunk != nullptr) + delete[] fStateChunk; + + if (fStateMap.size() == 0) + { + fStateChunk = new char[1]; + fStateChunk[0] = '\0'; + ret = 1; + } + else + { + std::string tmpStr; + + for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) + { + const d_string& key = it->first; + const d_string& value = it->second; + + tmpStr += (const char*)key; + tmpStr += "\xff"; + tmpStr += (const char*)value; + tmpStr += "\xff"; + } + + const size_t size(tmpStr.size()); + fStateChunk = new char[size]; + std::memcpy(fStateChunk, tmpStr.c_str(), size*sizeof(char)); + + for (size_t i=0; i < size; ++i) + { + if (fStateChunk[i] == '\xff') + fStateChunk[i] = '\0'; + } + + ret = size; + } + + *(void**)ptr = fStateChunk; + break; + + case effSetChunk: + if (value <= 0) + return 0; + if (value == 1) + return 1; + + if (const char* const state = (const char*)ptr) + { + const size_t stateSize = value; + const char* stateKey = state; + const char* stateValue = nullptr; + + for (size_t i=0; i < stateSize; ++i) + { + // find next null char + if (state[i] != '\0') + continue; + + // found, set value + stateValue = &state[i+1]; + setSharedState(stateKey, stateValue); + + if (fVstUi != nullptr) + fVstUi->setStateFromPlugin(stateKey, stateValue); + + // increment text position + i += std::strlen(stateValue) + 2; + + // check if end of data + if (i >= stateSize) + break; + + // get next key + stateKey = &state[i]; + } + + ret = 1; + } + + break; +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + case effProcessEvents: + if (const VstEvents* const events = (const VstEvents*)ptr) + { + if (events->numEvents == 0) + break; + + for (int i=0, count=events->numEvents; i < count; ++i) + { + const VstMidiEvent* const vstMidiEvent((const VstMidiEvent*)events->events[i]); + + if (vstMidiEvent == nullptr) + break; + if (vstMidiEvent->type != kVstMidiType) + continue; + if (fMidiEventCount >= kMaxMidiEvents) + break; + + MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); + midiEvent.frame = vstMidiEvent->deltaFrames; + midiEvent.size = 3; + std::memcpy(midiEvent.buf, vstMidiEvent->midiData, 3*sizeof(uint8_t)); + } + } + break; +#endif + + case effCanBeAutomated: + if (index < static_cast<int32_t>(fPlugin.getParameterCount())) + { + const uint32_t hints(fPlugin.getParameterHints(index)); + + // must be automable, and not output + if ((hints & PARAMETER_IS_AUTOMABLE) != 0 && (hints & PARAMETER_IS_OUTPUT) == 0) + ret = 1; + } + break; + +#if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS) + case effCanDo: + if (const char* const canDo = (const char*)ptr) + { +# if DISTRHO_PLUGIN_IS_SYNTH + if (std::strcmp(canDo, "receiveVstEvents") == 0) + return 1; + if (std::strcmp(canDo, "receiveVstMidiEvent") == 0) + return 1; +# endif +# if DISTRHO_PLUGIN_WANT_TIMEPOS + if (std::strcmp(canDo, "receiveVstTimeInfo") == 0) + return 1; +# endif + } + break; +#endif + + //case effStartProcess: + //case effStopProcess: + // unused + // break; + } + + return ret; + } + + float vst_getParameter(const int32_t index) + { + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + return ranges.getNormalizedValue(fPlugin.getParameterValue(index)); + } + + void vst_setParameter(const int32_t index, const float value) + { + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + const float realValue(ranges.getUnnormalizedValue(value)); + fPlugin.setParameterValue(index, realValue); + +#if DISTRHO_PLUGIN_HAS_UI + if (fVstUi != nullptr) + fVstUi->setParameterValueFromPlugin(index, realValue); +#endif + } + + void vst_processReplacing(float** const inputs, float** const outputs, const int32_t sampleFrames) + { +#if DISTRHO_PLUGIN_WANT_TIMEPOS + if (const VstTimeInfo* const timeInfo = (const VstTimeInfo*)fEffect->dispatcher(fEffect, audioMasterGetTime, 0, kVstTempoValid, nullptr, 0.0f)) + fPlugin.setTimePos((timeInfo->flags & kVstTransportPlaying) != 0, timeInfo->samplePos, timeInfo->tempo); +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + fPlugin.run(inputs, outputs, sampleFrames, fMidiEvents, fMidiEventCount); + fMidiEventCount = 0; +#else + fPlugin.run(inputs, outputs, sampleFrames); +#endif + } + + // ------------------------------------------------------------------- + + friend class UIVst; + +private: + // VST stuff + const audioMasterCallback fAudioMaster; + AEffect* const fEffect; + + // Plugin + PluginExporter fPlugin; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + int32_t fCurProgram; +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + uint32_t fMidiEventCount; + MidiEvent fMidiEvents[kMaxMidiEvents]; +#endif + +#if DISTRHO_PLUGIN_HAS_UI + UIVst* fVstUi; + ERect fVstRect; +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + char* fStateChunk; + StringMap fStateMap; + + void setSharedState(const char* const newKey, const char* const newValue) override + { + fPlugin.setState(newKey, newValue); + + // check if key already exists + for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) + { + const d_string& key = it->first; + + if (key == newKey) + { + it->second = newValue; + return; + } + } + + // add a new one then + d_string d_key(newKey); + fStateMap[d_key] = newValue; + } +#endif +}; + +// ----------------------------------------------------------------------- + +#ifdef VESTIGE_HEADER +# define handlePtr ((PluginVst*)effect->ptr2) +# define validEffect effect != nullptr && effect->ptr2 != nullptr +#else +# define handlePtr ((PluginVst*)effect->resvd2) +# define validEffect effect != nullptr && effect->resvd2 != 0 +#endif + +static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) +{ + // first internal init + bool doInternalInit = (opcode == -1729 && index == 0xdead && value == 0xf00d); + + if (doInternalInit) + { + // set valid but dummy values + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + } + + // Create dummy plugin to get data from + static PluginExporter plugin; + + if (doInternalInit) + { + // unset + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + *(PluginExporter**)ptr = &plugin; + return 0; + } + + // handle base opcodes + switch (opcode) + { + case effOpen: +#ifdef VESTIGE_HEADER + if (effect != nullptr && effect->ptr3 != nullptr) + { + audioMasterCallback audioMaster = (audioMasterCallback)effect->ptr3; +#else + if (effect != nullptr && effect->object != nullptr) + { + audioMasterCallback audioMaster = (audioMasterCallback)effect->object; +#endif + d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); + d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); + PluginVst* const plugin(new PluginVst(audioMaster, effect)); +#ifdef VESTIGE_HEADER + effect->ptr2 = plugin; +#else + effect->resvd2 = (intptr_t)plugin; +#endif + return 1; + } + return 0; + + case effClose: + if (validEffect) + { +#ifdef VESTIGE_HEADER + delete (PluginVst*)effect->ptr2; + effect->ptr2 = nullptr; + effect->ptr3 = nullptr; +#else + delete (PluginVst*)effect->resvd2; + effect->resvd2 = 0; + effect->object = nullptr; +#endif + delete effect; + return 1; + } + return 0; + + case effGetParamLabel: + if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) + { + DISTRHO::strncpy((char*)ptr, plugin.getParameterUnit(index), 8); + return 1; + } + return 0; + + case effGetParamName: + if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) + { + DISTRHO::strncpy((char*)ptr, plugin.getParameterName(index), 8); + return 1; + } + return 0; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + case effGetProgramNameIndexed: + if (ptr != nullptr && index < static_cast<int32_t>(plugin.getProgramCount())) + { + DISTRHO::strncpy((char*)ptr, plugin.getProgramName(index), 24); + return 1; + } + return 0; +#endif + + case effGetPlugCategory: +#if DISTRHO_PLUGIN_IS_SYNTH + return kPlugCategSynth; +#else + return kPlugCategEffect; +#endif + + case effGetEffectName: + if (ptr != nullptr) + { + DISTRHO::strncpy((char*)ptr, plugin.getName(), 64); + return 1; + } + return 0; + + case effGetVendorString: + if (ptr != nullptr) + { + DISTRHO::strncpy((char*)ptr, plugin.getMaker(), 64); + return 1; + } + return 0; + + case effGetProductString: + if (ptr != nullptr) + { + DISTRHO::strncpy((char*)ptr, plugin.getLabel(), 32); + return 1; + } + return 0; + + case effGetVendorVersion: + return plugin.getVersion(); + + case effGetVstVersion: + return kVstVersion; + }; + + // handle advanced opcodes + if (validEffect) + return handlePtr->vst_dispatcher(opcode, index, value, ptr, opt); + + return 0; +} + +static float vst_getParameterCallback(AEffect* effect, int32_t index) +{ + if (validEffect) + return handlePtr->vst_getParameter(index); + return 0.0f; +} + +static void vst_setParameterCallback(AEffect* effect, int32_t index, float value) +{ + if (validEffect) + handlePtr->vst_setParameter(index, value); +} + +static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) +{ + if (validEffect) + handlePtr->vst_processReplacing(inputs, outputs, sampleFrames); +} + +static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) +{ + if (validEffect) + handlePtr->vst_processReplacing(inputs, outputs, sampleFrames); +} + +#undef handlePtr + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +DISTRHO_PLUGIN_EXPORT +const AEffect* VSTPluginMain(audioMasterCallback audioMaster) +{ + USE_NAMESPACE_DISTRHO + + // old version + if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0) + return nullptr; + + // first internal init + PluginExporter* plugin = nullptr; + vst_dispatcherCallback(nullptr, -1729, 0xdead, 0xf00d, &plugin, 0.0f); + + AEffect* const effect(new AEffect); + std::memset(effect, 0, sizeof(AEffect)); + + // vst fields + effect->magic = kEffectMagic; + effect->uniqueID = plugin->getUniqueId(); +#ifdef VESTIGE_HEADER + *(int32_t*)&effect->unknown1 = plugin->getVersion(); +#else + effect->version = plugin->getVersion(); +#endif + + // plugin fields + effect->numParams = plugin->getParameterCount(); +#if DISTRHO_PLUGIN_WANT_PROGRAMS + effect->numPrograms = plugin->getProgramCount(); +#endif + effect->numInputs = DISTRHO_PLUGIN_NUM_INPUTS; + effect->numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS; + + // plugin flags + effect->flags |= effFlagsCanReplacing; +#if DISTRHO_PLUGIN_IS_SYNTH + effect->flags |= effFlagsIsSynth; +#endif +#if DISTRHO_PLUGIN_HAS_UI + effect->flags |= effFlagsHasEditor; +#endif +#if DISTRHO_PLUGIN_WANT_STATE + effect->flags |= effFlagsProgramChunks; +#endif + + // static calls + effect->dispatcher = vst_dispatcherCallback; + effect->process = vst_processCallback; + effect->getParameter = vst_getParameterCallback; + effect->setParameter = vst_setParameterCallback; + effect->processReplacing = vst_processReplacingCallback; + + // pointers +#ifdef VESTIGE_HEADER + effect->ptr3 = (void*)audioMaster; +#else + effect->object = (void*)audioMaster; +#endif + + return effect; +} diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp @@ -0,0 +1,89 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoUIInternal.hpp" + +START_NAMESPACE_DGL +extern Window* dgl_lastUiParent; +END_NAMESPACE_DGL + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Static data + +double d_lastUiSampleRate = 0.0; + +// ----------------------------------------------------------------------- +// UI + +UI::UI() + : DGL::Widget(*DGL::dgl_lastUiParent), + pData(new PrivateData()) +{ + assert(DGL::dgl_lastUiParent != nullptr); + + DGL::dgl_lastUiParent = nullptr; +} + +UI::~UI() +{ + delete pData; +} + +// ----------------------------------------------------------------------- +// Host DSP State + +double UI::d_getSampleRate() const noexcept +{ + return pData->sampleRate; +} + +void UI::d_editParameter(uint32_t index, bool started) +{ + pData->editParamCallback(index + pData->parameterOffset, started); +} + +void UI::d_setParameterValue(uint32_t index, float value) +{ + pData->setParamCallback(index + pData->parameterOffset, value); +} + +#if DISTRHO_PLUGIN_WANT_STATE +void UI::d_setState(const char* key, const char* value) +{ + pData->setStateCallback(key, value); +} +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH +void UI::d_sendNote(uint8_t channel, uint8_t note, uint8_t velocity) +{ + pData->sendNoteCallback(channel, note, velocity); +} +#endif + +// ----------------------------------------------------------------------- +// Host UI State + +void UI::d_uiResize(unsigned int width, unsigned int height) +{ + pData->uiResizeCallback(width, height); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp @@ -0,0 +1,489 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoUIInternal.hpp" + +#include <lo/lo.h> + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +struct OscData { + lo_address addr; + const char* path; + lo_server server; + + OscData() + : addr(nullptr), + path(nullptr), + server(nullptr) {} + + void idle() const + { + if (server == nullptr) + return; + + while (lo_server_recv_noblock(server, 0) != 0) {} + } + + void send_configure(const char* const key, const char* const value) const + { + char targetPath[std::strlen(path)+11]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/configure"); + lo_send(addr, targetPath, "ss", key, value); + } + + void send_control(const int32_t index, const float value) const + { + char targetPath[std::strlen(path)+9]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/control"); + lo_send(addr, targetPath, "if", index, value); + } + + void send_midi(unsigned char data[4]) const + { + char targetPath[std::strlen(path)+6]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/midi"); + lo_send(addr, targetPath, "m", data); + } + + void send_update(const char* const url) const + { + char targetPath[std::strlen(path)+8]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/update"); + lo_send(addr, targetPath, "s", url); + } + + void send_exiting() const + { + char targetPath[std::strlen(path)+9]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/exiting"); + lo_send(addr, targetPath, ""); + } +}; + +// ----------------------------------------------------------------------- + +class UIDssi +{ +public: + UIDssi(const OscData& oscData, const char* const uiTitle) + : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback), + fHostClosed(false), + fOscData(oscData) + { + fUI.setTitle(uiTitle); + } + + ~UIDssi() + { + if (fOscData.server != nullptr && ! fHostClosed) + fOscData.send_exiting(); + } + + void exec() + { + for (;;) + { + fOscData.idle(); + + if (! fUI.idle()) + break; + + d_msleep(50); + } + } + + // ------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_STATE + void dssiui_configure(const char* key, const char* value) + { + fUI.stateChanged(key, value); + } +#endif + + void dssiui_control(unsigned long index, float value) + { + fUI.parameterChanged(index, value); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void dssiui_program(unsigned long bank, unsigned long program) + { + fUI.programChanged(bank * 128 + program); + } +#endif + + void dssiui_show() + { + fUI.setVisible(true); + } + + void dssiui_hide() + { + fUI.setVisible(false); + } + + void dssiui_quit() + { + fHostClosed = true; + fUI.quit(); + } + + // ------------------------------------------------------------------- + +protected: + void setParameterValue(const uint32_t rindex, const float value) + { + if (fOscData.server == nullptr) + return; + + fOscData.send_control(rindex, value); + } + + void setState(const char* const key, const char* const value) + { + if (fOscData.server == nullptr) + return; + + fOscData.send_configure(key, value); + } + + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + if (fOscData.server == nullptr) + return; + if (channel > 0xF) + return; + + uint8_t mdata[4] = { 0, channel, note, velocity }; + mdata[1] += (velocity != 0) ? 0x90 : 0x80; + + fOscData.send_midi(mdata); + } + + void uiResize(const unsigned int width, const unsigned int height) + { + fUI.setSize(width, height); + } + +private: + UIExporter fUI; + bool fHostClosed; + + const OscData& fOscData; + + // ------------------------------------------------------------------- + // Callbacks + + #define uiPtr ((UIDssi*)ptr) + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + uiPtr->setParameterValue(rindex, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { + uiPtr->setState(key, value); + } + + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + uiPtr->sendNote(channel, note, velocity); + } + + static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) + { + uiPtr->uiResize(width, height); + } + + #undef uiPtr +}; + +// ----------------------------------------------------------------------- + +static OscData gOscData; +static const char* gUiTitle = nullptr; +static UIDssi* globalUI = nullptr; + +static void initUiIfNeeded() +{ + if (globalUI != nullptr) + return; + + if (d_lastUiSampleRate == 0.0) + d_lastUiSampleRate = 44100.0; + + globalUI = new UIDssi(gOscData, gUiTitle); +} + +// ----------------------------------------------------------------------- + +int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_debug_handler(\"%s\")", path); + return 0; +} + +void osc_error_handler(int num, const char* msg, const char* path) +{ + d_stderr("osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); +} + +#if DISTRHO_PLUGIN_WANT_STATE +int osc_configure_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + const char* const key = &argv[0]->s; + const char* const value = &argv[1]->s; + d_debug("osc_configure_handler(\"%s\", \"%s\")", key, value); + + initUiIfNeeded(); + + globalUI->dssiui_configure(key, value); + + return 0; +} +#endif + +int osc_control_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + const int32_t rindex = argv[0]->i; + const float value = argv[1]->f; + d_debug("osc_control_handler(%i, %f)", rindex, value); + + int32_t index = rindex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS; + + // latency +#if DISTRHO_PLUGIN_WANT_LATENCY + index -= 1; +#endif + + if (index < 0) + return 0; + + initUiIfNeeded(); + + globalUI->dssiui_control(index, value); + + return 0; +} + +#if DISTRHO_PLUGIN_WANT_PROGRAMS +int osc_program_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + const int32_t bank = argv[0]->i; + const int32_t program = argv[1]->f; + d_debug("osc_program_handler(%i, %i)", bank, program); + + initUiIfNeeded(); + + globalUI->dssiui_program(bank, program); + + return 0; +} +#endif + +int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + const int32_t sampleRate = argv[0]->i; + d_debug("osc_sample_rate_handler(%i)", sampleRate); + + d_lastUiSampleRate = sampleRate; + + return 0; +} + +int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_show_handler()"); + + initUiIfNeeded(); + + globalUI->dssiui_show(); + + return 0; +} + +int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_hide_handler()"); + + if (globalUI != nullptr) + globalUI->dssiui_hide(); + + return 0; +} + +int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_quit_handler()"); + + if (globalUI != nullptr) + globalUI->dssiui_quit(); + + return 0; +} + +END_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + USE_NAMESPACE_DISTRHO + + // dummy test mode + if (argc == 1) + { + gUiTitle = "DSSI UI Test"; + + initUiIfNeeded(); + globalUI->dssiui_show(); + globalUI->exec(); + + return 0; + } + + if (argc != 5) + { + fprintf(stderr, "Usage: %s <osc-url> <plugin-dll> <plugin-label> <instance-name>\n", argv[0]); + return 1; + } + + const char* oscUrl = argv[1]; + const char* uiTitle = argv[4]; + + char* const oscHost = lo_url_get_hostname(oscUrl); + char* const oscPort = lo_url_get_port(oscUrl); + char* const oscPath = lo_url_get_path(oscUrl); + size_t oscPathSize = strlen(oscPath); + lo_address oscAddr = lo_address_new(oscHost, oscPort); + lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, osc_error_handler); + + char* const oscServerPath = lo_server_get_url(oscServer); + + char pluginPath[strlen(oscServerPath)+oscPathSize]; + strcpy(pluginPath, oscServerPath); + strcat(pluginPath, oscPath+1); + +#if DISTRHO_PLUGIN_WANT_STATE + char oscPathConfigure[oscPathSize+11]; + strcpy(oscPathConfigure, oscPath); + strcat(oscPathConfigure, "/configure"); + lo_server_add_method(oscServer, oscPathConfigure, "ss", osc_configure_handler, nullptr); +#endif + + char oscPathControl[oscPathSize+9]; + strcpy(oscPathControl, oscPath); + strcat(oscPathControl, "/control"); + lo_server_add_method(oscServer, oscPathControl, "if", osc_control_handler, nullptr); + + d_stdout("oscServerPath: \"%s\"", oscServerPath); + d_stdout("pluginPath: \"%s\"", pluginPath); + d_stdout("oscPathControl: \"%s\"", oscPathControl); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + char oscPathProgram[oscPathSize+9]; + strcpy(oscPathProgram, oscPath); + strcat(oscPathProgram, "/program"); + lo_server_add_method(oscServer, oscPathProgram, "ii", osc_program_handler, nullptr); +#endif + + char oscPathSampleRate[oscPathSize+13]; + strcpy(oscPathSampleRate, oscPath); + strcat(oscPathSampleRate, "/sample-rate"); + lo_server_add_method(oscServer, oscPathSampleRate, "i", osc_sample_rate_handler, nullptr); + + char oscPathShow[oscPathSize+6]; + strcpy(oscPathShow, oscPath); + strcat(oscPathShow, "/show"); + lo_server_add_method(oscServer, oscPathShow, "", osc_show_handler, nullptr); + + char oscPathHide[oscPathSize+6]; + strcpy(oscPathHide, oscPath); + strcat(oscPathHide, "/hide"); + lo_server_add_method(oscServer, oscPathHide, "", osc_hide_handler, nullptr); + + char oscPathQuit[oscPathSize+6]; + strcpy(oscPathQuit, oscPath); + strcat(oscPathQuit, "/quit"); + lo_server_add_method(oscServer, oscPathQuit, "", osc_quit_handler, nullptr); + + lo_server_add_method(oscServer, nullptr, nullptr, osc_debug_handler, nullptr); + + gUiTitle = uiTitle; + + gOscData.addr = oscAddr; + gOscData.path = oscPath; + gOscData.server = oscServer; + gOscData.send_update(pluginPath); + + // wait for init + for (int i=0; i < 100; ++i) + { + lo_server_recv(oscServer); + + if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) + break; + + d_msleep(50); + } + + int ret = 1; + + if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) + { + initUiIfNeeded(); + + globalUI->exec(); + + delete globalUI; + globalUI = nullptr; + + ret = 0; + } + +#if DISTRHO_PLUGIN_WANT_STATE + lo_server_del_method(oscServer, oscPathConfigure, "ss"); +#endif + lo_server_del_method(oscServer, oscPathControl, "if"); +#if DISTRHO_PLUGIN_WANT_PROGRAMS + lo_server_del_method(oscServer, oscPathProgram, "ii"); +#endif + lo_server_del_method(oscServer, oscPathSampleRate, "i"); + lo_server_del_method(oscServer, oscPathShow, ""); + lo_server_del_method(oscServer, oscPathHide, ""); + lo_server_del_method(oscServer, oscPathQuit, ""); + lo_server_del_method(oscServer, nullptr, nullptr); + + std::free(oscServerPath); + std::free(oscHost); + std::free(oscPort); + std::free(oscPath); + + lo_address_free(oscAddr); + lo_server_free(oscServer); + + return ret; +} diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp @@ -0,0 +1,249 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_UI_INTERNAL_HPP_INCLUDED +#define DISTRHO_UI_INTERNAL_HPP_INCLUDED + +#include "../DistrhoUI.hpp" + +#include "../../dgl/App.hpp" +#include "../../dgl/Window.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Static data, see DistrhoUI.cpp + +extern double d_lastUiSampleRate; + +// ----------------------------------------------------------------------- +// UI callbacks + +typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); +typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); +typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); +typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); +typedef void (*uiResizeFunc) (void* ptr, unsigned int width, unsigned int height); + +// ----------------------------------------------------------------------- +// UI private data + +struct UI::PrivateData { + // DSP + double sampleRate; + uint32_t parameterOffset; + + // Callbacks + editParamFunc editParamCallbackFunc; + setParamFunc setParamCallbackFunc; + setStateFunc setStateCallbackFunc; + sendNoteFunc sendNoteCallbackFunc; + uiResizeFunc uiResizeCallbackFunc; + void* ptr; + + PrivateData() noexcept + : sampleRate(d_lastUiSampleRate), + parameterOffset(0), + editParamCallbackFunc(nullptr), + setParamCallbackFunc(nullptr), + setStateCallbackFunc(nullptr), + sendNoteCallbackFunc(nullptr), + uiResizeCallbackFunc(nullptr), + ptr(nullptr) + { + assert(sampleRate != 0.0); + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) + parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; +# if DISTRHO_PLUGIN_WANT_LATENCY + parameterOffset += 1; +# endif +#endif +#ifdef DISTRHO_PLUGIN_TARGET_LV2 +# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) + parameterOffset += 1; +# if DISTRHO_PLUGIN_WANT_STATE + parameterOffset += 1; +# endif +# endif +#endif + } + + void editParamCallback(const uint32_t rindex, const bool started) + { + if (editParamCallbackFunc != nullptr) + editParamCallbackFunc(ptr, rindex, started); + } + + void setParamCallback(const uint32_t rindex, const float value) + { + if (setParamCallbackFunc != nullptr) + setParamCallbackFunc(ptr, rindex, value); + } + + void setStateCallback(const char* const key, const char* const value) + { + if (setStateCallbackFunc != nullptr) + setStateCallbackFunc(ptr, key, value); + } + + void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + if (sendNoteCallbackFunc != nullptr) + sendNoteCallbackFunc(ptr, channel, note, velocity); + } + + void uiResizeCallback(const unsigned int width, const unsigned int height) + { + if (uiResizeCallbackFunc != nullptr) + uiResizeCallbackFunc(ptr, width, height); + } +}; + +// ----------------------------------------------------------------------- +// UI exporter class + +class UIExporter +{ +public: + UIExporter(void* const ptr, const intptr_t winId, + const editParamFunc editParamCall, const setParamFunc setParamCall, const setStateFunc setStateCall, const sendNoteFunc sendNoteCall, const uiResizeFunc uiResizeCall) + : glApp(), + glWindow(glApp, winId), + fUi(createUI()), + fData((fUi != nullptr) ? fUi->pData : nullptr) + { + assert(fUi != nullptr); + + if (fUi == nullptr) + return; + + fData->ptr = ptr; + fData->editParamCallbackFunc = editParamCall; + fData->setParamCallbackFunc = setParamCall; + fData->setStateCallbackFunc = setStateCall; + fData->sendNoteCallbackFunc = sendNoteCall; + fData->uiResizeCallbackFunc = uiResizeCall; + + glWindow.setSize(fUi->d_getWidth(), fUi->d_getHeight()); + glWindow.setResizable(false); + } + + ~UIExporter() + { + delete fUi; + } + + // ------------------------------------------------------------------- + + const char* getName() const noexcept + { + return (fUi != nullptr) ? fUi->d_getName() : ""; + } + + unsigned int getWidth() const noexcept + { + return (fUi != nullptr) ? fUi->d_getWidth() : 0; + } + + unsigned int getHeight() const noexcept + { + return (fUi != nullptr) ? fUi->d_getHeight() : 0; + } + + // ------------------------------------------------------------------- + + uint32_t getParameterOffset() const noexcept + { + return (fData != nullptr) ? fData->parameterOffset : 0; + } + + // ------------------------------------------------------------------- + + void parameterChanged(const uint32_t index, const float value) + { + if (fUi != nullptr) + fUi->d_parameterChanged(index, value); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void programChanged(const uint32_t index) + { + if (fUi != nullptr) + fUi->d_programChanged(index); + } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + void stateChanged(const char* const key, const char* const value) + { + if (fUi != nullptr) + fUi->d_stateChanged(key, value); + } +#endif + + // ------------------------------------------------------------------- + + bool idle() + { + if (fUi != nullptr) + fUi->d_uiIdle(); + + glApp.idle(); + + return ! glApp.isQuiting(); + } + + void quit() + { + glWindow.close(); + glApp.quit(); + } + + void setSize(const unsigned int width, const unsigned int height) + { + glWindow.setSize(width, height); + } + + void setTitle(const char* const uiTitle) + { + glWindow.setTitle(uiTitle); + } + + void setVisible(const bool yesNo) + { + glWindow.setVisible(yesNo); + } + +private: + // ------------------------------------------------------------------- + // DGL Application and Window for this plugin + + DGL::App glApp; + DGL::Window glWindow; + + // ------------------------------------------------------------------- + // private members accessed by DistrhoPlugin class + + UI* const fUi; + UI::PrivateData* const fData; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_INTERNAL_HPP_INCLUDED diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp @@ -0,0 +1,333 @@ +/* + * DISTRHO Plugin Toolkit (DPT) + * Copyright (C) 2012-2013 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 + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoUIInternal.hpp" + +#include "lv2/atom.h" +#include "lv2/atom-util.h" +#include "lv2/options.h" +#include "lv2/ui.h" +#include "lv2/urid.h" +#include "lv2/lv2_programs.h" + +#include <string> + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +class UiLv2 +{ +public: + UiLv2(const intptr_t winId, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch, const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc) + : fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback), + fUridMap(uridMap), + fUiResize(uiResz), + fUiTouch(uiTouch), + fController(controller), + fWriteFunction(writeFunc) + { + fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight()); + } + + // ------------------------------------------------------------------- + + void lv2ui_port_event(const uint32_t rindex, const uint32_t bufferSize, const uint32_t format, const void* const buffer) + { + if (format == 0) + { + const uint32_t parameterOffset(fUI.getParameterOffset()); + + if (rindex < parameterOffset) + return; + if (bufferSize != sizeof(float)) + return; + + const float value(*(const float*)buffer); + fUI.parameterChanged(rindex-parameterOffset, value); + } + else + { + //fUI.stateChanged(key, value); + } + } + + // ------------------------------------------------------------------- + + int lv2ui_idle() + { + fUI.idle(); + return 0; + } + + // ------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void lv2ui_select_program(const uint32_t bank, const uint32_t program) + { + const uint32_t realProgram(bank * 128 + program); + + fUI.programChanged(realProgram); + } +#endif + + // ------------------------------------------------------------------- + +protected: + void editParameterValue(const uint32_t rindex, const bool started) + { + if (fUiTouch != nullptr && fUiTouch->touch != nullptr) + fUiTouch->touch(fUiTouch->handle, rindex, started); + } + + void setParameterValue(const uint32_t rindex, const float value) + { + if (fWriteFunction != nullptr) + fWriteFunction(fController, rindex, sizeof(float), 0, &value); + } + + void setState(const char* const key, const char* const value) + { + if (fWriteFunction == nullptr) + return; + + const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); + + // join key and value + std::string tmpStr; + tmpStr += std::string(key); + tmpStr += std::string("\0", 1); + tmpStr += std::string(value); + + // get msg size + const size_t msgSize(tmpStr.size()+1); + + // reserve atom space + const size_t atomSize(lv2_atom_pad_size(sizeof(LV2_Atom) + msgSize)); + char atomBuf[atomSize]; + std::memset(atomBuf, 0, atomSize); + + // set atom info + LV2_Atom* const atom((LV2_Atom*)atomBuf); + atom->size = msgSize; + atom->type = fUridMap->map(fUridMap->handle, "urn:distrho:keyValueState"); + + // set atom data + std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.data(), msgSize-1); + + // send to DSP side + fWriteFunction(fController, eventInPortIndex, atomSize, fUridMap->map(fUridMap->handle, LV2_ATOM__eventTransfer), atom); + } + + void sendNote(const uint8_t /*channel*/, const uint8_t /*note*/, const uint8_t /*velocity*/) + { + } + + void uiResize(const unsigned int width, const unsigned int height) + { + fUI.setSize(width, height); + fUiResize->ui_resize(fUiResize->handle, width, height); + } + +private: + UIExporter fUI; + + // LV2 features + const LV2_URID_Map* const fUridMap; + const LV2UI_Resize* const fUiResize; + const LV2UI_Touch* const fUiTouch; + + // LV2 UI stuff + const LV2UI_Controller fController; + const LV2UI_Write_Function fWriteFunction; + + // ------------------------------------------------------------------- + // Callbacks + + #define uiPtr ((UiLv2*)ptr) + + static void editParameterCallback(void* ptr, uint32_t rindex, bool started) + { + uiPtr->editParameterValue(rindex, started); + } + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + uiPtr->setParameterValue(rindex, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { + uiPtr->setState(key, value); + } + + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + uiPtr->sendNote(channel, note, velocity); + } + + static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) + { + uiPtr->uiResize(width, height); + } + + #undef uiPtr +}; + +// ----------------------------------------------------------------------- + +static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char*, LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0) + { + d_stderr("Invalid plugin URI"); + return nullptr; + } + + const LV2_Options_Option* options = nullptr; + const LV2_URID_Map* uridMap = nullptr; + const LV2UI_Resize* uiResize = nullptr; + const LV2UI_Touch* uiTouch = nullptr; + void* parentId = nullptr; + + for (int i=0; features[i] != nullptr; ++i) + { + if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) + options = (const LV2_Options_Option*)features[i]->data; + else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) + uridMap = (const LV2_URID_Map*)features[i]->data; + else if (std::strcmp(features[i]->URI, LV2_UI__resize) == 0) + uiResize = (const LV2UI_Resize*)features[i]->data; + else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0) + parentId = features[i]->data; + } + + if (options == nullptr) + { + d_stderr("Options feature missing, cannot continue!"); + return nullptr; + } + + if (uridMap == nullptr) + { + d_stderr("URID Map feature missing, cannot continue!"); + return nullptr; + } + + if (uiResize == nullptr) + { + d_stderr("UI Resize feature missing, cannot continue!"); + return nullptr; + } + + if (parentId == nullptr) + { + d_stderr("Parent Window Id missing, cannot continue!"); + return nullptr; + } + + *widget = parentId; + + const intptr_t winId(*((intptr_t*)&parentId)); + + for (int i=0; options[i].key != 0; ++i) + { + if (options[i].key == uridMap->map(uridMap->handle, LV2_CORE__sampleRate)) + { + if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Double)) + d_lastUiSampleRate = *(const double*)options[i].value; + else + d_stderr("Host provides sampleRate but has wrong value type"); + + break; + } + } + + if (d_lastUiSampleRate == 0.0) + d_lastUiSampleRate = 44100.0; + + return new UiLv2(winId, uridMap, uiResize, uiTouch, controller, writeFunction); +} + +#define uiPtr ((UiLv2*)ui) + +static void lv2ui_cleanup(LV2UI_Handle ui) +{ + delete uiPtr; +} + +static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) +{ + uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); +} + +// ----------------------------------------------------------------------- + +static int lv2ui_idle(LV2UI_Handle ui) +{ + return uiPtr->lv2ui_idle(); +} + +#if DISTRHO_PLUGIN_WANT_PROGRAMS +static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program) +{ + uiPtr->lv2ui_select_program(bank, program); +} +#endif + +// ----------------------------------------------------------------------- + +static const void* lv2ui_extension_data(const char* uri) +{ + static const LV2UI_Idle_Interface uiIdle = { lv2ui_idle }; + + if (std::strcmp(uri, LV2_UI__idleInterface) == 0) + return &uiIdle; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program }; + + if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0) + return &uiPrograms; +#endif + + return nullptr; +} + +#undef instancePtr + +// ----------------------------------------------------------------------- + +static const LV2UI_Descriptor sLv2UiDescriptor = { + DISTRHO_UI_URI, + lv2ui_instantiate, + lv2ui_cleanup, + lv2ui_port_event, + lv2ui_extension_data +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +DISTRHO_PLUGIN_EXPORT +const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &sLv2UiDescriptor : nullptr; +} + +// ----------------------------------------------------------------------- diff --git a/distrho/src/dssi/dssi.h b/distrho/src/dssi/dssi.h @@ -0,0 +1,441 @@ +/* -*- c-basic-offset: 4 -*- */ + +/* dssi.h + + DSSI version 1.0 + Copyright (c) 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef DSSI_INCLUDED +#define DSSI_INCLUDED + +#include "../ladspa/ladspa.h" +#include "seq_event-compat.h" + +#define DSSI_VERSION "1.0" +#define DSSI_VERSION_MAJOR 1 +#define DSSI_VERSION_MINOR 0 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + There is a need for an API that supports hosted MIDI soft synths + with GUIs in Linux audio applications. In time the GMPI initiative + should comprehensively address this need, but the requirement for + Linux applications to be able to support simple hosted synths is + here now, and GMPI is not. This proposal (the "DSSI Soft Synth + Interface" or DSSI, pronounced "dizzy") aims to provide a simple + solution in a way that we hope will prove complete and compelling + enough to support now, yet not so compelling as to supplant GMPI or + any other comprehensive future proposal. + + For simplicity and familiarity, this API is based as far as + possible on existing work -- the LADSPA plugin API for control + values and audio processing, and the ALSA sequencer event types for + MIDI event communication. The GUI part of the proposal is quite + new, but may also be applicable retroactively to LADSPA plugins + that do not otherwise support this synth interface. +*/ + +typedef struct _DSSI_Program_Descriptor { + + /** Bank number for this program. Note that DSSI does not support + MIDI-style separation of bank LSB and MSB values. There is no + restriction on the set of available banks: the numbers do not + need to be contiguous, there does not need to be a bank 0, etc. */ + unsigned long Bank; + + /** Program number (unique within its bank) for this program. + There is no restriction on the set of available programs: the + numbers do not need to be contiguous, there does not need to + be a program 0, etc. */ + unsigned long Program; + + /** Name of the program. */ + const char * Name; + +} DSSI_Program_Descriptor; + + +typedef struct _DSSI_Descriptor { + + /** + * DSSI_API_Version + * + * This member indicates the DSSI API level used by this plugin. + * If we're lucky, this will never be needed. For now all plugins + * must set it to 1. + */ + int DSSI_API_Version; + + /** + * LADSPA_Plugin + * + * A DSSI synth plugin consists of a LADSPA plugin plus an + * additional framework for controlling program settings and + * transmitting MIDI events. A plugin must fully implement the + * LADSPA descriptor fields as well as the required LADSPA + * functions including instantiate() and (de)activate(). It + * should also implement run(), with the same behaviour as if + * run_synth() (below) were called with no synth events. + * + * In order to instantiate a synth the host calls the LADSPA + * instantiate function, passing in this LADSPA_Descriptor + * pointer. The returned LADSPA_Handle is used as the argument + * for the DSSI functions below as well as for the LADSPA ones. + */ + const LADSPA_Descriptor *LADSPA_Plugin; + + /** + * configure() + * + * This member is a function pointer that sends a piece of + * configuration data to the plugin. The key argument specifies + * some aspect of the synth's configuration that is to be changed, + * and the value argument specifies a new value for it. A plugin + * that does not require this facility at all may set this member + * to NULL. + * + * This call is intended to set some session-scoped aspect of a + * plugin's behaviour, for example to tell the plugin to load + * sample data from a particular file. The plugin should act + * immediately on the request. The call should return NULL on + * success, or an error string that may be shown to the user. The + * host will free the returned value after use if it is non-NULL. + * + * Calls to configure() are not automated as timed events. + * Instead, a host should remember the last value associated with + * each key passed to configure() during a given session for a + * given plugin instance, and should call configure() with the + * correct value for each key the next time it instantiates the + * "same" plugin instance, for example on reloading a project in + * which the plugin was used before. Plugins should note that a + * host may typically instantiate a plugin multiple times with the + * same configuration values, and should share data between + * instances where practical. + * + * Calling configure() completely invalidates the program and bank + * information last obtained from the plugin. + * + * Reserved and special key prefixes + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The DSSI: prefix + * ---------------- + * Configure keys starting with DSSI: are reserved for particular + * purposes documented in the DSSI specification. At the moment, + * there is one such key: DSSI:PROJECT_DIRECTORY. A host may call + * configure() passing this key and a directory path value. This + * indicates to the plugin and its UI that a directory at that + * path exists and may be used for project-local data. Plugins + * may wish to use the project directory as a fallback location + * when looking for other file data, or as a base for relative + * paths in other configuration values. + * + * The GLOBAL: prefix + * ------------------ + * Configure keys starting with GLOBAL: may be used by the plugin + * and its UI for any purpose, but are treated specially by the + * host. When one of these keys is used in a configure OSC call + * from the plugin UI, the host makes the corresponding configure + * call (preserving the GLOBAL: prefix) not only to the target + * plugin but also to all other plugins in the same instance + * group, as well as their UIs. Note that if any instance + * returns non-NULL from configure to indicate error, the host + * may stop there (and the set of plugins on which configure has + * been called will thus depend on the host implementation). + * See also the configure OSC call documentation in RFC.txt. + */ + char *(*configure)(LADSPA_Handle Instance, + const char *Key, + const char *Value); + + #define DSSI_RESERVED_CONFIGURE_PREFIX "DSSI:" + #define DSSI_GLOBAL_CONFIGURE_PREFIX "GLOBAL:" + #define DSSI_PROJECT_DIRECTORY_KEY \ + DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY" + + /** + * get_program() + * + * This member is a function pointer that provides a description + * of a program (named preset sound) available on this synth. A + * plugin that does not support programs at all should set this + * member to NULL. + * + * The Index argument is an index into the plugin's list of + * programs, not a program number as represented by the Program + * field of the DSSI_Program_Descriptor. (This distinction is + * needed to support synths that use non-contiguous program or + * bank numbers.) + * + * This function returns a DSSI_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program, + * deactivate, or configure, on the same plugin instance. This + * function must return NULL if passed an Index argument out of + * range, so that the host can use it to query the number of + * programs as well as their properties. + */ + const DSSI_Program_Descriptor *(*get_program)(LADSPA_Handle Instance, + unsigned long Index); + + /** + * select_program() + * + * This member is a function pointer that selects a new program + * for this synth. The program change should take effect + * immediately at the start of the next run_synth() call. (This + * means that a host providing the capability of changing programs + * between any two notes on a track must vary the block size so as + * to place the program change at the right place. A host that + * wanted to avoid this would probably just instantiate a plugin + * for each program.) + * + * A plugin that does not support programs at all should set this + * member NULL. Plugins should ignore a select_program() call + * with an invalid bank or program. + * + * A plugin is not required to select any particular default + * program on activate(): it's the host's duty to set a program + * explicitly. The current program is invalidated by any call to + * configure(). + * + * A plugin is permitted to re-write the values of its input + * control ports when select_program is called. The host should + * re-read the input control port values and update its own + * records appropriately. (This is the only circumstance in + * which a DSSI plugin is allowed to modify its own input ports.) + */ + void (*select_program)(LADSPA_Handle Instance, + unsigned long Bank, + unsigned long Program); + + /** + * get_midi_controller_for_port() + * + * This member is a function pointer that returns the MIDI + * controller number or NRPN that should be mapped to the given + * input control port. If the given port should not have any MIDI + * controller mapped to it, the function should return DSSI_NONE. + * The behaviour of this function is undefined if the given port + * number does not correspond to an input control port. A plugin + * that does not want MIDI controllers mapped to ports at all may + * set this member NULL. + * + * Correct values can be got using the macros DSSI_CC(num) and + * DSSI_NRPN(num) as appropriate, and values can be combined using + * bitwise OR: e.g. DSSI_CC(23) | DSSI_NRPN(1069) means the port + * should respond to CC #23 and NRPN #1069. + * + * The host is responsible for doing proper scaling from MIDI + * controller and NRPN value ranges to port ranges according to + * the plugin's LADSPA port hints. Hosts should not deliver + * through run_synth any MIDI controller events that have already + * been mapped to control port values. + * + * A plugin should not attempt to request mappings from + * controllers 0 or 32 (MIDI Bank Select MSB and LSB). + */ + int (*get_midi_controller_for_port)(LADSPA_Handle Instance, + unsigned long Port); + + /** + * run_synth() + * + * This member is a function pointer that runs a synth for a + * block. This is identical in function to the LADSPA run() + * function, except that it also supplies events to the synth. + * + * A plugin may provide this function, run_multiple_synths() (see + * below), both, or neither (if it is not in fact a synth). A + * plugin that does not provide this function must set this member + * to NULL. Authors of synth plugins are encouraged to provide + * this function if at all possible. + * + * The Events pointer points to a block of EventCount ALSA + * sequencer events, which is used to communicate MIDI and related + * events to the synth. Each event is timestamped relative to the + * start of the block, (mis)using the ALSA "tick time" field as a + * frame count. The host is responsible for ensuring that events + * with differing timestamps are already ordered by time. + * + * See also the notes on activation, port connection etc in + * ladpsa.h, in the context of the LADSPA run() function. + * + * Note Events + * ~~~~~~~~~~~ + * There are two minor requirements aimed at making the plugin + * writer's life as simple as possible: + * + * 1. A host must never send events of type SND_SEQ_EVENT_NOTE. + * Notes should always be sent as separate SND_SEQ_EVENT_NOTE_ON + * and NOTE_OFF events. A plugin should discard any one-point + * NOTE events it sees. + * + * 2. A host must not attempt to switch notes off by sending + * zero-velocity NOTE_ON events. It should always send true + * NOTE_OFFs. It is the host's responsibility to remap events in + * cases where an external MIDI source has sent it zero-velocity + * NOTE_ONs. + * + * Bank and Program Events + * ~~~~~~~~~~~~~~~~~~~~~~~ + * Hosts must map MIDI Bank Select MSB and LSB (0 and 32) + * controllers and MIDI Program Change events onto the banks and + * programs specified by the plugin, using the DSSI select_program + * call. No host should ever deliver a program change or bank + * select controller to a plugin via run_synth. + */ + void (*run_synth)(LADSPA_Handle Instance, + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); + + /** + * run_synth_adding() + * + * This member is a function pointer that runs an instance of a + * synth for a block, adding its outputs to the values already + * present at the output ports. This is provided for symmetry + * with LADSPA run_adding(), and is equally optional. A plugin + * that does not provide it must set this member to NULL. + */ + void (*run_synth_adding)(LADSPA_Handle Instance, + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); + + /** + * run_multiple_synths() + * + * This member is a function pointer that runs multiple synth + * instances for a block. This is very similar to run_synth(), + * except that Instances, Events, and EventCounts each point to + * arrays that hold the LADSPA handles, event buffers, and + * event counts for each of InstanceCount instances. That is, + * Instances points to an array of InstanceCount pointers to + * DSSI plugin instantiations, Events points to an array of + * pointers to each instantiation's respective event list, and + * EventCounts points to an array containing each instantiation's + * respective event count. + * + * A host using this function must guarantee that ALL active + * instances of the plugin are represented in each call to the + * function -- that is, a host may not call run_multiple_synths() + * for some instances of a given plugin and then call run_synth() + * as well for others. 'All .. instances of the plugin' means + * every instance sharing the same LADSPA label and shared object + * (*.so) file (rather than every instance sharing the same *.so). + * 'Active' means any instance for which activate() has been called + * but deactivate() has not. + * + * A plugin may provide this function, run_synths() (see above), + * both, or neither (if it not in fact a synth). A plugin that + * does not provide this function must set this member to NULL. + * Plugin authors implementing run_multiple_synths are strongly + * encouraged to implement run_synth as well if at all possible, + * to aid simplistic hosts, even where it would be less efficient + * to use it. + */ + void (*run_multiple_synths)(unsigned long InstanceCount, + LADSPA_Handle *Instances, + unsigned long SampleCount, + snd_seq_event_t **Events, + unsigned long *EventCounts); + + /** + * run_multiple_synths_adding() + * + * This member is a function pointer that runs multiple synth + * instances for a block, adding each synth's outputs to the + * values already present at the output ports. This is provided + * for symmetry with both the DSSI run_multiple_synths() and LADSPA + * run_adding() functions, and is equally optional. A plugin + * that does not provide it must set this member to NULL. + */ + void (*run_multiple_synths_adding)(unsigned long InstanceCount, + LADSPA_Handle *Instances, + unsigned long SampleCount, + snd_seq_event_t **Events, + unsigned long *EventCounts); + + /** + * set_custom_data() + */ + int (*set_custom_data)(LADSPA_Handle Instance, + void *Data, + unsigned long DataLength); + + /** + * get_custom_data() + */ + int (*get_custom_data)(LADSPA_Handle Instance, + void **Data, + unsigned long *DataLength); + +} DSSI_Descriptor; + +/** + * DSSI supports a plugin discovery method similar to that of LADSPA: + * + * - DSSI hosts may wish to locate DSSI plugin shared object files by + * searching the paths contained in the DSSI_PATH and LADSPA_PATH + * environment variables, if they are present. Both are expected + * to be colon-separated lists of directories to be searched (in + * order), and DSSI_PATH should be searched first if both variables + * are set. + * + * - Each shared object file containing DSSI plugins must include a + * function dssi_descriptor(), with the following function prototype + * and C-style linkage. Hosts may enumerate the plugin types + * available in the shared object file by repeatedly calling + * this function with successive Index values (beginning from 0), + * until a return value of NULL indicates no more plugin types are + * available. Each non-NULL return is the DSSI_Descriptor + * of a distinct plugin type. + */ + +const DSSI_Descriptor *dssi_descriptor(unsigned long Index); + +typedef const DSSI_Descriptor *(*DSSI_Descriptor_Function)(unsigned long Index); + +/* + * Macros to specify particular MIDI controllers in return values from + * get_midi_controller_for_port() + */ + +#define DSSI_CC_BITS 0x20000000 +#define DSSI_NRPN_BITS 0x40000000 + +#define DSSI_NONE -1 +#define DSSI_CONTROLLER_IS_SET(n) (DSSI_NONE != (n)) + +#define DSSI_CC(n) (DSSI_CC_BITS | (n)) +#define DSSI_IS_CC(n) (DSSI_CC_BITS & (n)) +#define DSSI_CC_NUMBER(n) ((n) & 0x7f) + +#define DSSI_NRPN(n) (DSSI_NRPN_BITS | ((n) << 7)) +#define DSSI_IS_NRPN(n) (DSSI_NRPN_BITS & (n)) +#define DSSI_NRPN_NUMBER(n) (((n) >> 7) & 0x3fff) + +#ifdef __cplusplus +} +#endif + +#endif /* DSSI_INCLUDED */ diff --git a/distrho/src/dssi/seq_event-compat.h b/distrho/src/dssi/seq_event-compat.h @@ -0,0 +1,272 @@ +/* + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __ALSA_SEQ_EVENT_COMPAT_H +#define __ALSA_SEQ_EVENT_COMPAT_H + +/** + * Sequencer event data type + */ +typedef unsigned char snd_seq_event_type_t; + +/** Sequencer event type */ +enum snd_seq_event_type { + /** system status; event data type = #snd_seq_result_t */ + SND_SEQ_EVENT_SYSTEM = 0, + /** returned result status; event data type = #snd_seq_result_t */ + SND_SEQ_EVENT_RESULT, + + /** note on and off with duration; event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_NOTE = 5, + /** note on; event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_NOTEON, + /** note off; event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_NOTEOFF, + /** key pressure change (aftertouch); event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_KEYPRESS, + + /** controller; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_CONTROLLER = 10, + /** program change; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_PGMCHANGE, + /** channel pressure; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_CHANPRESS, + /** pitchwheel; event data type = #snd_seq_ev_ctrl_t; data is from -8192 to 8191) */ + SND_SEQ_EVENT_PITCHBEND, + /** 14 bit controller value; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_CONTROL14, + /** 14 bit NRPN; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_NONREGPARAM, + /** 14 bit RPN; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_REGPARAM, + + /** SPP with LSB and MSB values; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_SONGPOS = 20, + /** Song Select with song ID number; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_SONGSEL, + /** midi time code quarter frame; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_QFRAME, + /** SMF Time Signature event; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_TIMESIGN, + /** SMF Key Signature event; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_KEYSIGN, + + /** MIDI Real Time Start message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_START = 30, + /** MIDI Real Time Continue message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_CONTINUE, + /** MIDI Real Time Stop message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_STOP, + /** Set tick queue position; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_SETPOS_TICK, + /** Set real-time queue position; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_SETPOS_TIME, + /** (SMF) Tempo event; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_TEMPO, + /** MIDI Real Time Clock message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_CLOCK, + /** MIDI Real Time Tick message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_TICK, + /** Queue timer skew; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_QUEUE_SKEW, + /** Sync position changed; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_SYNC_POS, + + /** Tune request; event data type = none */ + SND_SEQ_EVENT_TUNE_REQUEST = 40, + /** Reset to power-on state; event data type = none */ + SND_SEQ_EVENT_RESET, + /** Active sensing event; event data type = none */ + SND_SEQ_EVENT_SENSING, + + /** Echo-back event; event data type = any type */ + SND_SEQ_EVENT_ECHO = 50, + /** OSS emulation raw event; event data type = any type */ + SND_SEQ_EVENT_OSS, + + /** New client has connected; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_CLIENT_START = 60, + /** Client has left the system; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_CLIENT_EXIT, + /** Client status/info has changed; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_CLIENT_CHANGE, + /** New port was created; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_PORT_START, + /** Port was deleted from system; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_PORT_EXIT, + /** Port status/info has changed; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_PORT_CHANGE, + + /** Ports connected; event data type = #snd_seq_connect_t */ + SND_SEQ_EVENT_PORT_SUBSCRIBED, + /** Ports disconnected; event data type = #snd_seq_connect_t */ + SND_SEQ_EVENT_PORT_UNSUBSCRIBED, + + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR0 = 90, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR1, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR2, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR3, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR4, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR5, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR6, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR7, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR8, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR9, + + /** system exclusive data (variable length); event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_SYSEX = 130, + /** error event; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_BOUNCE, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR0 = 135, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR1, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR2, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR3, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR4, + + /** NOP; ignored in any case */ + SND_SEQ_EVENT_NONE = 255 +}; + +/** Sequencer event address */ +typedef struct snd_seq_addr { + unsigned char client; /**< Client id */ + unsigned char port; /**< Port id */ +} snd_seq_addr_t; + +/** Connection (subscription) between ports */ +typedef struct snd_seq_connect { + snd_seq_addr_t sender; /**< sender address */ + snd_seq_addr_t dest; /**< destination address */ +} snd_seq_connect_t; + +/** Real-time data record */ +typedef struct snd_seq_real_time { + unsigned int tv_sec; /**< seconds */ + unsigned int tv_nsec; /**< nanoseconds */ +} snd_seq_real_time_t; + +/** (MIDI) Tick-time data record */ +typedef unsigned int snd_seq_tick_time_t; + +/** unioned time stamp */ +typedef union snd_seq_timestamp { + snd_seq_tick_time_t tick; /**< tick-time */ + struct snd_seq_real_time time; /**< real-time */ +} snd_seq_timestamp_t; + +/** Note event */ +typedef struct snd_seq_ev_note { + unsigned char channel; /**< channel number */ + unsigned char note; /**< note */ + unsigned char velocity; /**< velocity */ + unsigned char off_velocity; /**< note-off velocity; only for #SND_SEQ_EVENT_NOTE */ + unsigned int duration; /**< duration until note-off; only for #SND_SEQ_EVENT_NOTE */ +} snd_seq_ev_note_t; + +/** Controller event */ +typedef struct snd_seq_ev_ctrl { + unsigned char channel; /**< channel number */ + unsigned char unused[3]; /**< reserved */ + unsigned int param; /**< control parameter */ + signed int value; /**< control value */ +} snd_seq_ev_ctrl_t; + +/** generic set of bytes (12x8 bit) */ +typedef struct snd_seq_ev_raw8 { + unsigned char d[12]; /**< 8 bit value */ +} snd_seq_ev_raw8_t; + +/** generic set of integers (3x32 bit) */ +typedef struct snd_seq_ev_raw32 { + unsigned int d[3]; /**< 32 bit value */ +} snd_seq_ev_raw32_t; + +/** external stored data */ +typedef struct snd_seq_ev_ext { + unsigned int len; /**< length of data */ + void *ptr; /**< pointer to data (note: can be 64-bit) */ +} __attribute__((packed)) snd_seq_ev_ext_t; + +/** Result events */ +typedef struct snd_seq_result { + int event; /**< processed event type */ + int result; /**< status */ +} snd_seq_result_t; + +/** Queue skew values */ +typedef struct snd_seq_queue_skew { + unsigned int value; /**< skew value */ + unsigned int base; /**< skew base */ +} snd_seq_queue_skew_t; + +/** queue timer control */ +typedef struct snd_seq_ev_queue_control { + unsigned char queue; /**< affected queue */ + unsigned char unused[3]; /**< reserved */ + union { + signed int value; /**< affected value (e.g. tempo) */ + snd_seq_timestamp_t time; /**< time */ + unsigned int position; /**< sync position */ + snd_seq_queue_skew_t skew; /**< queue skew */ + unsigned int d32[2]; /**< any data */ + unsigned char d8[8]; /**< any data */ + } param; /**< data value union */ +} snd_seq_ev_queue_control_t; + +/** Sequencer event */ +typedef struct snd_seq_event { + snd_seq_event_type_t type; /**< event type */ + unsigned char flags; /**< event flags */ + unsigned char tag; /**< tag */ + + unsigned char queue; /**< schedule queue */ + snd_seq_timestamp_t time; /**< schedule time */ + + snd_seq_addr_t source; /**< source address */ + snd_seq_addr_t dest; /**< destination address */ + + union { + snd_seq_ev_note_t note; /**< note information */ + snd_seq_ev_ctrl_t control; /**< MIDI control information */ + snd_seq_ev_raw8_t raw8; /**< raw8 data */ + snd_seq_ev_raw32_t raw32; /**< raw32 data */ + snd_seq_ev_ext_t ext; /**< external data */ + snd_seq_ev_queue_control_t queue; /**< queue control */ + snd_seq_timestamp_t time; /**< timestamp */ + snd_seq_addr_t addr; /**< address */ + snd_seq_connect_t connect; /**< connect information */ + snd_seq_result_t result; /**< operation result code */ + } data; /**< event data... */ +} snd_seq_event_t; + +#endif /* __ALSA_SEQ_EVENT_COMPAT_H */ + diff --git a/distrho/src/ladspa/ladspa.h b/distrho/src/ladspa/ladspa.h @@ -0,0 +1,603 @@ +/* ladspa.h + + Linux Audio Developer's Simple Plugin API Version 1.1[LGPL]. + Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, + Stefan Westerfeld. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. */ + +#ifndef LADSPA_INCLUDED +#define LADSPA_INCLUDED + +#define LADSPA_VERSION "1.1" +#define LADSPA_VERSION_MAJOR 1 +#define LADSPA_VERSION_MINOR 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +/* Overview: + + There is a large number of synthesis packages in use or development + on the Linux platform at this time. This API (`The Linux Audio + Developer's Simple Plugin API') attempts to give programmers the + ability to write simple `plugin' audio processors in C/C++ and link + them dynamically (`plug') into a range of these packages (`hosts'). + It should be possible for any host and any plugin to communicate + completely through this interface. + + This API is deliberately short and simple. To achieve compatibility + with a range of promising Linux sound synthesis packages it + attempts to find the `greatest common divisor' in their logical + behaviour. Having said this, certain limiting decisions are + implicit, notably the use of a fixed type (LADSPA_Data) for all + data transfer and absence of a parameterised `initialisation' + phase. See below for the LADSPA_Data typedef. + + Plugins are expected to distinguish between control and audio + data. Plugins have `ports' that are inputs or outputs for audio or + control data and each plugin is `run' for a `block' corresponding + to a short time interval measured in samples. Audio data is + communicated using arrays of LADSPA_Data, allowing a block of audio + to be processed by the plugin in a single pass. Control data is + communicated using single LADSPA_Data values. Control data has a + single value at the start of a call to the `run()' or `run_adding()' + function, and may be considered to remain this value for its + duration. The plugin may assume that all its input and output ports + have been connected to the relevant data location (see the + `connect_port()' function below) before it is asked to run. + + Plugins will reside in shared object files suitable for dynamic + linking by dlopen() and family. The file will provide a number of + `plugin types' that can be used to instantiate actual plugins + (sometimes known as `plugin instances') that can be connected + together to perform tasks. + + This API contains very limited error-handling. */ + +/*****************************************************************************/ + +/* Fundamental data type passed in and out of plugin. This data type + is used to communicate audio samples and control values. It is + assumed that the plugin will work sensibly given any numeric input + value although it may have a preferred range (see hints below). + + For audio it is generally assumed that 1.0f is the `0dB' reference + amplitude and is a `normal' signal level. */ + +typedef float LADSPA_Data; + +/*****************************************************************************/ + +/* Special Plugin Properties: + + Optional features of the plugin type are encapsulated in the + LADSPA_Properties type. This is assembled by ORing individual + properties together. */ + +typedef int LADSPA_Properties; + +/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a + real-time dependency (e.g. listens to a MIDI device) and so its + output must not be cached or subject to significant latency. */ +#define LADSPA_PROPERTY_REALTIME 0x1 + +/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin + may cease to work correctly if the host elects to use the same data + location for both input and output (see connect_port()). This + should be avoided as enabling this flag makes it impossible for + hosts to use the plugin to process audio `in-place.' */ +#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2 + +/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin + is capable of running not only in a conventional host but also in a + `hard real-time' environment. To qualify for this the plugin must + satisfy all of the following: + + (1) The plugin must not use malloc(), free() or other heap memory + management within its run() or run_adding() functions. All new + memory used in run() must be managed via the stack. These + restrictions only apply to the run() function. + + (2) The plugin will not attempt to make use of any library + functions with the exceptions of functions in the ANSI standard C + and C maths libraries, which the host is expected to provide. + + (3) The plugin will not access files, devices, pipes, sockets, IPC + or any other mechanism that might result in process or thread + blocking. + + (4) The plugin will take an amount of time to execute a run() or + run_adding() call approximately of form (A+B*SampleCount) where A + and B depend on the machine and host in use. This amount of time + may not depend on input signals or plugin state. The host is left + the responsibility to perform timings to estimate upper bounds for + A and B. */ +#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4 + +#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME) +#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN) +#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE) + +/*****************************************************************************/ + +/* Plugin Ports: + + Plugins have `ports' that are inputs or outputs for audio or + data. Ports can communicate arrays of LADSPA_Data (for audio + inputs/outputs) or single LADSPA_Data values (for control + input/outputs). This information is encapsulated in the + LADSPA_PortDescriptor type which is assembled by ORing individual + properties together. + + Note that a port must be an input or an output port but not both + and that a port must be a control or audio port but not both. */ + +typedef int LADSPA_PortDescriptor; + +/* Property LADSPA_PORT_INPUT indicates that the port is an input. */ +#define LADSPA_PORT_INPUT 0x1 + +/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */ +#define LADSPA_PORT_OUTPUT 0x2 + +/* Property LADSPA_PORT_CONTROL indicates that the port is a control + port. */ +#define LADSPA_PORT_CONTROL 0x4 + +/* Property LADSPA_PORT_AUDIO indicates that the port is a audio + port. */ +#define LADSPA_PORT_AUDIO 0x8 + +#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT) +#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT) +#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL) +#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO) + +/*****************************************************************************/ + +/* Plugin Port Range Hints: + + The host may wish to provide a representation of data entering or + leaving a plugin (e.g. to generate a GUI automatically). To make + this more meaningful, the plugin should provide `hints' to the host + describing the usual values taken by the data. + + Note that these are only hints. The host may ignore them and the + plugin must not assume that data supplied to it is meaningful. If + the plugin receives invalid input data it is expected to continue + to run without failure and, where possible, produce a sensible + output (e.g. a high-pass filter given a negative cutoff frequency + might switch to an all-pass mode). + + Hints are meaningful for all input and output ports but hints for + input control ports are expected to be particularly useful. + + More hint information is encapsulated in the + LADSPA_PortRangeHintDescriptor type which is assembled by ORing + individual hint types together. Hints may require further + LowerBound and UpperBound information. + + All the hint information for a particular port is aggregated in the + LADSPA_PortRangeHint structure. */ + +typedef int LADSPA_PortRangeHintDescriptor; + +/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) lower + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of LowerBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_BELOW 0x1 + +/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) upper + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of UpperBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_ABOVE 0x2 + +/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be + considered a Boolean toggle. Data less than or equal to zero should + be considered `off' or `false,' and data above zero should be + considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in + conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or + LADSPA_HINT_DEFAULT_1. */ +#define LADSPA_HINT_TOGGLED 0x4 + +/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified + should be interpreted as multiples of the sample rate. For + instance, a frequency range from 0Hz to the Nyquist frequency (half + the sample rate) could be requested by this hint in conjunction + with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds + at all must support this hint to retain meaning. */ +#define LADSPA_HINT_SAMPLE_RATE 0x8 + +/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the + user will find it more intuitive to view values using a logarithmic + scale. This is particularly useful for frequencies and gains. */ +#define LADSPA_HINT_LOGARITHMIC 0x10 + +/* Hint LADSPA_HINT_INTEGER indicates that a user interface would + probably wish to provide a stepped control taking only integer + values. Any bounds set should be slightly wider than the actual + integer range required to avoid floating point rounding errors. For + instance, the integer set {0,1,2,3} might be described as [-0.1, + 3.1]. */ +#define LADSPA_HINT_INTEGER 0x20 + +/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal' + value for the port that is sensible as a default. For instance, + this value is suitable for use as an initial value in a user + interface or as a value the host might assign to a control port + when the user has not provided one. Defaults are encoded using a + mask so only one default may be specified for a port. Some of the + hints make use of lower and upper bounds, in which case the + relevant bound or bounds must be available and + LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting + default must be rounded if LADSPA_HINT_INTEGER is present. Default + values were introduced in LADSPA v1.1. */ +#define LADSPA_HINT_DEFAULT_MASK 0x3C0 + +/* This default values indicates that no default is provided. */ +#define LADSPA_HINT_DEFAULT_NONE 0x0 + +/* This default hint indicates that the suggested lower bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MINIMUM 0x40 + +/* This default hint indicates that a low value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 + + log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper + * 0.25). */ +#define LADSPA_HINT_DEFAULT_LOW 0x80 + +/* This default hint indicates that a middle value between the + suggested lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 + + log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper * + 0.5). */ +#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0 + +/* This default hint indicates that a high value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 + + log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper + * 0.75). */ +#define LADSPA_HINT_DEFAULT_HIGH 0x100 + +/* This default hint indicates that the suggested upper bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140 + +/* This default hint indicates that the number 0 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_0 0x200 + +/* This default hint indicates that the number 1 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_1 0x240 + +/* This default hint indicates that the number 100 should be used. */ +#define LADSPA_HINT_DEFAULT_100 0x280 + +/* This default hint indicates that the Hz frequency of `concert A' + should be used. This will be 440 unless the host uses an unusual + tuning convention, in which case it may be within a few Hz. */ +#define LADSPA_HINT_DEFAULT_440 0x2C0 + +#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW) +#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE) +#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED) +#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE) +#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC) +#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER) + +#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK) +#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MINIMUM) +#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_LOW) +#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MIDDLE) +#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_HIGH) +#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MAXIMUM) +#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_0) +#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_1) +#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_100) +#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_440) + +typedef struct _LADSPA_PortRangeHint { + + /* Hints about the port. */ + LADSPA_PortRangeHintDescriptor HintDescriptor; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data LowerBound; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data UpperBound; + +} LADSPA_PortRangeHint; + +/*****************************************************************************/ + +/* Plugin Handles: + + This plugin handle indicates a particular instance of the plugin + concerned. It is valid to compare this to NULL (0 for C++) but + otherwise the host should not attempt to interpret it. The plugin + may use it to reference internal instance data. */ + +typedef void * LADSPA_Handle; + +/*****************************************************************************/ + +/* Descriptor for a Type of Plugin: + + This structure is used to describe a plugin type. It provides a + number of functions to examine the type, instantiate it, link it to + buffers and workspaces and to run it. */ + +typedef struct _LADSPA_Descriptor { + + /* This numeric identifier indicates the plugin type + uniquely. Plugin programmers may reserve ranges of IDs from a + central body to avoid clashes. Hosts may assume that IDs are + below 0x1000000. */ + unsigned long UniqueID; + + /* This identifier can be used as a unique, case-sensitive + identifier for the plugin type within the plugin file. Plugin + types should be identified by file and label rather than by index + or plugin name, which may be changed in new plugin + versions. Labels must not contain white-space characters. */ + const char * Label; + + /* This indicates a number of properties of the plugin. */ + LADSPA_Properties Properties; + + /* This member points to the null-terminated name of the plugin + (e.g. "Sine Oscillator"). */ + const char * Name; + + /* This member points to the null-terminated string indicating the + maker of the plugin. This can be an empty string but not NULL. */ + const char * Maker; + + /* This member points to the null-terminated string indicating any + copyright applying to the plugin. If no Copyright applies the + string "None" should be used. */ + const char * Copyright; + + /* This indicates the number of ports (input AND output) present on + the plugin. */ + unsigned long PortCount; + + /* This member indicates an array of port descriptors. Valid indices + vary from 0 to PortCount-1. */ + const LADSPA_PortDescriptor * PortDescriptors; + + /* This member indicates an array of null-terminated strings + describing ports (e.g. "Frequency (Hz)"). Valid indices vary from + 0 to PortCount-1. */ + const char * const * PortNames; + + /* This member indicates an array of range hints for each port (see + above). Valid indices vary from 0 to PortCount-1. */ + const LADSPA_PortRangeHint * PortRangeHints; + + /* This may be used by the plugin developer to pass any custom + implementation data into an instantiate call. It must not be used + or interpreted by the host. It is expected that most plugin + writers will not use this facility as LADSPA_Handle should be + used to hold instance data. */ + void * ImplementationData; + + /* This member is a function pointer that instantiates a plugin. A + handle is returned indicating the new plugin instance. The + instantiation function accepts a sample rate as a parameter. The + plugin descriptor from which this instantiate function was found + must also be passed. This function must return NULL if + instantiation fails. + + Note that instance initialisation should generally occur in + activate() rather than here. */ + LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor, + unsigned long SampleRate); + + /* This member is a function pointer that connects a port on an + instantiated plugin to a memory location at which a block of data + for the port will be read/written. The data location is expected + to be an array of LADSPA_Data for audio ports or a single + LADSPA_Data value for control ports. Memory issues will be + managed by the host. The plugin must read/write the data at these + locations every time run() or run_adding() is called and the data + present at the time of this connection call should not be + considered meaningful. + + connect_port() may be called more than once for a plugin instance + to allow the host to change the buffers that the plugin is + reading or writing. These calls may be made before or after + activate() or deactivate() calls. + + connect_port() must be called at least once for each port before + run() or run_adding() is called. When working with blocks of + LADSPA_Data the plugin should pay careful attention to the block + size passed to the run function as the block allocated may only + just be large enough to contain the block of samples. + + Plugin writers should be aware that the host may elect to use the + same buffer for more than one port and even use the same buffer + for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN). + However, overlapped buffers or use of a single buffer for both + audio and control data may result in unexpected behaviour. */ + void (*connect_port)(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation); + + /* This member is a function pointer that initialises a plugin + instance and activates it for use. This is separated from + instantiate() to aid real-time support and so that hosts can + reinitialise a plugin instance by calling deactivate() and then + activate(). In this case the plugin instance must reset all state + information dependent on the history of the plugin instance + except for any data locations provided by connect_port() and any + gain set by set_run_adding_gain(). If there is nothing for + activate() to do then the plugin writer may provide a NULL rather + than an empty function. + + When present, hosts must call this function once before run() (or + run_adding()) is called for the first time. This call should be + made as close to the run() call as possible and indicates to + real-time plugins that they are now live. Plugins should not rely + on a prompt call to run() after activate(). activate() may not be + called again unless deactivate() is called first. Note that + connect_port() may be called before or after a call to + activate(). */ + void (*activate)(LADSPA_Handle Instance); + + /* This method is a function pointer that runs an instance of a + plugin for a block. Two parameters are required: the first is a + handle to the particular instance to be run and the second + indicates the block size (in samples) for which the plugin + instance may run. + + Note that if an activate() function exists then it must be called + before run() or run_adding(). If deactivate() is called for a + plugin instance then the plugin instance may not be reused until + activate() has been called again. + + If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE + then there are various things that the plugin should not do + within the run() or run_adding() functions (see above). */ + void (*run)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that runs an instance of a + plugin for a block. This has identical behaviour to run() except + in the way data is output from the plugin. When run() is used, + values are written directly to the memory areas associated with + the output ports. However when run_adding() is called, values + must be added to the values already present in the memory + areas. Furthermore, output values written must be scaled by the + current gain set by set_run_adding_gain() (see below) before + addition. + + run_adding() is optional. When it is not provided by a plugin, + this function pointer must be set to NULL. When it is provided, + the function set_run_adding_gain() must be provided also. */ + void (*run_adding)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that sets the output gain for + use when run_adding() is called (see above). If this function is + never called the gain is assumed to default to 1. Gain + information should be retained when activate() or deactivate() + are called. + + This function should be provided by the plugin if and only if the + run_adding() function is provided. When it is absent this + function pointer must be set to NULL. */ + void (*set_run_adding_gain)(LADSPA_Handle Instance, + LADSPA_Data Gain); + + /* This is the counterpart to activate() (see above). If there is + nothing for deactivate() to do then the plugin writer may provide + a NULL rather than an empty function. + + Hosts must deactivate all activated units after they have been + run() (or run_adding()) for the last time. This call should be + made as close to the last run() call as possible and indicates to + real-time plugins that they are no longer live. Plugins should + not rely on prompt deactivation. Note that connect_port() may be + called before or after a call to deactivate(). + + Deactivation is not similar to pausing as the plugin instance + will be reinitialised when activate() is called to reuse it. */ + void (*deactivate)(LADSPA_Handle Instance); + + /* Once an instance of a plugin has been finished with it can be + deleted using the following function. The instance handle passed + ceases to be valid after this call. + + If activate() was called for a plugin instance then a + corresponding call to deactivate() must be made before cleanup() + is called. */ + void (*cleanup)(LADSPA_Handle Instance); + +} LADSPA_Descriptor; + +/**********************************************************************/ + +/* Accessing a Plugin: */ + +/* The exact mechanism by which plugins are loaded is host-dependent, + however all most hosts will need to know is the name of shared + object file containing the plugin types. To allow multiple hosts to + share plugin types, hosts may wish to check for environment + variable LADSPA_PATH. If present, this should contain a + colon-separated path indicating directories that should be searched + (in order) when loading plugin types. + + A plugin programmer must include a function called + "ladspa_descriptor" with the following function prototype within + the shared object file. This function will have C-style linkage (if + you are using C++ this is taken care of by the `extern "C"' clause + at the top of the file). + + A host will find the plugin shared object file by one means or + another, find the ladspa_descriptor() function, call it, and + proceed from there. + + Plugin types are accessed by index (not ID) using values from 0 + upwards. Out of range indexes must result in this function + returning NULL, so the plugin count can be determined by checking + for the least index that results in NULL being returned. */ + +const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index); + +/* Datatype corresponding to the ladspa_descriptor() function. */ +typedef const LADSPA_Descriptor * +(*LADSPA_Descriptor_Function)(unsigned long Index); + +/**********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* LADSPA_INCLUDED */ + +/* EOF */ diff --git a/distrho/src/lv2/atom-forge.h b/distrho/src/lv2/atom-forge.h @@ -0,0 +1,619 @@ +/* + Copyright 2008-2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file forge.h An API for constructing LV2 atoms. + + This file provides an API for constructing Atoms which makes it relatively + simple to build nested atoms of arbitrary complexity without requiring + dynamic memory allocation. + + The API is based on successively appending the appropriate pieces to build a + complete Atom. The size of containers is automatically updated. Functions + that begin a container return (via their frame argument) a stack frame which + must be popped when the container is finished. + + All output is written to a user-provided buffer or sink function. This + makes it popssible to create create atoms on the stack, on the heap, in LV2 + port buffers, in a ringbuffer, or elsewhere, all using the same API. + + This entire API is realtime safe if used with a buffer or a realtime safe + sink, except lv2_atom_forge_init() which is only realtime safe if the URI + map function is. + + Note these functions are all static inline, do not take their address. + + This header is non-normative, it is provided for convenience. +*/ + +#ifndef LV2_ATOM_FORGE_H +#define LV2_ATOM_FORGE_H + +#include <assert.h> + +#include "atom.h" +#include "atom-util.h" +#include "urid.h" + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +/** Handle for LV2_Atom_Forge_Sink. */ +typedef void* LV2_Atom_Forge_Sink_Handle; + +/** A reference to a chunk of written output. */ +typedef intptr_t LV2_Atom_Forge_Ref; + +/** Sink function for writing output. See lv2_atom_forge_set_sink(). */ +typedef LV2_Atom_Forge_Ref +(*LV2_Atom_Forge_Sink)(LV2_Atom_Forge_Sink_Handle handle, + const void* buf, + uint32_t size); + +/** Function for resolving a reference. See lv2_atom_forge_set_sink(). */ +typedef LV2_Atom* +(*LV2_Atom_Forge_Deref_Func)(LV2_Atom_Forge_Sink_Handle handle, + LV2_Atom_Forge_Ref ref); + +/** A stack frame used for keeping track of nested Atom containers. */ +typedef struct _LV2_Atom_Forge_Frame { + struct _LV2_Atom_Forge_Frame* parent; + LV2_Atom_Forge_Ref ref; +} LV2_Atom_Forge_Frame; + +/** A "forge" for creating atoms by appending to a buffer. */ +typedef struct { + uint8_t* buf; + uint32_t offset; + uint32_t size; + + LV2_Atom_Forge_Sink sink; + LV2_Atom_Forge_Deref_Func deref; + LV2_Atom_Forge_Sink_Handle handle; + + LV2_Atom_Forge_Frame* stack; + + LV2_URID Blank; + LV2_URID Bool; + LV2_URID Chunk; + LV2_URID Double; + LV2_URID Float; + LV2_URID Int; + LV2_URID Long; + LV2_URID Literal; + LV2_URID Path; + LV2_URID Property; + LV2_URID Resource; + LV2_URID Sequence; + LV2_URID String; + LV2_URID Tuple; + LV2_URID URI; + LV2_URID URID; + LV2_URID Vector; +} LV2_Atom_Forge; + +static inline void +lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); + +/** + Initialise @p forge. + + URIs will be mapped using @p map and stored, a reference to @p map itself is + not held. +*/ +static inline void +lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) +{ + lv2_atom_forge_set_buffer(forge, NULL, 0); + forge->Blank = map->map(map->handle, LV2_ATOM__Blank); + forge->Bool = map->map(map->handle, LV2_ATOM__Bool); + forge->Chunk = map->map(map->handle, LV2_ATOM__Chunk); + forge->Double = map->map(map->handle, LV2_ATOM__Double); + forge->Float = map->map(map->handle, LV2_ATOM__Float); + forge->Int = map->map(map->handle, LV2_ATOM__Int); + forge->Long = map->map(map->handle, LV2_ATOM__Long); + forge->Literal = map->map(map->handle, LV2_ATOM__Literal); + forge->Path = map->map(map->handle, LV2_ATOM__Path); + forge->Property = map->map(map->handle, LV2_ATOM__Property); + forge->Resource = map->map(map->handle, LV2_ATOM__Resource); + forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence); + forge->String = map->map(map->handle, LV2_ATOM__String); + forge->Tuple = map->map(map->handle, LV2_ATOM__Tuple); + forge->URI = map->map(map->handle, LV2_ATOM__URI); + forge->URID = map->map(map->handle, LV2_ATOM__URID); + forge->Vector = map->map(map->handle, LV2_ATOM__Vector); +} + +static inline LV2_Atom* +lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref) +{ + if (forge->buf) { + return (LV2_Atom*)ref; + } else { + return forge->deref(forge->handle, ref); + } +} + +/** + @name Object Stack + @{ +*/ + +/** + Push a stack frame. + This is done automatically by container functions (which take a stack frame + pointer), but may be called by the user to push the top level container when + writing to an existing Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_push(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + LV2_Atom_Forge_Ref ref) +{ + frame->parent = forge->stack; + frame->ref = ref; + forge->stack = frame; + return ref; +} + +/** Pop a stack frame. This must be called when a container is finished. */ +static inline void +lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) +{ + assert(frame == forge->stack); + forge->stack = frame->parent; +} + +/** Return true iff the top of the stack has the given type. */ +static inline bool +lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type) +{ + return forge->stack && + lv2_atom_forge_deref(forge, forge->stack->ref)->type == type; +} + +/** + @} + @name Output Configuration + @{ +*/ + +/** Set the output buffer where @p forge will write atoms. */ +static inline void +lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size) +{ + forge->buf = buf; + forge->size = size; + forge->offset = 0; + forge->deref = NULL; + forge->sink = NULL; + forge->handle = NULL; + forge->stack = NULL; +} + +/** + Set the sink function where @p forge will write output. + + The return value of forge functions is an LV2_Atom_Forge_Ref which is an + integer type safe to use as a pointer but is otherwise opaque. The sink + function must return a ref that can be dereferenced to access as least + sizeof(LV2_Atom) bytes of the written data, so sizes can be updated. For + ringbuffers, this should be possible as long as the size of the buffer is a + multiple of sizeof(LV2_Atom), since atoms are always aligned. + + Note that 0 is an invalid reference, so if you are using a buffer offset be + sure to offset it such that 0 is never a valid reference. You will get + confusing errors otherwise. +*/ +static inline void +lv2_atom_forge_set_sink(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Sink sink, + LV2_Atom_Forge_Deref_Func deref, + LV2_Atom_Forge_Sink_Handle handle) +{ + forge->buf = NULL; + forge->size = forge->offset = 0; + forge->deref = deref; + forge->sink = sink; + forge->handle = handle; + forge->stack = NULL; +} + +/** + @} + @name Low Level Output + @{ +*/ + +/** + Write raw output. This is used internally, but is also useful for writing + atom types not explicitly supported by the forge API. Note the caller is + responsible for ensuring the output is approriately padded. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size) +{ + LV2_Atom_Forge_Ref out = 0; + if (forge->sink) { + out = forge->sink(forge->handle, data, size); + } else { + out = (LV2_Atom_Forge_Ref)forge->buf + forge->offset; + uint8_t* mem = forge->buf + forge->offset; + if (forge->offset + size > forge->size) { + return 0; + } + forge->offset += size; + memcpy(mem, data, size); + } + for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) { + lv2_atom_forge_deref(forge, f->ref)->size += size; + } + return out; +} + +/** Pad output accordingly so next write is 64-bit aligned. */ +static inline void +lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written) +{ + const uint64_t pad = 0; + const uint32_t pad_size = lv2_atom_pad_size(written) - written; + lv2_atom_forge_raw(forge, &pad, pad_size); +} + +/** Write raw output, padding to 64-bits as necessary. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) +{ + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size); + if (out) { + lv2_atom_forge_pad(forge, size); + } + return out; +} + +/** Write a null-terminated string body. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_string_body(LV2_Atom_Forge* forge, + const char* str, + uint32_t len) +{ + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len); + if (out && (out = lv2_atom_forge_raw(forge, "", 1))) { + lv2_atom_forge_pad(forge, len + 1); + } + return out; +} + +/** + @} + @name Atom Output + @{ +*/ + +/** Write an atom:Atom header. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type) +{ + const LV2_Atom a = { size, type }; + return lv2_atom_forge_raw(forge, &a, sizeof(a)); +} + +/** Write a primitive (fixed-size) atom. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a) +{ + if (lv2_atom_forge_top_is(forge, forge->Vector)) { + return lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(a), a->size); + } else { + return lv2_atom_forge_write(forge, a, sizeof(LV2_Atom) + a->size); + } +} + +/** Write an atom:Int. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val) +{ + const LV2_Atom_Int a = { { sizeof(val), forge->Int }, val }; + return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Long. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val) +{ + const LV2_Atom_Long a = { { sizeof(val), forge->Long }, val }; + return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Float. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_float(LV2_Atom_Forge* forge, float val) +{ + const LV2_Atom_Float a = { { sizeof(val), forge->Float }, val }; + return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Double. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_double(LV2_Atom_Forge* forge, double val) +{ + const LV2_Atom_Double a = { { sizeof(val), forge->Double }, val }; + return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Bool. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val) +{ + const LV2_Atom_Bool a = { { sizeof(int32_t), forge->Bool }, val ? 1 : 0 }; + return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:URID. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id) +{ + const LV2_Atom_URID a = { { sizeof(id), forge->URID }, id }; + return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom compatible with atom:String. Used internally. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_typed_string(LV2_Atom_Forge* forge, + uint32_t type, + const char* str, + uint32_t len) +{ + const LV2_Atom_String a = { { len + 1, type } }; + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); + if (out) { + if (!lv2_atom_forge_string_body(forge, str, len)) { + LV2_Atom* atom = lv2_atom_forge_deref(forge, out); + atom->size = atom->type = 0; + out = 0; + } + } + return out; +} + +/** Write an atom:String. Note that @p str need not be NULL terminated. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len) +{ + return lv2_atom_forge_typed_string(forge, forge->String, str, len); +} + +/** + Write an atom:URI. Note that @p uri need not be NULL terminated. + This does not map the URI, but writes the complete URI string. To write + a mapped URI, use lv2_atom_forge_urid(). +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len) +{ + return lv2_atom_forge_typed_string(forge, forge->URI, uri, len); +} + +/** Write an atom:Path. Note that @p path need not be NULL terminated. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len) +{ + return lv2_atom_forge_typed_string(forge, forge->Path, path, len); +} + +/** Write an atom:Literal. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_literal(LV2_Atom_Forge* forge, + const char* str, + uint32_t len, + uint32_t datatype, + uint32_t lang) +{ + const LV2_Atom_Literal a = { + { (uint32_t)(sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1), + forge->Literal }, + { datatype, + lang } + }; + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); + if (out) { + if (!lv2_atom_forge_string_body(forge, str, len)) { + LV2_Atom* atom = lv2_atom_forge_deref(forge, out); + atom->size = atom->type = 0; + out = 0; + } + } + return out; +} + +/** Start an atom:Vector. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_vector_head(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + uint32_t child_size, + uint32_t child_type) +{ + const LV2_Atom_Vector a = { + { sizeof(LV2_Atom_Vector_Body), forge->Vector }, + { child_size, child_type } + }; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** Write a complete atom:Vector. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_vector(LV2_Atom_Forge* forge, + uint32_t child_size, + uint32_t child_type, + uint32_t n_elems, + const void* elems) +{ + const LV2_Atom_Vector a = { + { (uint32_t)(sizeof(LV2_Atom_Vector_Body) + n_elems * child_size), + forge->Vector }, + { child_size, child_type } + }; + LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); + if (out) { + lv2_atom_forge_write(forge, elems, child_size * n_elems); + } + return out; +} + +/** + Write the header of an atom:Tuple. + + The passed frame will be initialised to represent this tuple. To complete + the tuple, write a sequence of atoms, then pop the frame with + lv2_atom_forge_pop(). + + For example: + @code + // Write tuple (1, 2.0) + LV2_Atom_Forge_Frame frame; + LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame); + lv2_atom_forge_int32(forge, 1); + lv2_atom_forge_float(forge, 2.0); + lv2_atom_forge_pop(forge, &frame); + @endcode +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) +{ + const LV2_Atom_Tuple a = { { 0, forge->Tuple } }; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** + Write the header of an atom:Resource. + + The passed frame will be initialised to represent this object. To complete + the object, write a sequence of properties, then pop the frame with + lv2_atom_forge_pop(). + + For example: + @code + LV2_URID eg_Cat = map("http://example.org/Cat"); + LV2_URID eg_name = map("http://example.org/name"); + + // Write object header + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_resource(forge, &frame, 1, eg_Cat); + + // Write property: eg:name = "Hobbes" + lv2_atom_forge_property_head(forge, eg_name, 0); + lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes")); + + // Finish object + lv2_atom_forge_pop(forge, &frame); + @endcode +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_resource(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + LV2_URID id, + LV2_URID otype) +{ + const LV2_Atom_Object a = { + { sizeof(LV2_Atom_Object) - sizeof(LV2_Atom), forge->Resource }, + { id, otype } + }; + LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); + return lv2_atom_forge_push(forge, frame, out); +} + +/** + The same as lv2_atom_forge_resource(), but for object:Blank. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_blank(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + uint32_t id, + LV2_URID otype) +{ + const LV2_Atom_Object a = { + { sizeof(LV2_Atom_Object) - sizeof(LV2_Atom), forge->Blank }, + { id, otype } + }; + LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); + return lv2_atom_forge_push(forge, frame, out); +} + +/** + Write the header for a property body (likely in an Object). + See lv2_atom_forge_resource() documentation for an example. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_property_head(LV2_Atom_Forge* forge, + LV2_URID key, + LV2_URID context) +{ + const LV2_Atom_Property_Body a = { key, context, { 0, 0 } }; + return lv2_atom_forge_write(forge, &a, 2 * sizeof(uint32_t)); +} + +/** + Write the header for a Sequence. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + uint32_t unit) +{ + const LV2_Atom_Sequence a = { + { sizeof(LV2_Atom_Sequence) - sizeof(LV2_Atom), forge->Sequence }, + { unit, 0 } + }; + LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); + return lv2_atom_forge_push(forge, frame, out); +} + +/** + Write the time stamp header of an Event (in a Sequence) in audio frames. + After this, call the appropriate forge method(s) to write the body. Note + the returned reference is to an LV2_Event which is NOT an Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames) +{ + return lv2_atom_forge_write(forge, &frames, sizeof(frames)); +} + +/** + Write the time stamp header of an Event (in a Sequence) in beats. After + this, call the appropriate forge method(s) to write the body. Note the + returned reference is to an LV2_Event which is NOT an Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats) +{ + return lv2_atom_forge_write(forge, &beats, sizeof(beats)); +} + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_ATOM_FORGE_H */ diff --git a/distrho/src/lv2/atom-helpers.h b/distrho/src/lv2/atom-helpers.h @@ -0,0 +1,255 @@ +// lv2_atom_helpers.h +// +/**************************************************************************** + Copyright (C) 2005-2012, rncbc aka Rui Nuno Capela. All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +*****************************************************************************/ + +/* Helper functions for LV2 atom:Sequence event buffer. + * + * tentatively adapted from: + * + * - lv2_evbuf.h,c - An abstract/opaque LV2 event buffer implementation. + * + * - event-helpers.h - Helper functions for the LV2 Event extension. + * <http://lv2plug.in/ns/ext/event> + * + * Copyright 2008-2012 David Robillard <http://drobilla.net> + */ + +#ifndef LV2_ATOM_HELPERS_H +#define LV2_ATOM_HELPERS_H + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "atom.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// An abstract/opaque LV2 atom:Sequence buffer. +// +typedef +struct _LV2_Atom_Buffer +{ + uint32_t capacity; + uint32_t chunk_type; + uint32_t sequence_type; + LV2_Atom_Sequence atoms; + +} LV2_Atom_Buffer; + + +// Pad a size to 64 bits (for LV2 atom:Sequence event sizes). +// +static inline +uint32_t lv2_atom_buffer_pad_size ( uint32_t size ) +{ + return (size + 7) & (~7); +} + + +// Clear and initialize an existing LV2 atom:Sequenece buffer. +// +static inline +void lv2_atom_buffer_reset ( LV2_Atom_Buffer *buf, bool input ) +{ + if (input) + buf->atoms.atom.size = sizeof(LV2_Atom_Sequence_Body); + else + buf->atoms.atom.size = buf->capacity; + + buf->atoms.atom.type = buf->sequence_type; + buf->atoms.body.unit = 0; + buf->atoms.body.pad = 0; +} + + +// Allocate a new, empty LV2 atom:Sequence buffer. +// +static inline +LV2_Atom_Buffer *lv2_atom_buffer_new ( + uint32_t capacity, uint32_t sequence_type, bool input ) +{ + LV2_Atom_Buffer *buf = (LV2_Atom_Buffer *) + malloc(sizeof(LV2_Atom_Buffer) + sizeof(LV2_Atom_Sequence) + capacity); + + buf->capacity = capacity; + buf->sequence_type = sequence_type; + + lv2_atom_buffer_reset(buf, input); + + return buf; +} + + +// Free an LV2 atom:Sequenece buffer allocated with lv2_atome_buffer_new. +// +static inline +void lv2_atom_buffer_free ( LV2_Atom_Buffer *buf ) +{ + free(buf); +} + + +// Return the total padded size of events stored in a LV2 atom:Sequence buffer. +// +static inline +uint32_t lv2_atom_buffer_get_size ( LV2_Atom_Buffer *buf ) +{ + return buf->atoms.atom.size - sizeof(LV2_Atom_Sequence_Body); +} + + +// Return the actual LV2 atom:Sequence implementation. +// +static inline +LV2_Atom_Sequence *lv2_atom_buffer_get_sequence ( LV2_Atom_Buffer *buf ) +{ + return &buf->atoms; +} + + +// An iterator over an atom:Sequence buffer. +// +typedef +struct _LV2_Atom_Buffer_Iterator +{ + LV2_Atom_Buffer *buf; + uint32_t offset; + +} LV2_Atom_Buffer_Iterator; + + +// Reset an iterator to point to the start of an LV2 atom:Sequence buffer. +// +static inline +bool lv2_atom_buffer_begin ( + LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *buf ) +{ + iter->buf = buf; + iter->offset = 0; + + return (buf->atoms.atom.size > 0); +} + + +// Reset an iterator to point to the end of an LV2 atom:Sequence buffer. +// +static inline +bool lv2_atom_buffer_end ( + LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *buf ) +{ + iter->buf = buf; + iter->offset = lv2_atom_buffer_pad_size(lv2_atom_buffer_get_size(buf)); + + return (iter->offset < buf->capacity - sizeof(LV2_Atom_Event)); +} + + +// Check if a LV2 atom:Sequenece buffer iterator is valid. +// +static inline +bool lv2_atom_buffer_is_valid ( LV2_Atom_Buffer_Iterator *iter ) +{ + return iter->offset < lv2_atom_buffer_get_size(iter->buf); +} + + +// Advance a LV2 atom:Sequenece buffer iterator forward one event. +// +static inline +bool lv2_atom_buffer_increment ( LV2_Atom_Buffer_Iterator *iter ) +{ + if (!lv2_atom_buffer_is_valid(iter)) + return false; + + LV2_Atom_Buffer *buf = iter->buf; + LV2_Atom_Sequence *atoms = &buf->atoms; + uint32_t size = ((LV2_Atom_Event *) ((char *) + LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset))->body.size; + iter->offset += lv2_atom_buffer_pad_size(sizeof(LV2_Atom_Event) + size); + + return true; +} + + +// Get the event currently pointed at a LV2 atom:Sequence buffer iterator. +// +static inline +LV2_Atom_Event *lv2_atom_buffer_get ( + LV2_Atom_Buffer_Iterator *iter, uint8_t **data ) +{ + if (!lv2_atom_buffer_is_valid(iter)) + return NULL; + + LV2_Atom_Buffer *buf = iter->buf; + LV2_Atom_Sequence *atoms = &buf->atoms; + LV2_Atom_Event *ev = (LV2_Atom_Event *) ((char *) + LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset); + + *data = (uint8_t *) LV2_ATOM_BODY(&ev->body); + + return ev; +} + + +// Write an event at a LV2 atom:Sequence buffer iterator. + +static inline +bool lv2_atom_buffer_write ( + LV2_Atom_Buffer_Iterator *iter, + uint32_t frames, + uint32_t /*subframes*/, + uint32_t type, + uint32_t size, + const uint8_t *data ) +{ + LV2_Atom_Buffer *buf = iter->buf; + LV2_Atom_Sequence *atoms = &buf->atoms; + if (buf->capacity - sizeof(LV2_Atom) - atoms->atom.size + < sizeof(LV2_Atom_Event) + size) + return false; + + LV2_Atom_Event *ev = (LV2_Atom_Event*) ((char *) + LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset); + + ev->time.frames = frames; + ev->body.type = type; + ev->body.size = size; + + memcpy(LV2_ATOM_BODY(&ev->body), data, size); + + size = lv2_atom_buffer_pad_size(sizeof(LV2_Atom_Event) + size); + atoms->atom.size += size; + iter->offset += size; + + return true; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // LV2_ATOM_HELPERS_H + +// end of lv2_atom_helpers.h diff --git a/distrho/src/lv2/atom-util.h b/distrho/src/lv2/atom-util.h @@ -0,0 +1,401 @@ +/* + Copyright 2008-2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file util.h Helper functions for the LV2 Atom extension. + + Note these functions are all static inline, do not take their address. + + This header is non-normative, it is provided for convenience. +*/ + +#ifndef LV2_ATOM_UTIL_H +#define LV2_ATOM_UTIL_H + +#include <stdarg.h> +#include <stdint.h> +#include <string.h> + +#include "atom.h" + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +/** Pad a size to 64 bits. */ +static inline uint32_t +lv2_atom_pad_size(uint32_t size) +{ + return (size + 7) & (~7); +} + +/** Return the total size of @p atom, including the header. */ +static inline uint32_t +lv2_atom_total_size(const LV2_Atom* atom) +{ + return sizeof(LV2_Atom) + atom->size; +} + +/** Return true iff @p atom is null. */ +static inline bool +lv2_atom_is_null(const LV2_Atom* atom) +{ + return !atom || (atom->type == 0 && atom->size == 0); +} + +/** Return true iff @p a is equal to @p b. */ +static inline bool +lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b) +{ + return (a == b) || ((a->type == b->type) && + (a->size == b->size) && + !memcmp(a + 1, b + 1, a->size)); +} + +/** + @name Sequence Iterator + @{ +*/ + +/** Get an iterator pointing to the first event in a Sequence body. */ +static inline const LV2_Atom_Event* +lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body) +{ + return (const LV2_Atom_Event*)(body + 1); +} + +/** Get an iterator pointing to the end of a Sequence body. */ +static inline const LV2_Atom_Event* +lv2_atom_sequence_end(const LV2_Atom_Sequence_Body* body, uint32_t size) +{ + return (const LV2_Atom_Event*)((const uint8_t*)body + lv2_atom_pad_size(size)); +} + +/** Return true iff @p i has reached the end of @p body. */ +static inline bool +lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, + uint32_t size, + const LV2_Atom_Event* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the element following @p i. */ +static inline const LV2_Atom_Event* +lv2_atom_sequence_next(const LV2_Atom_Event* i) +{ + if (!i) return NULL; + return (const LV2_Atom_Event*)((const uint8_t*)i + + sizeof(LV2_Atom_Event) + + lv2_atom_pad_size(i->body.size)); +} + +/** + A macro for iterating over all events in a Sequence. + @param seq The sequence to iterate over + @param iter The name of the iterator + + This macro is used similarly to a for loop (which it expands to), e.g.: + @code + LV2_ATOM_SEQUENCE_FOREACH(sequence, ev) { + // Do something with ev (an LV2_Atom_Event*) here... + } + @endcode +*/ +#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ + for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(&(seq)->body); \ + !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ + (iter) = lv2_atom_sequence_next(iter)) + +/** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ +#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ + for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(body); \ + !lv2_atom_sequence_is_end(body, size, (iter)); \ + (iter) = lv2_atom_sequence_next(iter)) + +/** + @} + @name Tuple Iterator + @{ +*/ + +/** Get an iterator pointing to the first element in @p tup. */ +static inline const LV2_Atom* +lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup) +{ + return (const LV2_Atom*)(LV2_ATOM_BODY_CONST(tup)); +} + +/** Return true iff @p i has reached the end of @p body. */ +static inline bool +lv2_atom_tuple_is_end(const void* body, uint32_t size, LV2_Atom* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the element following @p i. */ +static inline const LV2_Atom* +lv2_atom_tuple_next(const LV2_Atom* i) +{ + return (const LV2_Atom*)( + (const uint8_t*)i + sizeof(LV2_Atom) + lv2_atom_pad_size(i->size)); +} + +/** + A macro for iterating over all properties of a Tuple. + @param tuple The tuple to iterate over + @param iter The name of the iterator + + This macro is used similarly to a for loop (which it expands to), e.g.: + @code + LV2_ATOMO_TUPLE_FOREACH(tuple, elem) { + // Do something with elem (an LV2_Atom*) here... + } + @endcode +*/ +#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ + for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \ + !lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), (tuple)->size, (iter)); \ + (iter) = lv2_atom_tuple_next(iter)) + +/** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ +#define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ + for (LV2_Atom* (iter) = (LV2_Atom*)body; \ + !lv2_atom_tuple_is_end(body, size, (iter)); \ + (iter) = lv2_atom_tuple_next(iter)) + +/** + @} + @name Object Iterator + @{ +*/ + +/** Return a pointer to the first property in @p body. */ +static inline const LV2_Atom_Property_Body* +lv2_atom_object_begin(const LV2_Atom_Object_Body* body) +{ + return (const LV2_Atom_Property_Body*)(body + 1); +} + +/** Return true iff @p i has reached the end of @p obj. */ +static inline bool +lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, + uint32_t size, + const LV2_Atom_Property_Body* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the property following @p i. */ +static inline const LV2_Atom_Property_Body* +lv2_atom_object_next(const LV2_Atom_Property_Body* i) +{ + const LV2_Atom* const value = (const LV2_Atom*)( + (const uint8_t*)i + 2 * sizeof(uint32_t)); + return (const LV2_Atom_Property_Body*)( + (const uint8_t*)i + lv2_atom_pad_size(sizeof(LV2_Atom_Property_Body) + + value->size)); +} + +/** + A macro for iterating over all properties of an Object. + @param obj The object to iterate over + @param iter The name of the iterator + + This macro is used similarly to a for loop (which it expands to), e.g.: + @code + LV2_ATOM_OBJECT_FOREACH(object, i) { + // Do something with prop (an LV2_Atom_Property_Body*) here... + } + @endcode +*/ +#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ + for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(&(obj)->body); \ + !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ + (iter) = lv2_atom_object_next(iter)) + +/** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ +#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ + for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(body); \ + !lv2_atom_object_is_end(body, size, (iter)); \ + (iter) = lv2_atom_object_next(iter)) + +/** + @} + @name Object Query + @{ +*/ + +/** A single entry in an Object query. */ +typedef struct { + uint32_t key; /**< Key to query (input set by user) */ + const LV2_Atom** value; /**< Found value (output set by query function) */ +} LV2_Atom_Object_Query; + +static const LV2_Atom_Object_Query LV2_ATOM_OBJECT_QUERY_END = { 0, NULL }; + +/** + Get an object's values for various keys. + + The value pointer of each item in @p query will be set to the location of + the corresponding value in @p object. Every value pointer in @p query MUST + be initialised to NULL. This function reads @p object in a single linear + sweep. By allocating @p query on the stack, objects can be "queried" + quickly without allocating any memory. This function is realtime safe. + + This function can only do "flat" queries, it is not smart enough to match + variables in nested objects. + + For example: + @code + const LV2_Atom* name = NULL; + const LV2_Atom* age = NULL; + LV2_Atom_Object_Query q[] = { + { urids.eg_name, &name }, + { urids.eg_age, &age }, + LV2_ATOM_OBJECT_QUERY_END + }; + lv2_atom_object_query(obj, q); + // name and age are now set to the appropriate values in obj, or NULL. + @endcode +*/ +static inline int +lv2_atom_object_query(const LV2_Atom_Object* object, + LV2_Atom_Object_Query* query) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of query keys so we can short-circuit when done */ + for (LV2_Atom_Object_Query* q = query; q->key; ++q) { + ++n_queries; + } + + LV2_ATOM_OBJECT_FOREACH(object, prop) { + for (LV2_Atom_Object_Query* q = query; q->key; ++q) { + if (q->key == prop->key && !*q->value) { + *q->value = &prop->value; + if (++matches == n_queries) { + return matches; + } + break; + } + } + } + return matches; +} + +/** + Body only version of lv2_atom_object_get(). +*/ +static inline int +lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of keys so we can short-circuit when done */ + va_list args; + va_start(args, body); + for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { + if (!va_arg(args, const LV2_Atom**)) { + return -1; + } + } + va_end(args); + + LV2_ATOM_OBJECT_BODY_FOREACH(body, size, prop) { + va_start(args, body); + for (int i = 0; i < n_queries; ++i) { + uint32_t qkey = va_arg(args, uint32_t); + const LV2_Atom** qval = va_arg(args, const LV2_Atom**); + if (qkey == prop->key && !*qval) { + *qval = &prop->value; + if (++matches == n_queries) { + return matches; + } + break; + } + } + va_end(args); + } + return matches; +} + +/** + Variable argument version of lv2_atom_object_query(). + + This is nicer-looking in code, but a bit more error-prone since it is not + type safe and the argument list must be terminated. + + The arguments should be a series of uint32_t key and const LV2_Atom** value + pairs, terminated by a zero key. The value pointers MUST be initialized to + NULL. For example: + + @code + const LV2_Atom* name = NULL; + const LV2_Atom* age = NULL; + lv2_atom_object_get(obj, + uris.name_key, &name, + uris.age_key, &age, + 0); + @endcode +*/ +static inline int +lv2_atom_object_get(const LV2_Atom_Object* object, ...) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of keys so we can short-circuit when done */ + va_list args; + va_start(args, object); + for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { + if (!va_arg(args, const LV2_Atom**)) { + return -1; + } + } + va_end(args); + + LV2_ATOM_OBJECT_FOREACH(object, prop) { + va_start(args, object); + for (int i = 0; i < n_queries; ++i) { + uint32_t qkey = va_arg(args, uint32_t); + const LV2_Atom** qval = va_arg(args, const LV2_Atom**); + if (qkey == prop->key && !*qval) { + *qval = &prop->value; + if (++matches == n_queries) { + return matches; + } + break; + } + } + va_end(args); + } + return matches; +} + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_ATOM_UTIL_H */ diff --git a/distrho/src/lv2/atom.h b/distrho/src/lv2/atom.h @@ -0,0 +1,246 @@ +/* + Copyright 2008-2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file atom.h C header for the LV2 Atom extension + <http://lv2plug.in/ns/ext/atom>. +*/ + +#ifndef LV2_ATOM_H +#define LV2_ATOM_H + +#include <stdint.h> +#include <stddef.h> + +#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" +#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" + +#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" +#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" +#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" +#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" +#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" +#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" +#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" +#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" +#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" +#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" +#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" +#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" +#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" +#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" +#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" +#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" +#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" +#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" +#define LV2_ATOM__String LV2_ATOM_PREFIX "String" +#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" +#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" +#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" +#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" +#define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" +#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" +#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" +#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" +#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" +#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" +#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" +#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" + +#define LV2_ATOM_REFERENCE_TYPE 0 + +#ifdef __cplusplus +extern "C" { +#endif + +/** This expression will fail to compile if double does not fit in 64 bits. */ +typedef char lv2_atom_assert_double_fits_in_64_bits[ + ((sizeof(double) <= sizeof(uint64_t)) * 2) - 1]; + +/** + Return a pointer to the contents of an Atom. The "contents" of an atom + is the data past the complete type-specific header. + @param type The type of the atom, e.g. LV2_Atom_String. + @param atom A variable-sized atom. +*/ +#define LV2_ATOM_CONTENTS(type, atom) \ + ((uint8_t*)(atom) + sizeof(type)) + +/** + Const version of LV2_ATOM_CONTENTS. +*/ +#define LV2_ATOM_CONTENTS_CONST(type, atom) \ + ((const uint8_t*)(atom) + sizeof(type)) + +/** + Return a pointer to the body of an Atom. The "body" of an atom is the + data just past the LV2_Atom head (i.e. the same offset for all types). +*/ +#define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom) + +/** + Const version of LV2_ATOM_BODY. +*/ +#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom) + +/** The header of an atom:Atom. */ +typedef struct { + uint32_t size; /**< Size in bytes, not including type and size. */ + uint32_t type; /**< Type of this atom (mapped URI). */ +} LV2_Atom; + +/** An atom:Int or atom:Bool. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + int32_t body; /**< Integer value. */ +} LV2_Atom_Int; + +/** An atom:Long. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + int64_t body; /**< Integer value. */ +} LV2_Atom_Long; + +/** An atom:Float. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + float body; /**< Floating point value. */ +} LV2_Atom_Float; + +/** An atom:Double. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + double body; /**< Floating point value. */ +} LV2_Atom_Double; + +/** An atom:Bool. May be cast to LV2_Atom. */ +typedef LV2_Atom_Int LV2_Atom_Bool; + +/** An atom:URID. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + uint32_t body; /**< URID. */ +} LV2_Atom_URID; + +/** An atom:String. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + /* Contents (a null-terminated UTF-8 string) follow here. */ +} LV2_Atom_String; + +/** The body of an atom:Literal. */ +typedef struct { + uint32_t datatype; /**< Datatype URID. */ + uint32_t lang; /**< Language URID. */ + /* Contents (a null-terminated UTF-8 string) follow here. */ +} LV2_Atom_Literal_Body; + +/** An atom:Literal. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Literal_Body body; /**< Body. */ +} LV2_Atom_Literal; + +/** An atom:Tuple. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + /* Contents (a series of complete atoms) follow here. */ +} LV2_Atom_Tuple; + +/** The body of an atom:Vector. */ +typedef struct { + uint32_t child_size; /**< The size of each element in the vector. */ + uint32_t child_type; /**< The type of each element in the vector. */ + /* Contents (a series of packed atom bodies) follow here. */ +} LV2_Atom_Vector_Body; + +/** An atom:Vector. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Vector_Body body; /**< Body. */ +} LV2_Atom_Vector; + +/** The body of an atom:Property (e.g. in an atom:Object). */ +typedef struct { + uint32_t key; /**< Key (predicate) (mapped URI). */ + uint32_t context; /**< Context URID (may be, and generally is, 0). */ + LV2_Atom value; /**< Value atom header. */ + /* Value atom body follows here. */ +} LV2_Atom_Property_Body; + +/** An atom:Property. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Property_Body body; /**< Body. */ +} LV2_Atom_Property; + +/** The body of an atom:Object. May be cast to LV2_Atom. */ +typedef struct { + uint32_t id; /**< URID (atom:Resource) or blank ID (atom:Blank). */ + uint32_t otype; /**< Type URID (same as rdf:type, for fast dispatch). */ + /* Contents (a series of property bodies) follow here. */ +} LV2_Atom_Object_Body; + +/** An atom:Object. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Object_Body body; /**< Body. */ +} LV2_Atom_Object; + +/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */ +typedef struct { + /** Time stamp. Which type is valid is determined by context. */ + union { + int64_t frames; /**< Time in audio frames. */ + double beats; /**< Time in beats. */ + } time; + LV2_Atom body; /**< Event body atom header. */ + /* Body atom contents follow here. */ +} LV2_Atom_Event; + +/** + The body of an atom:Sequence (a sequence of events). + + The unit field is either a URID that described an appropriate time stamp + type, or may be 0 where a default stamp type is known. For + LV2_Descriptor::run(), the default stamp type is audio frames. + + The contents of a sequence is a series of LV2_Atom_Event, each aligned + to 64-bits, e.g.: + <pre> + | Event 1 (size 6) | Event 2 + | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + |FRAMES |SUBFRMS|TYPE |SIZE |DATADATADATAPAD|FRAMES |SUBFRMS|... + </pre> +*/ +typedef struct { + uint32_t unit; /**< URID of unit of event time stamps. */ + uint32_t pad; /**< Currently unused. */ + /* Contents (a series of events) follow here. */ +} LV2_Atom_Sequence_Body; + +/** An atom:Sequence. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Sequence_Body body; /**< Body. */ +} LV2_Atom_Sequence; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_ATOM_H */ diff --git a/distrho/src/lv2/buf-size.h b/distrho/src/lv2/buf-size.h @@ -0,0 +1,30 @@ +/* + Copyright 2007-2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef LV2_BUF_SIZE_H +#define LV2_BUF_SIZE_H + +#define LV2_BUF_SIZE_URI "http://lv2plug.in/ns/ext/buf-size" +#define LV2_BUF_SIZE_PREFIX LV2_BUF_SIZE_URI "#" + +#define LV2_BUF_SIZE__boundedBlockLength LV2_BUF_SIZE_PREFIX "boundedBlockLength" +#define LV2_BUF_SIZE__fixedBlockLength LV2_BUF_SIZE_PREFIX "fixedBlockLength" +#define LV2_BUF_SIZE__maxBlockLength LV2_BUF_SIZE_PREFIX "maxBlockLength" +#define LV2_BUF_SIZE__minBlockLength LV2_BUF_SIZE_PREFIX "minBlockLength" +#define LV2_BUF_SIZE__powerOf2BlockLength LV2_BUF_SIZE_PREFIX "powerOf2BlockLength" +#define LV2_BUF_SIZE__sequenceSize LV2_BUF_SIZE_PREFIX "sequenceSize" + +#endif /* LV2_BUF_SIZE_H */ diff --git a/distrho/src/lv2/data-access.h b/distrho/src/lv2/data-access.h @@ -0,0 +1,63 @@ +/* + LV2 Data Access Extension + Copyright 2008-2011 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file data-access.h + C header for the LV2 Extension Data extension + <http://lv2plug.in/ns/ext/data-access>. + + This extension defines a method for (e.g.) plugin UIs to have (possibly + marshalled) access to the extension_data function on a plugin instance. +*/ + +#ifndef LV2_DATA_ACCESS_H +#define LV2_DATA_ACCESS_H + +#define LV2_DATA_ACCESS_URI "http://lv2plug.in/ns/ext/data-access" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The data field of the LV2_Feature for this extension. + + To support this feature the host must pass an LV2_Feature struct to the + instantiate method with URI "http://lv2plug.in/ns/ext/data-access" + and data pointed to an instance of this struct. +*/ +typedef struct { + /** + A pointer to a method the UI can call to get data (of a type specified + by some other extension) from the plugin. + + This call never is never guaranteed to return anything, UIs should + degrade gracefully if direct access to the plugin data is not possible + (in which case this function will return NULL). + + This is for access to large data that can only possibly work if the UI + and plugin are running in the same process. For all other things, use + the normal LV2 UI communication system. + */ + const void* (*data_access)(const char* uri); +} LV2_Extension_Data_Feature; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_DATA_ACCESS_H */ diff --git a/distrho/src/lv2/dynmanifest.h b/distrho/src/lv2/dynmanifest.h @@ -0,0 +1,144 @@ +/* + Dynamic manifest specification for LV2 + Copyright 2008-2011 Stefano D'Angelo <zanga.mail@gmail.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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file dynmanifest.h + C header for the LV2 Dynamic Manifest extension + <http://lv2plug.in/ns/ext/dynmanifest>. + Revision: 1.2 +*/ + +#ifndef LV2_DYN_MANIFEST_H_INCLUDED +#define LV2_DYN_MANIFEST_H_INCLUDED + +#include <stdio.h> + +#include "lv2.h" + +#define LV2_DYN_MANIFEST_URI "http://lv2plug.in/ns/ext/dynmanifest" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Dynamic manifest generator handle. + + This handle indicates a particular status of a dynamic manifest generator. + The host MUST NOT attempt to interpret it and, unlikely LV2_Handle, it is + NOT even valid to compare this to NULL. The dynamic manifest generator MAY + use it to reference internal data. +*/ +typedef void * LV2_Dyn_Manifest_Handle; + +/** + Generate the dynamic manifest. + + @param handle Pointer to an uninitialized dynamic manifest generator handle. + + @param features NULL terminated array of LV2_Feature structs which represent + the features the host supports. The dynamic manifest generator may refuse to + (re)generate the dynamic manifest if required features are not found here + (however hosts SHOULD NOT use this as a discovery mechanism, instead of + reading the static manifest file). This array must always exist; if a host + has no features, it MUST pass a single element array containing NULL. + + @return 0 on success, otherwise a non-zero error code. The host SHOULD + evaluate the result of the operation by examining the returned value and + MUST NOT try to interpret the value of handle. +*/ +int lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle * handle, + const LV2_Feature *const * features); + +/** + Fetch a "list" of subject URIs described in the dynamic manifest. + + The dynamic manifest generator has to fill the resource only with the needed + triples to make the host aware of the "objects" it wants to expose. For + example, if the plugin library exposes a regular LV2 plugin, it should + output only a triple like the following: + + <http://www.example.com/plugin/uri> a lv2:Plugin . + + The objects that are elegible for exposure are those that would need to be + represented by a subject node in a static manifest. + + @param handle Dynamic manifest generator handle. + + @param fp FILE * identifying the resource the host has to set up for the + dynamic manifest generator. The host MUST pass a writable, empty resource to + this function, and the dynamic manifest generator MUST ONLY perform write + operations on it at the end of the stream (e.g., using only fprintf(), + fwrite() and similar). + + @return 0 on success, otherwise a non-zero error code. +*/ +int lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle, + FILE * fp); + +/** + Function that fetches data related to a specific URI. + + The dynamic manifest generator has to fill the resource with data related to + object represented by the given URI. For example, if the library exposes a + regular LV2 plugin whose URI, as retrieved by the host using + lv2_dyn_manifest_get_subjects() is http://www.example.com/plugin/uri, it + should output something like: + + <pre> + <http://www.example.com/plugin/uri> + a lv2:Plugin ; + doap:name "My Plugin" ; + lv2:binary <mylib.so> ; + etc:etc "..." . + </pre> + + @param handle Dynamic manifest generator handle. + + @param fp FILE * identifying the resource the host has to set up for the + dynamic manifest generator. The host MUST pass a writable resource to this + function, and the dynamic manifest generator MUST ONLY perform write + operations on it at the current position of the stream (e.g. using only + fprintf(), fwrite() and similar). + + @param uri URI to get data about (in the "plain" form, i.e., absolute URI + without Turtle prefixes). + + @return 0 on success, otherwise a non-zero error code. +*/ +int lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle, + FILE * fp, + const char * uri); + +/** + Function that ends the operations on the dynamic manifest generator. + + This function SHOULD be used by the dynamic manifest generator to perform + cleanup operations, etc. + + Once this function is called, referring to handle will cause undefined + behavior. + + @param handle Dynamic manifest generator handle. +*/ +void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle); + +#ifdef __cplusplus +} +#endif + +#endif /* LV2_DYN_MANIFEST_H_INCLUDED */ diff --git a/distrho/src/lv2/event-helpers.h b/distrho/src/lv2/event-helpers.h @@ -0,0 +1,263 @@ +/* + Copyright 2008-2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file event-helpers.h Helper functions for the LV2 Event extension + <http://lv2plug.in/ns/ext/event>. +*/ + +#ifndef LV2_EVENT_HELPERS_H +#define LV2_EVENT_HELPERS_H + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "event.h" + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +/** @file + * Helper functions for the LV2 Event extension + * <http://lv2plug.in/ns/ext/event>. + * + * These functions are provided for convenience only, use of them is not + * required for supporting lv2ev (i.e. the events extension is defined by the + * raw buffer format described in lv2_event.h and NOT by this API). + * + * Note that these functions are all static inline which basically means: + * do not take the address of these functions. */ + + +/** Pad a size to 64 bits (for event sizes) */ +static inline uint16_t +lv2_event_pad_size(uint16_t size) +{ + return (size + 7) & (~7); +} + + +/** Initialize (empty, reset..) an existing event buffer. + * The contents of buf are ignored entirely and overwritten, except capacity + * which is unmodified. */ +static inline void +lv2_event_buffer_reset(LV2_Event_Buffer* buf, + uint16_t stamp_type, + uint8_t *data) +{ + buf->data = data; + buf->header_size = sizeof(LV2_Event_Buffer); + buf->stamp_type = stamp_type; + buf->event_count = 0; + buf->size = 0; +} + + +/** Allocate a new, empty event buffer. */ +static inline LV2_Event_Buffer* +lv2_event_buffer_new(uint32_t capacity, uint16_t stamp_type) +{ + const size_t size = sizeof(LV2_Event_Buffer) + capacity; + LV2_Event_Buffer* buf = (LV2_Event_Buffer*)malloc(size); + if (buf != NULL) { + buf->capacity = capacity; + lv2_event_buffer_reset(buf, stamp_type, (uint8_t *)(buf + 1)); + return buf; + } else { + return NULL; + } +} + + +/** An iterator over an LV2_Event_Buffer. + * + * Multiple simultaneous read iterators over a single buffer is fine, + * but changing the buffer invalidates all iterators (e.g. RW Lock). */ +typedef struct { + LV2_Event_Buffer* buf; + uint32_t offset; +} LV2_Event_Iterator; + + +/** Reset an iterator to point to the start of @a buf. + * @return True if @a iter is valid, otherwise false (buffer is empty) */ +static inline bool +lv2_event_begin(LV2_Event_Iterator* iter, + LV2_Event_Buffer* buf) +{ + iter->buf = buf; + iter->offset = 0; + return (buf->size > 0); +} + + +/** Check if @a iter is valid. + * @return True if @a iter is valid, otherwise false (past end of buffer) */ +static inline bool +lv2_event_is_valid(LV2_Event_Iterator* iter) +{ + return (iter->buf && (iter->offset < iter->buf->size)); +} + + +/** Advance @a iter forward one event. + * @a iter must be valid. + * @return True if @a iter is valid, otherwise false (reached end of buffer) */ +static inline bool +lv2_event_increment(LV2_Event_Iterator* iter) +{ + if (!lv2_event_is_valid(iter)) { + return false; + } + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + iter->offset += lv2_event_pad_size(sizeof(LV2_Event) + ev->size); + + return true; +} + + +/** Dereference an event iterator (get the event currently pointed at). + * @a iter must be valid. + * @a data if non-NULL, will be set to point to the contents of the event + * returned. + * @return A Pointer to the event @a iter is currently pointing at, or NULL + * if the end of the buffer is reached (in which case @a data is + * also set to NULL). */ +static inline LV2_Event* +lv2_event_get(LV2_Event_Iterator* iter, + uint8_t** data) +{ + if (!lv2_event_is_valid(iter)) { + return NULL; + } + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + if (data) + *data = (uint8_t*)ev + sizeof(LV2_Event); + + return ev; +} + + +/** Write an event at @a iter. + * The event (if any) pointed to by @a iter will be overwritten, and @a iter + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data) +{ + if (!iter->buf) + return false; + + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) + return false; + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size); + ++iter->buf->event_count; + + size = lv2_event_pad_size(sizeof(LV2_Event) + size); + iter->buf->size += size; + iter->offset += size; + + return true; +} + + +/** Reserve space for an event in the buffer and return a pointer to + the memory where the caller can write the event data, or NULL if there + is not enough room in the buffer. */ +static inline uint8_t* +lv2_event_reserve(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size) +{ + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) + return NULL; + + LV2_Event* const ev = (LV2_Event*)((uint8_t*)iter->buf->data + + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + ++iter->buf->event_count; + + size = lv2_event_pad_size(sizeof(LV2_Event) + size); + iter->buf->size += size; + iter->offset += size; + + return (uint8_t*)ev + sizeof(LV2_Event); +} + + +/** Write an event at @a iter. + * The event (if any) pointed to by @a iter will be overwritten, and @a iter + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write_event(LV2_Event_Iterator* iter, + const LV2_Event* ev, + const uint8_t* data) +{ + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + ev->size) + return false; + + LV2_Event* const write_ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + *write_ev = *ev; + memcpy((uint8_t*)write_ev + sizeof(LV2_Event), data, ev->size); + ++iter->buf->event_count; + + const uint16_t size = lv2_event_pad_size(sizeof(LV2_Event) + ev->size); + iter->buf->size += size; + iter->offset += size; + + return true; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_EVENT_HELPERS_H */ + diff --git a/distrho/src/lv2/event.h b/distrho/src/lv2/event.h @@ -0,0 +1,294 @@ +/* + Copyright 2008-2011 David Robillard <http://drobilla.net> + Copyright 2006-2007 Lars Luthman <lars.luthman@gmail.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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file event.h + C API for the LV2 Event extension <http://lv2plug.in/ns/ext/event>. + + This extension is a generic transport mechanism for time stamped events + of any type (e.g. MIDI, OSC, ramps, etc). Each port can transport mixed + events of any type; the type of events and timestamps are defined by a URI + which is mapped to an integer by the host for performance reasons. + + This extension requires the host to support the LV2 URI Map extension. + Any host which supports this extension MUST guarantee that any call to + the LV2 URI Map uri_to_id function with the URI of this extension as the + 'map' argument returns a value within the range of uint16_t. +*/ + +#ifndef LV2_EVENT_H +#define LV2_EVENT_H + +#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" +#define LV2_EVENT_PREFIX LV2_EVENT_URI "#" + +#define LV2_EVENT__Event LV2_EVENT_PREFIX "Event" +#define LV2_EVENT__EventPort LV2_EVENT_PREFIX "EventPort" +#define LV2_EVENT__FrameStamp LV2_EVENT_PREFIX "FrameStamp" +#define LV2_EVENT__TimeStamp LV2_EVENT_PREFIX "TimeStamp" +#define LV2_EVENT__generatesTimeStamp LV2_EVENT_PREFIX "generatesTimeStamp" +#define LV2_EVENT__generic LV2_EVENT_PREFIX "generic" +#define LV2_EVENT__inheritsEvent LV2_EVENT_PREFIX "inheritsEvent" +#define LV2_EVENT__inheritsTimeStamp LV2_EVENT_PREFIX "inheritsTimeStamp" +#define LV2_EVENT__supportsEvent LV2_EVENT_PREFIX "supportsEvent" +#define LV2_EVENT__supportsTimeStamp LV2_EVENT_PREFIX "supportsTimeStamp" + +#define LV2_EVENT_AUDIO_STAMP 0 + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The best Pulses Per Quarter Note for tempo-based uint32_t timestamps. + Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisble + by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. +*/ +static const uint32_t LV2_EVENT_PPQN = 3136573440U; + +/** + An LV2 event (header only). + + LV2 events are generic time-stamped containers for any type of event. + The type field defines the format of a given event's contents. + + This struct defines the header of an LV2 event. An LV2 event is a single + chunk of POD (plain old data), usually contained in a flat buffer (see + LV2_EventBuffer below). Unless a required feature says otherwise, hosts may + assume a deep copy of an LV2 event can be created safely using a simple: + + memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) +*/ +typedef struct { + /** + The frames portion of timestamp. The units used here can optionally be + set for a port (with the lv2ev:timeUnits property), otherwise this is + audio frames, corresponding to the sample_count parameter of the LV2 run + method (e.g. frame 0 is the first frame for that call to run). + */ + uint32_t frames; + + /** + The sub-frames portion of timestamp. The units used here can optionally + be set for a port (with the lv2ev:timeUnits property), otherwise this is + 1/(2^32) of an audio frame. + */ + uint32_t subframes; + + /** + The type of this event, as a number which represents some URI + defining an event type. This value MUST be some value previously + returned from a call to the uri_to_id function defined in the LV2 + URI map extension (see lv2_uri_map.h). + There are special rules which must be followed depending on the type + of an event. If the plugin recognizes an event type, the definition + of that event type will describe how to interpret the event, and + any required behaviour. Otherwise, if the type is 0, this event is a + non-POD event and lv2_event_unref MUST be called if the event is + 'dropped' (see above). Even if the plugin does not understand an event, + it may pass the event through to an output by simply copying (and NOT + calling lv2_event_unref). These rules are designed to allow for generic + event handling plugins and large non-POD events, but with minimal hassle + on simple plugins that "don't care" about these more advanced features. + */ + uint16_t type; + + /** + The size of the data portion of this event in bytes, which immediately + follows. The header size (12 bytes) is not included in this value. + */ + uint16_t size; + + /* size bytes of data follow here */ +} LV2_Event; + + +/** + A buffer of LV2 events (header only). + + Like events (which this contains) an event buffer is a single chunk of POD: + the entire buffer (including contents) can be copied with a single memcpy. + The first contained event begins sizeof(LV2_EventBuffer) bytes after the + start of this struct. + + After this header, the buffer contains an event header (defined by struct + LV2_Event), followed by that event's contents (padded to 64 bits), followed + by another header, etc: + + | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | + |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... +*/ +typedef struct { + /** + The contents of the event buffer. This may or may not reside in the + same block of memory as this header, plugins must not assume either. + The host guarantees this points to at least capacity bytes of allocated + memory (though only size bytes of that are valid events). + */ + uint8_t* data; + + /** + The size of this event header in bytes (including everything). + + This is to allow for extending this header in the future without + breaking binary compatibility. Whenever this header is copied, + it MUST be done using this field (and NOT the sizeof this struct). + */ + uint16_t header_size; + + /** + The type of the time stamps for events in this buffer. + As a special exception, '0' always means audio frames and subframes + (1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate. + + INPUTS: The host must set this field to the numeric ID of some URI + defining the meaning of the frames/subframes fields of contained events + (obtained by the LV2 URI Map uri_to_id function with the URI of this + extension as the 'map' argument, see lv2_uri_map.h). The host must + never pass a plugin a buffer which uses a stamp type the plugin does not + 'understand'. The value of this field must never change, except when + connect_port is called on the input port, at which time the host MUST + have set the stamp_type field to the value that will be used for all + subsequent run calls. + + OUTPUTS: The plugin may set this to any value that has been returned + from uri_to_id with the URI of this extension for a 'map' argument. + When connected to a buffer with connect_port, output ports MUST set this + field to the type of time stamp they will be writing. On any call to + connect_port on an event input port, the plugin may change this field on + any output port, it is the responsibility of the host to check if any of + these values have changed and act accordingly. + */ + uint16_t stamp_type; + + /** + The number of events in this buffer. + + INPUTS: The host must set this field to the number of events contained + in the data buffer before calling run(). The plugin must not change + this field. + + OUTPUTS: The plugin must set this field to the number of events it has + written to the buffer before returning from run(). Any initial value + should be ignored by the plugin. + */ + uint32_t event_count; + + /** + The size of the data buffer in bytes. + This is set by the host and must not be changed by the plugin. + The host is allowed to change this between run() calls. + */ + uint32_t capacity; + + /** + The size of the initial portion of the data buffer containing data. + + INPUTS: The host must set this field to the number of bytes used + by all events it has written to the buffer (including headers) + before calling the plugin's run(). + The plugin must not change this field. + + OUTPUTS: The plugin must set this field to the number of bytes + used by all events it has written to the buffer (including headers) + before returning from run(). + Any initial value should be ignored by the plugin. + */ + uint32_t size; +} LV2_Event_Buffer; + + +/** + Opaque pointer to host data. +*/ +typedef void* LV2_Event_Callback_Data; + + +/** + Non-POD events feature. + + To support this feature the host must pass an LV2_Feature struct to the + plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event" + and data pointed to an instance of this struct. Note this feature + is not mandatory to support the event extension. +*/ +typedef struct { + /** + Opaque pointer to host data. + + The plugin MUST pass this to any call to functions in this struct. + Otherwise, it must not be interpreted in any way. + */ + LV2_Event_Callback_Data callback_data; + + /** + Take a reference to a non-POD event. + + If a plugin receives an event with type 0, it means the event is a + pointer to some object in memory and not a flat sequence of bytes + in the buffer. When receiving a non-POD event, the plugin already + has an implicit reference to the event. If the event is stored AND + passed to an output, lv2_event_ref MUST be called on that event. + If the event is only stored OR passed through, this is not necessary + (as the plugin already has 1 implicit reference). + + @param event An event received at an input that will not be copied to + an output or stored in any way. + + @param context The calling context. Like event types, this is a mapped + URI, see lv2_context.h. Simple plugin with just a run() method should + pass 0 here (the ID of the 'standard' LV2 run context). The host + guarantees that this function is realtime safe iff @a context is + realtime safe. + + PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); + + /** + Drop a reference to a non-POD event. + + If a plugin receives an event with type 0, it means the event is a + pointer to some object in memory and not a flat sequence of bytes + in the buffer. If the plugin does not pass the event through to + an output or store it internally somehow, it MUST call this function + on the event (more information on using non-POD events below). + + @param event An event received at an input that will not be copied to an + output or stored in any way. + + @param context The calling context. Like event types, this is a mapped + URI, see lv2_context.h. Simple plugin with just a run() method should + pass 0 here (the ID of the 'standard' LV2 run context). The host + guarantees that this function is realtime safe iff @a context is + realtime safe. + + PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); +} LV2_Event_Feature; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_EVENT_H */ diff --git a/distrho/src/lv2/instance-access.h b/distrho/src/lv2/instance-access.h @@ -0,0 +1,37 @@ +/* + LV2 Instance Access Extension + Copyright 2008-2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef LV2_INSTANCE_ACCESS_H +#define LV2_INSTANCE_ACCESS_H + +#define LV2_INSTANCE_ACCESS_URI "http://lv2plug.in/ns/ext/instance-access" + +/** + @file instance-access.h + C header for the LV2 Instance Access extension + <http://lv2plug.in/ns/ext/instance-access>. + + This extension defines a method for (e.g.) plugin UIs to get a direct + handle to an LV2 plugin instance (LV2_Handle), if possible. + + To support this feature the host must pass an LV2_Feature struct to the + UI instantiate method with URI "http://lv2plug.in/ns/ext/instance-access" + and data pointed directly to the LV2_Handle of the plugin instance. +*/ + +#endif /* LV2_INSTANCE_ACCESS_H */ + diff --git a/distrho/src/lv2/log.h b/distrho/src/lv2/log.h @@ -0,0 +1,99 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file log.h C header for the LV2 Log extension + <http://lv2plug.in/ns/ext/log>. +*/ + +#ifndef LV2_LOG_H +#define LV2_LOG_H + +#define LV2_LOG_URI "http://lv2plug.in/ns/ext/log" +#define LV2_LOG_PREFIX LV2_LOG_URI "#" + +#define LV2_LOG__Entry LV2_LOG_PREFIX "Entry" +#define LV2_LOG__Error LV2_LOG_PREFIX "Error" +#define LV2_LOG__Note LV2_LOG_PREFIX "Note" +#define LV2_LOG__Trace LV2_LOG_PREFIX "Trace" +#define LV2_LOG__Warning LV2_LOG_PREFIX "Warning" +#define LV2_LOG__log LV2_LOG_PREFIX "log" + +#include <stdarg.h> + +#include "urid.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +/** Allow type checking of printf-like functions. */ +# define LV2_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) +#else +# define LV2_LOG_FUNC(fmt, arg1) +#endif + +/** + Opaque data to host data for LV2_Log_Log. +*/ +typedef void* LV2_Log_Handle; + +/** + Log feature (LV2_LOG__log) +*/ +typedef struct _LV2_Log { + /** + Opaque pointer to host data. + + This MUST be passed to methods in this struct whenever they are called. + Otherwise, it must not be interpreted in any way. + */ + LV2_Log_Handle handle; + + /** + Log a message, passing format parameters directly. + + The API of this function matches that of the standard C printf function, + except for the addition of the first two parameters. This function may + be called from any non-realtime context, or from any context if @p type + is @ref LV2_LOG__Trace. + */ + LV2_LOG_FUNC(3, 4) + int (*printf)(LV2_Log_Handle handle, + LV2_URID type, + const char* fmt, ...); + + /** + Log a message, passing format parameters in a va_list. + + The API of this function matches that of the standard C vprintf + function, except for the addition of the first two parameters. This + function may be called from any non-realtime context, or from any + context if @p type is @ref LV2_LOG__Trace. + */ + LV2_LOG_FUNC(3, 0) + int (*vprintf)(LV2_Log_Handle handle, + LV2_URID type, + const char* fmt, + va_list ap); +} LV2_Log_Log; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_LOG_H */ diff --git a/distrho/src/lv2/logger.h b/distrho/src/lv2/logger.h @@ -0,0 +1,145 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file logger.h Convenience API for easy logging in plugin code. + + This file provides simple wrappers for the most common log operations for + use in plugin implementations. If host support for logging is not + available, then these functions will print to stderr instead. + + This header is non-normative, it is provided for convenience. +*/ + +#ifndef LV2_ATOM_LOGGER_H +#define LV2_ATOM_LOGGER_H + +#include <stdio.h> + +#include "log.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Logger convenience API state. +*/ +typedef struct { + LV2_Log_Log* log; + + LV2_URID Error; + LV2_URID Note; + LV2_URID Trace; + LV2_URID Warning; +} LV2_Log_Logger; + +/** + Initialise @p logger. + + URIs will be mapped using @p map and stored, a reference to @p map itself is + not held. Both @p map and @p log may be NULL when unsupported by the host, + in which case the implementation will fall back to printing to stderr. +*/ +static inline void +lv2_log_logger_init(LV2_Log_Logger* logger, + LV2_URID_Map* map, + LV2_Log_Log* log) +{ + memset(logger, 0, sizeof(LV2_Log_Logger)); + logger->log = log; + if (map) { + logger->Error = map->map(map->handle, LV2_LOG__Error); + logger->Note = map->map(map->handle, LV2_LOG__Note); + logger->Trace = map->map(map->handle, LV2_LOG__Trace); + logger->Warning = map->map(map->handle, LV2_LOG__Warning); + } +} + +/** + Log a message to the host, or stderr if support is unavailable. +*/ +LV2_LOG_FUNC(3, 0) +static inline int +lv2_log_vprintf(LV2_Log_Logger* logger, + LV2_URID type, + const char* fmt, + va_list args) +{ + if (logger->log) { + return logger->log->vprintf(logger->log->handle, type, fmt, args); + } else { + return vfprintf(stderr, fmt, args); + } +} + +/** Log an error via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_error(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Error, fmt, args); + va_end(args); + return ret; +} + +/** Log a note via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_note(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Note, fmt, args); + va_end(args); + return ret; +} + +/** Log a trace via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_trace(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Trace, fmt, args); + va_end(args); + return ret; +} + +/** Log a warning via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_warning(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Warning, fmt, args); + va_end(args); + return ret; +} + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_LOG_LOGGER_H */ diff --git a/distrho/src/lv2/lv2-midifunctions.h b/distrho/src/lv2/lv2-midifunctions.h @@ -0,0 +1,98 @@ +/**************************************************************************** + + lv2-midifunctions.h - support file for using MIDI in LV2 plugins + + Copyright (C) 2006 Lars Luthman <lars.luthman@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + +****************************************************************************/ + +#ifndef LV2_MIDIFUNCTIONS +#define LV2_MIDIFUNCTIONS + +#include "lv2-miditype.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + LV2_MIDI* midi; + uint32_t frame_count; + uint32_t position; +} LV2_MIDIState; + + +inline double lv2midi_get_event(LV2_MIDIState* state, + double* timestamp, + uint32_t* size, + unsigned char** data) { + + if (state->position >= state->midi->size) { + state->position = state->midi->size; + *timestamp = state->frame_count; + *size = 0; + *data = NULL; + return *timestamp; + } + + *timestamp = *(double*)(state->midi->data + state->position); + *size = *(size_t*)(state->midi->data + state->position + sizeof(double)); + *data = state->midi->data + state->position + + sizeof(double) + sizeof(size_t); + return *timestamp; +} + + +inline double lv2midi_step(LV2_MIDIState* state) { + + if (state->position >= state->midi->size) { + state->position = state->midi->size; + return state->frame_count; + } + + state->position += sizeof(double); + size_t size = *(size_t*)(state->midi->data + state->position); + state->position += sizeof(size_t); + state->position += size; + return *(double*)(state->midi->data + state->position); +} + + +inline void lv2midi_put_event(LV2_MIDIState* state, + double timestamp, + uint32_t size, + const unsigned char* data) { + + if (state->midi->size + sizeof(double) + sizeof(size_t) + size < state->midi->capacity) + { + *((double*)(state->midi->data + state->midi->size)) = timestamp; + state->midi->size += sizeof(double); + *((size_t*)(state->midi->data + state->midi->size)) = size; + state->midi->size += sizeof(size_t); + memcpy(state->midi->data + state->midi->size, data, size); + + state->midi->size += size; + state->midi->event_count++; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/distrho/src/lv2/lv2-miditype.h b/distrho/src/lv2/lv2-miditype.h @@ -0,0 +1,175 @@ +/**************************************************************************** + + lv2-miditype.h - header file for using MIDI in LV2 plugins + + Copyright (C) 2006 Lars Luthman <lars.luthman@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + +****************************************************************************/ + +#ifndef LV2_MIDITYPE_H +#define LV2_MIDITYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** This data structure is used to contain the MIDI events for one run() + cycle. The port buffer for a LV2 port that has the datatype + <http://ll-plugins.nongnu.org/lv2/ext/miditype> should be a pointer + to an instance of this struct. + + To store two Note On events on MIDI channel 0 in a buffer, with timestamps + 12 and 35.5, you could use something like this code (assuming that + midi_data is a variable of type LV2_MIDI): + @code + + size_t buffer_offset = 0; + *(double*)(midi_data->data + buffer_offset) = 12; + buffer_offset += sizeof(double); + *(size_t*)(midi_data->data + buffer_offset) = 3; + buffer_offset += sizeof(size_t); + midi_data->data[buffer_offset++] = 0x90; + midi_data->data[buffer_offset++] = 0x48; + midi_data->data[buffer_offset++] = 0x64; + ++midi_data->event_count; + + *(double*)(midi_data->data + buffer_offset) = 35.5; + buffer_offset += sizeof(double); + *(size_t*)(midi_data->data + buffer_offset) = 3; + buffer_offset += sizeof(size_t); + midi_data->data[buffer_offset++] = 0x90; + midi_data->data[buffer_offset++] = 0x55; + midi_data->data[buffer_offset++] = 0x64; + ++midi_data->event_count; + + midi_data->size = buffer_offset; + + @endcode + + This would be done by the host in the case of an input port, and by the + plugin in the case of an output port. Whoever is writing events to the + buffer must also take care not to exceed the capacity of the data buffer. + + To read events from a buffer, you could do something like this: + @code + + size_t buffer_offset = 0; + uint32_t i; + for (i = 0; i < midi_data->event_count; ++i) { + double timestamp = *(double*)(midi_data->data + buffer_offset); + buffer_offset += sizeof(double); + size_t size = *(size_t*)(midi_data->data + buffer_offset); + buffer_offset += sizeof(size_t); + do_something_with_event(timestamp, size, + midi_data->data + buffer_offset); + buffer_offset += size; + } + + @endcode +*/ +typedef struct { + + /** The number of MIDI events in the data buffer. + INPUT PORTS: It's the host's responsibility to set this field to the + number of MIDI events contained in the data buffer before calling the + plugin's run() function. The plugin may not change this field. + OUTPUT PORTS: It's the plugin's responsibility to set this field to the + number of MIDI events it has stored in the data buffer before returning + from the run() function. Any initial value should be ignored by the + plugin. + */ + uint32_t event_count; + + /** The size of the data buffer in bytes. It is set by the host and may not + be changed by the plugin. The host is allowed to change this between + run() calls. + */ + uint32_t capacity; + + /** The size of the initial part of the data buffer that actually contains + data. + INPUT PORTS: It's the host's responsibility to set this field to the + number of bytes used by all MIDI events it has written to the buffer + (including timestamps and size fields) before calling the plugin's + run() function. The plugin may not change this field. + OUTPUT PORTS: It's the plugin's responsibility to set this field to + the number of bytes used by all MIDI events it has written to the + buffer (including timestamps and size fields) before returning from + the run() function. Any initial value should be ignored by the plugin. + */ + uint32_t size; + + /** The data buffer that is used to store MIDI events. The events are packed + after each other, and the format of each event is as follows: + + First there is a timestamp, which should have the type "double", + i.e. have the same bit size as a double and the same bit layout as a + double (whatever that is on the current platform). This timestamp gives + the offset from the beginning of the current cycle, in frames, that + the MIDI event occurs on. It must be strictly smaller than the 'nframes' + parameter to the current run() call. The MIDI events in the buffer must + be ordered by their timestamp, e.g. an event with a timestamp of 123.23 + must be stored after an event with a timestamp of 65.0. + + The second part of the event is a size field, which should have the type + "size_t" (as defined in the standard C header stddef.h). It should + contain the size of the MIDI data for this event, i.e. the number of + bytes used to store the actual MIDI event. The bytes used by the + timestamp and the size field should not be counted. + + The third part of the event is the actual MIDI data. There are some + requirements that must be followed: + + * Running status is not allowed. Every event must have its own status + byte. + * Note On events with velocity 0 are not allowed. These events are + equivalent to Note Off in standard MIDI streams, but in order to make + plugins and hosts easier to write, as well as more efficient, only + proper Note Off events are allowed as Note Off. + * "Realtime events" (status bytes 0xF8 to 0xFF) are allowed, but may not + occur inside other events like they are allowed to in hardware MIDI + streams. + * All events must be fully contained in a single data buffer, i.e. events + may not "wrap around" by storing the first few bytes in one buffer and + then wait for the next run() call to store the rest of the event. If + there isn't enough space in the current data buffer to store an event, + the event will either have to wait until next run() call, be ignored, + or compensated for in some more clever way. + * All events must be valid MIDI events. This means for example that + only the first byte in each event (the status byte) may have the eighth + bit set, that Note On and Note Off events are always 3 bytes long etc. + The MIDI writer (host or plugin) is responsible for writing valid MIDI + events to the buffer, and the MIDI reader (plugin or host) can assume + that all events are valid. + + On a platform where double is 8 bytes and size_t is 4 bytes, the data + buffer layout for a 3-byte event followed by a 4-byte event may look + something like this: + _______________________________________________________________ + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ... + |TIMESTAMP 1 |SIZE 1 |DATA |TIMESTAMP 2 |SIZE 2 |DATA | ... + + */ + unsigned char* data; + +} LV2_MIDI; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/distrho/src/lv2/lv2.h b/distrho/src/lv2/lv2.h @@ -0,0 +1,454 @@ +/* + LV2 - An audio plugin interface specification. + Copyright 2006-2012 Steve Harris, David Robillard. + + Based on LADSPA, Copyright 2000-2002 Richard W.E. Furse, + Paul Barton-Davis, Stefan Westerfeld. + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file lv2.h + API for the LV2 specification <http://lv2plug.in/ns/lv2core>. + Revision: 6.5 +*/ + +#ifndef LV2_H_INCLUDED +#define LV2_H_INCLUDED + +#include <stdint.h> + +#define LV2_CORE_URI "http://lv2plug.in/ns/lv2core" +#define LV2_CORE_PREFIX LV2_CORE_URI "#" + +#define LV2_CORE__AllpassPlugin LV2_CORE_PREFIX "AllpassPlugin" +#define LV2_CORE__AmplifierPlugin LV2_CORE_PREFIX "AmplifierPlugin" +#define LV2_CORE__AnalyserPlugin LV2_CORE_PREFIX "AnalyserPlugin" +#define LV2_CORE__AudioPort LV2_CORE_PREFIX "AudioPort" +#define LV2_CORE__BandpassPlugin LV2_CORE_PREFIX "BandpassPlugin" +#define LV2_CORE__CVPort LV2_CORE_PREFIX "CVPort" +#define LV2_CORE__ChorusPlugin LV2_CORE_PREFIX "ChorusPlugin" +#define LV2_CORE__CombPlugin LV2_CORE_PREFIX "CombPlugin" +#define LV2_CORE__CompressorPlugin LV2_CORE_PREFIX "CompressorPlugin" +#define LV2_CORE__ConstantPlugin LV2_CORE_PREFIX "ConstantPlugin" +#define LV2_CORE__ControlPort LV2_CORE_PREFIX "ControlPort" +#define LV2_CORE__ConverterPlugin LV2_CORE_PREFIX "ConverterPlugin" +#define LV2_CORE__DelayPlugin LV2_CORE_PREFIX "DelayPlugin" +#define LV2_CORE__DistortionPlugin LV2_CORE_PREFIX "DistortionPlugin" +#define LV2_CORE__DynamicsPlugin LV2_CORE_PREFIX "DynamicsPlugin" +#define LV2_CORE__EQPlugin LV2_CORE_PREFIX "EQPlugin" +#define LV2_CORE__EnvelopePlugin LV2_CORE_PREFIX "EnvelopePlugin" +#define LV2_CORE__ExpanderPlugin LV2_CORE_PREFIX "ExpanderPlugin" +#define LV2_CORE__ExtensionData LV2_CORE_PREFIX "ExtensionData" +#define LV2_CORE__Feature LV2_CORE_PREFIX "Feature" +#define LV2_CORE__FilterPlugin LV2_CORE_PREFIX "FilterPlugin" +#define LV2_CORE__FlangerPlugin LV2_CORE_PREFIX "FlangerPlugin" +#define LV2_CORE__FunctionPlugin LV2_CORE_PREFIX "FunctionPlugin" +#define LV2_CORE__GatePlugin LV2_CORE_PREFIX "GatePlugin" +#define LV2_CORE__GeneratorPlugin LV2_CORE_PREFIX "GeneratorPlugin" +#define LV2_CORE__HighpassPlugin LV2_CORE_PREFIX "HighpassPlugin" +#define LV2_CORE__InputPort LV2_CORE_PREFIX "InputPort" +#define LV2_CORE__InstrumentPlugin LV2_CORE_PREFIX "InstrumentPlugin" +#define LV2_CORE__LimiterPlugin LV2_CORE_PREFIX "LimiterPlugin" +#define LV2_CORE__LowpassPlugin LV2_CORE_PREFIX "LowpassPlugin" +#define LV2_CORE__MixerPlugin LV2_CORE_PREFIX "MixerPlugin" +#define LV2_CORE__ModulatorPlugin LV2_CORE_PREFIX "ModulatorPlugin" +#define LV2_CORE__MultiEQPlugin LV2_CORE_PREFIX "MultiEQPlugin" +#define LV2_CORE__OscillatorPlugin LV2_CORE_PREFIX "OscillatorPlugin" +#define LV2_CORE__OutputPort LV2_CORE_PREFIX "OutputPort" +#define LV2_CORE__ParaEQPlugin LV2_CORE_PREFIX "ParaEQPlugin" +#define LV2_CORE__PhaserPlugin LV2_CORE_PREFIX "PhaserPlugin" +#define LV2_CORE__PitchPlugin LV2_CORE_PREFIX "PitchPlugin" +#define LV2_CORE__Plugin LV2_CORE_PREFIX "Plugin" +#define LV2_CORE__PluginBase LV2_CORE_PREFIX "PluginBase" +#define LV2_CORE__Point LV2_CORE_PREFIX "Point" +#define LV2_CORE__Port LV2_CORE_PREFIX "Port" +#define LV2_CORE__PortProperty LV2_CORE_PREFIX "PortProperty" +#define LV2_CORE__Resource LV2_CORE_PREFIX "Resource" +#define LV2_CORE__ReverbPlugin LV2_CORE_PREFIX "ReverbPlugin" +#define LV2_CORE__ScalePoint LV2_CORE_PREFIX "ScalePoint" +#define LV2_CORE__SimulatorPlugin LV2_CORE_PREFIX "SimulatorPlugin" +#define LV2_CORE__SpatialPlugin LV2_CORE_PREFIX "SpatialPlugin" +#define LV2_CORE__Specification LV2_CORE_PREFIX "Specification" +#define LV2_CORE__SpectralPlugin LV2_CORE_PREFIX "SpectralPlugin" +#define LV2_CORE__UtilityPlugin LV2_CORE_PREFIX "UtilityPlugin" +#define LV2_CORE__WaveshaperPlugin LV2_CORE_PREFIX "WaveshaperPlugin" +#define LV2_CORE__appliesTo LV2_CORE_PREFIX "appliesTo" +#define LV2_CORE__binary LV2_CORE_PREFIX "binary" +#define LV2_CORE__connectionOptional LV2_CORE_PREFIX "connectionOptional" +#define LV2_CORE__control LV2_CORE_PREFIX "control" +#define LV2_CORE__default LV2_CORE_PREFIX "default" +#define LV2_CORE__designation LV2_CORE_PREFIX "designation" +#define LV2_CORE__documentation LV2_CORE_PREFIX "documentation" +#define LV2_CORE__enumeration LV2_CORE_PREFIX "enumeration" +#define LV2_CORE__extensionData LV2_CORE_PREFIX "extensionData" +#define LV2_CORE__freeWheeling LV2_CORE_PREFIX "freeWheeling" +#define LV2_CORE__hardRTCapable LV2_CORE_PREFIX "hardRTCapable" +#define LV2_CORE__inPlaceBroken LV2_CORE_PREFIX "inPlaceBroken" +#define LV2_CORE__index LV2_CORE_PREFIX "index" +#define LV2_CORE__integer LV2_CORE_PREFIX "integer" +#define LV2_CORE__isLive LV2_CORE_PREFIX "isLive" +#define LV2_CORE__latency LV2_CORE_PREFIX "latency" +#define LV2_CORE__maximum LV2_CORE_PREFIX "maximum" +#define LV2_CORE__microVersion LV2_CORE_PREFIX "microVersion" +#define LV2_CORE__minimum LV2_CORE_PREFIX "minimum" +#define LV2_CORE__minorVersion LV2_CORE_PREFIX "minorVersion" +#define LV2_CORE__name LV2_CORE_PREFIX "name" +#define LV2_CORE__optionalFeature LV2_CORE_PREFIX "optionalFeature" +#define LV2_CORE__port LV2_CORE_PREFIX "port" +#define LV2_CORE__portProperty LV2_CORE_PREFIX "portProperty" +#define LV2_CORE__project LV2_CORE_PREFIX "project" +#define LV2_CORE__reportsLatency LV2_CORE_PREFIX "reportsLatency" +#define LV2_CORE__requiredFeature LV2_CORE_PREFIX "requiredFeature" +#define LV2_CORE__sampleRate LV2_CORE_PREFIX "sampleRate" +#define LV2_CORE__scalePoint LV2_CORE_PREFIX "scalePoint" +#define LV2_CORE__symbol LV2_CORE_PREFIX "symbol" +#define LV2_CORE__toggled LV2_CORE_PREFIX "toggled" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Plugin Instance Handle. + + This is a handle for one particular instance of a plugin. It is valid to + compare to NULL (or 0 for C++) but otherwise the host MUST NOT attempt to + interpret it. +*/ +typedef void * LV2_Handle; + +/** + Feature. + + Features allow hosts to make additional functionality available to plugins + without requiring modification to the LV2 API. Extensions may define new + features and specify the @ref URI and @ref data to be used if necessary. + Some features, such as lv2:isLive, do not require the host to pass data. +*/ +typedef struct _LV2_Feature { + /** + A globally unique, case-sensitive identifier (URI) for this feature. + + This MUST be a valid URI string as defined by RFC 3986. + */ + const char * URI; + + /** + Pointer to arbitrary data. + + The format of this data is defined by the extension which describes the + feature with the given @ref URI. + */ + void * data; +} LV2_Feature; + +/** + Plugin Descriptor. + + This structure provides the core functions necessary to instantiate and use + a plugin. +*/ +typedef struct _LV2_Descriptor { + /** + A globally unique, case-sensitive identifier for this plugin. + + This MUST be a valid URI string as defined by RFC 3986. All plugins with + the same URI MUST be compatible to some degree, see + http://lv2plug.in/ns/lv2core for details. + */ + const char * URI; + + /** + Instantiate the plugin. + + Note that instance initialisation should generally occur in activate() + rather than here. If a host calls instantiate(), it MUST call cleanup() + at some point in the future. + + @param descriptor Descriptor of the plugin to instantiate. + + @param sample_rate Sample rate, in Hz, for the new plugin instance. + + @param bundle_path Path to the LV2 bundle which contains this plugin + binary. It MUST include the trailing directory separator (e.g. '/') so + that simply appending a filename will yield the path to that file in the + bundle. + + @param features A NULL terminated array of LV2_Feature structs which + represent the features the host supports. Plugins may refuse to + instantiate if required features are not found here. However, hosts MUST + NOT use this as a discovery mechanism: instead, use the RDF data to + determine which features are required and do not attempt to instantiate + unsupported plugins at all. This parameter MUST NOT be NULL, i.e. a host + that supports no features MUST pass a single element array containing + NULL. + + @return A handle for the new plugin instance, or NULL if instantiation + has failed. + */ + LV2_Handle (*instantiate)(const struct _LV2_Descriptor * descriptor, + double sample_rate, + const char * bundle_path, + const LV2_Feature *const * features); + + /** + Connect a port on a plugin instance to a memory location. + + Plugin writers should be aware that the host may elect to use the same + buffer for more than one port and even use the same buffer for both + input and output (see lv2:inPlaceBroken in lv2.ttl). + + If the plugin has the feature lv2:hardRTCapable then there are various + things that the plugin MUST NOT do within the connect_port() function; + see lv2core.ttl for details. + + connect_port() MUST be called at least once for each port before run() + is called, unless that port is lv2:connectionOptional. The plugin must + pay careful attention to the block size passed to run() since the block + allocated may only just be large enough to contain the data, and is not + guaranteed to remain constant between run() calls. + + connect_port() may be called more than once for a plugin instance to + allow the host to change the buffers that the plugin is reading or + writing. These calls may be made before or after activate() or + deactivate() calls. + + @param instance Plugin instance containing the port. + + @param port Index of the port to connect. The host MUST NOT try to + connect a port index that is not defined in the plugin's RDF data. If + it does, the plugin's behaviour is undefined (a crash is likely). + + @param data_location Pointer to data of the type defined by the port + type in the plugin's RDF data (e.g. an array of float for an + lv2:AudioPort). This pointer must be stored by the plugin instance and + used to read/write data when run() is called. Data present at the time + of the connect_port() call MUST NOT be considered meaningful. + */ + void (*connect_port)(LV2_Handle instance, + uint32_t port, + void * data_location); + + /** + Initialise a plugin instance and activate it for use. + + This is separated from instantiate() to aid real-time support and so + that hosts can reinitialise a plugin instance by calling deactivate() + and then activate(). In this case the plugin instance MUST reset all + state information dependent on the history of the plugin instance except + for any data locations provided by connect_port(). If there is nothing + for activate() to do then this field may be NULL. + + When present, hosts MUST call this function once before run() is called + for the first time. This call SHOULD be made as close to the run() call + as possible and indicates to real-time plugins that they are now live, + however plugins MUST NOT rely on a prompt call to run() after + activate(). + + The host MUST NOT call activate() again until deactivate() has been + called first. If a host calls activate(), it MUST call deactivate() at + some point in the future. Note that connect_port() may be called before + or after activate(). + */ + void (*activate)(LV2_Handle instance); + + /** + Run a plugin instance for a block. + + Note that if an activate() function exists then it must be called before + run(). If deactivate() is called for a plugin instance then run() may + not be called until activate() has been called again. + + If the plugin has the feature lv2:hardRTCapable then there are various + things that the plugin MUST NOT do within the run() function (see + lv2core.ttl for details). + + As a special case, when @p sample_count == 0, the plugin should update + any output ports that represent a single instant in time (e.g. control + ports, but not audio ports). This is particularly useful for latent + plugins, which should update their latency output port so hosts can + pre-roll plugins to compute latency. Plugins MUST NOT crash when + @p sample_count == 0. + + @param instance Instance to be run. + + @param sample_count The block size (in samples) for which the plugin + instance must run. + */ + void (*run)(LV2_Handle instance, + uint32_t sample_count); + + /** + Deactivate a plugin instance (counterpart to activate()). + + Hosts MUST deactivate all activated instances after they have been run() + for the last time. This call SHOULD be made as close to the last run() + call as possible and indicates to real-time plugins that they are no + longer live, however plugins MUST NOT rely on prompt deactivation. If + there is nothing for deactivate() to do then this field may be NULL + + Deactivation is not similar to pausing since the plugin instance will be + reinitialised by activate(). However, deactivate() itself MUST NOT fully + reset plugin state. For example, the host may deactivate a plugin, then + store its state (using some extension to do so). + + Hosts MUST NOT call deactivate() unless activate() was previously + called. Note that connect_port() may be called before or after + deactivate(). + */ + void (*deactivate)(LV2_Handle instance); + + /** + Clean up a plugin instance (counterpart to instantiate()). + + Once an instance of a plugin has been finished with it must be deleted + using this function. The instance handle passed ceases to be valid after + this call. + + If activate() was called for a plugin instance then a corresponding call + to deactivate() MUST be made before cleanup() is called. Hosts MUST NOT + call cleanup() unless instantiate() was previously called. + */ + void (*cleanup)(LV2_Handle instance); + + /** + Return additional plugin data defined by some extenion. + + A typical use of this facility is to return a struct containing function + pointers to extend the LV2_Descriptor API. + + The actual type and meaning of the returned object MUST be specified + precisely by the extension. This function MUST return NULL for any + unsupported URI. If a plugin does not support any extension data, this + field may be NULL. + + The host is never responsible for freeing the returned value. + */ + const void * (*extension_data)(const char * uri); +} LV2_Descriptor; + +/** + Put this (LV2_SYMBOL_EXPORT) before any functions that are to be loaded + by the host as a symbol from the dynamic library. +*/ +#ifdef _WIN32 +# define LV2_SYMBOL_EXPORT __declspec(dllexport) +#else +# define LV2_SYMBOL_EXPORT +#endif + +/** + Prototype for plugin accessor function. + + This is part of the old discovery API, which has been replaced due to being + inadequate for some plugins. It is limited because the bundle path is not + available during discovery, and it relies on non-portable shared library + constructors/destructors. However, this API is still supported and plugins + are not required to migrate. + + Plugins are discovered by hosts using RDF data (not by loading libraries). + See http://lv2plug.in for details on the discovery process, though most + hosts should use an existing library to implement this functionality. + + A plugin library MUST include a function called "lv2_descriptor" with this + prototype. This function MUST have C-style linkage (if you are using C++ + this is taken care of by the 'extern "C"' clause at the top of this file). + + When it is time to load a plugin (designated by its URI), the host loads the + plugin's library, gets the lv2_descriptor() function from it, and uses this + function to find the LV2_Descriptor for the desired plugin. Plugins are + accessed by index using values from 0 upwards. This function MUST return + NULL for out of range indices, so the host can enumerate plugins by + increasing @p index until NULL is returned. + + Note that @p index has no meaning, hosts MUST NOT depend on it remaining + consistent between loads of the plugin library. +*/ +LV2_SYMBOL_EXPORT +const LV2_Descriptor * lv2_descriptor(uint32_t index); + +/** + Type of the lv2_descriptor() function in a library (old discovery API). +*/ +typedef const LV2_Descriptor * +(*LV2_Descriptor_Function)(uint32_t index); + +/** + Handle for a library descriptor. +*/ +typedef void* LV2_Lib_Handle; + +/** + Descriptor for a plugin library. + + To access a plugin library, the host creates an LV2_Lib_Descriptor via the + lv2_lib_descriptor() function in the shared object. +*/ +typedef struct { + /** + Opaque library data which must be passed as the first parameter to all + the methods of this struct. + */ + LV2_Lib_Handle handle; + + /** + The total size of this struct. This allows for this struct to be + expanded in the future if necessary. This MUST be set by the library to + sizeof(LV2_Lib_Descriptor). The host MUST NOT access any fields of this + struct beyond get_plugin() unless this field indicates they are present. + */ + uint32_t size; + + /** + Destroy this library descriptor and free all related resources. + */ + void (*cleanup)(LV2_Lib_Handle handle); + + /** + Plugin accessor. + + Plugins are accessed by index using values from 0 upwards. Out of range + indices MUST result in this function returning NULL, so the host can + enumerate plugins by increasing @a index until NULL is returned. + */ + const LV2_Descriptor * (*get_plugin)(LV2_Lib_Handle handle, + uint32_t index); +} LV2_Lib_Descriptor; + +/** + Prototype for library accessor function. + + This is the entry point for a plugin library. Hosts load this symbol from + the library and call this function to obtain a library descriptor which can + be used to access all the contained plugins. The returned object must not + be destroyed (using LV2_Lib_Descriptor::cleanup()) until all plugins loaded + from that library have been destroyed. +*/ +const LV2_Lib_Descriptor * +lv2_lib_descriptor(const char * bundle_path, + const LV2_Feature *const * features); + +/** + Type of the lv2_lib_descriptor() function in an LV2 library. +*/ +typedef const LV2_Lib_Descriptor * +(*LV2_Lib_Descriptor_Function)(const char * bundle_path, + const LV2_Feature *const * features); + +#ifdef __cplusplus +} +#endif + +#endif /* LV2_H_INCLUDED */ diff --git a/distrho/src/lv2/lv2_external_ui.h b/distrho/src/lv2/lv2_external_ui.h @@ -0,0 +1,107 @@ +/* + LV2 External UI extension + This work is in public domain. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + If you have questions, contact Filipe Coelho (aka falkTX) <falktx@falktx.com> + or ask in #lad channel, FreeNode IRC network. +*/ + +/** + @file lv2_external_ui.h + C header for the LV2 External UI extension <http://kxstudio.sf.net/ns/lv2ext/external-ui>. +*/ + +#ifndef LV2_EXTERNAL_UI_H +#define LV2_EXTERNAL_UI_H + +#include "ui.h" + +#define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui" +#define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#" + +#define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host" +#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget" + +/** This extension used to be defined by a lv2plug.in URI */ +#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned + * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget. + * UI is created in invisible state. + */ +typedef struct _LV2_External_UI_Widget { + /** + * Host calls this function regulary. UI library implementing the + * callback may do IPC or redraw the UI. + * + * @param _this_ the UI context + */ + void (*run)(struct _LV2_External_UI_Widget * _this_); + + /** + * Host calls this function to make the plugin UI visible. + * + * @param _this_ the UI context + */ + void (*show)(struct _LV2_External_UI_Widget * _this_); + + /** + * Host calls this function to make the plugin UI invisible again. + * + * @param _this_ the UI context + */ + void (*hide)(struct _LV2_External_UI_Widget * _this_); + +} LV2_External_UI_Widget; + +#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) +#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) +#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) + +/** + * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature. + * LV2_Feature::data must be pointer to LV2_External_UI_Host. + */ +typedef struct _LV2_External_UI_Host { + /** + * Callback that plugin UI will call + * when UI (GUI window) is closed by user. + * This callback will be called during execution of LV2_External_UI_Widget::run() + * (i.e. not from background thread). + * + * After this callback is called, UI is defunct. Host must call + * LV2UI_Descriptor::cleanup(). If host wants to make the UI visible + * again UI must be reinstantiated. + * + * @param controller Host context associated with plugin UI, as + * supplied to LV2UI_Descriptor::instantiate() + */ + void (*ui_closed)(LV2UI_Controller controller); + + /** + * Optional (may be NULL) "user friendly" identifier which the UI + * may display to allow a user to easily associate this particular + * UI instance with the correct plugin instance as it is represented + * by the host (e.g. "track 1" or "channel 4"). + * + * If supplied by host, the string will be referenced only during + * LV2UI_Descriptor::instantiate() + */ + const char * plugin_human_id; + +} LV2_External_UI_Host; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_EXTERNAL_UI_H */ diff --git a/distrho/src/lv2/lv2_programs.h b/distrho/src/lv2/lv2_programs.h @@ -0,0 +1,174 @@ +/* + LV2 Programs Extension + Copyright 2012 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file lv2_programs.h + C header for the LV2 programs extension <http://kxstudio.sf.net/ns/lv2ext/programs>. +*/ + +#ifndef LV2_PROGRAMS_H +#define LV2_PROGRAMS_H + +#include "lv2.h" +#include "ui.h" + +#define LV2_PROGRAMS_URI "http://kxstudio.sf.net/ns/lv2ext/programs" +#define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#" + +#define LV2_PROGRAMS__Host LV2_PROGRAMS_PREFIX "Host" +#define LV2_PROGRAMS__Interface LV2_PROGRAMS_PREFIX "Interface" +#define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* LV2_Programs_Handle; + +typedef struct _LV2_Program_Descriptor { + + /** Bank number for this program. Note that this extension does not + support MIDI-style separation of bank LSB and MSB values. There is + no restriction on the set of available banks: the numbers do not + need to be contiguous, there does not need to be a bank 0, etc. */ + uint32_t bank; + + /** Program number (unique within its bank) for this program. There is + no restriction on the set of available programs: the numbers do not + need to be contiguous, there does not need to be a program 0, etc. */ + uint32_t program; + + /** Name of the program. */ + const char * name; + +} LV2_Program_Descriptor; + +/** + Programs extension, plugin data. + + When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface, + the plugin MUST return an LV2_Programs_Instance structure, which remains valid + for the lifetime of the plugin. +*/ +typedef struct _LV2_Programs_Interface { + /** + * get_program() + * + * This member is a function pointer that provides a description + * of a program (named preset sound) available on this plugin. + * + * The index argument is an index into the plugin's list of + * programs, not a program number as represented by the Program + * field of the LV2_Program_Descriptor. (This distinction is + * needed to support plugins that use non-contiguous program or + * bank numbers.) + * + * This function returns a LV2_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program + * or deactivate, on the same plugin instance. This function must + * return NULL if passed an index argument out of range, so that + * the host can use it to query the number of programs as well as + * their properties. + */ + const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle, + uint32_t index); + + /** + * select_program() + * + * This member is a function pointer that selects a new program + * for this plugin. The program change should take effect + * immediately at the start of the next run() call. (This + * means that a host providing the capability of changing programs + * between any two notes on a track must vary the block size so as + * to place the program change at the right place. A host that + * wanted to avoid this would probably just instantiate a plugin + * for each program.) + * + * Plugins should ignore a select_program() call with an invalid + * bank or program. + * + * A plugin is not required to select any particular default + * program on activate(): it's the host's duty to set a program + * explicitly. + * + * A plugin is permitted to re-write the values of its input + * control ports when select_program is called. The host should + * re-read the input control port values and update its own + * records appropriately. (This is the only circumstance in which + * a LV2 plugin is allowed to modify its own control-input ports.) + */ + void (*select_program)(LV2_Handle handle, + uint32_t bank, + uint32_t program); + +} LV2_Programs_Interface; + +/** + Programs extension, UI data. + + When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface, + the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid + for the lifetime of the UI. +*/ +typedef struct _LV2_Programs_UI_Interface { + /** + * select_program() + * + * This is exactly the same as select_program in LV2_Programs_Instance, + * but this struct relates to the UI instead of the plugin. + * + * When called, UIs should update their state to match the selected program. + */ + void (*select_program)(LV2UI_Handle handle, + uint32_t bank, + uint32_t program); + +} LV2_Programs_UI_Interface; + +/** + Feature data for LV2_PROGRAMS__Host. +*/ +typedef struct _LV2_Programs_Host { + /** + * Opaque host data. + */ + LV2_Programs_Handle handle; + + /** + * program_changed() + * + * Tell the host to reload a plugin's program. + * Parameter handle MUST be the 'handle' member of this struct. + * Parameter index is program index to change. + * When index is -1, host should reload all the programs. + * + * The plugin MUST NEVER call this function on a RT context or during run(). + * + * NOTE: This call is to inform the host about a program's bank, program or name change. + * It DOES NOT change the current selected program. + */ + void (*program_changed)(LV2_Programs_Handle handle, + int32_t index); + +} LV2_Programs_Host; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_PROGRAMS_H */ diff --git a/distrho/src/lv2/lv2_rtmempool.h b/distrho/src/lv2/lv2_rtmempool.h @@ -0,0 +1,105 @@ +/* + LV2 realtime safe memory pool extension definition + This work is in public domain. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + If you have questions, contact Filipe Coelho (aka falkTX) <falktx@falktx.com> + or ask in #lad channel, FreeNode IRC network. +*/ + +/** + * @file lv2_rtmempool.h + * C header for the LV2 rtmempool extension <http://kxstudio.sf.net/ns/lv2ext/rtmempool>. + * + */ + +#ifndef LV2_RTMEMPOOL_H +#define LV2_RTMEMPOOL_H + +#define LV2_RTSAFE_MEMORY_POOL_URI "http://kxstudio.sf.net/ns/lv2ext/rtmempool" +#define LV2_RTSAFE_MEMORY_POOL_PREFIX LV2_RTSAFE_MEMORY_POOL_URI "#" + +#define LV2_RTSAFE_MEMORY_POOL__Pool LV2_RTSAFE_MEMORY_POOL_URI "Pool" + +/** max size of memory pool name, in chars, including terminating zero char */ +#define LV2_RTSAFE_MEMORY_POOL_NAME_MAX 128 + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif + +/** + * Opaque data to host data for LV2_RtMemPool_Pool. + */ +typedef void* LV2_RtMemPool_Handle; + +/** + * On instantiation, host must supply LV2_RTSAFE_MEMORY_POOL__Pool feature. + * LV2_Feature::data must be pointer to LV2_RtMemPool_Pool. + */ +typedef struct _LV2_RtMemPool_Pool { + /** + * This function is called when plugin wants to create memory pool + * + * <b>may/will sleep</b> + * + * @param pool_name pool name, for debug purposes, max RTSAFE_MEMORY_POOL_NAME_MAX chars, including terminating zero char. May be NULL. + * @param data_size memory chunk size + * @param min_preallocated min chunks preallocated + * @param max_preallocated max chunks preallocated + * + * @return Success status, true if successful + */ + bool (*create)(LV2_RtMemPool_Handle * handle_ptr, + const char * pool_name, + size_t data_size, + size_t min_preallocated, + size_t max_preallocated); + + /** + * This function is called when plugin wants to destroy previously created memory pool + * + * <b>may/will sleep</b> + */ + void (*destroy)(LV2_RtMemPool_Handle handle); + + /** + * This function is called when plugin wants to allocate memory in context where sleeping is not allowed + * + * <b>will not sleep</b> + * + * @return Pointer to allocated memory or NULL if memory no memory is available + */ + void * (*allocate_atomic)(LV2_RtMemPool_Handle handle); + + /** + * This function is called when plugin wants to allocate memory in context where sleeping is allowed + * + * <b>may/will sleep</b> + * + * @return Pointer to allocated memory or NULL if memory no memory is available (should not happen under normal conditions) + */ + void * (*allocate_sleepy)(LV2_RtMemPool_Handle handle); + + /** + * This function is called when plugin wants to deallocate previously allocated memory + * + * <b>will not sleep</b> + * + * @param memory_ptr pointer to previously allocated memory chunk + */ + void (*deallocate)(LV2_RtMemPool_Handle handle, + void * memory_ptr); + +} LV2_RtMemPool_Pool; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_RTMEMPOOL_H */ diff --git a/distrho/src/lv2/midi.h b/distrho/src/lv2/midi.h @@ -0,0 +1,226 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file midi.h + C definitions for the LV2 MIDI extension <http://lv2plug.in/ns/ext/midi>. +*/ + +#ifndef LV2_MIDI_H +#define LV2_MIDI_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +#define LV2_MIDI_URI "http://lv2plug.in/ns/ext/midi" +#define LV2_MIDI_PREFIX LV2_MIDI_URI "#" + +#define LV2_MIDI__ActiveSense LV2_MIDI_PREFIX "ActiveSense" +#define LV2_MIDI__Aftertouch LV2_MIDI_PREFIX "Aftertouch" +#define LV2_MIDI__Bender LV2_MIDI_PREFIX "Bender" +#define LV2_MIDI__ChannelPressure LV2_MIDI_PREFIX "ChannelPressure" +#define LV2_MIDI__Chunk LV2_MIDI_PREFIX "Chunk" +#define LV2_MIDI__Clock LV2_MIDI_PREFIX "Clock" +#define LV2_MIDI__Continue LV2_MIDI_PREFIX "Continue" +#define LV2_MIDI__Controller LV2_MIDI_PREFIX "Controller" +#define LV2_MIDI__MidiEvent LV2_MIDI_PREFIX "MidiEvent" +#define LV2_MIDI__NoteOff LV2_MIDI_PREFIX "NoteOff" +#define LV2_MIDI__NoteOn LV2_MIDI_PREFIX "NoteOn" +#define LV2_MIDI__ProgramChange LV2_MIDI_PREFIX "ProgramChange" +#define LV2_MIDI__QuarterFrame LV2_MIDI_PREFIX "QuarterFrame" +#define LV2_MIDI__Reset LV2_MIDI_PREFIX "Reset" +#define LV2_MIDI__SongPosition LV2_MIDI_PREFIX "SongPosition" +#define LV2_MIDI__SongSelect LV2_MIDI_PREFIX "SongSelect" +#define LV2_MIDI__Start LV2_MIDI_PREFIX "Start" +#define LV2_MIDI__Stop LV2_MIDI_PREFIX "Stop" +#define LV2_MIDI__SystemCommon LV2_MIDI_PREFIX "SystemCommon" +#define LV2_MIDI__SystemExclusive LV2_MIDI_PREFIX "SystemExclusive" +#define LV2_MIDI__SystemMessage LV2_MIDI_PREFIX "SystemMessage" +#define LV2_MIDI__SystemRealtime LV2_MIDI_PREFIX "SystemRealtime" +#define LV2_MIDI__Tick LV2_MIDI_PREFIX "Tick" +#define LV2_MIDI__TuneRequest LV2_MIDI_PREFIX "TuneRequest" +#define LV2_MIDI__VoiceMessage LV2_MIDI_PREFIX "VoiceMessage" +#define LV2_MIDI__benderValue LV2_MIDI_PREFIX "benderValue" +#define LV2_MIDI__binding LV2_MIDI_PREFIX "binding" +#define LV2_MIDI__byteNumber LV2_MIDI_PREFIX "byteNumber" +#define LV2_MIDI__channel LV2_MIDI_PREFIX "channel" +#define LV2_MIDI__chunk LV2_MIDI_PREFIX "chunk" +#define LV2_MIDI__controllerNumber LV2_MIDI_PREFIX "controllerNumber" +#define LV2_MIDI__controllerValue LV2_MIDI_PREFIX "controllerValue" +#define LV2_MIDI__noteNumber LV2_MIDI_PREFIX "noteNumber" +#define LV2_MIDI__pressure LV2_MIDI_PREFIX "pressure" +#define LV2_MIDI__programNumber LV2_MIDI_PREFIX "programNumber" +#define LV2_MIDI__property LV2_MIDI_PREFIX "property" +#define LV2_MIDI__songNumber LV2_MIDI_PREFIX "songNumber" +#define LV2_MIDI__songPosition LV2_MIDI_PREFIX "songPosition" +#define LV2_MIDI__status LV2_MIDI_PREFIX "status" +#define LV2_MIDI__statusMask LV2_MIDI_PREFIX "statusMask" +#define LV2_MIDI__velocity LV2_MIDI_PREFIX "velocity" + +/** + MIDI Message Type. + + This includes both voice messages (which have a channel) and system messages + (which do not), as well as a sentinel value for invalid messages. To get + the type of a message suitable for use in a switch statement, use + lv2_midi_get_type() on the status byte. +*/ +typedef enum { + LV2_MIDI_MSG_INVALID = 0, /**< Invalid Message */ + LV2_MIDI_MSG_NOTE_OFF = 0x80, /**< Note Off */ + LV2_MIDI_MSG_NOTE_ON = 0x90, /**< Note On */ + LV2_MIDI_MSG_NOTE_PRESSURE = 0xA0, /**< Note Pressure */ + LV2_MIDI_MSG_CONTROLLER = 0xB0, /**< Controller */ + LV2_MIDI_MSG_PGM_CHANGE = 0xC0, /**< Program Change */ + LV2_MIDI_MSG_CHANNEL_PRESSURE = 0xD0, /**< Channel Pressure */ + LV2_MIDI_MSG_BENDER = 0xE0, /**< Pitch Bender */ + LV2_MIDI_MSG_SYSTEM_EXCLUSIVE = 0xF0, /**< System Exclusive Begin */ + LV2_MIDI_MSG_MTC_QUARTER = 0xF1, /**< MTC Quarter Frame */ + LV2_MIDI_MSG_SONG_POS = 0xF2, /**< Song Position */ + LV2_MIDI_MSG_SONG_SELECT = 0xF3, /**< Song Select */ + LV2_MIDI_MSG_TUNE_REQUEST = 0xF6, /**< Tune Request */ + LV2_MIDI_MSG_CLOCK = 0xF8, /**< Clock */ + LV2_MIDI_MSG_START = 0xFA, /**< Start */ + LV2_MIDI_MSG_CONTINUE = 0xFB, /**< Continue */ + LV2_MIDI_MSG_STOP = 0xFC, /**< Stop */ + LV2_MIDI_MSG_ACTIVE_SENSE = 0xFE, /**< Active Sensing */ + LV2_MIDI_MSG_RESET = 0xFF /**< Reset */ +} LV2_Midi_Message_Type; + +/** + Standard MIDI Controller Numbers. +*/ +typedef enum { + LV2_MIDI_CTL_MSB_BANK = 0x00, /**< Bank Selection */ + LV2_MIDI_CTL_MSB_MODWHEEL = 0x01, /**< Modulation */ + LV2_MIDI_CTL_MSB_BREATH = 0x02, /**< Breath */ + LV2_MIDI_CTL_MSB_FOOT = 0x04, /**< Foot */ + LV2_MIDI_CTL_MSB_PORTAMENTO_TIME = 0x05, /**< Portamento Time */ + LV2_MIDI_CTL_MSB_DATA_ENTRY = 0x06, /**< Data Entry */ + LV2_MIDI_CTL_MSB_MAIN_VOLUME = 0x07, /**< Main Volume */ + LV2_MIDI_CTL_MSB_BALANCE = 0x08, /**< Balance */ + LV2_MIDI_CTL_MSB_PAN = 0x0A, /**< Panpot */ + LV2_MIDI_CTL_MSB_EXPRESSION = 0x0B, /**< Expression */ + LV2_MIDI_CTL_MSB_EFFECT1 = 0x0C, /**< Effect1 */ + LV2_MIDI_CTL_MSB_EFFECT2 = 0x0D, /**< Effect2 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE1 = 0x10, /**< General Purpose 1 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE2 = 0x11, /**< General Purpose 2 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE3 = 0x12, /**< General Purpose 3 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE4 = 0x13, /**< General Purpose 4 */ + LV2_MIDI_CTL_LSB_BANK = 0x20, /**< Bank Selection */ + LV2_MIDI_CTL_LSB_MODWHEEL = 0x21, /**< Modulation */ + LV2_MIDI_CTL_LSB_BREATH = 0x22, /**< Breath */ + LV2_MIDI_CTL_LSB_FOOT = 0x24, /**< Foot */ + LV2_MIDI_CTL_LSB_PORTAMENTO_TIME = 0x25, /**< Portamento Time */ + LV2_MIDI_CTL_LSB_DATA_ENTRY = 0x26, /**< Data Entry */ + LV2_MIDI_CTL_LSB_MAIN_VOLUME = 0x27, /**< Main Volume */ + LV2_MIDI_CTL_LSB_BALANCE = 0x28, /**< Balance */ + LV2_MIDI_CTL_LSB_PAN = 0x2A, /**< Panpot */ + LV2_MIDI_CTL_LSB_EXPRESSION = 0x2B, /**< Expression */ + LV2_MIDI_CTL_LSB_EFFECT1 = 0x2C, /**< Effect1 */ + LV2_MIDI_CTL_LSB_EFFECT2 = 0x2D, /**< Effect2 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE1 = 0x30, /**< General Purpose 1 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE2 = 0x31, /**< General Purpose 2 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE3 = 0x32, /**< General Purpose 3 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE4 = 0x33, /**< General Purpose 4 */ + LV2_MIDI_CTL_SUSTAIN = 0x40, /**< Sustain Pedal */ + LV2_MIDI_CTL_PORTAMENTO = 0x41, /**< Portamento */ + LV2_MIDI_CTL_SOSTENUTO = 0x42, /**< Sostenuto */ + LV2_MIDI_CTL_SOFT_PEDAL = 0x43, /**< Soft Pedal */ + LV2_MIDI_CTL_LEGATO_FOOTSWITCH = 0x44, /**< Legato Foot Switch */ + LV2_MIDI_CTL_HOLD2 = 0x45, /**< Hold2 */ + LV2_MIDI_CTL_SC1_SOUND_VARIATION = 0x46, /**< SC1 Sound Variation */ + LV2_MIDI_CTL_SC2_TIMBRE = 0x47, /**< SC2 Timbre */ + LV2_MIDI_CTL_SC3_RELEASE_TIME = 0x48, /**< SC3 Release Time */ + LV2_MIDI_CTL_SC4_ATTACK_TIME = 0x49, /**< SC4 Attack Time */ + LV2_MIDI_CTL_SC5_BRIGHTNESS = 0x4A, /**< SC5 Brightness */ + LV2_MIDI_CTL_SC6 = 0x4B, /**< SC6 */ + LV2_MIDI_CTL_SC7 = 0x4C, /**< SC7 */ + LV2_MIDI_CTL_SC8 = 0x4D, /**< SC8 */ + LV2_MIDI_CTL_SC9 = 0x4E, /**< SC9 */ + LV2_MIDI_CTL_SC10 = 0x4F, /**< SC10 */ + LV2_MIDI_CTL_GENERAL_PURPOSE5 = 0x50, /**< General Purpose 5 */ + LV2_MIDI_CTL_GENERAL_PURPOSE6 = 0x51, /**< General Purpose 6 */ + LV2_MIDI_CTL_GENERAL_PURPOSE7 = 0x52, /**< General Purpose 7 */ + LV2_MIDI_CTL_GENERAL_PURPOSE8 = 0x53, /**< General Purpose 8 */ + LV2_MIDI_CTL_PORTAMENTO_CONTROL = 0x54, /**< Portamento Control */ + LV2_MIDI_CTL_E1_REVERB_DEPTH = 0x5B, /**< E1 Reverb Depth */ + LV2_MIDI_CTL_E2_TREMOLO_DEPTH = 0x5C, /**< E2 Tremolo Depth */ + LV2_MIDI_CTL_E3_CHORUS_DEPTH = 0x5D, /**< E3 Chorus Depth */ + LV2_MIDI_CTL_E4_DETUNE_DEPTH = 0x5E, /**< E4 Detune Depth */ + LV2_MIDI_CTL_E5_PHASER_DEPTH = 0x5F, /**< E5 Phaser Depth */ + LV2_MIDI_CTL_DATA_INCREMENT = 0x60, /**< Data Increment */ + LV2_MIDI_CTL_DATA_DECREMENT = 0x61, /**< Data Decrement */ + LV2_MIDI_CTL_NRPN_LSB = 0x62, /**< Non-registered Parameter Number */ + LV2_MIDI_CTL_NRPN_MSB = 0x63, /**< Non-registered Parameter Number */ + LV2_MIDI_CTL_RPN_LSB = 0x64, /**< Registered Parameter Number */ + LV2_MIDI_CTL_RPN_MSB = 0x65, /**< Registered Parameter Number */ + LV2_MIDI_CTL_ALL_SOUNDS_OFF = 0x78, /**< All Sounds Off */ + LV2_MIDI_CTL_RESET_CONTROLLERS = 0x79, /**< Reset Controllers */ + LV2_MIDI_CTL_LOCAL_CONTROL_SWITCH = 0x7A, /**< Local Control Switch */ + LV2_MIDI_CTL_ALL_NOTES_OFF = 0x7B, /**< All Notes Off */ + LV2_MIDI_CTL_OMNI_OFF = 0x7C, /**< Omni Off */ + LV2_MIDI_CTL_OMNI_ON = 0x7D, /**< Omni On */ + LV2_MIDI_CTL_MONO1 = 0x7E, /**< Mono1 */ + LV2_MIDI_CTL_MONO2 = 0x7F /**< Mono2 */ +} LV2_Midi_Controller; + +/** + Return true iff @p msg is a MIDI voice message (which has a channel). +*/ +static inline bool +lv2_midi_is_voice_message(const uint8_t* msg) { + return msg[0] >= 0x80 && msg[0] < 0xF0; +} + +/** + Return true iff @p msg is a MIDI system message (which has no channel). +*/ +static inline bool +lv2_midi_is_system_message(const uint8_t* msg) { + switch (msg[0]) { + case 0xF4: case 0xF5: case 0xF7: case 0xF9: case 0xFD: + return false; + default: + return (msg[0] & 0xF0) == 0xF0; + } +} + +/** + Return the type of a MIDI message. + @param msg Pointer to the start (status byte) of a MIDI message. +*/ +static inline LV2_Midi_Message_Type +lv2_midi_message_type(const uint8_t* msg) { + if (lv2_midi_is_voice_message(msg)) { + return (LV2_Midi_Message_Type)(msg[0] & 0xF0); + } else if (lv2_midi_is_system_message(msg)) { + return (LV2_Midi_Message_Type)msg[0]; + } else { + return LV2_MIDI_MSG_INVALID; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_MIDI_H */ diff --git a/distrho/src/lv2/morph.h b/distrho/src/lv2/morph.h @@ -0,0 +1,34 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef LV2_MORPH_H +#define LV2_MORPH_H + +#include <stdint.h> + +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" + +#define LV2_MORPH_URI "http://lv2plug.in/ns/ext/morph" +#define LV2_MORPH_PREFIX LV2_MORPH_URI "#" + +#define LV2_MORPH__AutoMorphPort LV2_MORPH_PREFIX "AutoMorphPort" +#define LV2_MORPH__MorphPort LV2_MORPH_PREFIX "MorphPort" +#define LV2_MORPH__interface LV2_MORPH_PREFIX "interface" +#define LV2_MORPH__supportsType LV2_MORPH_PREFIX "supportsType" +#define LV2_MORPH__currentType LV2_MORPH_PREFIX "currentType" + +#endif /* LV2_MORPH_H */ diff --git a/distrho/src/lv2/options.h b/distrho/src/lv2/options.h @@ -0,0 +1,132 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef LV2_OPTIONS_H +#define LV2_OPTIONS_H + +#include <stdint.h> + +#include "urid.h" +#include "lv2.h" + +#define LV2_OPTIONS_URI "http://lv2plug.in/ns/ext/options" +#define LV2_OPTIONS_PREFIX LV2_OPTIONS_URI "#" + +#define LV2_OPTIONS__Option LV2_OPTIONS_PREFIX "Option" +#define LV2_OPTIONS__interface LV2_OPTIONS_PREFIX "interface" +#define LV2_OPTIONS__options LV2_OPTIONS_PREFIX "options" +#define LV2_OPTIONS__requiredOption LV2_OPTIONS_PREFIX "requiredOption" +#define LV2_OPTIONS__supportedOption LV2_OPTIONS_PREFIX "supportedOption" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The context of an Option, which defines the subject it applies to. +*/ +typedef enum { + /** + This option applies to the instance itself. The subject must be + ignored. + */ + LV2_OPTIONS_INSTANCE, + + /** + This option applies to some named resource. The subject is a URI mapped + to an integer (a LV2_URID, like the key) + */ + LV2_OPTIONS_RESOURCE, + + /** + This option applies to some blank node. The subject is a blank node + identifier, which is valid only within the current local scope. + */ + LV2_OPTIONS_BLANK, + + /** + This option applies to a port on the instance. The subject is the + port's index. + */ + LV2_OPTIONS_PORT +} LV2_Options_Context; + +/** + An option. + + This is a property with a subject, also known as a triple or statement. + + This struct is useful anywhere a statement needs to be passed where no + memory ownership issues are present (since the value is a const pointer). + + Options can be passed to an instance via the feature LV2_OPTIONS__options + with data pointed to an array of options terminated by a zeroed option, or + accessed/manipulated using LV2_Options_Interface. +*/ +typedef struct _LV2_Options_Option { + LV2_Options_Context context; /**< Context (type of subject). */ + uint32_t subject; /**< Subject. */ + LV2_URID key; /**< Key (property). */ + uint32_t size; /**< Size of value in bytes. */ + LV2_URID type; /**< Type of value (datatype). */ + const void* value; /**< Pointer to value (object). */ +} LV2_Options_Option; + +/** A status code for option functions. */ +typedef enum { + LV2_OPTIONS_SUCCESS = 0, /**< Completed successfully. */ + LV2_OPTIONS_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_OPTIONS_ERR_BAD_SUBJECT = 1 << 1, /**< Invalid/unsupported subject. */ + LV2_OPTIONS_ERR_BAD_KEY = 1 << 2, /**< Invalid/unsupported key. */ + LV2_OPTIONS_ERR_BAD_VALUE = 1 << 3 /**< Invalid/unsupported value. */ +} LV2_Options_Status; + +/** + Interface for dynamically setting options (LV2_OPTIONS__interface). +*/ +typedef struct _LV2_Options_Interface { + /** + Get the given options. + + Each element of the passed options array MUST have type, subject, and + key set. All other fields (size, type, value) MUST be initialised to + zero, and are set to the option value if such an option is found. + + This function is in the "instantiation" LV2 threading class, so no other + instance functions may be called concurrently. + + @return Bitwise OR of LV2_Options_Status values. + */ + uint32_t (*get)(LV2_Handle instance, + LV2_Options_Option* options); + + /** + Set the given options. + + This function is in the "instantiation" LV2 threading class, so no other + instance functions may be called concurrently. + + @return Bitwise OR of LV2_Options_Status values. + */ + uint32_t (*set)(LV2_Handle instance, + const LV2_Options_Option* options); +} LV2_Options_Interface; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_OPTIONS_H */ diff --git a/distrho/src/lv2/parameters.h b/distrho/src/lv2/parameters.h @@ -0,0 +1,49 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef LV2_PARAMETERS_H +#define LV2_PARAMETERS_H + +#define LV2_PARAMETERS_URI "http://lv2plug.in/ns/ext/parameters" +#define LV2_PARAMETERS_PREFIX LV2_PARAMETERS_URI "#" + +#define LV2_PARAMETERS__CompressorControls LV2_PARAMETERS_PREFIX "CompressorControls" +#define LV2_PARAMETERS__ControlGroup LV2_PARAMETERS_PREFIX "ControlGroup" +#define LV2_PARAMETERS__EnvelopeControls LV2_PARAMETERS_PREFIX "EnvelopeControls" +#define LV2_PARAMETERS__FilterControls LV2_PARAMETERS_PREFIX "FilterControls" +#define LV2_PARAMETERS__OscillatorControls LV2_PARAMETERS_PREFIX "OscillatorControls" +#define LV2_PARAMETERS__amplitude LV2_PARAMETERS_PREFIX "amplitude" +#define LV2_PARAMETERS__attack LV2_PARAMETERS_PREFIX "attack" +#define LV2_PARAMETERS__bypass LV2_PARAMETERS_PREFIX "bypass" +#define LV2_PARAMETERS__cutoffFrequency LV2_PARAMETERS_PREFIX "cutoffFrequency" +#define LV2_PARAMETERS__decay LV2_PARAMETERS_PREFIX "decay" +#define LV2_PARAMETERS__delay LV2_PARAMETERS_PREFIX "delay" +#define LV2_PARAMETERS__dryLevel LV2_PARAMETERS_PREFIX "dryLevel" +#define LV2_PARAMETERS__frequency LV2_PARAMETERS_PREFIX "frequency" +#define LV2_PARAMETERS__gain LV2_PARAMETERS_PREFIX "gain" +#define LV2_PARAMETERS__hold LV2_PARAMETERS_PREFIX "hold" +#define LV2_PARAMETERS__pulseWidth LV2_PARAMETERS_PREFIX "pulseWidth" +#define LV2_PARAMETERS__ratio LV2_PARAMETERS_PREFIX "ratio" +#define LV2_PARAMETERS__release LV2_PARAMETERS_PREFIX "release" +#define LV2_PARAMETERS__resonance LV2_PARAMETERS_PREFIX "resonance" +#define LV2_PARAMETERS__sampleRate LV2_PARAMETERS_PREFIX "sampleRate" +#define LV2_PARAMETERS__sustain LV2_PARAMETERS_PREFIX "sustain" +#define LV2_PARAMETERS__threshold LV2_PARAMETERS_PREFIX "threshold" +#define LV2_PARAMETERS__waveform LV2_PARAMETERS_PREFIX "waveform" +#define LV2_PARAMETERS__wetDryRatio LV2_PARAMETERS_PREFIX "wetDryRatio" +#define LV2_PARAMETERS__wetLevel LV2_PARAMETERS_PREFIX "wetLevel" + +#endif /* LV2_PARAMETERS_H */ diff --git a/distrho/src/lv2/patch.h b/distrho/src/lv2/patch.h @@ -0,0 +1,55 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file patch.h C header for the LV2 Patch extension + <http://lv2plug.in/ns/ext/patch>. + + The patch extension is purely data, this header merely defines URIs + for convenience. +*/ + +#ifndef LV2_PATCH_H +#define LV2_PATCH_H + +#define LV2_PATCH_URI "http://lv2plug.in/ns/ext/patch" +#define LV2_PATCH_PREFIX LV2_PATCH_URI "#" + +#define LV2_PATCH__Ack LV2_PATCH_PREFIX "Ack" +#define LV2_PATCH__Delete LV2_PATCH_PREFIX "Delete" +#define LV2_PATCH__Error LV2_PATCH_PREFIX "Error" +#define LV2_PATCH__Get LV2_PATCH_PREFIX "Get" +#define LV2_PATCH__Message LV2_PATCH_PREFIX "Message" +#define LV2_PATCH__Move LV2_PATCH_PREFIX "Move" +#define LV2_PATCH__Patch LV2_PATCH_PREFIX "Patch" +#define LV2_PATCH__Post LV2_PATCH_PREFIX "Post" +#define LV2_PATCH__Put LV2_PATCH_PREFIX "Put" +#define LV2_PATCH__Request LV2_PATCH_PREFIX "Request" +#define LV2_PATCH__Response LV2_PATCH_PREFIX "Response" +#define LV2_PATCH__Set LV2_PATCH_PREFIX "Set" +#define LV2_PATCH__add LV2_PATCH_PREFIX "add" +#define LV2_PATCH__body LV2_PATCH_PREFIX "body" +#define LV2_PATCH__destination LV2_PATCH_PREFIX "destination" +#define LV2_PATCH__property LV2_PATCH_PREFIX "property" +#define LV2_PATCH__readable LV2_PATCH_PREFIX "readable" +#define LV2_PATCH__remove LV2_PATCH_PREFIX "remove" +#define LV2_PATCH__request LV2_PATCH_PREFIX "request" +#define LV2_PATCH__subject LV2_PATCH_PREFIX "subject" +#define LV2_PATCH__value LV2_PATCH_PREFIX "value" +#define LV2_PATCH__wildcard LV2_PATCH_PREFIX "wildcard" +#define LV2_PATCH__writable LV2_PATCH_PREFIX "writable" + +#endif /* LV2_PATCH_H */ diff --git a/distrho/src/lv2/port-groups.h b/distrho/src/lv2/port-groups.h @@ -0,0 +1,64 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file port-groups.h + C definitions for the LV2 Port Groups extension + <http://lv2plug.in/ns/ext/port-groups>. +*/ + +#ifndef LV2_PORT_GROUPS_H +#define LV2_PORT_GROUPS_H + +#define LV2_PORT_GROUPS_URI "http://lv2plug.in/ns/ext/port-groups" +#define LV2_PORT_GROUPS_PREFIX LV2_PORT_GROUPS_URI "#" + +#define LV2_PORT_GROUPS__DiscreteGroup LV2_PORT_GROUPS_PREFIX "DiscreteGroup" +#define LV2_PORT_GROUPS__Element LV2_PORT_GROUPS_PREFIX "Element" +#define LV2_PORT_GROUPS__FivePointOneGroup LV2_PORT_GROUPS_PREFIX "FivePointOneGroup" +#define LV2_PORT_GROUPS__FivePointZeroGroup LV2_PORT_GROUPS_PREFIX "FivePointZeroGroup" +#define LV2_PORT_GROUPS__FourPointZeroGroup LV2_PORT_GROUPS_PREFIX "FourPointZeroGroup" +#define LV2_PORT_GROUPS__Group LV2_PORT_GROUPS_PREFIX "Group" +#define LV2_PORT_GROUPS__InputGroup LV2_PORT_GROUPS_PREFIX "InputGroup" +#define LV2_PORT_GROUPS__MidSideGroup LV2_PORT_GROUPS_PREFIX "MidSideGroup" +#define LV2_PORT_GROUPS__MonoGroup LV2_PORT_GROUPS_PREFIX "MonoGroup" +#define LV2_PORT_GROUPS__OutputGroup LV2_PORT_GROUPS_PREFIX "OutputGroup" +#define LV2_PORT_GROUPS__SevenPointOneGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneGroup" +#define LV2_PORT_GROUPS__SevenPointOneWideGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneWideGroup" +#define LV2_PORT_GROUPS__SixPointOneGroup LV2_PORT_GROUPS_PREFIX "SixPointOneGroup" +#define LV2_PORT_GROUPS__StereoGroup LV2_PORT_GROUPS_PREFIX "StereoGroup" +#define LV2_PORT_GROUPS__ThreePointZeroGroup LV2_PORT_GROUPS_PREFIX "ThreePointZeroGroup" +#define LV2_PORT_GROUPS__center LV2_PORT_GROUPS_PREFIX "center" +#define LV2_PORT_GROUPS__centerLeft LV2_PORT_GROUPS_PREFIX "centerLeft" +#define LV2_PORT_GROUPS__centerRight LV2_PORT_GROUPS_PREFIX "centerRight" +#define LV2_PORT_GROUPS__element LV2_PORT_GROUPS_PREFIX "element" +#define LV2_PORT_GROUPS__group LV2_PORT_GROUPS_PREFIX "group" +#define LV2_PORT_GROUPS__left LV2_PORT_GROUPS_PREFIX "left" +#define LV2_PORT_GROUPS__lowFrequencyEffects LV2_PORT_GROUPS_PREFIX "lowFrequencyEffects" +#define LV2_PORT_GROUPS__mainInput LV2_PORT_GROUPS_PREFIX "mainInput" +#define LV2_PORT_GROUPS__mainOutput LV2_PORT_GROUPS_PREFIX "mainOutput" +#define LV2_PORT_GROUPS__rearCenter LV2_PORT_GROUPS_PREFIX "rearCenter" +#define LV2_PORT_GROUPS__rearLeft LV2_PORT_GROUPS_PREFIX "rearLeft" +#define LV2_PORT_GROUPS__rearRight LV2_PORT_GROUPS_PREFIX "rearRight" +#define LV2_PORT_GROUPS__right LV2_PORT_GROUPS_PREFIX "right" +#define LV2_PORT_GROUPS__side LV2_PORT_GROUPS_PREFIX "side" +#define LV2_PORT_GROUPS__sideChainOf LV2_PORT_GROUPS_PREFIX "sideChainOf" +#define LV2_PORT_GROUPS__sideLeft LV2_PORT_GROUPS_PREFIX "sideLeft" +#define LV2_PORT_GROUPS__sideRight LV2_PORT_GROUPS_PREFIX "sideRight" +#define LV2_PORT_GROUPS__source LV2_PORT_GROUPS_PREFIX "source" +#define LV2_PORT_GROUPS__subGroupOf LV2_PORT_GROUPS_PREFIX "subGroupOf" + +#endif /* LV2_PORT_GROUPS_H */ diff --git a/distrho/src/lv2/port-props.h b/distrho/src/lv2/port-props.h @@ -0,0 +1,42 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file port-props.h + C definitions for the LV2 Port Props extension + <http://lv2plug.in/ns/ext/port-props>. +*/ + +#ifndef LV2_PORT_PROPS_H +#define LV2_PORT_PROPS_H + +#define LV2_PORT_PROPS_URI "http://lv2plug.in/ns/ext/port-props" +#define LV2_PORT_PROPS_PREFIX LV2_PORT_PROPS_URI "#" + +#define LV2_PORT_PROPS__causesArtifacts LV2_PORT_PROPS_PREFIX "causesArtifacts" +#define LV2_PORT_PROPS__continuousCV LV2_PORT_PROPS_PREFIX "continuousCV" +#define LV2_PORT_PROPS__discreteCV LV2_PORT_PROPS_PREFIX "discreteCV" +#define LV2_PORT_PROPS__displayPriority LV2_PORT_PROPS_PREFIX "displayPriority" +#define LV2_PORT_PROPS__expensive LV2_PORT_PROPS_PREFIX "expensive" +#define LV2_PORT_PROPS__hasStrictBounds LV2_PORT_PROPS_PREFIX "hasStrictBounds" +#define LV2_PORT_PROPS__logarithmic LV2_PORT_PROPS_PREFIX "logarithmic" +#define LV2_PORT_PROPS__notAutomatic LV2_PORT_PROPS_PREFIX "notAutomatic" +#define LV2_PORT_PROPS__notOnGUI LV2_PORT_PROPS_PREFIX "notOnGUI" +#define LV2_PORT_PROPS__rangeSteps LV2_PORT_PROPS_PREFIX "rangeSteps" +#define LV2_PORT_PROPS__supportsStrictBounds LV2_PORT_PROPS_PREFIX "supportsStrictBounds" +#define LV2_PORT_PROPS__trigger LV2_PORT_PROPS_PREFIX "trigger" + +#endif /* LV2_PORT_PROPS_H */ diff --git a/distrho/src/lv2/presets.h b/distrho/src/lv2/presets.h @@ -0,0 +1,34 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file presets.h + + C definitions for the LV2 Presets extension + <http://lv2plug.in/ns/ext/presets>. +*/ + +#ifndef LV2_PRESETS_H +#define LV2_PRESETS_H + +#define LV2_PRESETS_URI "http://lv2plug.in/ns/ext/presets" +#define LV2_PRESETS_PREFIX LV2_PRESETS_URI "#" + +#define LV2_PRESETS__Preset LV2_PRESETS_PREFIX "Preset" +#define LV2_PRESETS__preset LV2_PRESETS_PREFIX "preset" +#define LV2_PRESETS__value LV2_PRESETS_PREFIX "value" + +#endif /* LV2_PRESETS_H */ diff --git a/distrho/src/lv2/resize-port.h b/distrho/src/lv2/resize-port.h @@ -0,0 +1,72 @@ +/* + Copyright 2007-2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef LV2_RESIZE_PORT_H +#define LV2_RESIZE_PORT_H + +#include <stddef.h> +#include <stdint.h> + +#define LV2_RESIZE_PORT_URI "http://lv2plug.in/ns/ext/resize-port" +#define LV2_RESIZE_PORT_PREFIX LV2_RESIZE_PORT_URI "#" + +#define LV2_RESIZE_PORT__asLargeAs LV2_RESIZE_PORT_PREFIX "asLargeAs" +#define LV2_RESIZE_PORT__minimumSize LV2_RESIZE_PORT_PREFIX "minimumSize" +#define LV2_RESIZE_PORT__resize LV2_RESIZE_PORT_PREFIX "resize" + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +/** A status code for state functions. */ +typedef enum { + LV2_RESIZE_PORT_SUCCESS = 0, /**< Completed successfully. */ + LV2_RESIZE_PORT_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_RESIZE_PORT_ERR_NO_SPACE = 2 /**< Insufficient space. */ +} LV2_Resize_Port_Status; + +typedef void* LV2_Resize_Port_Feature_Data; + +typedef struct { + LV2_Resize_Port_Feature_Data data; + + /** + Resize a port buffer to at least @a size bytes. + + This function MAY return an error, in which case the port buffer was not + resized and the port is still connected to the same location. Plugins + MUST gracefully handle this situation. + + This function is in the audio threading class. + + The host MUST preserve the contents of the port buffer when resizing. + + Plugins MAY resize a port many times in a single run callback. Hosts + SHOULD make this as inexpensive as possible. + */ + LV2_Resize_Port_Status (*resize)(LV2_Resize_Port_Feature_Data data, + uint32_t index, + size_t size); +} LV2_Resize_Port_Resize; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_RESIZE_PORT_H */ + diff --git a/distrho/src/lv2/state.h b/distrho/src/lv2/state.h @@ -0,0 +1,352 @@ +/* + Copyright 2010-2012 David Robillard <http://drobilla.net> + Copyright 2010 Leonard Ritter <paniq@paniq.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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file state.h + C API for the LV2 State extension <http://lv2plug.in/ns/ext/state>. +*/ + +#ifndef LV2_STATE_H +#define LV2_STATE_H + +#include <stddef.h> +#include <stdint.h> + +#include "lv2.h" + +#define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" +#define LV2_STATE_PREFIX LV2_STATE_URI "#" + +#define LV2_STATE__State LV2_STATE_PREFIX "State" +#define LV2_STATE__interface LV2_STATE_PREFIX "interface" +#define LV2_STATE__loadDefaultState LV2_STATE_PREFIX "loadDefaultState" +#define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" +#define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" +#define LV2_STATE__state LV2_STATE_PREFIX "state" + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +typedef void* LV2_State_Handle; +typedef void* LV2_State_Map_Path_Handle; +typedef void* LV2_State_Make_Path_Handle; + +/** + Flags describing value characteristics. + + These flags are used along with the value's type URI to determine how to + (de-)serialise the value data, or whether it is even possible to do so. +*/ +typedef enum { + /** + Plain Old Data. + + Values with this flag contain no pointers or references to other areas + of memory. It is safe to copy POD values with a simple memcpy and store + them for the duration of the process. A POD value is not necessarily + safe to trasmit between processes or machines (e.g. filenames are POD), + see LV2_STATE_IS_PORTABLE for details. + + Implementations MUST NOT attempt to copy or serialise a non-POD value if + they do not understand its type (and thus know how to correctly do so). + */ + LV2_STATE_IS_POD = 1, + + /** + Portable (architecture independent) data. + + Values with this flag are in a format that is usable on any + architecture. A portable value saved on one machine can be restored on + another machine regardless of architecture. The format of portable + values MUST NOT depend on architecture-specific properties like + endianness or alignment. Portable values MUST NOT contain filenames. + */ + LV2_STATE_IS_PORTABLE = 1 << 1, + + /** + Native data. + + This flag is used by the host to indicate that the saved data is only + going to be used locally in the currently running process (e.g. for + instance duplication or snapshots), so the plugin should use the most + efficient representation possible and not worry about serialisation + and portability. + */ + LV2_STATE_IS_NATIVE = 1 << 2 +} LV2_State_Flags; + +/** A status code for state functions. */ +typedef enum { + LV2_STATE_SUCCESS = 0, /**< Completed successfully. */ + LV2_STATE_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ + LV2_STATE_ERR_BAD_FLAGS = 3, /**< Failed due to unsupported flags. */ + LV2_STATE_ERR_NO_FEATURE = 4, /**< Failed due to missing features. */ + LV2_STATE_ERR_NO_PROPERTY = 5 /**< Failed due to missing property. */ +} LV2_State_Status; + +/** + A host-provided function to store a property. + @param handle Must be the handle passed to LV2_State_Interface.save(). + @param key The key to store @p value under (URID). + @param value Pointer to the value to be stored. + @param size The size of @p value in bytes. + @param type The type of @p value (URID). + @param flags LV2_State_Flags for @p value. + @return 0 on success, otherwise a non-zero error code. + + The host passes a callback of this type to LV2_State_Interface.save(). This + callback is called repeatedly by the plugin to store all the properties that + describe its current state. + + DO NOT INVENT NONSENSE URI SCHEMES FOR THE KEY. Best is to use keys from + existing vocabularies. If nothing appropriate is available, use http URIs + that point to somewhere you can host documents so documentation can be made + resolvable (e.g. a child of the plugin or project URI). If this is not + possible, invent a URN scheme, e.g. urn:myproj:whatever. The plugin MUST + NOT pass an invalid URI key. + + The host MAY fail to store a property for whatever reason, but SHOULD + store any property that is LV2_STATE_IS_POD and LV2_STATE_IS_PORTABLE. + Implementations SHOULD use the types from the LV2 Atom extension + (http://lv2plug.in/ns/ext/atom) wherever possible. The plugin SHOULD + attempt to fall-back and avoid the error if possible. + + Note that @p size MUST be > 0, and @p value MUST point to a valid region of + memory @p size bytes long (this is required to make restore unambiguous). + + The plugin MUST NOT attempt to use this function outside of the + LV2_State_Interface.restore() context. +*/ +typedef LV2_State_Status (*LV2_State_Store_Function)( + LV2_State_Handle handle, + uint32_t key, + const void* value, + size_t size, + uint32_t type, + uint32_t flags); + +/** + A host-provided function to retrieve a property. + @param handle Must be the handle passed to LV2_State_Interface.restore(). + @param key The key of the property to retrieve (URID). + @param size (Output) If non-NULL, set to the size of the restored value. + @param type (Output) If non-NULL, set to the type of the restored value. + @param flags (Output) If non-NULL, set to the flags for the restored value. + @return A pointer to the restored value (object), or NULL if no value + has been stored under @p key. + + A callback of this type is passed by the host to + LV2_State_Interface.restore(). This callback is called repeatedly by the + plugin to retrieve any properties it requires to restore its state. + + The returned value MUST remain valid until LV2_State_Interface.restore() + returns. The plugin MUST NOT attempt to use this function, or any value + returned from it, outside of the LV2_State_Interface.restore() context. +*/ +typedef const void* (*LV2_State_Retrieve_Function)( + LV2_State_Handle handle, + uint32_t key, + size_t* size, + uint32_t* type, + uint32_t* flags); + +/** + LV2 Plugin State Interface. + + When the plugin's extension_data is called with argument + LV2_STATE__interface, the plugin MUST return an LV2_State_Interface + structure, which remains valid for the lifetime of the plugin. + + The host can use the contained function pointers to save and restore the + state of a plugin instance at any time, provided the threading restrictions + of the functions are met. + + Stored data is only guaranteed to be compatible between instances of plugins + with the same URI (i.e. if a change to a plugin would cause a fatal error + when restoring state saved by a previous version of that plugin, the plugin + URI MUST change just as it must when ports change incompatibly). Plugin + authors should consider this possibility, and always store sensible data + with meaningful types to avoid such problems in the future. +*/ +typedef struct _LV2_State_Interface { + /** + Save plugin state using a host-provided @p store callback. + + @param instance The instance handle of the plugin. + @param store The host-provided store callback. + @param handle An opaque pointer to host data which MUST be passed as the + handle parameter to @p store if it is called. + @param flags Flags describing desired properties of this save. These + flags may be used to determine the most appropriate values to store. + @param features Extensible parameter for passing any additional + features to be used for this save. + + The plugin is expected to store everything necessary to completely + restore its state later. Plugins SHOULD store simple POD data whenever + possible, and consider the possibility of state being restored much + later on a different machine. + + The @p handle pointer and @p store function MUST NOT be used + beyond the scope of save(). + + This function has its own special threading class: it may not be called + concurrently with any "Instantiation" function, but it may be called + concurrently with functions in any other class, unless the definition of + that class prohibits it (e.g. it may not be called concurrently with a + "Discovery" function, but it may be called concurrently with an "Audio" + function. The plugin is responsible for any locking or lock-free + techniques necessary to make this possible. + + Note that in the simple case where state is only modified by restore(), + there are no synchronization issues since save() is never called + concurrently with restore() (though run() may read it during a save). + + Plugins that dynamically modify state while running, however, must take + care to do so in such a way that a concurrent call to save() will save a + consistent representation of plugin state for a single instant in time. + */ + LV2_State_Status (*save)(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); + + /** + Restore plugin state using a host-provided @p retrieve callback. + + @param instance The instance handle of the plugin. + @param retrieve The host-provided retrieve callback. + @param handle An opaque pointer to host data which MUST be passed as the + handle parameter to @p retrieve if it is called. + @param flags Currently unused. + @param features Extensible parameter for passing any additional + features to be used for this restore. + + The plugin MAY assume a restored value was set by a previous call to + LV2_State_Interface.save() by a plugin with the same URI. + + The plugin MUST gracefully fall back to a default value when a value can + not be retrieved. This allows the host to reset the plugin state with + an empty map. + + The @p handle pointer and @p store function MUST NOT be used + beyond the scope of restore(). + + This function is in the "Instantiation" threading class as defined by + LV2. This means it MUST NOT be called concurrently with any other + function on the same plugin instance. + */ + LV2_State_Status (*restore)(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); +} LV2_State_Interface; + +/** + Feature data for state:mapPath (LV2_STATE__mapPath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Map_Path_Handle handle; + + /** + Map an absolute path to an abstract path for use in plugin state. + @param handle MUST be the @p handle member of this struct. + @param absolute_path The absolute path of a file. + @return An abstract path suitable for use in plugin state. + + The plugin MUST use this function to map any paths that will be stored + in plugin state. The returned value is an abstract path which MAY not + be an actual file system path; @ref absolute_path() MUST be used to map + it to an actual path in order to use the file. + + Plugins MUST NOT make any assumptions about abstract paths except that + they can be mapped back to the absolute path of the "same" file (though + not necessarily the same original path) using @ref absolute_path(). + + This function may only be called within the context of + LV2_State_Interface methods. The caller is responsible for freeing the + returned value with free(). + */ + char* (*abstract_path)(LV2_State_Map_Path_Handle handle, + const char* absolute_path); + + /** + Map an abstract path from plugin state to an absolute path. + @param handle MUST be the @p handle member of this struct. + @param abstract_path An abstract path (e.g. a path from plugin state). + @return An absolute file system path. + + The plugin MUST use this function in order to actually open or otherwise + use any paths loaded from plugin state. + + This function may only be called within the context of + LV2_State_Interface methods. The caller is responsible for freeing the + returned value with free(). + */ + char* (*absolute_path)(LV2_State_Map_Path_Handle handle, + const char* abstract_path); +} LV2_State_Map_Path; + +/** + Feature data for state:makePath (@ref LV2_STATE__makePath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Make_Path_Handle handle; + + /** + Return a path the plugin may use to create a new file. + @param handle MUST be the @p handle member of this struct. + @param path The path of the new file within a namespace unique to this + plugin instance. + @return The absolute path to use for the new file. + + This function can be used by plugins to create files and directories, + either at state saving time (if this feature is passed to + LV2_State_Interface.save()) or any time (if this feature is passed to + LV2_Descriptor.instantiate()). + + The host MUST do whatever is necessary for the plugin to be able to + create a file at the returned path (e.g. using fopen), including + creating any leading directories. + + If this function is passed to LV2_Descriptor.instantiate(), it may be + called from any non-realtime context. If it is passed to + LV2_State_Interface.save(), it may only be called within the dynamic + scope of that function call. + + The caller is responsible for freeing the returned value with free(). + */ + char* (*path)(LV2_State_Make_Path_Handle handle, + const char* path); +} LV2_State_Make_Path; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_STATE_H */ diff --git a/distrho/src/lv2/time.h b/distrho/src/lv2/time.h @@ -0,0 +1,49 @@ +/* + Copyright 2011 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file time.h C header for the LV2 Time extension + <http://lv2plug.in/ns/ext/time>. +*/ + +#ifndef LV2_TIME_H +#define LV2_TIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LV2_TIME_URI "http://lv2plug.in/ns/ext/time" + +#define LV2_TIME__Time LV2_TIME_URI "#Time" +#define LV2_TIME__Position LV2_TIME_URI "#Position" +#define LV2_TIME__Rate LV2_TIME_URI "#Rate" +#define LV2_TIME__position LV2_TIME_URI "#position" +#define LV2_TIME__barBeat LV2_TIME_URI "#barBeat" +#define LV2_TIME__bar LV2_TIME_URI "#bar" +#define LV2_TIME__beat LV2_TIME_URI "#beat" +#define LV2_TIME__beatUnit LV2_TIME_URI "#beatUnit" +#define LV2_TIME__beatsPerBar LV2_TIME_URI "#beatsPerBar" +#define LV2_TIME__beatsPerMinute LV2_TIME_URI "#beatsPerMinute" +#define LV2_TIME__frame LV2_TIME_URI "#frame" +#define LV2_TIME__framesPerSecond LV2_TIME_URI "#framesPerSecond" +#define LV2_TIME__speed LV2_TIME_URI "#speed" + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_TIME_H */ diff --git a/distrho/src/lv2/ui.h b/distrho/src/lv2/ui.h @@ -0,0 +1,407 @@ +/* + LV2 UI Extension + Copyright 2009-2012 David Robillard <d@drobilla.net> + Copyright 2006-2011 Lars Luthman <lars.luthman@gmail.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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file ui.h + C header for the LV2 UI extension <http://lv2plug.in/ns/extensions/ui>. +*/ + +#ifndef LV2_UI_H +#define LV2_UI_H + +#include <stdint.h> + +#include "lv2.h" + +#define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui" +#define LV2_UI_PREFIX LV2_UI_URI "#" + +#define LV2_UI__CocoaUI LV2_UI_PREFIX "CocoaUI" +#define LV2_UI__Gtk3UI LV2_UI_PREFIX "Gtk3UI" +#define LV2_UI__GtkUI LV2_UI_PREFIX "GtkUI" +#define LV2_UI__PortNotification LV2_UI_PREFIX "PortNotification" +#define LV2_UI__Qt4UI LV2_UI_PREFIX "Qt4UI" +#define LV2_UI__UI LV2_UI_PREFIX "UI" +#define LV2_UI__WindowsUI LV2_UI_PREFIX "WindowsUI" +#define LV2_UI__X11UI LV2_UI_PREFIX "X11UI" +#define LV2_UI__binary LV2_UI_PREFIX "binary" +#define LV2_UI__fixedSize LV2_UI_PREFIX "fixedSize" +#define LV2_UI__idleInterface LV2_UI_PREFIX "idleInterface" +#define LV2_UI__noUserResize LV2_UI_PREFIX "noUserResize" +#define LV2_UI__notifyType LV2_UI_PREFIX "notifyType" +#define LV2_UI__parent LV2_UI_PREFIX "parent" +#define LV2_UI__plugin LV2_UI_PREFIX "plugin" +#define LV2_UI__portIndex LV2_UI_PREFIX "portIndex" +#define LV2_UI__portMap LV2_UI_PREFIX "portMap" +#define LV2_UI__portNotification LV2_UI_PREFIX "portNotification" +#define LV2_UI__portSubscribe LV2_UI_PREFIX "portSubscribe" +#define LV2_UI__resize LV2_UI_PREFIX "resize" +#define LV2_UI__touch LV2_UI_PREFIX "touch" +#define LV2_UI__ui LV2_UI_PREFIX "ui" + +/** + The index returned by LV2_UI_Port_Port::port_index() for unknown ports. +*/ +#define LV2UI_INVALID_PORT_INDEX ((uint32_t)-1) + +#ifdef __cplusplus +extern "C" { +#else +# include <stdbool.h> +#endif + +/** + A pointer to some widget or other type of UI handle. + + The actual type is defined by the type of the UI. +*/ +typedef void* LV2UI_Widget; + +/** + A pointer to an instance of a UI. + + It is valid to compare this to NULL (0 for C++) but otherwise the host MUST + not attempt to interpret it. The UI plugin may use it to reference internal + instance data. +*/ +typedef void* LV2UI_Handle; + +/** + A pointer to a controller provided by the host. + + It is valid to compare this to NULL (0 for C++) but otherwise the UI plugin + MUST NOT attempt to interpret it. The host may use it to reference internal + instance data. +*/ +typedef void* LV2UI_Controller; + +/** + A pointer to opaque data for a feature. +*/ +typedef void* LV2UI_Feature_Handle; + +/** + The type of the host-provided function that the UI can use to + send data to a plugin's input ports. + + The @p buffer parameter must point to a block of data, @c buffer_size bytes + large. The format of this data and how the host should use it is defined by + the @p port_protocol. This buffer is owned by the UI and is only valid for + the duration of this call. + + The @p port_protocol parameter should either be 0 or the URID for a + ui:PortProtocol. If it is 0, the protocol is implicitly ui:floatProtocol, + the port must be an lv2:ControlPort input, @c buffer must point to a single + float value, and @c buffer_size must be sizeof(float). + + The UI SHOULD NOT use a PortProtocol not supported by the host (i.e. one not + passed by the host as a feature), but the host MUST gracefully ignore any + port_protocol it does not understand. +*/ +typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller, + uint32_t port_index, + uint32_t buffer_size, + uint32_t port_protocol, + const void* buffer); + +/** + The implementation of a UI. + + A pointer to an object of this type is returned by the lv2ui_descriptor() + function. +*/ +typedef struct _LV2UI_Descriptor { + /** + The URI for this UI (not for the plugin it controls). + */ + const char* URI; + + /** + Create a new UI object and return a handle to it. This function works + similarly to the instantiate() member in LV2_Descriptor. + + @param descriptor The descriptor for the UI that you want to instantiate. + + @param plugin_uri The URI of the plugin that this UI will control. + + @param bundle_path The path to the bundle containing the RDF data file + that references this shared object file, including the trailing '/'. + + @param write_function A function provided by the host that the UI can use + to send data to the plugin's input ports. + + @param controller A handle for the plugin instance that should be passed + as the first parameter of @p write_function. + + @param widget A pointer to an LV2UI_Widget. The UI will write a widget + pointer to this location (what type of widget depends on the RDF class of + the UI) that will be the main UI widget. + + @param features An array of LV2_Feature pointers. The host must pass all + feature URIs that it and the UI supports and any additional data, just + like in the LV2 plugin instantiate() function. Note that UI features and + plugin features are NOT necessarily the same, they just share the same + data structure - this will probably not be the same array as the one the + plugin host passes to a plugin. + + */ + LV2UI_Handle (*instantiate)(const struct _LV2UI_Descriptor* descriptor, + const char* plugin_uri, + const char* bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature* const* features); + + + /** + Destroy the UI object and the associated widget. The host must not try + to access the widget after calling this function. + */ + void (*cleanup)(LV2UI_Handle ui); + + /** + Tell the UI that something interesting has happened at a plugin port. + + What is interesting and how it is written to the buffer passed to this + function is defined by the @p format parameter, which has the same + meaning as in LV2UI_Write_Function. The only exception is ports of the + class lv2:ControlPort, for which this function should be called when the + port value changes (it does not have to be called for every single change + if the host's UI thread has problems keeping up with the thread the + plugin is running in), @p buffer_size should be 4, the buffer should + contain a single IEEE-754 float, and @p format should be 0. + + By default, the host should only call this function for input ports of + the lv2:ControlPort class. However, this can be modified by using + ui:portNotification in the UI data, or the ui:portSubscribe feature. + + The @p buffer is only valid during the time of this function call, so if + the UI wants to keep it for later use it has to copy the contents to an + internal buffer. + + This member may be set to NULL if the UI is not interested in any + port events. + */ + void (*port_event)(LV2UI_Handle ui, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer); + + /** + Return a data structure associated with an extension URI, for example + a struct containing additional function pointers. + + Avoid returning function pointers directly since standard C/C++ has no + valid way of casting a void* to a function pointer. This member may be set + to NULL if the UI is not interested in supporting any extensions. This is + similar to the extension_data() member in LV2_Descriptor. + */ + const void* (*extension_data)(const char* uri); +} LV2UI_Descriptor; + +/** + UI Resize Feature (LV2_UI__resize) + + This structure may be used in two ways: as a feature passed by the host via + LV2UI_Descriptor::instantiate(), or as extension data provided by a UI via + LV2UI_Descriptor::extension_data()). +*/ +typedef struct _LV2UI_Resize { + /** + Pointer to opaque data which must be passed to ui_resize(). + */ + LV2UI_Feature_Handle handle; + + /** + Request or advertise a size change. + + When this struct is provided by the host, the UI may call this + function to inform the host about the size of the UI. + + When this struct is provided by the UI, the host may call this + function to notify the UI that it should change its size accordingly. + + @return 0 on success. + */ + int (*ui_resize)(LV2UI_Feature_Handle handle, int width, int height); +} LV2UI_Resize; + +/** + Port Map Feature (LV2_UI__portMap). + + This feature can be used by the UI to get the index for a port with the + given symbol. This makes it possible to implement and distribute a UI + separately from the plugin (since symbol is a guaranteed stable port + identifier while index is not). +*/ +typedef struct _LV2UI_Port_Map { + /** + Pointer to opaque data which must be passed to ui_resize(). + */ + LV2UI_Feature_Handle handle; + + /** + Get the index for the port with the given @p symbol. + + @return The index of the port, or LV2_UI_INVALID_PORT_INDEX if no such + port is found. + */ + uint32_t (*port_index)(LV2UI_Feature_Handle handle, const char* symbol); +} LV2UI_Port_Map; + +/** + Port subscription feature (LV2_UI__portSubscribe); +*/ +typedef struct _LV2UI_Port_Subscribe { + /** + Pointer to opaque data which must be passed to ui_resize(). + */ + LV2UI_Feature_Handle handle; + + /** + Subscribe to updates for a port. + + This means that the host will call the UI's port_event() function when + the port value changes (as defined by protocol). + + Calling this function with the same @p port_index and @p port_protocol + as an already active subscription has no effect. + + @param handle The handle field of this struct. + @param port_index The index of the port. + @param port_protocol The URID of the ui:PortProtocol. + @param features Features for this subscription. + @return 0 on success. + */ + uint32_t (*subscribe)(LV2UI_Feature_Handle handle, + uint32_t port_index, + uint32_t port_protocol, + const LV2_Feature* const* features); + + /** + Unsubscribe from updates for a port. + + This means that the host will cease calling calling port_event() when + the port value changes. + + Calling this function with a @p port_index and @p port_protocol that + does not refer to an active port subscription has no effect. + + @param handle The handle field of this struct. + @param port_index The index of the port. + @param port_protocol The URID of the ui:PortProtocol. + @param features Features for this subscription. + @return 0 on success. + */ + uint32_t (*unsubscribe)(LV2UI_Feature_Handle handle, + uint32_t port_index, + uint32_t port_protocol, + const LV2_Feature* const* features); +} LV2UI_Port_Subscribe; + +/** + A feature to notify the host the user has grabbed a UI control. +*/ +typedef struct _LV2UI_Touch { + /** + Pointer to opaque data which must be passed to ui_resize(). + */ + LV2UI_Feature_Handle handle; + + /** + Notify the host that a control has been grabbed or released. + + @param handle The handle field of this struct. + @param port_index The index of the port associated with the control. + @param grabbed If true, the control has been grabbed, otherwise the + control has been released. + */ + void (*touch)(LV2UI_Feature_Handle handle, + uint32_t port_index, + bool grabbed); +} LV2UI_Touch; + +/** + UI Idle Feature (LV2_UI__idle) + + This feature is an addition to the UI API that provides a callback for the + host to call rapidly, e.g. to drive the idle callback of a toolkit. +*/ +typedef struct _LV2UI_Idle_Interface { + /** + Run a single iteration of the UI's idle loop. + + This will be called "frequently" in the UI thread at a rate appropriate + for a toolkit main loop. There are no precise timing guarantees. + + @return 0 on success, or anything else to stop being called. + */ + int (*idle)(LV2UI_Handle ui); +} LV2UI_Idle_Interface; + +/** + Peak data for a slice of time, the update format for ui:peakProtocol. +*/ +typedef struct _LV2UI_Peak_Data { + /** + The start of the measurement period. This is just a running counter + that is only meaningful in comparison to previous values and must not be + interpreted as an absolute time. + */ + uint32_t period_start; + + /** + The size of the measurement period, in the same units as period_start. + */ + uint32_t period_size; + + /** + The peak value for the measurement period. This should be the maximal + value for abs(sample) over all the samples in the period. + */ + float peak; +} LV2UI_Peak_Data; + +/** + A plugin UI programmer must include a function called "lv2ui_descriptor" + with the following function prototype within the shared object file. This + function will have C-style linkage (if you are using C++ this is taken care + of by the 'extern "C"' clause at the top of the file). This function is + loaded from the library by the UI host and called to get a + LV2UI_Descriptor for the wanted plugin. + + Just like lv2_descriptor(), this function takes an index parameter. The + index should only be used for enumeration and not as any sort of ID number - + the host should just iterate from 0 and upwards until the function returns + NULL or a descriptor with an URI matching the one the host is looking for. +*/ +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index); + +/** + The type of the lv2ui_descriptor() function. +*/ +typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index); + +#ifdef __cplusplus +} +#endif + +#endif /* LV2_UI_H */ diff --git a/distrho/src/lv2/units.h b/distrho/src/lv2/units.h @@ -0,0 +1,62 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file units.h + C definitions for the LV2 Units extension + <http://lv2plug.in/ns/extensions/units>. +*/ + +#ifndef LV2_UNITS_H +#define LV2_UNITS_H + +#define LV2_UNITS_URI "http://lv2plug.in/ns/extensions/units" +#define LV2_UNITS_PREFIX LV2_UNITS_URI "#" + +#define LV2_UNITS__Conversion LV2_UNITS_PREFIX "Conversion" +#define LV2_UNITS__Unit LV2_UNITS_PREFIX "Unit" +#define LV2_UNITS__bar LV2_UNITS_PREFIX "bar" +#define LV2_UNITS__beat LV2_UNITS_PREFIX "beat" +#define LV2_UNITS__bpm LV2_UNITS_PREFIX "bpm" +#define LV2_UNITS__cent LV2_UNITS_PREFIX "cent" +#define LV2_UNITS__cm LV2_UNITS_PREFIX "cm" +#define LV2_UNITS__coef LV2_UNITS_PREFIX "coef" +#define LV2_UNITS__conversion LV2_UNITS_PREFIX "conversion" +#define LV2_UNITS__db LV2_UNITS_PREFIX "db" +#define LV2_UNITS__degree LV2_UNITS_PREFIX "degree" +#define LV2_UNITS__frame LV2_UNITS_PREFIX "frame" +#define LV2_UNITS__hz LV2_UNITS_PREFIX "hz" +#define LV2_UNITS__inch LV2_UNITS_PREFIX "inch" +#define LV2_UNITS__khz LV2_UNITS_PREFIX "khz" +#define LV2_UNITS__km LV2_UNITS_PREFIX "km" +#define LV2_UNITS__m LV2_UNITS_PREFIX "m" +#define LV2_UNITS__mhz LV2_UNITS_PREFIX "mhz" +#define LV2_UNITS__midiNote LV2_UNITS_PREFIX "midiNote" +#define LV2_UNITS__mile LV2_UNITS_PREFIX "mile" +#define LV2_UNITS__min LV2_UNITS_PREFIX "min" +#define LV2_UNITS__mm LV2_UNITS_PREFIX "mm" +#define LV2_UNITS__ms LV2_UNITS_PREFIX "ms" +#define LV2_UNITS__name LV2_UNITS_PREFIX "name" +#define LV2_UNITS__oct LV2_UNITS_PREFIX "oct" +#define LV2_UNITS__pc LV2_UNITS_PREFIX "pc" +#define LV2_UNITS__prefixConversion LV2_UNITS_PREFIX "prefixConversion" +#define LV2_UNITS__render LV2_UNITS_PREFIX "render" +#define LV2_UNITS__s LV2_UNITS_PREFIX "s" +#define LV2_UNITS__semitone12TET LV2_UNITS_PREFIX "semitone12TET" +#define LV2_UNITS__symbol LV2_UNITS_PREFIX "symbol" +#define LV2_UNITS__unit LV2_UNITS_PREFIX "unit" + +#endif /* LV2_UNITS_H */ diff --git a/distrho/src/lv2/uri-map.h b/distrho/src/lv2/uri-map.h @@ -0,0 +1,98 @@ +/* + Copyright 2008-2011 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file + C header for the LV2 URI Map extension <http://lv2plug.in/ns/ext/uri-map>. + + This extension defines a simple mechanism for plugins to map URIs to + integers, usually for performance reasons (e.g. processing events typed by + URIs in real time). The expected use case is for plugins to map URIs to + integers for things they 'understand' at instantiation time, and store those + values for use in the audio thread without doing any string comparison. + This allows the extensibility of RDF with the performance of integers (or + centrally defined enumerations). +*/ + +#ifndef LV2_URI_MAP_H +#define LV2_URI_MAP_H + +#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Opaque pointer to host data. +*/ +typedef void* LV2_URI_Map_Callback_Data; + +/** + URI Map Feature. + + To support this feature the host must pass an LV2_Feature struct to the + plugin's instantiate method with URI "http://lv2plug.in/ns/ext/uri-map" + and data pointed to an instance of this struct. +*/ +typedef struct { + /** + Opaque pointer to host data. + + The plugin MUST pass this to any call to functions in this struct. + Otherwise, it must not be interpreted in any way. + */ + LV2_URI_Map_Callback_Data callback_data; + + /** + Get the numeric ID of a URI from the host. + + @param callback_data Must be the callback_data member of this struct. + @param map The 'context' of this URI. Certain extensions may define a + URI that must be passed here with certain restrictions on the return + value (e.g. limited range). This value may be NULL if the plugin needs + an ID for a URI in general. Extensions SHOULD NOT define a context + unless there is a specific need to do so, e.g. to restrict the range of + the returned value. + @param uri The URI to be mapped to an integer ID. + + This function is referentially transparent; any number of calls with the + same arguments is guaranteed to return the same value over the life of a + plugin instance (though the same URI may return different values with a + different map parameter). However, this function is not necessarily very + fast: plugins SHOULD cache any IDs they might need in performance + critical situations. + + The return value 0 is reserved and indicates that an ID for that URI + could not be created for whatever reason. Extensions MAY define more + precisely what this means in a certain context, but in general plugins + SHOULD handle this situation as gracefully as possible. However, hosts + SHOULD NOT return 0 from this function in non-exceptional circumstances + (e.g. the URI map SHOULD be dynamic). Hosts that statically support only + a fixed set of URIs should not expect plugins to function correctly. + */ + uint32_t (*uri_to_id)(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri); +} LV2_URI_Map_Feature; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_URI_MAP_H */ diff --git a/distrho/src/lv2/urid.h b/distrho/src/lv2/urid.h @@ -0,0 +1,129 @@ +/* + Copyright 2008-2012 David Robillard <http://drobilla.net> + Copyright 2011 Gabriel M. Beddingfield <gabrbedd@gmail.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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file urid.h + C header for the LV2 URID extension <http://lv2plug.in/ns/ext/urid> +*/ + +#ifndef LV2_URID_H +#define LV2_URID_H + +#define LV2_URID_URI "http://lv2plug.in/ns/ext/urid" +#define LV2_URID_PREFIX LV2_URID_URI "#" + +#define LV2_URID__map LV2_URID_PREFIX "map" +#define LV2_URID__unmap LV2_URID_PREFIX "unmap" + +/* Legacy defines */ +#define LV2_URID_MAP_URI LV2_URID__map +#define LV2_URID_UNMAP_URI LV2_URID__unmap + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Opaque pointer to host data for LV2_URID_Map. +*/ +typedef void* LV2_URID_Map_Handle; + +/** + Opaque pointer to host data for LV2_URID_Unmap. +*/ +typedef void* LV2_URID_Unmap_Handle; + +/** + URI mapped to an integer. +*/ +typedef uint32_t LV2_URID; + +/** + URID Map Feature (LV2_URID__map) +*/ +typedef struct _LV2_URID_Map { + /** + Opaque pointer to host data. + + This MUST be passed to map_uri() whenever it is called. + Otherwise, it must not be interpreted in any way. + */ + LV2_URID_Map_Handle handle; + + /** + Get the numeric ID of a URI. + + If the ID does not already exist, it will be created. + + This function is referentially transparent; any number of calls with the + same arguments is guaranteed to return the same value over the life of a + plugin instance. Note, however, that several URIs MAY resolve to the + same ID if the host considers those URIs equivalent. + + This function is not necessarily very fast or RT-safe: plugins SHOULD + cache any IDs they might need in performance critical situations. + + The return value 0 is reserved and indicates that an ID for that URI + could not be created for whatever reason. However, hosts SHOULD NOT + return 0 from this function in non-exceptional circumstances (i.e. the + URI map SHOULD be dynamic). + + @param handle Must be the callback_data member of this struct. + @param uri The URI to be mapped to an integer ID. + */ + LV2_URID (*map)(LV2_URID_Map_Handle handle, + const char* uri); +} LV2_URID_Map; + +/** + URI Unmap Feature (LV2_URID__unmap) +*/ +typedef struct _LV2_URID_Unmap { + /** + Opaque pointer to host data. + + This MUST be passed to unmap() whenever it is called. + Otherwise, it must not be interpreted in any way. + */ + LV2_URID_Unmap_Handle handle; + + /** + Get the URI for a previously mapped numeric ID. + + Returns NULL if @p urid is not yet mapped. Otherwise, the corresponding + URI is returned in a canonical form. This MAY not be the exact same + string that was originally passed to LV2_URID_Map::map(), but it MUST be + an identical URI according to the URI syntax specification (RFC3986). A + non-NULL return for a given @p urid will always be the same for the life + of the plugin. Plugins that intend to perform string comparison on + unmapped URIs SHOULD first canonicalise URI strings with a call to + map_uri() followed by a call to unmap_uri(). + + @param handle Must be the callback_data member of this struct. + @param urid The ID to be mapped back to the URI string. + */ + const char* (*unmap)(LV2_URID_Unmap_Handle handle, + LV2_URID urid); +} LV2_URID_Unmap; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_URID_H */ diff --git a/distrho/src/lv2/worker.h b/distrho/src/lv2/worker.h @@ -0,0 +1,158 @@ +/* + Copyright 2012 David Robillard <http://drobilla.net> + + 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 permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file worker.h C header for the LV2 Worker extension + <http://lv2plug.in/ns/ext/worker>. +*/ + +#ifndef LV2_WORKER_H +#define LV2_WORKER_H + +#include <stdint.h> + +#include "lv2.h" + +#define LV2_WORKER_URI "http://lv2plug.in/ns/ext/worker" +#define LV2_WORKER_PREFIX LV2_WORKER_URI "#" + +#define LV2_WORKER__interface LV2_WORKER_PREFIX "interface" +#define LV2_WORKER__schedule LV2_WORKER_PREFIX "schedule" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + A status code for worker functions. +*/ +typedef enum { + LV2_WORKER_SUCCESS = 0, /**< Completed successfully. */ + LV2_WORKER_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_WORKER_ERR_NO_SPACE = 2 /**< Failed due to lack of space. */ +} LV2_Worker_Status; + +typedef void* LV2_Worker_Respond_Handle; + +/** + A function to respond to run() from the worker method. + + The @p data MUST be safe for the host to copy and later pass to + work_response(), and the host MUST guarantee that it will be eventually + passed to work_response() if this function returns LV2_WORKER_SUCCESS. +*/ +typedef LV2_Worker_Status (*LV2_Worker_Respond_Function)( + LV2_Worker_Respond_Handle handle, + uint32_t size, + const void* data); + +/** + LV2 Plugin Worker Interface. + + This is the interface provided by the plugin to implement a worker method. + The plugin's extension_data() method should return an LV2_Worker_Interface + when called with LV2_WORKER__interface as its argument. +*/ +typedef struct _LV2_Worker_Interface { + /** + The worker method. This is called by the host in a non-realtime context + as requested, possibly with an arbitrary message to handle. + + A response can be sent to run() using @p respond. The plugin MUST NOT + make any assumptions about which thread calls this method, other than + the fact that there are no real-time requirements. + + @param instance The LV2 instance this is a method on. + @param respond A function for sending a response to run(). + @param handle Must be passed to @p respond if it is called. + @param size The size of @p data. + @param data Data from run(), or NULL. + */ + LV2_Worker_Status (*work)(LV2_Handle instance, + LV2_Worker_Respond_Function respond, + LV2_Worker_Respond_Handle handle, + uint32_t size, + const void* data); + + /** + Handle a response from the worker. This is called by the host in the + run() context when a response from the worker is ready. + + @param instance The LV2 instance this is a method on. + @param size The size of @p body. + @param body Message body, or NULL. + */ + LV2_Worker_Status (*work_response)(LV2_Handle instance, + uint32_t size, + const void* body); + + /** + Called when all responses for this cycle have been delivered. + + Since work_response() may be called after run() finished, this provides + a hook for code that must run after the cycle is completed. + + This field may be NULL if the plugin has no use for it. Otherwise, the + host MUST call it after every run(), regardless of whether or not any + responses were sent that cycle. + */ + LV2_Worker_Status (*end_run)(LV2_Handle instance); +} LV2_Worker_Interface; + +typedef void* LV2_Worker_Schedule_Handle; + +typedef struct _LV2_Worker_Schedule { + /** + Opaque host data. + */ + LV2_Worker_Schedule_Handle handle; + + /** + Request from run() that the host call the worker. + + This function is in the audio threading class. It should be called from + run() to request that the host call the work() method in a non-realtime + context with the given arguments. + + This function is always safe to call from run(), but it is not + guaranteed that the worker is actually called from a different thread. + In particular, when free-wheeling (e.g. for offline rendering), the + worker may be executed immediately. This allows single-threaded + processing with sample accuracy and avoids timing problems when run() is + executing much faster or slower than real-time. + + Plugins SHOULD be written in such a way that if the worker runs + immediately, and responses from the worker are delivered immediately, + the effect of the work takes place immediately with sample accuracy. + + The @p data MUST be safe for the host to copy and later pass to work(), + and the host MUST guarantee that it will be eventually passed to work() + if this function returns LV2_WORKER_SUCCESS. + + @param handle The handle field of this struct. + @param size The size of @p data. + @param data Message to pass to work(), or NULL. + */ + LV2_Worker_Status (*schedule_work)(LV2_Worker_Schedule_Handle handle, + uint32_t size, + const void* data); +} LV2_Worker_Schedule; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_WORKER_H */ diff --git a/distrho/src/vestige/aeffectx.h b/distrho/src/vestige/aeffectx.h @@ -0,0 +1,279 @@ +/* + * aeffectx.h - simple header to allow VeSTige compilation and eventually work + * + * Copyright (c) 2006 Javier Serrano Polo <jasp00/at/users.sourceforge.net> + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ +#include <stdint.h> +#ifndef _AEFFECTX_H +#define _AEFFECTX_H + +#define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) | \ + ( ( (int) b ) << 16 ) | \ + ( ( (int) c ) << 8 ) | \ + ( ( (int) d ) << 0 ) ) + +#define audioMasterAutomate 0 +#define audioMasterVersion 1 +#define audioMasterCurrentId 2 +#define audioMasterIdle 3 +#define audioMasterPinConnected 4 +// unsupported? 5 +#define audioMasterWantMidi 6 +#define audioMasterGetTime 7 +#define audioMasterProcessEvents 8 +#define audioMasterSetTime 9 +#define audioMasterTempoAt 10 +#define audioMasterGetNumAutomatableParameters 11 +#define audioMasterGetParameterQuantization 12 +#define audioMasterIOChanged 13 +#define audioMasterNeedIdle 14 +#define audioMasterSizeWindow 15 +#define audioMasterGetSampleRate 16 +#define audioMasterGetBlockSize 17 +#define audioMasterGetInputLatency 18 +#define audioMasterGetOutputLatency 19 +#define audioMasterGetPreviousPlug 20 +#define audioMasterGetNextPlug 21 +#define audioMasterWillReplaceOrAccumulate 22 +#define audioMasterGetCurrentProcessLevel 23 +#define audioMasterGetAutomationState 24 +#define audioMasterOfflineStart 25 +#define audioMasterOfflineRead 26 +#define audioMasterOfflineWrite 27 +#define audioMasterOfflineGetCurrentPass 28 +#define audioMasterOfflineGetCurrentMetaPass 29 +#define audioMasterSetOutputSampleRate 30 +// unsupported? 31 +#define audioMasterGetSpeakerArrangement 31 // deprecated in 2.4? +#define audioMasterGetVendorString 32 +#define audioMasterGetProductString 33 +#define audioMasterGetVendorVersion 34 +#define audioMasterVendorSpecific 35 +#define audioMasterSetIcon 36 +#define audioMasterCanDo 37 +#define audioMasterGetLanguage 38 +#define audioMasterOpenWindow 39 +#define audioMasterCloseWindow 40 +#define audioMasterGetDirectory 41 +#define audioMasterUpdateDisplay 42 +#define audioMasterBeginEdit 43 +#define audioMasterEndEdit 44 +#define audioMasterOpenFileSelector 45 +#define audioMasterCloseFileSelector 46 // currently unused +#define audioMasterEditFile 47 // currently unused +#define audioMasterGetChunkFile 48 // currently unused +#define audioMasterGetInputSpeakerArrangement 49 // currently unused + +#define effFlagsHasEditor 1 +#define effFlagsCanReplacing (1 << 4) // very likely +#define effFlagsIsSynth (1 << 8) // currently unused + +#define effOpen 0 +#define effClose 1 // currently unused +#define effSetProgram 2 // currently unused +#define effGetProgram 3 // currently unused +#define effGetProgramName 5 // currently unused +#define effGetParamName 8 // currently unused +#define effSetSampleRate 10 +#define effSetBlockSize 11 +#define effMainsChanged 12 +#define effEditGetRect 13 +#define effEditOpen 14 +#define effEditClose 15 +#define effEditIdle 19 +#define effEditTop 20 +#define effProcessEvents 25 +#define effGetEffectName 45 +#define effGetVendorString 47 +#define effGetProductString 48 +#define effGetVendorVersion 49 +#define effCanDo 51 // currently unused +/* from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +#define effGetParameterProperties 56 +#define effGetVstVersion 58 // currently unused + +#define kEffectMagic (CCONST( 'V', 's', 't', 'P' )) +#define kVstLangEnglish 1 +#define kVstMidiType 1 +#define kVstTempoValid (1 << 10) +#define kVstTransportPlaying (1 << 1) + + +struct RemoteVstPlugin; + +#define kVstNanosValid (1 << 8) +#define kVstPpqPosValid (1 << 9) +#define kVstTempoValid (1 << 10) +#define kVstBarsValid (1 << 11) +#define kVstCyclePosValid (1 << 12) +#define kVstTimeSigValid (1 << 13) +#define kVstSmpteValid (1 << 14) +#define kVstClockValid (1 << 15) + +struct _VstMidiEvent +{ + // 00 + int type; + // 04 + int byteSize; + // 08 + int deltaFrames; + // 0c? + int flags; + // 10? + int noteLength; + // 14? + int noteOffset; + // 18 + char midiData[4]; + // 1c? + char detune; + // 1d? + char noteOffVelocity; + // 1e? + char reserved1; + // 1f? + char reserved2; +}; + +typedef struct _VstMidiEvent VstMidiEvent; + + +struct _VstEvent +{ + char dump[sizeof (VstMidiEvent)]; + +}; + +typedef struct _VstEvent VstEvent; + +struct _VstEvents +{ + // 00 + int numEvents; + // 04 + void *reserved; + // 08 + VstEvent * events[2]; +}; + +typedef struct _VstEvents VstEvents; + +/* this struct taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +struct _VstParameterProperties +{ + float stepFloat; + float smallStepFloat; + float largeStepFloat; + char label[64]; + int32_t flags; + int32_t minInteger; + int32_t maxInteger; + int32_t stepInteger; + int32_t largeStepInteger; + char shortLabel[8]; +}; + +typedef struct _VstParameterProperties VstParameterProperties; + +/* this enum taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +enum VstParameterFlags +{ + kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ + kVstParameterUsesIntegerMinMax = 1 << 1, /* minInteger, maxInteger valid */ + kVstParameterUsesFloatStep = 1 << 2, /* stepFloat, smallStepFloat, largeStepFloat valid */ + kVstParameterUsesIntStep = 1 << 3, /* stepInteger, largeStepInteger valid */ + kVstParameterSupportsDisplayIndex = 1 << 4, /* displayIndex valid */ + kVstParameterSupportsDisplayCategory = 1 << 5, /* category, etc. valid */ + kVstParameterCanRamp = 1 << 6 /* set if parameter value can ramp up/down */ +}; + +struct _AEffect +{ + // Never use virtual functions!!! + // 00-03 + int magic; + // dispatcher 04-07 + intptr_t (* dispatcher) (struct _AEffect *, int, int, intptr_t, void *, float); + // process, quite sure 08-0b + void (* process) (struct _AEffect *, float **, float **, int); + // setParameter 0c-0f + void (* setParameter) (struct _AEffect *, int, float); + // getParameter 10-13 + float (* getParameter) (struct _AEffect *, int); + // programs 14-17 + int numPrograms; + // Params 18-1b + int numParams; + // Input 1c-1f + int numInputs; + // Output 20-23 + int numOutputs; + // flags 24-27 + int flags; + // Fill somewhere 28-2b + void *ptr1; + void *ptr2; + // Zeroes 2c-2f 30-33 34-37 38-3b + char empty3[4 + 4 + 4]; + // 1.0f 3c-3f + float unkown_float; + // An object? pointer 40-43 + void *ptr3; + // Zeroes 44-47 + void *user; + // Id 48-4b + int32_t uniqueID; + // Don't know 4c-4f + char unknown1[4]; + // processReplacing 50-53 + void (* processReplacing) (struct _AEffect *, float **, float **, int); +}; + +typedef struct _AEffect AEffect; + +struct _VstTimeInfo +{ + // 00 + double samplePos; + // 08 + double sampleRate; + // unconfirmed 10 18 + char empty1[8 + 8]; + // 20? + double tempo; + // unconfirmed 28 30 38 + char empty2[8 + 8 + 8]; + // 40? + int timeSigNumerator; + // 44? + int timeSigDenominator; + // unconfirmed 48 4c 50 + char empty3[4 + 4 + 4]; + // 54 + int flags; +}; + +typedef struct _VstTimeInfo VstTimeInfo; + +typedef intptr_t (* audioMasterCallback) (AEffect *, int32_t, int32_t, intptr_t, void *, float); + +#endif diff --git a/lv2-ttl-generator/GNUmakefile b/lv2-ttl-generator/GNUmakefile @@ -0,0 +1,16 @@ +#!/usr/bin/makefile -f + +all: build + +build: ../lv2_ttl_generator +mingw: ../lv2_ttl_generator.exe + +../lv2_ttl_generator: lv2_ttl_generator.c + $(CXX) lv2_ttl_generator.c -o ../lv2_ttl_generator -ldl + +../lv2_ttl_generator.exe: lv2_ttl_generator.c + $(CXX) lv2_ttl_generator.c -o ../lv2_ttl_generator.exe -static + touch ../lv2_ttl_generator + +clean: + rm -f ../lv2_ttl_generator ../lv2_ttl_generator.exe diff --git a/lv2-ttl-generator/lv2_ttl_generator.c b/lv2-ttl-generator/lv2_ttl_generator.c @@ -0,0 +1,81 @@ +/* + * JUCE LV2 *.ttl generator + */ + +#include <stdio.h> +#include <string.h> + +#ifdef _WIN32 + #include <windows.h> + #define TTL_GENERATOR_WINDOWS +#else + #include <dlfcn.h> +#endif + +#ifndef nullptr + #define nullptr (0) +#endif + +typedef void (*TTL_Generator_Function)(const char* basename); + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + printf("usage: %s /path/to/plugin-DLL\n", argv[0]); + return 1; + } + +#ifdef TTL_GENERATOR_WINDOWS + const HMODULE handle = LoadLibraryA(argv[1]); +#else + void* const handle = dlopen(argv[1], RTLD_LAZY); +#endif + + if (! handle) + { +#ifdef TTL_GENERATOR_WINDOWS + printf("Failed to open plugin DLL\n"); +#else + printf("Failed to open plugin DLL, error was:\n%s\n", dlerror()); +#endif + return 2; + } + +#ifdef TTL_GENERATOR_WINDOWS + const TTL_Generator_Function ttlFn = (TTL_Generator_Function)GetProcAddress(handle, "lv2_generate_ttl"); +#else + const TTL_Generator_Function ttlFn = (TTL_Generator_Function)dlsym(handle, "lv2_generate_ttl"); +#endif + + if (ttlFn != NULL) + { + char basename[strlen(argv[1])+1]; + +#ifdef TTL_GENERATOR_WINDOWS + if (char* base2 = strrchr(argv[1], '\\')) +#else + if (char* base2 = strrchr(argv[1], '/')) +#endif + { + strcpy(basename, base2+1); + basename[strrchr(base2, '.')-base2-1] = '\0'; + } + else + strcpy(basename, argv[1]); + + printf("Generate ttl data for '%s', basename: '%s'\n", argv[1], basename); + + ttlFn(basename); + } + else + printf("Failed to find 'lv2_generate_ttl' function\n"); + +#ifdef TTL_GENERATOR_WINDOWS + FreeLibrary(handle); +#else + dlclose(handle); +#endif + + return 0; +}