| @@ -0,0 +1,35 @@ | |||||
| /* | |||||
| * Carla Backend | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * 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 | |||||
| * 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. | |||||
| * | |||||
| * For a full copy of the GNU General Public License see the COPYING file | |||||
| */ | |||||
| #ifndef __DISTRHO_PLUGIN_INFO_H__ | |||||
| #define __DISTRHO_PLUGIN_INFO_H__ | |||||
| #define DISTRHO_PLUGIN_NAME "Carla" | |||||
| #define DISTRHO_PLUGIN_HAS_UI 0 | |||||
| #define DISTRHO_PLUGIN_IS_SYNTH 1 | |||||
| #define DISTRHO_PLUGIN_NUM_INPUTS 2 | |||||
| #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 | |||||
| #define DISTRHO_PLUGIN_WANT_LATENCY 1 | |||||
| #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 | |||||
| #define DISTRHO_PLUGIN_WANT_STATE 1 | |||||
| #define DISTRHO_PLUGIN_URI "http://kxstudio.sf.net/carla" | |||||
| #endif // __DISTRHO_PLUGIN_INFO_H__ | |||||
| @@ -0,0 +1,188 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_PLUGIN_H__ | |||||
| #define __DISTRHO_PLUGIN_H__ | |||||
| #include "DistrhoUtils.h" | |||||
| 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; | |||||
| float step; | |||||
| float stepSmall; | |||||
| float stepLarge; | |||||
| ParameterRanges() | |||||
| : def(0.0f), | |||||
| min(0.0f), | |||||
| max(1.0f), | |||||
| step(0.001f), | |||||
| stepSmall(0.00001f), | |||||
| stepLarge(0.01f) {} | |||||
| ParameterRanges(float def, float min, float max) | |||||
| : step(0.001f), | |||||
| stepSmall(0.00001f), | |||||
| stepLarge(0.01f) | |||||
| { | |||||
| this->def = def; | |||||
| this->min = min; | |||||
| this->max = max; | |||||
| } | |||||
| ParameterRanges(float def, float min, float max, float step, float stepSmall, float stepLarge) | |||||
| { | |||||
| this->def = def; | |||||
| this->min = min; | |||||
| this->max = max; | |||||
| this->step = step; | |||||
| this->stepSmall = stepSmall; | |||||
| this->stepLarge = stepLarge; | |||||
| } | |||||
| void fixRange(float& value) const | |||||
| { | |||||
| if (value < min) | |||||
| value = min; | |||||
| else if (value > max) | |||||
| value = max; | |||||
| } | |||||
| }; | |||||
| // Parameter | |||||
| struct Parameter { | |||||
| uint32_t hints; | |||||
| d_string name; | |||||
| d_string symbol; | |||||
| d_string unit; | |||||
| ParameterRanges ranges; | |||||
| Parameter() | |||||
| : hints(0x0), | |||||
| name(nullptr), | |||||
| symbol(nullptr), | |||||
| unit(nullptr) {} | |||||
| }; | |||||
| // MidiEvent | |||||
| struct MidiEvent { | |||||
| uint32_t frame; | |||||
| uint8_t buffer[3]; | |||||
| MidiEvent() | |||||
| #if 0 // FIXME - code below is valid C++11 | |||||
| : frame(0), | |||||
| buffer{0} {} | |||||
| #else | |||||
| : frame(0) { buffer[0] = buffer[1] = buffer[2] = 0; } | |||||
| #endif | |||||
| }; | |||||
| // TimePos | |||||
| struct TimePos { | |||||
| double bpm; | |||||
| TimePos() | |||||
| : bpm(120.0) {} | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| struct PluginPrivateData; | |||||
| class Plugin | |||||
| { | |||||
| public: | |||||
| Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount); | |||||
| virtual ~Plugin(); | |||||
| // --------------------------------------------- | |||||
| // Host state | |||||
| uint32_t d_bufferSize() const; | |||||
| double d_sampleRate() const; | |||||
| const TimePos* d_timePos() const; | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| void d_setLatency(uint32_t samples); | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| // Information | |||||
| virtual const char* d_name() { return DISTRHO_PLUGIN_NAME; } | |||||
| virtual const char* d_label() = 0; | |||||
| virtual const char* d_maker() = 0; | |||||
| virtual const char* d_license() = 0; | |||||
| virtual uint32_t d_version() = 0; | |||||
| virtual long d_uniqueId() = 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_parameterValue(uint32_t index) = 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() = 0; | |||||
| virtual void d_deactivate() = 0; | |||||
| virtual void d_run(const float** inputs, float** outputs, uint32_t frames, uint32_t midiEventCount, const MidiEvent* midiEvents) = 0; | |||||
| // Callbacks | |||||
| virtual void d_bufferSizeChanged(uint32_t newBufferSize); | |||||
| // --------------------------------------------- | |||||
| private: | |||||
| PluginPrivateData* data; | |||||
| friend class PluginInternal; | |||||
| }; | |||||
| Plugin* createPlugin(); | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // __DISTRHO_PLUGIN_H__ | |||||
| @@ -0,0 +1,28 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "src/DistrhoPlugin.cpp" | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_JACK) | |||||
| # include "src/DistrhoPluginJACK.cpp" | |||||
| #elif 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" | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST) | |||||
| # include "src/DistrhoPluginVST.cpp" | |||||
| #endif | |||||
| @@ -0,0 +1,92 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_UI_H__ | |||||
| #define __DISTRHO_UI_H__ | |||||
| #include "DistrhoUtils.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| struct UIPrivateData; | |||||
| class UI | |||||
| { | |||||
| public: | |||||
| UI(); | |||||
| virtual ~UI(); | |||||
| // --------------------------------------------- | |||||
| // Host DSP State | |||||
| double d_sampleRate() const; | |||||
| void d_setParameterValue(uint32_t index, float value); | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void d_setState(const char* key, const char* value); | |||||
| #endif | |||||
| // Host UI State | |||||
| void d_uiEditParameter(uint32_t index, bool started); | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void d_uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity); | |||||
| #endif | |||||
| void d_uiResize(unsigned int width, unsigned int height); | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| // Information | |||||
| virtual unsigned int d_width() = 0; | |||||
| virtual unsigned int d_height() = 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 | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| virtual void d_uiNoteReceived(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity); | |||||
| #endif | |||||
| // UI Callbacks | |||||
| virtual void d_uiIdle() = 0; | |||||
| // --------------------------------------------- | |||||
| private: | |||||
| UIPrivateData* data; | |||||
| friend class UIInternal; | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| friend class Qt4UI; | |||||
| #else | |||||
| friend class OpenGLUI; | |||||
| friend class OpenGLExtUI; | |||||
| #endif | |||||
| }; | |||||
| UI* createUI(); | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // __DISTRHO_UI_H__ | |||||
| @@ -0,0 +1,35 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "src/DistrhoUI.cpp" | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_JACK) | |||||
| // nothing | |||||
| #elif 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 | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| # include "src/DistrhoUIQt4.cpp" | |||||
| #else | |||||
| # include "src/DistrhoUIOpenGL.cpp" | |||||
| # include "src/DistrhoUIOpenGLExt.cpp" | |||||
| #endif | |||||
| @@ -0,0 +1,131 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_UI_OPENGL_H__ | |||||
| #define __DISTRHO_UI_OPENGL_H__ | |||||
| #include "src/DistrhoDefines.h" | |||||
| #ifdef DISTRHO_UI_OPENGL | |||||
| #include "DistrhoUI.h" | |||||
| #if DISTRHO_OS_MAC | |||||
| # include <OpenGL/glu.h> | |||||
| #else | |||||
| # include <GL/glu.h> | |||||
| #endif | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| enum Char { | |||||
| CHAR_BACKSPACE = 0x08, | |||||
| CHAR_ESCAPE = 0x1B, | |||||
| CHAR_DELETE = 0x7F | |||||
| }; | |||||
| 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 | |||||
| }; | |||||
| enum Mod { | |||||
| MOD_SHIFT = 1 << 0, /**< Shift key */ | |||||
| MOD_CTRL = 1 << 1, /**< Control key */ | |||||
| MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||||
| MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| class OpenGLUI : public UI | |||||
| { | |||||
| public: | |||||
| OpenGLUI(); | |||||
| virtual ~OpenGLUI(); | |||||
| // --------------------------------------------- | |||||
| // Host UI State (OpenGL) | |||||
| int d_uiGetModifiers(); | |||||
| void d_uiIgnoreKeyRepeat(bool ignore); | |||||
| void d_uiRepaint(); | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| // Information | |||||
| virtual unsigned int d_width() = 0; | |||||
| virtual unsigned int d_height() = 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 | |||||
| virtual void d_uiIdle(); | |||||
| virtual void d_onInit() = 0; | |||||
| virtual void d_onDisplay() = 0; | |||||
| virtual void d_onKeyboard(bool press, uint32_t key) = 0; | |||||
| virtual void d_onMotion(int x, int y) = 0; | |||||
| virtual void d_onMouse(int button, bool press, int x, int y) = 0; | |||||
| virtual void d_onReshape(int width, int height) = 0; | |||||
| virtual void d_onScroll(float dx, float dy) = 0; | |||||
| virtual void d_onSpecial(bool press, Key key) = 0; | |||||
| virtual void d_onClose() = 0; | |||||
| private: | |||||
| friend class UIInternal; | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_UI_OPENGL | |||||
| #endif // __DISTRHO_UI_OPENGL_H__ | |||||
| @@ -0,0 +1,306 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_UI_OPENGL_EXT_H__ | |||||
| #define __DISTRHO_UI_OPENGL_EXT_H__ | |||||
| #include "src/DistrhoDefines.h" | |||||
| #ifdef DISTRHO_UI_OPENGL | |||||
| #include "DistrhoUIOpenGL.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| class Point | |||||
| { | |||||
| public: | |||||
| Point(int x, int y); | |||||
| Point(const Point& pos); | |||||
| int getX() const; | |||||
| int getY() const; | |||||
| void setX(int x); | |||||
| void setY(int y); | |||||
| Point& operator=(const Point& pos); | |||||
| Point& operator+=(const Point& pos); | |||||
| Point& operator-=(const Point& pos); | |||||
| bool operator==(const Point& pos) const; | |||||
| bool operator!=(const Point& pos) const; | |||||
| private: | |||||
| int _x, _y; | |||||
| friend class Rectangle; | |||||
| }; | |||||
| class Size | |||||
| { | |||||
| public: | |||||
| Size(int width, int height); | |||||
| Size(const Size& size); | |||||
| int getWidth() const; | |||||
| int getHeight() const; | |||||
| void setWidth(int width); | |||||
| void setHeight(int height); | |||||
| Size& operator=(const Size& size); | |||||
| Size& operator+=(const Size& size); | |||||
| Size& operator-=(const Size& size); | |||||
| Size& operator*=(int m); | |||||
| Size& operator/=(int d); | |||||
| Size& operator*=(float m); | |||||
| Size& operator/=(float d); | |||||
| private: | |||||
| int _width, _height; | |||||
| friend class Rectangle; | |||||
| }; | |||||
| class Rectangle | |||||
| { | |||||
| public: | |||||
| Rectangle(int x, int y, int width, int height); | |||||
| Rectangle(int x, int y, const Size& size); | |||||
| Rectangle(const Point& pos, int width, int height); | |||||
| Rectangle(const Point& pos, const Size& size); | |||||
| Rectangle(const Rectangle& rect); | |||||
| int getX() const; | |||||
| int getY() const; | |||||
| int getWidth() const; | |||||
| int getHeight() const; | |||||
| const Point& getPos() const; | |||||
| const Size& getSize() const; | |||||
| bool contains(int x, int y) const; | |||||
| bool contains(const Point& pos) const; | |||||
| bool containsX(int x) const; | |||||
| bool containsY(int y) const; | |||||
| void setX(int x); | |||||
| void setY(int y); | |||||
| void setPos(int x, int y); | |||||
| void setPos(const Point& pos); | |||||
| void move(int x, int y); | |||||
| void move(const Point& pos); | |||||
| void setWidth(int width); | |||||
| void setHeight(int height); | |||||
| void setSize(int width, int height); | |||||
| void setSize(const Size& size); | |||||
| void grow(int m); | |||||
| void grow(float m); | |||||
| void grow(int width, int height); | |||||
| void grow(const Size& size); | |||||
| void shrink(int m); | |||||
| void shrink(float m); | |||||
| void shrink(int width, int height); | |||||
| void shrink(const Size& size); | |||||
| Rectangle& operator=(const Rectangle& rect); | |||||
| Rectangle& operator+=(const Point& pos); | |||||
| Rectangle& operator-=(const Point& pos); | |||||
| Rectangle& operator+=(const Size& size); | |||||
| Rectangle& operator-=(const Size& size); | |||||
| private: | |||||
| Point _pos; | |||||
| Size _size; | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| class Image | |||||
| { | |||||
| public: | |||||
| Image(const char* data, int width, int height, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE); | |||||
| Image(const char* data, const Size& size, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE); | |||||
| Image(const Image& image); | |||||
| bool isValid() const; | |||||
| int getWidth() const; | |||||
| int getHeight() const; | |||||
| const Size& getSize() const; | |||||
| const char* getData() const; | |||||
| GLenum getFormat() const; | |||||
| GLenum getType() const; | |||||
| Image& operator=(const Image& image); | |||||
| private: | |||||
| const char* _data; | |||||
| Size _size; | |||||
| GLenum _format; | |||||
| GLenum _type; | |||||
| friend class OpenGLExtUI; | |||||
| }; | |||||
| class ImageButton | |||||
| { | |||||
| public: | |||||
| ImageButton(const Image& imageNormal, const Image& imageHover, const Image& imageDown, const Point& pos); | |||||
| ImageButton(const ImageButton& imageButton); | |||||
| int getWidth() const; | |||||
| int getHeight() const; | |||||
| const Size& getSize() const; | |||||
| ImageButton& operator=(const ImageButton& imageButton); | |||||
| private: | |||||
| Image _imageNormal; | |||||
| Image _imageHover; | |||||
| Image _imageDown; | |||||
| Image* _curImage; | |||||
| Point _pos; | |||||
| Rectangle _area; | |||||
| friend class OpenGLExtUI; | |||||
| }; | |||||
| class ImageKnob | |||||
| { | |||||
| public: | |||||
| enum Orientation { | |||||
| Horizontal, | |||||
| Vertical | |||||
| }; | |||||
| ImageKnob(const Image& image, const Point& pos, Orientation orientation = Vertical); | |||||
| ImageKnob(const ImageKnob& imageKnob); | |||||
| void setOrientation(Orientation orientation); | |||||
| void setRange(float min, float max); | |||||
| void setValue(float value); | |||||
| ImageKnob& operator=(const ImageKnob& slider); | |||||
| private: | |||||
| Image _image; | |||||
| Point _pos; | |||||
| Orientation _orientation; | |||||
| bool _isVertical; | |||||
| int _layerSize; | |||||
| int _layerCount; | |||||
| Rectangle _area; | |||||
| float _min, _max, _value; | |||||
| friend class OpenGLExtUI; | |||||
| }; | |||||
| class ImageSlider | |||||
| { | |||||
| public: | |||||
| ImageSlider(const Image& image, const Point& startPos, const Point& endPos); | |||||
| ImageSlider(const ImageSlider& imageSlider); | |||||
| int getWidth() const; | |||||
| int getHeight() const; | |||||
| void setRange(float min, float max); | |||||
| void setValue(float value); | |||||
| ImageSlider& operator=(const ImageSlider& slider); | |||||
| private: | |||||
| Image _image; | |||||
| Point _startPos; | |||||
| Point _endPos; | |||||
| Rectangle _area; | |||||
| float _min, _max, _value; | |||||
| friend class OpenGLExtUI; | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| struct OpenGLExtUIPrivateData; | |||||
| class OpenGLExtUI : public OpenGLUI | |||||
| { | |||||
| public: | |||||
| OpenGLExtUI(); | |||||
| virtual ~OpenGLExtUI(); | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| // Information | |||||
| virtual unsigned int d_width() = 0; | |||||
| virtual unsigned int d_height() = 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 | |||||
| virtual void d_uiIdle(); | |||||
| // Extended Calls | |||||
| void setBackgroundImage(const Image& image); | |||||
| void addImageButton(ImageButton* button); | |||||
| void addImageKnob(ImageKnob* knob); | |||||
| void addImageSlider(ImageSlider* slider); | |||||
| void showImageModalDialog(const Image& image, const char* title); | |||||
| // Extended Callbacks | |||||
| virtual void imageButtonClicked(ImageButton* button); | |||||
| virtual void imageKnobDragStarted(ImageKnob* knob); | |||||
| virtual void imageKnobDragFinished(ImageKnob* knob); | |||||
| virtual void imageKnobValueChanged(ImageKnob* knob, float value); | |||||
| virtual void imageSliderDragStarted(ImageSlider* slider); | |||||
| virtual void imageSliderDragFinished(ImageSlider* slider); | |||||
| virtual void imageSliderValueChanged(ImageSlider* slider, float value); | |||||
| private: | |||||
| OpenGLExtUIPrivateData* data; | |||||
| // Implemented internally | |||||
| void d_onInit(); | |||||
| void d_onDisplay(); | |||||
| void d_onKeyboard(bool press, uint32_t key); | |||||
| void d_onMotion(int x, int y); | |||||
| void d_onMouse(int button, bool press, int x, int y); | |||||
| void d_onReshape(int width, int height); | |||||
| void d_onScroll(float dx, float dy); | |||||
| void d_onSpecial(bool press, Key key); | |||||
| void d_onClose(); | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_UI_OPENGL | |||||
| #endif // __DISTRHO_UI_OPENGL_EXT_H__ | |||||
| @@ -0,0 +1,74 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_UI_QT4_H__ | |||||
| #define __DISTRHO_UI_QT4_H__ | |||||
| #include "src/DistrhoDefines.h" | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| #include "DistrhoUI.h" | |||||
| #include <QtGui/QWidget> | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| class Qt4UI : public UI, | |||||
| public QWidget | |||||
| { | |||||
| public: | |||||
| Qt4UI(); | |||||
| virtual ~Qt4UI(); | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| // Information | |||||
| virtual bool d_resizable() { return false; } | |||||
| virtual int d_minimumWidth() { return 100; } | |||||
| virtual int d_minimumHeight() { return 100; } | |||||
| // 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 | |||||
| virtual void d_uiIdle(); | |||||
| // Implement resize internally | |||||
| unsigned int d_width() { return width(); } | |||||
| unsigned int d_height() { return height(); } | |||||
| private: | |||||
| friend class UIInternal; | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_UI_QT4 | |||||
| #endif // __DISTRHO_UI_QT4_H__ | |||||
| @@ -0,0 +1,256 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_UTILS_H__ | |||||
| #define __DISTRHO_UTILS_H__ | |||||
| #include "src/DistrhoDefines.h" | |||||
| #if DISTRHO_OS_WINDOWS | |||||
| # include <windows.h> | |||||
| #else | |||||
| # include <unistd.h> | |||||
| #endif | |||||
| #include <cstdio> | |||||
| #include <cstdint> | |||||
| #include <cstdlib> | |||||
| #include <cstring> | |||||
| // ------------------------------------------------- | |||||
| inline | |||||
| float d_absf(float value) | |||||
| { | |||||
| return (value < 0.0f) ? -value : value; | |||||
| } | |||||
| inline | |||||
| float d_minf(float x, float y) | |||||
| { | |||||
| return (x < y ? x : y); | |||||
| } | |||||
| inline | |||||
| float d_maxf(float x, float y) | |||||
| { | |||||
| return (x > y ? x : y); | |||||
| } | |||||
| inline | |||||
| long d_cconst(int a, int b, int c, int d) | |||||
| { | |||||
| return (a << 24) | (b << 16) | (c << 8) | (d << 0); | |||||
| } | |||||
| inline | |||||
| void d_sleep(unsigned int seconds) | |||||
| { | |||||
| #if DISTRHO_OS_WINDOWS | |||||
| Sleep(seconds * 1000); | |||||
| #else | |||||
| sleep(seconds); | |||||
| #endif | |||||
| } | |||||
| inline | |||||
| void d_msleep(unsigned int mseconds) | |||||
| { | |||||
| #if DISTRHO_OS_WINDOWS | |||||
| Sleep(mseconds); | |||||
| #else | |||||
| usleep(mseconds * 1000); | |||||
| #endif | |||||
| } | |||||
| inline | |||||
| void d_usleep(unsigned int useconds) | |||||
| { | |||||
| #if DISTRHO_OS_WINDOWS | |||||
| Sleep(useconds / 1000); | |||||
| #else | |||||
| usleep(useconds); | |||||
| #endif | |||||
| } | |||||
| inline | |||||
| void d_setenv(const char* key, const char* value) | |||||
| { | |||||
| #if DISTRHO_OS_WINDOWS | |||||
| SetEnvironmentVariableA(key, value); | |||||
| #else | |||||
| setenv(key, value, 1); | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| class d_string | |||||
| { | |||||
| public: | |||||
| d_string() | |||||
| { | |||||
| buffer = strdup(""); | |||||
| } | |||||
| d_string(const char* strBuf) | |||||
| { | |||||
| buffer = strdup(strBuf ? strBuf : ""); | |||||
| } | |||||
| d_string(const d_string& str) | |||||
| { | |||||
| buffer = strdup(str.buffer); | |||||
| } | |||||
| d_string(int value) | |||||
| { | |||||
| size_t strBufSize = abs(value/10) + 3; | |||||
| char strBuf[strBufSize]; | |||||
| snprintf(strBuf, strBufSize, "%d", value); | |||||
| buffer = strdup(strBuf); | |||||
| } | |||||
| d_string(unsigned int value) | |||||
| { | |||||
| size_t strBufSize = value/10 + 2; | |||||
| char strBuf[strBufSize]; | |||||
| snprintf(strBuf, strBufSize, "%u", value); | |||||
| buffer = strdup(strBuf); | |||||
| } | |||||
| d_string(float value) | |||||
| { | |||||
| char strBuf[255]; | |||||
| snprintf(strBuf, 255, "%f", value); | |||||
| buffer = strdup(strBuf); | |||||
| } | |||||
| ~d_string() | |||||
| { | |||||
| free(buffer); | |||||
| } | |||||
| size_t length() const | |||||
| { | |||||
| return strlen(buffer); | |||||
| } | |||||
| bool isEmpty() const | |||||
| { | |||||
| return (*buffer == 0); | |||||
| } | |||||
| // --------------------------------------------- | |||||
| operator const char*() const | |||||
| { | |||||
| return buffer; | |||||
| } | |||||
| bool operator==(const char* strBuf) const | |||||
| { | |||||
| return (strcmp(buffer, strBuf) == 0); | |||||
| } | |||||
| bool operator==(const d_string& str) const | |||||
| { | |||||
| return operator==(str.buffer); | |||||
| } | |||||
| bool operator!=(const char* strBuf) const | |||||
| { | |||||
| return !operator==(strBuf); | |||||
| } | |||||
| bool operator!=(const d_string& str) const | |||||
| { | |||||
| return !operator==(str.buffer); | |||||
| } | |||||
| d_string& operator=(const char* strBuf) | |||||
| { | |||||
| free(buffer); | |||||
| buffer = strdup(strBuf); | |||||
| return *this; | |||||
| } | |||||
| d_string& operator=(const d_string& str) | |||||
| { | |||||
| return operator=(str.buffer); | |||||
| } | |||||
| d_string& operator+=(const char* strBuf) | |||||
| { | |||||
| size_t newBufSize = strlen(buffer) + strlen(strBuf) + 1; | |||||
| char newBuf[newBufSize]; | |||||
| strcpy(newBuf, buffer); | |||||
| strcat(newBuf, strBuf); | |||||
| free(buffer); | |||||
| buffer = strdup(newBuf); | |||||
| return *this; | |||||
| } | |||||
| d_string& operator+=(const d_string& str) | |||||
| { | |||||
| return operator+=(str.buffer); | |||||
| } | |||||
| d_string operator+(const char* strBuf) | |||||
| { | |||||
| size_t newBufSize = strlen(buffer) + strlen(strBuf) + 1; | |||||
| char newBuf[newBufSize]; | |||||
| strcpy(newBuf, buffer); | |||||
| strcat(newBuf, strBuf); | |||||
| return d_string(newBuf); | |||||
| } | |||||
| d_string operator+(const d_string& str) | |||||
| { | |||||
| return operator+(str.buffer); | |||||
| } | |||||
| private: | |||||
| char* buffer; | |||||
| }; | |||||
| static inline | |||||
| d_string operator+(const char* strBufBefore, const d_string& strAfter) | |||||
| { | |||||
| const char* strBufAfter = (const char*)strAfter; | |||||
| size_t newBufSize = strlen(strBufBefore) + strlen(strBufAfter) + 1; | |||||
| char newBuf[newBufSize]; | |||||
| strcpy(newBuf, strBufBefore); | |||||
| strcat(newBuf, strBufAfter); | |||||
| return d_string(newBuf); | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| #endif // __DISTRHO_UTILS_H__ | |||||
| @@ -0,0 +1,289 @@ | |||||
| # Doxyfile 1.7.6.1 | |||||
| #--------------------------------------------------------------------------- | |||||
| # Project related configuration options | |||||
| #--------------------------------------------------------------------------- | |||||
| DOXYFILE_ENCODING = UTF-8 | |||||
| PROJECT_NAME = "DISTRHO Plugin Toolkit" | |||||
| PROJECT_NUMBER = | |||||
| PROJECT_BRIEF = | |||||
| PROJECT_LOGO = | |||||
| OUTPUT_DIRECTORY = doxygen | |||||
| CREATE_SUBDIRS = NO | |||||
| OUTPUT_LANGUAGE = English | |||||
| BRIEF_MEMBER_DESC = YES | |||||
| REPEAT_BRIEF = YES | |||||
| ABBREVIATE_BRIEF = | |||||
| ALWAYS_DETAILED_SEC = NO | |||||
| INLINE_INHERITED_MEMB = NO | |||||
| FULL_PATH_NAMES = YES | |||||
| STRIP_FROM_PATH = | |||||
| STRIP_FROM_INC_PATH = | |||||
| SHORT_NAMES = NO | |||||
| JAVADOC_AUTOBRIEF = NO | |||||
| QT_AUTOBRIEF = NO | |||||
| MULTILINE_CPP_IS_BRIEF = NO | |||||
| INHERIT_DOCS = YES | |||||
| SEPARATE_MEMBER_PAGES = NO | |||||
| TAB_SIZE = 4 | |||||
| ALIASES = | |||||
| TCL_SUBST = | |||||
| OPTIMIZE_OUTPUT_FOR_C = NO | |||||
| OPTIMIZE_OUTPUT_JAVA = NO | |||||
| OPTIMIZE_FOR_FORTRAN = NO | |||||
| OPTIMIZE_OUTPUT_VHDL = NO | |||||
| EXTENSION_MAPPING = | |||||
| BUILTIN_STL_SUPPORT = NO | |||||
| CPP_CLI_SUPPORT = NO | |||||
| SIP_SUPPORT = NO | |||||
| IDL_PROPERTY_SUPPORT = YES | |||||
| DISTRIBUTE_GROUP_DOC = NO | |||||
| SUBGROUPING = YES | |||||
| INLINE_GROUPED_CLASSES = NO | |||||
| INLINE_SIMPLE_STRUCTS = NO | |||||
| TYPEDEF_HIDES_STRUCT = NO | |||||
| SYMBOL_CACHE_SIZE = 0 | |||||
| LOOKUP_CACHE_SIZE = 0 | |||||
| #--------------------------------------------------------------------------- | |||||
| # Build related configuration options | |||||
| #--------------------------------------------------------------------------- | |||||
| EXTRACT_ALL = YES | |||||
| EXTRACT_PRIVATE = NO | |||||
| EXTRACT_STATIC = YES | |||||
| EXTRACT_LOCAL_CLASSES = YES | |||||
| EXTRACT_LOCAL_METHODS = YES | |||||
| EXTRACT_ANON_NSPACES = NO | |||||
| HIDE_UNDOC_MEMBERS = NO | |||||
| HIDE_UNDOC_CLASSES = NO | |||||
| HIDE_FRIEND_COMPOUNDS = NO | |||||
| HIDE_IN_BODY_DOCS = NO | |||||
| INTERNAL_DOCS = NO | |||||
| CASE_SENSE_NAMES = YES | |||||
| HIDE_SCOPE_NAMES = NO | |||||
| SHOW_INCLUDE_FILES = YES | |||||
| FORCE_LOCAL_INCLUDES = NO | |||||
| INLINE_INFO = YES | |||||
| SORT_MEMBER_DOCS = NO | |||||
| SORT_BRIEF_DOCS = NO | |||||
| SORT_MEMBERS_CTORS_1ST = NO | |||||
| SORT_GROUP_NAMES = NO | |||||
| SORT_BY_SCOPE_NAME = NO | |||||
| STRICT_PROTO_MATCHING = NO | |||||
| GENERATE_TODOLIST = YES | |||||
| GENERATE_TESTLIST = YES | |||||
| GENERATE_BUGLIST = YES | |||||
| GENERATE_DEPRECATEDLIST= YES | |||||
| ENABLED_SECTIONS = | |||||
| MAX_INITIALIZER_LINES = 30 | |||||
| SHOW_USED_FILES = YES | |||||
| SHOW_DIRECTORIES = NO | |||||
| SHOW_FILES = YES | |||||
| SHOW_NAMESPACES = YES | |||||
| FILE_VERSION_FILTER = | |||||
| LAYOUT_FILE = | |||||
| CITE_BIB_FILES = | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to warning and progress messages | |||||
| #--------------------------------------------------------------------------- | |||||
| QUIET = NO | |||||
| WARNINGS = YES | |||||
| WARN_IF_UNDOCUMENTED = YES | |||||
| WARN_IF_DOC_ERROR = YES | |||||
| WARN_NO_PARAMDOC = NO | |||||
| WARN_FORMAT = "$file:$line: $text" | |||||
| WARN_LOGFILE = | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the input files | |||||
| #--------------------------------------------------------------------------- | |||||
| INPUT = | |||||
| INPUT_ENCODING = UTF-8 | |||||
| FILE_PATTERNS = | |||||
| RECURSIVE = NO | |||||
| EXCLUDE = | |||||
| EXCLUDE_SYMLINKS = NO | |||||
| EXCLUDE_PATTERNS = | |||||
| EXCLUDE_SYMBOLS = | |||||
| EXAMPLE_PATH = | |||||
| EXAMPLE_PATTERNS = | |||||
| EXAMPLE_RECURSIVE = NO | |||||
| IMAGE_PATH = | |||||
| INPUT_FILTER = | |||||
| FILTER_PATTERNS = | |||||
| FILTER_SOURCE_FILES = NO | |||||
| FILTER_SOURCE_PATTERNS = | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to source browsing | |||||
| #--------------------------------------------------------------------------- | |||||
| SOURCE_BROWSER = NO | |||||
| INLINE_SOURCES = NO | |||||
| STRIP_CODE_COMMENTS = YES | |||||
| REFERENCED_BY_RELATION = NO | |||||
| REFERENCES_RELATION = NO | |||||
| REFERENCES_LINK_SOURCE = YES | |||||
| USE_HTAGS = NO | |||||
| VERBATIM_HEADERS = YES | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the alphabetical class index | |||||
| #--------------------------------------------------------------------------- | |||||
| ALPHABETICAL_INDEX = YES | |||||
| COLS_IN_ALPHA_INDEX = 5 | |||||
| IGNORE_PREFIX = | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the HTML output | |||||
| #--------------------------------------------------------------------------- | |||||
| GENERATE_HTML = YES | |||||
| HTML_OUTPUT = html | |||||
| HTML_FILE_EXTENSION = .html | |||||
| HTML_HEADER = | |||||
| HTML_FOOTER = | |||||
| HTML_STYLESHEET = | |||||
| HTML_EXTRA_FILES = | |||||
| HTML_COLORSTYLE_HUE = 220 | |||||
| HTML_COLORSTYLE_SAT = 100 | |||||
| HTML_COLORSTYLE_GAMMA = 80 | |||||
| HTML_TIMESTAMP = YES | |||||
| HTML_ALIGN_MEMBERS = YES | |||||
| HTML_DYNAMIC_SECTIONS = NO | |||||
| GENERATE_DOCSET = NO | |||||
| DOCSET_FEEDNAME = "Doxygen generated docs" | |||||
| DOCSET_BUNDLE_ID = org.doxygen.Project | |||||
| DOCSET_PUBLISHER_ID = org.doxygen.Publisher | |||||
| DOCSET_PUBLISHER_NAME = Publisher | |||||
| GENERATE_HTMLHELP = NO | |||||
| CHM_FILE = | |||||
| HHC_LOCATION = | |||||
| GENERATE_CHI = NO | |||||
| CHM_INDEX_ENCODING = | |||||
| BINARY_TOC = NO | |||||
| TOC_EXPAND = NO | |||||
| GENERATE_QHP = NO | |||||
| QCH_FILE = | |||||
| QHP_NAMESPACE = org.doxygen.Project | |||||
| QHP_VIRTUAL_FOLDER = doc | |||||
| QHP_CUST_FILTER_NAME = | |||||
| QHP_CUST_FILTER_ATTRS = | |||||
| QHP_SECT_FILTER_ATTRS = | |||||
| QHG_LOCATION = | |||||
| GENERATE_ECLIPSEHELP = NO | |||||
| ECLIPSE_DOC_ID = org.doxygen.Project | |||||
| DISABLE_INDEX = NO | |||||
| GENERATE_TREEVIEW = NO | |||||
| ENUM_VALUES_PER_LINE = 4 | |||||
| USE_INLINE_TREES = NO | |||||
| TREEVIEW_WIDTH = 250 | |||||
| EXT_LINKS_IN_WINDOW = NO | |||||
| FORMULA_FONTSIZE = 10 | |||||
| FORMULA_TRANSPARENT = YES | |||||
| USE_MATHJAX = NO | |||||
| MATHJAX_RELPATH = http://www.mathjax.org/mathjax | |||||
| MATHJAX_EXTENSIONS = | |||||
| SEARCHENGINE = YES | |||||
| SERVER_BASED_SEARCH = NO | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the LaTeX output | |||||
| #--------------------------------------------------------------------------- | |||||
| GENERATE_LATEX = NO | |||||
| LATEX_OUTPUT = latex | |||||
| LATEX_CMD_NAME = latex | |||||
| MAKEINDEX_CMD_NAME = makeindex | |||||
| COMPACT_LATEX = NO | |||||
| PAPER_TYPE = a4 | |||||
| EXTRA_PACKAGES = | |||||
| LATEX_HEADER = | |||||
| LATEX_FOOTER = | |||||
| PDF_HYPERLINKS = YES | |||||
| USE_PDFLATEX = YES | |||||
| LATEX_BATCHMODE = NO | |||||
| LATEX_HIDE_INDICES = NO | |||||
| LATEX_SOURCE_CODE = NO | |||||
| LATEX_BIB_STYLE = plain | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the RTF output | |||||
| #--------------------------------------------------------------------------- | |||||
| GENERATE_RTF = NO | |||||
| RTF_OUTPUT = rtf | |||||
| COMPACT_RTF = NO | |||||
| RTF_HYPERLINKS = NO | |||||
| RTF_STYLESHEET_FILE = | |||||
| RTF_EXTENSIONS_FILE = | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the man page output | |||||
| #--------------------------------------------------------------------------- | |||||
| GENERATE_MAN = NO | |||||
| MAN_OUTPUT = man | |||||
| MAN_EXTENSION = .3 | |||||
| MAN_LINKS = NO | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the XML output | |||||
| #--------------------------------------------------------------------------- | |||||
| GENERATE_XML = NO | |||||
| XML_OUTPUT = xml | |||||
| XML_SCHEMA = | |||||
| XML_DTD = | |||||
| XML_PROGRAMLISTING = YES | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options for the AutoGen Definitions output | |||||
| #--------------------------------------------------------------------------- | |||||
| GENERATE_AUTOGEN_DEF = NO | |||||
| #--------------------------------------------------------------------------- | |||||
| # configuration options related to the Perl module output | |||||
| #--------------------------------------------------------------------------- | |||||
| GENERATE_PERLMOD = NO | |||||
| PERLMOD_LATEX = NO | |||||
| PERLMOD_PRETTY = YES | |||||
| PERLMOD_MAKEVAR_PREFIX = | |||||
| #--------------------------------------------------------------------------- | |||||
| # Configuration options related to the preprocessor | |||||
| #--------------------------------------------------------------------------- | |||||
| ENABLE_PREPROCESSING = YES | |||||
| MACRO_EXPANSION = NO | |||||
| EXPAND_ONLY_PREDEF = NO | |||||
| SEARCH_INCLUDES = YES | |||||
| INCLUDE_PATH = | |||||
| INCLUDE_FILE_PATTERNS = | |||||
| PREDEFINED = DOXYGEN \ | |||||
| DISTRHO_PLUGIN_TARGET_LADSPA DISTRHO_PLUGIN_TARGET_DSSI DISTRHO_PLUGIN_TARGET_LV2 DISTRHO_PLUGIN_TARGET_VST \ | |||||
| DISTRHO_PLUGIN_HAS_UI DISTRHO_PLUGIN_IS_SYNTH DISTRHO_PLUGIN_WANT_PROGRAMS DISTRHO_PLUGIN_WANT_STATE | |||||
| EXPAND_AS_DEFINED = | |||||
| SKIP_FUNCTION_MACROS = YES | |||||
| #--------------------------------------------------------------------------- | |||||
| # Configuration::additions related to external references | |||||
| #--------------------------------------------------------------------------- | |||||
| TAGFILES = | |||||
| GENERATE_TAGFILE = | |||||
| ALLEXTERNALS = NO | |||||
| EXTERNAL_GROUPS = YES | |||||
| PERL_PATH = /usr/bin/perl | |||||
| #--------------------------------------------------------------------------- | |||||
| # Configuration options related to the dot tool | |||||
| #--------------------------------------------------------------------------- | |||||
| CLASS_DIAGRAMS = YES | |||||
| MSCGEN_PATH = | |||||
| HIDE_UNDOC_RELATIONS = YES | |||||
| HAVE_DOT = NO | |||||
| DOT_NUM_THREADS = 0 | |||||
| DOT_FONTNAME = Helvetica | |||||
| DOT_FONTSIZE = 10 | |||||
| DOT_FONTPATH = | |||||
| CLASS_GRAPH = YES | |||||
| COLLABORATION_GRAPH = YES | |||||
| GROUP_GRAPHS = YES | |||||
| UML_LOOK = NO | |||||
| TEMPLATE_RELATIONS = NO | |||||
| INCLUDE_GRAPH = YES | |||||
| INCLUDED_BY_GRAPH = YES | |||||
| CALL_GRAPH = NO | |||||
| CALLER_GRAPH = NO | |||||
| GRAPHICAL_HIERARCHY = YES | |||||
| DIRECTORY_GRAPH = YES | |||||
| DOT_IMAGE_FORMAT = png | |||||
| INTERACTIVE_SVG = NO | |||||
| DOT_PATH = | |||||
| DOTFILE_DIRS = | |||||
| MSCFILE_DIRS = | |||||
| DOT_GRAPH_MAX_NODES = 50 | |||||
| MAX_DOT_GRAPH_DEPTH = 0 | |||||
| DOT_TRANSPARENT = NO | |||||
| DOT_MULTI_TARGETS = YES | |||||
| GENERATE_LEGEND = YES | |||||
| DOT_CLEANUP = YES | |||||
| @@ -0,0 +1,91 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_DEFINES_H__ | |||||
| #define __DISTRHO_DEFINES_H__ | |||||
| #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 | |||||
| #if defined(__WIN32__) || defined(__WIN64__) | |||||
| # define DISTRHO_PLUGIN_EXPORT extern "C" __declspec (dllexport) | |||||
| # define DISTRHO_OS_WINDOWS 1 | |||||
| # define DISTRHO_DLL_EXT "dll" | |||||
| #else | |||||
| # define DISTRHO_PLUGIN_EXPORT extern "C" __attribute__ ((visibility("default"))) | |||||
| # if defined(__APPLE__) | |||||
| # define DISTRHO_OS_MAC 1 | |||||
| # define DISTRHO_DLL_EXT "dylib" | |||||
| # elif defined(__HAIKU__) | |||||
| # define DISTRHO_OS_HAIKU 1 | |||||
| # define DISTRHO_DLL_EXT "so" | |||||
| # elif defined(__linux__) | |||||
| # define DISTRHO_OS_LINUX 1 | |||||
| # define DISTRHO_DLL_EXT "so" | |||||
| # else | |||||
| # define DISTRHO_DLL_EXT "so" | |||||
| # endif | |||||
| #endif | |||||
| #ifndef DISTRHO_NO_NAMESPACE | |||||
| # define START_NAMESPACE_DISTRHO namespace DISTRHO { | |||||
| # define END_NAMESPACE_DISTRHO } | |||||
| # define USE_NAMESPACE_DISTRHO using namespace DISTRHO; | |||||
| #else | |||||
| # define START_NAMESPACE_DISTRHO | |||||
| # define END_NAMESPACE_DISTRHO | |||||
| # define USE_NAMESPACE_DISTRHO | |||||
| #endif | |||||
| #ifndef DISTRHO_UI_QT4 | |||||
| # define DISTRHO_UI_OPENGL | |||||
| #endif | |||||
| #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||||
| #endif // __DISTRHO_DEFINES_H__ | |||||
| @@ -0,0 +1,95 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "DistrhoPluginInternal.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| const d_string PluginInternal::fallbackString; | |||||
| const ParameterRanges PluginInternal::fallbackRanges; | |||||
| // ------------------------------------------------- | |||||
| Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) | |||||
| { | |||||
| data = new PluginPrivateData; | |||||
| if (parameterCount > 0) | |||||
| { | |||||
| data->parameterCount = parameterCount; | |||||
| data->parameters = new Parameter [parameterCount]; | |||||
| } | |||||
| if (programCount > 0) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| data->programCount = programCount; | |||||
| data->programNames = new d_string [programCount]; | |||||
| #endif | |||||
| } | |||||
| if (stateCount > 0) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| data->stateCount = stateCount; | |||||
| data->stateKeys = new d_string [stateCount]; | |||||
| #endif | |||||
| } | |||||
| } | |||||
| Plugin::~Plugin() | |||||
| { | |||||
| delete data; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| // Host state | |||||
| uint32_t Plugin::d_bufferSize() const | |||||
| { | |||||
| return data->bufferSize; | |||||
| } | |||||
| double Plugin::d_sampleRate() const | |||||
| { | |||||
| return data->sampleRate; | |||||
| } | |||||
| const TimePos* Plugin::d_timePos() const | |||||
| { | |||||
| return &data->timePos; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| void Plugin::d_setLatency(uint32_t samples) | |||||
| { | |||||
| data->latency = samples; | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| // Callbacks | |||||
| void Plugin::d_bufferSizeChanged(uint32_t) | |||||
| { | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,337 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_PLUGIN_INTERNAL_H__ | |||||
| #define __DISTRHO_PLUGIN_INTERNAL_H__ | |||||
| #include "DistrhoPlugin.h" | |||||
| #include <cassert> | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| #define MAX_MIDI_EVENTS 512 | |||||
| static uint32_t d_lastBufferSize = 0; | |||||
| static double d_lastSampleRate = 0.0; | |||||
| struct PluginPrivateData { | |||||
| uint32_t bufferSize; | |||||
| double sampleRate; | |||||
| 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 | |||||
| TimePos timePos; | |||||
| PluginPrivateData() | |||||
| : bufferSize(d_lastBufferSize), | |||||
| sampleRate(d_lastSampleRate), | |||||
| 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 | |||||
| timePos() | |||||
| { | |||||
| assert(d_lastSampleRate != 0.0); | |||||
| } | |||||
| ~PluginPrivateData() | |||||
| { | |||||
| if (parameterCount > 0 && parameters) | |||||
| delete[] parameters; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| if (programCount > 0 && programNames) | |||||
| delete[] programNames; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| if (stateCount > 0 && stateKeys) | |||||
| delete[] stateKeys; | |||||
| #endif | |||||
| } | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| class PluginInternal | |||||
| { | |||||
| public: | |||||
| PluginInternal() | |||||
| : plugin(createPlugin()), | |||||
| data(nullptr) | |||||
| { | |||||
| assert(plugin); | |||||
| if (! plugin) | |||||
| return; | |||||
| data = plugin->data; | |||||
| for (uint32_t i=0; i < data->parameterCount; i++) | |||||
| plugin->d_initParameter(i, data->parameters[i]); | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| for (uint32_t i=0; i < data->programCount; i++) | |||||
| plugin->d_initProgramName(i, data->programNames[i]); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| for (uint32_t i=0; i < data->stateCount; i++) | |||||
| plugin->d_initStateKey(i, data->stateKeys[i]); | |||||
| #endif | |||||
| } | |||||
| ~PluginInternal() | |||||
| { | |||||
| if (plugin) | |||||
| delete plugin; | |||||
| } | |||||
| // --------------------------------------------- | |||||
| const char* name() | |||||
| { | |||||
| assert(plugin); | |||||
| return plugin ? plugin->d_name() : ""; | |||||
| } | |||||
| const char* label() | |||||
| { | |||||
| assert(plugin); | |||||
| return plugin ? plugin->d_label() : ""; | |||||
| } | |||||
| const char* maker() | |||||
| { | |||||
| assert(plugin); | |||||
| return plugin ? plugin->d_maker() : ""; | |||||
| } | |||||
| const char* license() | |||||
| { | |||||
| assert(plugin); | |||||
| return plugin ? plugin->d_license() : ""; | |||||
| } | |||||
| uint32_t version() | |||||
| { | |||||
| assert(plugin); | |||||
| return plugin ? plugin->d_version() : 1000; | |||||
| } | |||||
| long uniqueId() | |||||
| { | |||||
| assert(plugin); | |||||
| return plugin ? plugin->d_uniqueId() : 0; | |||||
| } | |||||
| // --------------------------------------------- | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| uint32_t latency() const | |||||
| { | |||||
| assert(data); | |||||
| return data ? data->latency : 0; | |||||
| } | |||||
| #endif | |||||
| uint32_t parameterCount() const | |||||
| { | |||||
| assert(data); | |||||
| return data ? data->parameterCount : 0; | |||||
| } | |||||
| uint32_t parameterHints(uint32_t index) const | |||||
| { | |||||
| assert(data && index < data->parameterCount); | |||||
| return (data && index < data->parameterCount) ? data->parameters[index].hints : 0x0; | |||||
| } | |||||
| bool parameterIsOutput(uint32_t index) const | |||||
| { | |||||
| uint32_t hints = parameterHints(index); | |||||
| return bool(hints & PARAMETER_IS_OUTPUT); | |||||
| } | |||||
| const d_string& parameterName(uint32_t index) const | |||||
| { | |||||
| assert(data && index < data->parameterCount); | |||||
| return (data && index < data->parameterCount) ? data->parameters[index].name : fallbackString; | |||||
| } | |||||
| const d_string& parameterSymbol(uint32_t index) const | |||||
| { | |||||
| assert(data && index < data->parameterCount); | |||||
| return (data && index < data->parameterCount) ? data->parameters[index].symbol : fallbackString; | |||||
| } | |||||
| const d_string& parameterUnit(uint32_t index) const | |||||
| { | |||||
| assert(data && index < data->parameterCount); | |||||
| return (data && index < data->parameterCount) ? data->parameters[index].unit : fallbackString; | |||||
| } | |||||
| const ParameterRanges* parameterRanges(uint32_t index) const | |||||
| { | |||||
| assert(data && index < data->parameterCount); | |||||
| return (data && index < data->parameterCount) ? &data->parameters[index].ranges : &fallbackRanges; | |||||
| } | |||||
| float parameterValue(uint32_t index) | |||||
| { | |||||
| assert(plugin && index < data->parameterCount); | |||||
| return (plugin && index < data->parameterCount) ? plugin->d_parameterValue(index) : 0.0f; | |||||
| } | |||||
| void setParameterValue(uint32_t index, float value) | |||||
| { | |||||
| assert(plugin && index < data->parameterCount); | |||||
| if (plugin && index < data->parameterCount) | |||||
| plugin->d_setParameterValue(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| uint32_t programCount() const | |||||
| { | |||||
| assert(data); | |||||
| return data ? data->programCount : 0; | |||||
| } | |||||
| const d_string& programName(uint32_t index) const | |||||
| { | |||||
| assert(data && index < data->programCount); | |||||
| return (data && index < data->programCount) ? data->programNames[index] : fallbackString; | |||||
| } | |||||
| void setProgram(uint32_t index) | |||||
| { | |||||
| assert(plugin && index < data->programCount); | |||||
| if (plugin && index < data->programCount) | |||||
| plugin->d_setProgram(index); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| uint32_t stateCount() const | |||||
| { | |||||
| assert(data); | |||||
| return data ? data->stateCount : 0; | |||||
| } | |||||
| const d_string& stateKey(uint32_t index) const | |||||
| { | |||||
| assert(data && index < data->stateCount); | |||||
| return (data && index < data->stateCount) ? data->stateKeys[index] : fallbackString; | |||||
| } | |||||
| void setState(const char* key, const char* value) | |||||
| { | |||||
| assert(plugin && key && value); | |||||
| if (plugin && key && value) | |||||
| plugin->d_setState(key, value); | |||||
| } | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| void activate() | |||||
| { | |||||
| assert(plugin); | |||||
| if (plugin) | |||||
| plugin->d_activate(); | |||||
| } | |||||
| void deactivate() | |||||
| { | |||||
| assert(plugin); | |||||
| if (plugin) | |||||
| plugin->d_deactivate(); | |||||
| } | |||||
| void run(const float** inputs, float** outputs, uint32_t frames, uint32_t midiEventCount, const MidiEvent* midiEvents) | |||||
| { | |||||
| assert(plugin && frames >= 2); | |||||
| if (plugin) | |||||
| plugin->d_run(inputs, outputs, frames, midiEventCount, midiEvents); | |||||
| } | |||||
| void setBufferSize(uint32_t bufferSize, bool callback = false) | |||||
| { | |||||
| assert(data && plugin && bufferSize >= 2); | |||||
| if (callback && data->bufferSize == bufferSize) | |||||
| callback = false; | |||||
| if (data) | |||||
| data->bufferSize = bufferSize; | |||||
| if (plugin && callback) | |||||
| { | |||||
| plugin->d_deactivate(); | |||||
| plugin->d_bufferSizeChanged(bufferSize); | |||||
| plugin->d_activate(); | |||||
| } | |||||
| } | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| Plugin* const plugin; | |||||
| PluginPrivateData* data; | |||||
| private: | |||||
| static const d_string fallbackString; | |||||
| static const ParameterRanges fallbackRanges; | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // __DISTRHO_PLUGIN_INTERNAL_H__ | |||||
| @@ -0,0 +1,366 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_JACK | |||||
| #include "DistrhoDefines.h" | |||||
| #if ! DISTRHO_PLUGIN_HAS_UI | |||||
| # error Standalone JACK mode requires UI | |||||
| #endif | |||||
| #include "DistrhoPluginInternal.h" | |||||
| #include "DistrhoUIInternal.h" | |||||
| #include <jack/jack.h> | |||||
| #include <jack/midiport.h> | |||||
| #include <jack/transport.h> | |||||
| #include <QtCore/QSettings> | |||||
| #include <QtGui/QApplication> | |||||
| #include <QtGui/QMainWindow> | |||||
| #include <QtGui/QMessageBox> | |||||
| // ------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| class PluginJack : public QMainWindow | |||||
| { | |||||
| public: | |||||
| PluginJack(jack_client_t* client_) | |||||
| : QMainWindow(nullptr), | |||||
| widget(this), | |||||
| settings("DISTRHO", DISTRHO_PLUGIN_NAME), | |||||
| ui(this, widget.winId(), setParameterCallback, setStateCallback, nullptr, uiNoteCallback, uiResizeCallback), | |||||
| client(client_) | |||||
| { | |||||
| setCentralWidget(&widget); | |||||
| setFixedSize(ui.getWidth(), ui.getHeight()); | |||||
| setWindowTitle(DISTRHO_PLUGIN_NAME); | |||||
| if (DISTRHO_PLUGIN_NUM_INPUTS > 0) | |||||
| { | |||||
| portsIn = new jack_port_t* [DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| for (int i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| portsIn[i] = jack_port_register(client, d_string("Audio Input ") + d_string(i+1), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||||
| } | |||||
| else | |||||
| portsIn = nullptr; | |||||
| if (DISTRHO_PLUGIN_NUM_OUTPUTS > 0) | |||||
| { | |||||
| portsOut = new jack_port_t* [DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||||
| for (int i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| portsOut[i] = jack_port_register(client, d_string("Audio Output ") + d_string(i+1), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||||
| } | |||||
| else | |||||
| portsOut = nullptr; | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| portMidi = jack_port_register(client, "Midi Input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||||
| #endif | |||||
| jack_set_process_callback(client, jackProcessCallback, this); | |||||
| uiTimer = startTimer(30); | |||||
| // load settings | |||||
| restoreGeometry(settings.value("Global/Geometry", QByteArray()).toByteArray()); | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| bool ok; | |||||
| float value = settings.value(QString("Parameters/%1").arg(i)).toFloat(&ok); | |||||
| if (ok) | |||||
| { | |||||
| plugin.setParameterValue(i, value); | |||||
| ui.parameterChanged(i, value); | |||||
| } | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| for (uint32_t i=0; i < plugin.stateCount(); i++) | |||||
| { | |||||
| const char* key = plugin.stateKey(i); | |||||
| QString stringValue(settings.value(key).toString()); | |||||
| if (! stringValue.isEmpty()) | |||||
| ui.stateChanged(key, stringValue.toUtf8().constData()); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| ~PluginJack() | |||||
| { | |||||
| // save settings | |||||
| settings.setValue("Global/Geometry", saveGeometry()); | |||||
| if (uiTimer) | |||||
| killTimer(uiTimer); | |||||
| if (portsIn) | |||||
| delete[] portsIn; | |||||
| if (portsOut) | |||||
| delete[] portsOut; | |||||
| } | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| void setParameterValue(uint32_t index, float value) | |||||
| { | |||||
| plugin.setParameterValue(index, value); | |||||
| settings.setValue(QString("Parameters/%1").arg(index), value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void setState(const char* key, const char* value) | |||||
| { | |||||
| plugin.setState(key, value); | |||||
| settings.setValue(key, value); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void uiNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| // TODO | |||||
| } | |||||
| #endif | |||||
| void uiResize(unsigned int width, unsigned int height) | |||||
| { | |||||
| widget.setFixedSize(width, height); | |||||
| setFixedSize(width, height); | |||||
| } | |||||
| int jackProcess(jack_nframes_t nframes) | |||||
| { | |||||
| if (nframes <= 1) | |||||
| return 1; | |||||
| // Check for updated bufferSize | |||||
| if (nframes != d_lastBufferSize) | |||||
| { | |||||
| d_lastBufferSize = nframes; | |||||
| plugin.setBufferSize(nframes, true); | |||||
| } | |||||
| const float* ins[DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| float* outs[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||||
| for (int i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| ins[i] = (float*)jack_port_get_buffer(portsIn[i], nframes); | |||||
| for (int i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| outs[i] = (float*)jack_port_get_buffer(portsOut[i], nframes); | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| uint32_t midiEventCount = 0; | |||||
| void* mIn = jack_port_get_buffer(portMidi, nframes); | |||||
| // TODO | |||||
| plugin.run(ins, outs, nframes, midiEventCount, midiEvents); | |||||
| #else | |||||
| plugin.run(ins, outs, nframes, 0, nullptr); | |||||
| #endif | |||||
| return 0; | |||||
| } | |||||
| // --------------------------------------------- | |||||
| void closeEvent(QCloseEvent* event) | |||||
| { | |||||
| QMainWindow::closeEvent(event); | |||||
| qApp->quit(); | |||||
| } | |||||
| void timerEvent(QTimerEvent* event) | |||||
| { | |||||
| if (event->timerId() == uiTimer) | |||||
| ui.idle(); | |||||
| QMainWindow::timerEvent(event); | |||||
| } | |||||
| // --------------------------------------------- | |||||
| private: | |||||
| // Qt4 stuff | |||||
| int uiTimer; | |||||
| QWidget widget; | |||||
| QSettings settings; | |||||
| PluginInternal plugin; | |||||
| UIInternal ui; | |||||
| jack_client_t* const client; | |||||
| jack_port_t** portsIn; | |||||
| jack_port_t** portsOut; | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| jack_port_t* portMidi; | |||||
| MidiEvent midiEvents[MAX_MIDI_EVENTS]; | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| // Callbacks | |||||
| static void setParameterCallback(void* ptr, uint32_t index, float value) | |||||
| { | |||||
| PluginJack* _this_ = (PluginJack*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setParameterValue(index, value); | |||||
| } | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| PluginJack* _this_ = (PluginJack*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setState(key, value); | |||||
| #else | |||||
| Q_UNUSED(ptr); | |||||
| Q_UNUSED(key); | |||||
| Q_UNUSED(value); | |||||
| #endif | |||||
| } | |||||
| static void uiNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| PluginJack* _this_ = (PluginJack*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiNote(onOff, channel, note, velocity); | |||||
| #else | |||||
| Q_UNUSED(ptr); | |||||
| Q_UNUSED(onOff); | |||||
| Q_UNUSED(channel); | |||||
| Q_UNUSED(note); | |||||
| Q_UNUSED(velocity); | |||||
| #endif | |||||
| } | |||||
| static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) | |||||
| { | |||||
| PluginJack* _this_ = (PluginJack*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiResize(width, height); | |||||
| } | |||||
| static int jackProcessCallback(jack_nframes_t nframes, void* arg) | |||||
| { | |||||
| PluginJack* _this_ = (PluginJack*)arg; | |||||
| assert(_this_); | |||||
| return _this_->jackProcess(nframes); | |||||
| } | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| std::string jack_status_get_error_string(const jack_status_t& status) | |||||
| { | |||||
| std::string errorString; | |||||
| if (status & JackFailure) | |||||
| errorString += "Overall operation failed;\n"; | |||||
| if (status & JackInvalidOption) | |||||
| errorString += "The operation contained an invalid or unsupported option;\n"; | |||||
| if (status & JackNameNotUnique) | |||||
| errorString += "The desired client name was not unique;\n"; | |||||
| if (status & JackServerStarted) | |||||
| errorString += "The JACK server was started as a result of this operation;\n"; | |||||
| if (status & JackServerFailed) | |||||
| errorString += "Unable to connect to the JACK server;\n"; | |||||
| if (status & JackServerError) | |||||
| errorString += "Communication error with the JACK server;\n"; | |||||
| if (status & JackNoSuchClient) | |||||
| errorString += "Requested client does not exist;\n"; | |||||
| if (status & JackLoadFailure) | |||||
| errorString += "Unable to load internal client;\n"; | |||||
| if (status & JackInitFailure) | |||||
| errorString += "Unable to initialize client;\n"; | |||||
| if (status & JackShmFailure) | |||||
| errorString += "Unable to access shared memory;\n"; | |||||
| if (status & JackVersionError) | |||||
| errorString += "Client's protocol version does not match;\n"; | |||||
| if (status & JackBackendError) | |||||
| errorString += "Backend Error;\n"; | |||||
| if (status & JackClientZombie) | |||||
| errorString += "Client is being shutdown against its will;\n"; | |||||
| if (errorString.size() > 0) | |||||
| errorString.replace(errorString.size()-2, 2, "."); | |||||
| return errorString; | |||||
| } | |||||
| int main(int argc, char* argv[]) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| QApplication app(argc, argv, true); | |||||
| jack_status_t status; | |||||
| jack_client_t* client = jack_client_open(DISTRHO_PLUGIN_NAME, JackNullOption, &status); | |||||
| if (! client) | |||||
| { | |||||
| std::string errorString(jack_status_get_error_string(status)); | |||||
| QMessageBox::critical(nullptr, app.translate(DISTRHO_PLUGIN_NAME, "Error"), | |||||
| app.translate(DISTRHO_PLUGIN_NAME, | |||||
| "Could not connect to JACK, possible reasons:\n" | |||||
| "%1").arg(QString::fromStdString(errorString))); | |||||
| return 1; | |||||
| } | |||||
| d_lastBufferSize = jack_get_buffer_size(client); | |||||
| d_lastSampleRate = jack_get_sample_rate(client); | |||||
| setLastUiSampleRate(d_lastSampleRate); | |||||
| PluginJack plugin(client); | |||||
| plugin.show(); | |||||
| jack_activate(client); | |||||
| int ret = app.exec(); | |||||
| jack_deactivate(client); | |||||
| jack_client_close(client); | |||||
| return ret; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_TARGET_JACK | |||||
| @@ -0,0 +1,690 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI) | |||||
| #include "DistrhoPluginInternal.h" | |||||
| #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 | |||||
| #include <vector> | |||||
| typedef LADSPA_Data* LADSPA_DataPtr; | |||||
| typedef const LADSPA_Data* LADSPA_DataConstPtr; | |||||
| typedef std::vector<LADSPA_Data> LADSPA_DataVector; | |||||
| typedef std::vector<LADSPA_DataPtr> LADSPA_DataPtrVector; | |||||
| // ------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| class PluginLadspaDssi | |||||
| { | |||||
| public: | |||||
| PluginLadspaDssi() | |||||
| : lastBufferSize(d_lastBufferSize), | |||||
| lastSampleRate(d_lastSampleRate) | |||||
| { | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| portAudioIns[i] = nullptr; | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| portAudioOuts[i] = nullptr; | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| portControls.push_back(nullptr); | |||||
| lastControlValues.push_back(plugin.parameterValue(i)); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| portLatency = nullptr; | |||||
| #endif | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI | |||||
| portSampleRate = nullptr; | |||||
| #endif | |||||
| } | |||||
| ~PluginLadspaDssi() | |||||
| { | |||||
| portControls.clear(); | |||||
| lastControlValues.clear(); | |||||
| } | |||||
| void ladspa_activate() | |||||
| { | |||||
| plugin.activate(); | |||||
| updateParameterOutputs(); | |||||
| } | |||||
| void ladspa_deactivate() | |||||
| { | |||||
| plugin.deactivate(); | |||||
| } | |||||
| void ladspa_connect_port(unsigned long port, LADSPA_DataPtr dataLocation) | |||||
| { | |||||
| unsigned long i, index = 0; | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| portAudioIns[i] = dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| portAudioOuts[i] = dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| if (port == index++) | |||||
| { | |||||
| portLatency = dataLocation; | |||||
| return; | |||||
| } | |||||
| #endif | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI | |||||
| if (port == index++) | |||||
| { | |||||
| portSampleRate = dataLocation; | |||||
| return; | |||||
| } | |||||
| #endif | |||||
| for (i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| portControls[i] = dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| char* dssi_configure(const char* key, const char* value) | |||||
| { | |||||
| if (strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, strlen(DSSI_RESERVED_CONFIGURE_PREFIX) == 0)) | |||||
| return nullptr; | |||||
| if (strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, strlen(DSSI_GLOBAL_CONFIGURE_PREFIX) == 0)) | |||||
| return nullptr; | |||||
| plugin.setState(key, value); | |||||
| return nullptr; | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| const DSSI_Program_Descriptor* dssi_get_program(unsigned long index) | |||||
| { | |||||
| if (index >= plugin.programCount()) | |||||
| return nullptr; | |||||
| static DSSI_Program_Descriptor desc; | |||||
| desc.Bank = index / 128; | |||||
| desc.Program = index % 128; | |||||
| desc.Name = plugin.programName(index); | |||||
| return &desc; | |||||
| } | |||||
| void dssi_select_program(unsigned long bank, unsigned long program) | |||||
| { | |||||
| const unsigned long realProgram = bank * 128 + program; | |||||
| if (realProgram >= plugin.programCount()) | |||||
| return; | |||||
| plugin.setProgram(realProgram); | |||||
| // Update parameters | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| if (! plugin.parameterIsOutput(i)) | |||||
| { | |||||
| lastControlValues[i] = plugin.parameterValue(i); | |||||
| if (portControls[i]) | |||||
| *portControls[i] = lastControlValues[i]; | |||||
| } | |||||
| } | |||||
| } | |||||
| # endif | |||||
| void ladspa_run(unsigned long bufferSize) | |||||
| { | |||||
| dssi_run_synth(bufferSize, nullptr, 0); | |||||
| } | |||||
| void dssi_run_synth(unsigned long bufferSize, snd_seq_event_t* events, unsigned long eventCount) | |||||
| #else | |||||
| void ladspa_run(unsigned long bufferSize) | |||||
| #endif | |||||
| { | |||||
| if (bufferSize <= 1) | |||||
| return; | |||||
| // Check for updated bufferSize | |||||
| if (bufferSize != lastBufferSize) | |||||
| { | |||||
| lastBufferSize = bufferSize; | |||||
| d_lastBufferSize = bufferSize; | |||||
| plugin.setBufferSize(bufferSize, true); | |||||
| } | |||||
| // Check for updated parameters | |||||
| float curValue; | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| curValue = *portControls[i]; | |||||
| if (lastControlValues[i] != curValue && ! plugin.parameterIsOutput(i)) | |||||
| { | |||||
| lastControlValues[i] = curValue; | |||||
| plugin.setParameterValue(i, curValue); | |||||
| } | |||||
| } | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||||
| // Get MIDI Events | |||||
| uint32_t midiEventCount = 0; | |||||
| for (uint32_t i=0, j=0; i < eventCount && midiEventCount < MAX_MIDI_EVENTS; i++) | |||||
| { | |||||
| snd_seq_event_t* event = &events[i]; | |||||
| memset(&midiEvents[midiEventCount], 0, sizeof(MidiEvent)); | |||||
| if (events[i].type == SND_SEQ_EVENT_NOTEON) | |||||
| { | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = event->time.tick; | |||||
| midiEvents[j].buffer[0] = 0x90 + event->data.note.channel; | |||||
| midiEvents[j].buffer[1] = event->data.note.note; | |||||
| midiEvents[j].buffer[2] = event->data.note.velocity; | |||||
| } | |||||
| else if (events[i].type == SND_SEQ_EVENT_NOTEOFF) | |||||
| { | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = event->time.tick; | |||||
| midiEvents[j].buffer[0] = 0x80 + event->data.note.channel; | |||||
| midiEvents[j].buffer[1] = event->data.note.note; | |||||
| } | |||||
| else if (events[i].type == SND_SEQ_EVENT_KEYPRESS) | |||||
| { | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = event->time.tick; | |||||
| midiEvents[j].buffer[0] = 0xA0 + event->data.note.channel; | |||||
| midiEvents[j].buffer[1] = event->data.note.note; | |||||
| midiEvents[j].buffer[2] = event->data.note.velocity; | |||||
| } | |||||
| else if (events[i].type == SND_SEQ_EVENT_CONTROLLER) | |||||
| { | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = event->time.tick; | |||||
| midiEvents[j].buffer[0] = 0xB0 + event->data.control.channel; | |||||
| midiEvents[j].buffer[1] = event->data.control.param; | |||||
| midiEvents[j].buffer[2] = event->data.control.value; | |||||
| } | |||||
| else if (events[i].type == SND_SEQ_EVENT_CHANPRESS) | |||||
| { | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = event->time.tick; | |||||
| midiEvents[j].buffer[0] = 0xD0 + event->data.control.channel; | |||||
| midiEvents[j].buffer[1] = event->data.control.value; | |||||
| } | |||||
| else if (events[i].type == SND_SEQ_EVENT_PITCHBEND) | |||||
| { | |||||
| // TODO | |||||
| //j = midiEventCount++; | |||||
| //midiEvents[j].frame = event->time.tick; | |||||
| //midiEvents[j].buffer[0] = 0xE0 + event->data.control.channel; | |||||
| //midiEvents[j].buffer[1] = 0; | |||||
| //midiEvents[j].buffer[2] = 0; | |||||
| } | |||||
| } | |||||
| # else | |||||
| // unused | |||||
| (void)events; | |||||
| (void)eventCount; | |||||
| # endif | |||||
| #endif | |||||
| // Run plugin for this cycle | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| plugin.run(portAudioIns, portAudioOuts, bufferSize, midiEventCount, midiEvents); | |||||
| #else | |||||
| plugin.run(portAudioIns, portAudioOuts, bufferSize, 0, nullptr); | |||||
| #endif | |||||
| updateParameterOutputs(); | |||||
| } | |||||
| private: | |||||
| PluginInternal plugin; | |||||
| // LADSPA ports | |||||
| LADSPA_DataConstPtr portAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| LADSPA_DataPtr portAudioOuts[DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| LADSPA_DataPtrVector portControls; | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| LADSPA_DataPtr portLatency; | |||||
| #endif | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI | |||||
| LADSPA_DataPtr portSampleRate; | |||||
| #endif | |||||
| // Temporary data | |||||
| unsigned long lastBufferSize; | |||||
| const double lastSampleRate; | |||||
| LADSPA_DataVector lastControlValues; | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| MidiEvent midiEvents[MAX_MIDI_EVENTS]; | |||||
| #endif | |||||
| void updateParameterOutputs() | |||||
| { | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| if (plugin.parameterIsOutput(i)) | |||||
| { | |||||
| lastControlValues[i] = plugin.parameterValue(i); | |||||
| if (portControls[i]) | |||||
| *portControls[i] = lastControlValues[i]; | |||||
| } | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| if (portLatency) | |||||
| *portLatency = plugin.latency(); | |||||
| #endif | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI | |||||
| if (portSampleRate) | |||||
| *portSampleRate = lastSampleRate; | |||||
| #endif | |||||
| } | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, unsigned long sampleRate) | |||||
| { | |||||
| if (d_lastBufferSize == 0) | |||||
| d_lastBufferSize = 512; | |||||
| d_lastSampleRate = sampleRate; | |||||
| return new PluginLadspaDssi(); | |||||
| } | |||||
| static void ladspa_connect_port(LADSPA_Handle instance, unsigned long port, LADSPA_Data* dataLocation) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| plugin->ladspa_connect_port(port, dataLocation); | |||||
| } | |||||
| static void ladspa_activate(LADSPA_Handle instance) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| plugin->ladspa_activate(); | |||||
| } | |||||
| static void ladspa_run(LADSPA_Handle instance, unsigned long sampleCount) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| plugin->ladspa_run(sampleCount); | |||||
| } | |||||
| static void ladspa_deactivate(LADSPA_Handle instance) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| plugin->ladspa_deactivate(); | |||||
| } | |||||
| static void ladspa_cleanup(LADSPA_Handle instance) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| delete plugin; | |||||
| } | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| static char* dssi_configure(LADSPA_Handle instance, const char* key, const char* value) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| return plugin->dssi_configure(key, value); | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static const DSSI_Program_Descriptor* dssi_get_program(LADSPA_Handle instance, unsigned long index) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| return plugin->dssi_get_program(index); | |||||
| } | |||||
| static void dssi_select_program(LADSPA_Handle instance, unsigned long bank, unsigned long program) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| plugin->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) | |||||
| { | |||||
| PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; | |||||
| assert(plugin); | |||||
| plugin->dssi_run_synth(sampleCount, events, eventCount); | |||||
| } | |||||
| # endif | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| static LADSPA_Descriptor ldescriptor = { | |||||
| /* 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 descriptor = { | |||||
| 1, | |||||
| &ldescriptor, | |||||
| # 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; | |||||
| PluginInternal plugin; | |||||
| d_lastBufferSize = 0; | |||||
| d_lastSampleRate = 0.0; | |||||
| // Get port count, init | |||||
| unsigned long i, port = 0; | |||||
| unsigned long portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.parameterCount(); | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| portCount += 1; | |||||
| #endif | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI | |||||
| portCount += 1; // sample-rate | |||||
| #endif | |||||
| const char** portNames = new const char* [portCount]; | |||||
| LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor [portCount]; | |||||
| LADSPA_PortRangeHint* portRangeHints = new LADSPA_PortRangeHint [portCount]; | |||||
| // Set ports | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++, port++) | |||||
| { | |||||
| char portName[24] = { 0 }; | |||||
| sprintf(portName, "Audio Input %lu", i+1); | |||||
| portNames[port] = strdup(portName); | |||||
| portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT; | |||||
| portRangeHints[port].HintDescriptor = 0; | |||||
| portRangeHints[port].LowerBound = 0.0f; | |||||
| portRangeHints[port].UpperBound = 1.0f; | |||||
| } | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++, port++) | |||||
| { | |||||
| char portName[24] = { 0 }; | |||||
| sprintf(portName, "Audio Output %lu", i+1); | |||||
| portNames[port] = strdup(portName); | |||||
| portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT; | |||||
| portRangeHints[port].HintDescriptor = 0; | |||||
| portRangeHints[port].LowerBound = 0.0f; | |||||
| portRangeHints[port].UpperBound = 1.0f; | |||||
| } | |||||
| #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 | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI | |||||
| // Set sample-rate port | |||||
| portNames[port] = strdup("_sample-rate"); | |||||
| portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT; | |||||
| portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; | |||||
| portRangeHints[port].LowerBound = 0.0f; | |||||
| portRangeHints[port].UpperBound = 512000.0f; | |||||
| port++; | |||||
| #endif | |||||
| for (i=0; i < plugin.parameterCount(); i++, port++) | |||||
| { | |||||
| portNames[port] = strdup(plugin.parameterName(i)); | |||||
| portDescriptors[port] = LADSPA_PORT_CONTROL; | |||||
| if (plugin.parameterIsOutput(i)) | |||||
| portDescriptors[port] |= LADSPA_PORT_OUTPUT; | |||||
| else | |||||
| portDescriptors[port] |= LADSPA_PORT_INPUT; | |||||
| { | |||||
| const ParameterRanges* ranges = plugin.parameterRanges(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 + ranges->max/2; | |||||
| const float middleLow = (ranges->min/2 + middleValue/2)/2 + middleValue/2; | |||||
| const float middleHigh = (ranges->max/2 + middleValue/2)/2 + middleValue/2; | |||||
| 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; | |||||
| } | |||||
| } | |||||
| { | |||||
| uint32_t hints = plugin.parameterHints(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 | |||||
| ldescriptor.UniqueID = plugin.uniqueId(); | |||||
| ldescriptor.Label = strdup(plugin.label()); | |||||
| ldescriptor.Name = strdup(plugin.name()); | |||||
| ldescriptor.Maker = strdup(plugin.maker()); | |||||
| ldescriptor.Copyright = strdup(plugin.license()); | |||||
| ldescriptor.PortCount = portCount; | |||||
| ldescriptor.PortNames = portNames; | |||||
| ldescriptor.PortDescriptors = portDescriptors; | |||||
| ldescriptor.PortRangeHints = portRangeHints; | |||||
| } | |||||
| ~DescriptorInitializer() | |||||
| { | |||||
| if (ldescriptor.Label) | |||||
| free((void*)ldescriptor.Label); | |||||
| if (ldescriptor.Name) | |||||
| free((void*)ldescriptor.Name); | |||||
| if (ldescriptor.Maker) | |||||
| free((void*)ldescriptor.Maker); | |||||
| if (ldescriptor.Copyright) | |||||
| free((void*)ldescriptor.Copyright); | |||||
| if (ldescriptor.PortDescriptors) | |||||
| delete[] ldescriptor.PortDescriptors; | |||||
| if (ldescriptor.PortRangeHints) | |||||
| delete[] ldescriptor.PortRangeHints; | |||||
| if (ldescriptor.PortNames) | |||||
| { | |||||
| for (unsigned long i=0; i < ldescriptor.PortCount; i++) | |||||
| { | |||||
| if (ldescriptor.PortNames[i]) | |||||
| free((void*)ldescriptor.PortNames[i]); | |||||
| } | |||||
| delete[] ldescriptor.PortNames; | |||||
| } | |||||
| } | |||||
| }; | |||||
| static DescriptorInitializer init; | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| const LADSPA_Descriptor* ladspa_descriptor(unsigned long index) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| return (index == 0) ? &ldescriptor : nullptr; | |||||
| } | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| const DSSI_Descriptor* dssi_descriptor(unsigned long index) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| return (index == 0) ? &descriptor : nullptr; | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_TARGET_LADSPA || DISTRHO_PLUGIN_TARGET_DSSI | |||||
| @@ -0,0 +1,597 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_LV2 | |||||
| #include "DistrhoPluginInternal.h" | |||||
| #include "lv2-sdk/lv2.h" | |||||
| #include "lv2-sdk/atom.h" | |||||
| #include "lv2-sdk/atom-util.h" | |||||
| #include "lv2-sdk/midi.h" | |||||
| #include "lv2-sdk/patch.h" | |||||
| #include "lv2-sdk/programs.h" | |||||
| #include "lv2-sdk/state.h" | |||||
| #include "lv2-sdk/urid.h" | |||||
| #include "lv2-sdk/worker.h" | |||||
| #include <map> | |||||
| #include <vector> | |||||
| #ifndef DISTRHO_PLUGIN_URI | |||||
| # error DISTRHO_PLUGIN_URI undefined! | |||||
| #endif | |||||
| #define DISTRHO_LV2_USE_EVENTS (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_STATE) | |||||
| #define DISTRHO_LV2_USE_EXTENSION_DATA (DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_STATE) | |||||
| typedef float* floatptr; | |||||
| typedef std::vector<float> floatVector; | |||||
| typedef std::vector<floatptr> floatptrVector; | |||||
| typedef std::map<d_string,d_string> stringMap; | |||||
| // ------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| class PluginLv2 | |||||
| { | |||||
| public: | |||||
| PluginLv2(const LV2_Feature* const* features) | |||||
| : lastBufferSize(d_lastBufferSize), | |||||
| lastSampleRate(d_lastSampleRate) | |||||
| { | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| portAudioIns.push_back(nullptr); | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| portAudioOuts.push_back(nullptr); | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| portControls.push_back(nullptr); | |||||
| lastControlValues.push_back(plugin.parameterValue(i)); | |||||
| } | |||||
| portLatency = nullptr; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| portSampleRate = nullptr; | |||||
| #endif | |||||
| #if DISTRHO_LV2_USE_EVENTS | |||||
| portEventsIn = nullptr; | |||||
| // URIDs | |||||
| uridMap = nullptr; | |||||
| uridIdAtomString = 0; | |||||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||||
| uridIdMidiEvent = 0; | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| uridIdPatchMessage = 0; | |||||
| workerSchedule = nullptr; | |||||
| # endif | |||||
| for (uint32_t i=0; features[i]; i++) | |||||
| { | |||||
| if (strcmp(features[i]->URI, LV2_URID__map) == 0) | |||||
| { | |||||
| uridMap = (LV2_URID_Map*)features[i]->data; | |||||
| uridIdAtomString = uridMap->map(uridMap->handle, LV2_ATOM__String); | |||||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||||
| uridIdMidiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent); | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| uridIdPatchMessage = uridMap->map(uridMap->handle, LV2_PATCH__Message); | |||||
| # endif | |||||
| } | |||||
| else if (strcmp(features[i]->URI, LV2_WORKER__schedule) == 0) | |||||
| { | |||||
| workerSchedule = (LV2_Worker_Schedule*)features[i]->data; | |||||
| } | |||||
| } | |||||
| #else | |||||
| // unused | |||||
| (void)features; | |||||
| #endif | |||||
| } | |||||
| ~PluginLv2() | |||||
| { | |||||
| portAudioIns.clear(); | |||||
| portAudioOuts.clear(); | |||||
| portControls.clear(); | |||||
| lastControlValues.clear(); | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| stateMap.clear(); | |||||
| #endif | |||||
| } | |||||
| void lv2_activate() | |||||
| { | |||||
| plugin.activate(); | |||||
| updateParameterOutputs(); | |||||
| } | |||||
| void lv2_deactivate() | |||||
| { | |||||
| plugin.deactivate(); | |||||
| } | |||||
| void lv2_connect_port(uint32_t port, void* dataLocation) | |||||
| { | |||||
| uint32_t i, index = 0; | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| portAudioIns[i] = (floatptr)dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| portAudioOuts[i] = (floatptr)dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| for (i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| portControls[i] = (floatptr)dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| #if DISTRHO_LV2_USE_EVENTS | |||||
| if (port == index++) | |||||
| { | |||||
| portEventsIn = (LV2_Atom_Sequence*)dataLocation; | |||||
| return; | |||||
| } | |||||
| #endif | |||||
| if (port == index++) | |||||
| { | |||||
| portLatency = (floatptr)dataLocation; | |||||
| return; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| if (port == index++) | |||||
| { | |||||
| portSampleRate = (floatptr)dataLocation; | |||||
| return; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| void lv2_run(uint32_t bufferSize) | |||||
| { | |||||
| if (bufferSize <= 1) | |||||
| return; | |||||
| // Check for updated bufferSize | |||||
| if (bufferSize != lastBufferSize) | |||||
| { | |||||
| lastBufferSize = bufferSize; | |||||
| d_lastBufferSize = bufferSize; | |||||
| plugin.setBufferSize(bufferSize, true); | |||||
| } | |||||
| // Check for updated parameters | |||||
| float curValue; | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| curValue = *portControls[i]; | |||||
| if (lastControlValues[i] != curValue && ! plugin.parameterIsOutput(i)) | |||||
| { | |||||
| lastControlValues[i] = curValue; | |||||
| plugin.setParameterValue(i, curValue); | |||||
| } | |||||
| } | |||||
| // Get Events, xxx | |||||
| uint32_t midiEventCount = 0; | |||||
| #if DISTRHO_LV2_USE_EVENTS | |||||
| LV2_ATOM_SEQUENCE_FOREACH(portEventsIn, iter) | |||||
| { | |||||
| const LV2_Atom_Event* event = /*(const LV2_Atom_Event*)*/iter; | |||||
| if (! event) | |||||
| continue; | |||||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||||
| if (event->body.type == uridIdMidiEvent) | |||||
| { | |||||
| if (event->time.frames >= bufferSize || midiEventCount >= MAX_MIDI_EVENTS) | |||||
| break; | |||||
| if (event->body.size > 3) | |||||
| continue; | |||||
| const uint8_t* data = (const uint8_t*)(event + 1); | |||||
| midiEvents[midiEventCount].frame = event->time.frames; | |||||
| memcpy(midiEvents[midiEventCount].buffer, data, event->body.size); | |||||
| midiEventCount += 1; | |||||
| continue; | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| if (event->body.type == uridIdPatchMessage) | |||||
| { | |||||
| // TODO | |||||
| //if (workerSchedule) | |||||
| // workerSchedule->schedule_work(workerSchedule->handle, event->body.size, ) | |||||
| } | |||||
| # endif | |||||
| } | |||||
| #endif | |||||
| // Run plugin for this cycle | |||||
| const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| inputs[i] = portAudioIns[i]; | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| outputs[i] = portAudioOuts[i]; | |||||
| plugin.run(inputs, outputs, bufferSize, midiEventCount, midiEvents); | |||||
| updateParameterOutputs(); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| const LV2_Program_Descriptor* lv2_get_program(uint32_t index) | |||||
| { | |||||
| if (index >= plugin.programCount()) | |||||
| return nullptr; | |||||
| static LV2_Program_Descriptor desc; | |||||
| desc.bank = index / 128; | |||||
| desc.program = index % 128; | |||||
| desc.name = plugin.programName(index); | |||||
| return &desc; | |||||
| } | |||||
| void lv2_select_program(uint32_t bank, uint32_t program) | |||||
| { | |||||
| const uint32_t realProgram = bank * 128 + program; | |||||
| if (realProgram >= plugin.programCount()) | |||||
| return; | |||||
| plugin.setProgram(realProgram); | |||||
| // Update parameters | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| if (! plugin.parameterIsOutput(i)) | |||||
| lastControlValues[i] = *portControls[i] = plugin.parameterValue(i); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| LV2_State_Status lv2_save(LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* /*features*/) | |||||
| { | |||||
| flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; | |||||
| for (auto i = stateMap.begin(); i != stateMap.end(); i++) | |||||
| { | |||||
| const d_string& key = i->first; | |||||
| const d_string& value = i->second; | |||||
| store(handle, uridMap->map(uridMap->handle, (const char*)key), (const char*)value, value.length(), uridIdAtomString, flags); | |||||
| } | |||||
| return LV2_STATE_SUCCESS; | |||||
| } | |||||
| LV2_State_Status lv2_restore(LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* /*features*/) | |||||
| { | |||||
| size_t size; | |||||
| uint32_t type; | |||||
| for (uint32_t i=0; i < plugin.stateCount(); i++) | |||||
| { | |||||
| const d_string& key = plugin.stateKey(i); | |||||
| const void* data = retrieve(handle, uridMap->map(uridMap->handle, (const char*)key), &size, &type, &flags); | |||||
| if (size == 0 || ! data) | |||||
| continue; | |||||
| if (type != uridIdAtomString) | |||||
| continue; | |||||
| const char* value = (const char*)data; | |||||
| setState(key, value); | |||||
| } | |||||
| return LV2_STATE_SUCCESS; | |||||
| } | |||||
| LV2_Worker_Status lv2_work(LV2_Worker_Respond_Function /*respond*/, LV2_Worker_Respond_Handle /*handle*/, uint32_t /*size*/, const void* /*data*/) | |||||
| { | |||||
| // TODO | |||||
| return LV2_WORKER_SUCCESS; | |||||
| } | |||||
| LV2_Worker_Status lv2_work_response(uint32_t /*size*/, const void* /*body*/) | |||||
| { | |||||
| // TODO | |||||
| return LV2_WORKER_SUCCESS; | |||||
| } | |||||
| #endif | |||||
| private: | |||||
| PluginInternal plugin; | |||||
| // LV2 ports | |||||
| floatptrVector portAudioIns; | |||||
| floatptrVector portAudioOuts; | |||||
| floatptrVector portControls; | |||||
| floatptr portLatency; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| floatptr portSampleRate; | |||||
| #endif | |||||
| // xxx | |||||
| #if DISTRHO_LV2_USE_EVENTS | |||||
| LV2_Atom_Sequence* portEventsIn; | |||||
| // URIDs | |||||
| LV2_URID_Map* uridMap; | |||||
| LV2_URID uridIdAtomString; | |||||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||||
| LV2_URID uridIdMidiEvent; | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| LV2_URID uridIdPatchMessage; | |||||
| LV2_Worker_Schedule* workerSchedule; | |||||
| # endif | |||||
| #endif | |||||
| // Temporary data | |||||
| uint32_t lastBufferSize; | |||||
| const double lastSampleRate; | |||||
| floatVector lastControlValues; | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| MidiEvent midiEvents[MAX_MIDI_EVENTS]; | |||||
| #else | |||||
| MidiEvent midiEvents[0]; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| stringMap stateMap; | |||||
| void setState(const char* newKey, const char* newValue) | |||||
| { | |||||
| plugin.setState(newKey, newValue); | |||||
| // check if key already exists | |||||
| for (auto i = stateMap.begin(); i != stateMap.end(); i++) | |||||
| { | |||||
| const d_string& key = i->first; | |||||
| if (key == newKey) | |||||
| { | |||||
| i->second = newValue; | |||||
| return; | |||||
| } | |||||
| } | |||||
| // add a new one then | |||||
| stateMap[newKey] = newValue; | |||||
| } | |||||
| #endif | |||||
| void updateParameterOutputs() | |||||
| { | |||||
| for (uint32_t i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| if (plugin.parameterIsOutput(i)) | |||||
| lastControlValues[i] = *portControls[i] = plugin.parameterValue(i); | |||||
| } | |||||
| if (portLatency) | |||||
| *portLatency = plugin.latency(); | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| if (portSampleRate) | |||||
| *portSampleRate = lastSampleRate; | |||||
| #endif | |||||
| } | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features) | |||||
| { | |||||
| // TODO - search features for initial bufferSize | |||||
| if (d_lastBufferSize == 0) | |||||
| d_lastBufferSize = 512; | |||||
| d_lastSampleRate = sampleRate; | |||||
| return new PluginLv2(features); | |||||
| } | |||||
| static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| plugin->lv2_connect_port(port, dataLocation); | |||||
| } | |||||
| static void lv2_activate(LV2_Handle instance) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| plugin->lv2_activate(); | |||||
| } | |||||
| static void lv2_run(LV2_Handle instance, uint32_t sampleCount) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| plugin->lv2_run(sampleCount); | |||||
| } | |||||
| static void lv2_deactivate(LV2_Handle instance) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| plugin->lv2_deactivate(); | |||||
| } | |||||
| static void lv2_cleanup(LV2_Handle instance) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| delete plugin; | |||||
| } | |||||
| #if DISTRHO_LV2_USE_EXTENSION_DATA | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static const LV2_Program_Descriptor* lv2_get_program(LV2_Handle instance, uint32_t index) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| return plugin->lv2_get_program(index); | |||||
| } | |||||
| static void lv2_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| plugin->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* features) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| return plugin->lv2_save(store, handle, flags, features); | |||||
| } | |||||
| static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| return plugin->lv2_restore(retrieve, handle, flags, features); | |||||
| } | |||||
| LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function respond, LV2_Worker_Respond_Handle handle, uint32_t size, const void* data) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| return plugin->lv2_work(respond, handle, size, data); | |||||
| } | |||||
| LV2_Worker_Status lv2_work_response(LV2_Handle instance, uint32_t size, const void* body) | |||||
| { | |||||
| PluginLv2* plugin = (PluginLv2*)instance; | |||||
| assert(plugin); | |||||
| return plugin->lv2_work_response(size, body); | |||||
| } | |||||
| # endif | |||||
| static const void* lv2_extension_data(const char* uri) | |||||
| { | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program }; | |||||
| if (strcmp(uri, LV2_PROGRAMS__Interface) == 0) | |||||
| return &programs; | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| static const LV2_State_Interface state = { lv2_save, lv2_restore }; | |||||
| if (strcmp(uri, LV2_STATE__interface) == 0) | |||||
| return &state; | |||||
| static const LV2_Worker_Interface worker = { lv2_work, lv2_work_response, nullptr }; | |||||
| if (strcmp(uri, LV2_WORKER__interface) == 0) | |||||
| return &worker; | |||||
| # endif | |||||
| return nullptr; | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| static LV2_Descriptor descriptor = { | |||||
| DISTRHO_PLUGIN_URI, | |||||
| lv2_instantiate, | |||||
| lv2_connect_port, | |||||
| lv2_activate, | |||||
| lv2_run, | |||||
| lv2_deactivate, | |||||
| lv2_cleanup, | |||||
| #if DISTRHO_LV2_USE_EXTENSION_DATA | |||||
| lv2_extension_data | |||||
| #else | |||||
| /* extension_data */ nullptr | |||||
| #endif | |||||
| }; | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| const LV2_Descriptor* lv2_descriptor(uint32_t index) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| return (index == 0) ? &descriptor : nullptr; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_TARGET_LV2 | |||||
| @@ -0,0 +1,347 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_LV2 | |||||
| #include "DistrhoPluginInternal.h" | |||||
| #include "lv2-sdk/lv2.h" | |||||
| #include "lv2-sdk/atom.h" | |||||
| #include "lv2-sdk/midi.h" | |||||
| #include "lv2-sdk/patch.h" | |||||
| #include "lv2-sdk/programs.h" | |||||
| #include "lv2-sdk/state.h" | |||||
| #include "lv2-sdk/urid.h" | |||||
| #include "lv2-sdk/ui.h" | |||||
| #include "lv2-sdk/units.h" | |||||
| #include "lv2-sdk/worker.h" | |||||
| #include <fstream> | |||||
| #include <iostream> | |||||
| #ifndef DISTRHO_PLUGIN_URI | |||||
| # error DISTRHO_PLUGIN_URI undefined! | |||||
| #endif | |||||
| #define DISTRHO_LV2_USE_EVENTS (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_STATE) | |||||
| // ------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| void lv2_generate_ttl_func() | |||||
| { | |||||
| PluginInternal plugin; | |||||
| d_string pluginLabel = plugin.label(); | |||||
| 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_EXT "> ;\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_EXT "> ;\n"; | |||||
| # if DISTRHO_LV2_USE_EVENTS | |||||
| manifestString += " lv2:optionalFeature <" LV2_URID__map "> ,\n"; | |||||
| manifestString += " ui:noUserResize .\n"; | |||||
| # else | |||||
| manifestString += " lv2:optionalFeature ui:noUserResize .\n"; | |||||
| # endif | |||||
| manifestString += "\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; | |||||
| #if DISTRHO_LV2_USE_EVENTS | |||||
| 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"; | |||||
| pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| pluginString += " a lv2:InstrumentPlugin, lv2:Plugin ;\n"; | |||||
| #else | |||||
| pluginString += " a lv2:Plugin ;\n"; | |||||
| #endif | |||||
| #if (DISTRHO_PLUGIN_IS_SYNTH && DISTRHO_PLUGIN_WANT_STATE) | |||||
| pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; | |||||
| pluginString += " <" LV2_WORKER__schedule "> ;\n"; | |||||
| pluginString += " lv2:requiredFeature <" LV2_URID__map "> ;\n"; | |||||
| #elif DISTRHO_PLUGIN_IS_SYNTH | |||||
| pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ;\n"; | |||||
| pluginString += " lv2:requiredFeature <" LV2_URID__map "> ;\n"; | |||||
| #elif DISTRHO_PLUGIN_WANT_STATE | |||||
| pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; | |||||
| pluginString += " <" LV2_URID__map "> ,\n"; | |||||
| pluginString += " <" LV2_WORKER__schedule "> ;\n"; | |||||
| #endif | |||||
| #if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_WANT_PROGRAMS) | |||||
| pluginString += " lv2:extensionData <" LV2_STATE__interface "> ,\n"; | |||||
| pluginString += " <" LV2_WORKER__interface "> ,\n"; | |||||
| pluginString += " <" LV2_PROGRAMS__Interface "> ;\n"; | |||||
| #elif DISTRHO_PLUGIN_WANT_STATE | |||||
| pluginString += " lv2:extensionData <" LV2_STATE__interface "> ,\n"; | |||||
| pluginString += " <" LV2_WORKER__interface "> ;\n"; | |||||
| #elif DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| pluginString += " lv2:extensionData <" LV2_PROGRAMS__Interface "> ;\n"; | |||||
| #endif | |||||
| pluginString += "\n"; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; | |||||
| pluginString += "\n"; | |||||
| #endif | |||||
| { | |||||
| uint32_t i, portIndex = 0; | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) | |||||
| { | |||||
| 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"; | |||||
| } | |||||
| for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) | |||||
| { | |||||
| 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"; | |||||
| } | |||||
| for (i=0; i < plugin.parameterCount(); i++) | |||||
| { | |||||
| if (i == 0) | |||||
| pluginString += " lv2:port [\n"; | |||||
| else | |||||
| pluginString += " [\n"; | |||||
| if (plugin.parameterIsOutput(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.parameterName(i) + "\" ;\n"; | |||||
| // symbol | |||||
| { | |||||
| d_string symbol(plugin.parameterSymbol(i)); | |||||
| if (symbol.isEmpty()) | |||||
| symbol = "lv2_port_" + d_string(portIndex-1); | |||||
| pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | |||||
| } | |||||
| // ranges | |||||
| { | |||||
| const ParameterRanges* ranges = plugin.parameterRanges(i); | |||||
| if (plugin.parameterHints(i) & PARAMETER_IS_INTEGER) | |||||
| { | |||||
| pluginString += " lv2:default " + d_string(int(plugin.parameterValue(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.parameterValue(i)) + " ;\n"; | |||||
| pluginString += " lv2:minimum " + d_string(ranges->min) + " ;\n"; | |||||
| pluginString += " lv2:maximum " + d_string(ranges->max) + " ;\n"; | |||||
| } | |||||
| } | |||||
| // unit | |||||
| { | |||||
| const d_string& unit = plugin.parameterUnit(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 | |||||
| { | |||||
| pluginString += " unit:unit [\n"; | |||||
| pluginString += " a unit:Unit ;\n"; | |||||
| pluginString += " unit:name \"" + unit + "\" ;\n"; | |||||
| pluginString += " unit:symbol \"" + unit + "\" ;\n"; | |||||
| pluginString += " unit:render \"%f f\" ;\n"; | |||||
| pluginString += " ] ;\n"; | |||||
| } | |||||
| } | |||||
| } | |||||
| // hints | |||||
| { | |||||
| uint32_t hints = plugin.parameterHints(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 (i+1 == plugin.parameterCount()) | |||||
| pluginString += " ] ;\n\n"; | |||||
| else | |||||
| pluginString += " ] ,\n"; | |||||
| } | |||||
| pluginString += " lv2:port [\n"; | |||||
| #if DISTRHO_LV2_USE_EVENTS | |||||
| 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_IS_SYNTH && DISTRHO_PLUGIN_WANT_STATE) | |||||
| pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ,\n"; | |||||
| pluginString += " <" LV2_PATCH__Message "> ;\n"; | |||||
| # elif DISTRHO_PLUGIN_IS_SYNTH | |||||
| pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; | |||||
| # else | |||||
| pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; | |||||
| # endif | |||||
| pluginString += " ] ,\n"; | |||||
| pluginString += " [\n"; | |||||
| #endif | |||||
| 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"; | |||||
| #if ! DISTRHO_PLUGIN_HAS_UI | |||||
| pluginString += " ] ;\n\n"; | |||||
| #else | |||||
| pluginString += " ] ,\n"; | |||||
| pluginString += " [\n"; | |||||
| pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |||||
| pluginString += " lv2:index " + d_string(portIndex++) + " ;\n"; | |||||
| pluginString += " lv2:name \"Sample Rate\" ;\n"; | |||||
| pluginString += " lv2:symbol \"lv2_sample_rate\" ;\n"; | |||||
| pluginString += " lv2:designation <http://lv2plug.in/ns/ext/parameters#sampleRate> ;\n"; | |||||
| pluginString += " ] ;\n\n"; | |||||
| #endif | |||||
| } | |||||
| pluginString += " doap:name \"" + d_string(plugin.name()) + "\" ;\n"; | |||||
| pluginString += " doap:maintainer [ foaf:name \"" + d_string(plugin.maker()) + "\" ] .\n"; | |||||
| pluginFile << pluginString << std::endl; | |||||
| pluginFile.close(); | |||||
| std::cout << " done!" << std::endl; | |||||
| } | |||||
| } | |||||
| // unused stuff | |||||
| void d_unusedStuff() | |||||
| { | |||||
| (void)d_lastBufferSize; | |||||
| (void)d_lastSampleRate; | |||||
| } | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| int main() | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| lv2_generate_ttl_func(); | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_TARGET_LV2 | |||||
| @@ -0,0 +1,724 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_VST | |||||
| #include "DistrhoPluginInternal.h" | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| # include "DistrhoUIInternal.h" | |||||
| #endif | |||||
| #define VST_FORCE_DEPRECATED 0 | |||||
| #include <pluginterfaces/vst2.x/aeffectx.h> | |||||
| // ------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| class UIVst | |||||
| { | |||||
| public: | |||||
| UIVst(audioMasterCallback audioMaster, AEffect* effect, PluginInternal* plugin, intptr_t winId) | |||||
| : m_audioMaster(audioMaster), | |||||
| m_effect(effect), | |||||
| m_plugin(plugin), | |||||
| ui(this, winId, setParameterCallback, setStateCallback, uiEditParameterCallback, uiSendNoteCallback, uiResizeCallback) | |||||
| { | |||||
| uint32_t paramCount = plugin->parameterCount(); | |||||
| if (paramCount > 0) | |||||
| { | |||||
| parameterChecks = new bool [paramCount]; | |||||
| parameterValues = new float [paramCount]; | |||||
| for (uint32_t i=0; i < paramCount; i++) | |||||
| { | |||||
| parameterChecks[i] = false; | |||||
| parameterValues[i] = 0.0f; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| parameterChecks = nullptr; | |||||
| parameterValues = nullptr; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| nextProgram = -1; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| midiEventCount = 0; | |||||
| #endif | |||||
| } | |||||
| ~UIVst() | |||||
| { | |||||
| if (parameterChecks) | |||||
| delete[] parameterChecks; | |||||
| if (parameterValues) | |||||
| delete[] parameterValues; | |||||
| } | |||||
| // --------------------------------------------- | |||||
| void idle() | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| if (nextProgram != -1) | |||||
| { | |||||
| ui.programChanged(nextProgram); | |||||
| nextProgram = -1; | |||||
| } | |||||
| #endif | |||||
| for (uint32_t i=0, count = m_plugin->parameterCount(); i < count; i++) | |||||
| { | |||||
| if (parameterChecks[i]) | |||||
| { | |||||
| parameterChecks[i] = false; | |||||
| ui.parameterChanged(i, parameterValues[i]); | |||||
| } | |||||
| } | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| // TODO - notes | |||||
| #endif | |||||
| ui.idle(); | |||||
| } | |||||
| int16_t getWidth() | |||||
| { | |||||
| return ui.getWidth(); | |||||
| } | |||||
| int16_t getHeight() | |||||
| { | |||||
| return ui.getHeight(); | |||||
| } | |||||
| void setParameterValueFromPlugin(uint32_t index, float perValue) | |||||
| { | |||||
| parameterChecks[index] = true; | |||||
| parameterValues[index] = perValue; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void setProgramFromPlugin(uint32_t index) | |||||
| { | |||||
| nextProgram = index; | |||||
| // set previous parameters invalid | |||||
| for (uint32_t i=0, count = m_plugin->parameterCount(); i < count; i++) | |||||
| parameterChecks[i] = false; | |||||
| } | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| intptr_t hostCallback(int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) | |||||
| { | |||||
| return m_audioMaster(m_effect, opcode, index, value, ptr, opt); | |||||
| } | |||||
| void setParameterValue(uint32_t index, float realValue) | |||||
| { | |||||
| const ParameterRanges* ranges = m_plugin->parameterRanges(index); | |||||
| float perValue = (realValue - ranges->min) / (ranges->max - ranges->min); | |||||
| m_plugin->setParameterValue(index, realValue); | |||||
| hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void setState(const char* key, const char* value) | |||||
| { | |||||
| m_plugin->setState(key, value); | |||||
| } | |||||
| #endif | |||||
| void uiEditParameter(uint32_t index, bool started) | |||||
| { | |||||
| if (started) | |||||
| hostCallback(audioMasterBeginEdit, index, 0, nullptr, 0.0f); | |||||
| else | |||||
| hostCallback(audioMasterEndEdit, index, 0, nullptr, 0.0f); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| // TODO | |||||
| } | |||||
| #endif | |||||
| void uiResize(unsigned int width, unsigned int height) | |||||
| { | |||||
| hostCallback(audioMasterSizeWindow, width, height, nullptr, 0.0f); | |||||
| } | |||||
| private: | |||||
| // Vst stuff | |||||
| audioMasterCallback const m_audioMaster; | |||||
| AEffect* const m_effect; | |||||
| PluginInternal* const m_plugin; | |||||
| // Plugin UI | |||||
| UIInternal ui; | |||||
| // Temporary data | |||||
| bool* parameterChecks; | |||||
| float* parameterValues; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| int32_t nextProgram; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| uint32_t midiEventCount; | |||||
| MidiEvent midiEvents[MAX_MIDI_EVENTS]; | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| // Callbacks | |||||
| static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||||
| { | |||||
| UIVst* _this_ = (UIVst*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setParameterValue(rindex, value); | |||||
| } | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| UIVst* _this_ = (UIVst*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setState(key, value); | |||||
| #else | |||||
| // unused | |||||
| (void)ptr; | |||||
| (void)key; | |||||
| (void)value; | |||||
| #endif | |||||
| } | |||||
| static void uiEditParameterCallback(void* ptr, uint32_t index, bool started) | |||||
| { | |||||
| UIVst* _this_ = (UIVst*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiEditParameter(index, started); | |||||
| } | |||||
| static void uiSendNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| UIVst* _this_ = (UIVst*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiSendNote(onOff, channel, note, velocity); | |||||
| #else | |||||
| // unused | |||||
| (void)ptr; | |||||
| (void)onOff; | |||||
| (void)channel; | |||||
| (void)note; | |||||
| (void)velocity; | |||||
| #endif | |||||
| } | |||||
| static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) | |||||
| { | |||||
| UIVst* _this_ = (UIVst*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiResize(width, height); | |||||
| } | |||||
| }; | |||||
| #endif | |||||
| class PluginVst | |||||
| { | |||||
| public: | |||||
| PluginVst(audioMasterCallback audioMaster, AEffect* effect) | |||||
| : m_audioMaster(audioMaster), | |||||
| m_effect(effect) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| vstui = nullptr; | |||||
| rect.top = 0; | |||||
| rect.left = 0; | |||||
| rect.bottom = 0; | |||||
| rect.right = 0; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| curProgram = -1; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| midiEventCount = 0; | |||||
| #endif | |||||
| } | |||||
| ~PluginVst() | |||||
| { | |||||
| } | |||||
| intptr_t vst_dispatcher(int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) | |||||
| { | |||||
| int32_t ret = 0; | |||||
| switch (opcode) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| case effSetProgram: | |||||
| if (value >= 0 && value < plugin.programCount()) | |||||
| { | |||||
| curProgram = value; | |||||
| plugin.setProgram(curProgram); | |||||
| if (vstui) | |||||
| vstui->setProgramFromPlugin(curProgram); | |||||
| ret = 1; | |||||
| } | |||||
| break; | |||||
| case effGetProgram: | |||||
| ret = curProgram; | |||||
| break; | |||||
| case effSetProgramName: | |||||
| break; | |||||
| case effGetProgramName: | |||||
| if (ptr && curProgram >= 0 && curProgram < (int32_t)plugin.programCount()) | |||||
| { | |||||
| strncpy((char*)ptr, plugin.programName(curProgram), kVstMaxProgNameLen); | |||||
| ret = 1; | |||||
| } | |||||
| break; | |||||
| #endif | |||||
| case effGetParamDisplay: | |||||
| if (ptr && index < (int32_t)plugin.parameterCount()) | |||||
| { | |||||
| snprintf((char*)ptr, kVstMaxParamStrLen, "%f", plugin.parameterValue(index)); | |||||
| ret = 1; | |||||
| } | |||||
| break; | |||||
| case effSetSampleRate: | |||||
| // should not happen | |||||
| break; | |||||
| case effSetBlockSize: | |||||
| plugin.setBufferSize(value, true); | |||||
| break; | |||||
| case effMainsChanged: | |||||
| if (value) | |||||
| plugin.activate(); | |||||
| else | |||||
| plugin.deactivate(); | |||||
| break; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| case effEditGetRect: | |||||
| if (rect.bottom == 0 && ! vstui) | |||||
| { | |||||
| // This is stupid, but some hosts want to know the UI size before creating it, | |||||
| // so we have to create a temporary UI here | |||||
| setLastUiSampleRate(d_lastSampleRate); | |||||
| UIInternal tempUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr); | |||||
| rect.bottom = tempUI.getHeight(); | |||||
| rect.right = tempUI.getWidth(); | |||||
| } | |||||
| else | |||||
| { | |||||
| rect.bottom = vstui->getHeight(); | |||||
| rect.right = vstui->getWidth(); | |||||
| } | |||||
| *(ERect**)ptr = ▭ | |||||
| ret = 1; | |||||
| break; | |||||
| case effEditOpen: | |||||
| if (! vstui) | |||||
| { | |||||
| setLastUiSampleRate(d_lastSampleRate); | |||||
| vstui = new UIVst(m_audioMaster, m_effect, &plugin, (intptr_t)ptr); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| if (curProgram >= 0) | |||||
| vstui->setProgramFromPlugin(curProgram); | |||||
| #endif | |||||
| for (uint32_t i=0, count = plugin.parameterCount(); i < count; i++) | |||||
| vstui->setParameterValueFromPlugin(i, plugin.parameterValue(i)); | |||||
| ret = 1; | |||||
| break; | |||||
| case effEditClose: | |||||
| if (vstui) | |||||
| { | |||||
| delete vstui; | |||||
| vstui = nullptr; | |||||
| ret = 1; | |||||
| } | |||||
| break; | |||||
| case effEditIdle: | |||||
| if (vstui) | |||||
| vstui->idle(); | |||||
| break; | |||||
| #endif | |||||
| case effGetChunk: | |||||
| case effSetChunk: | |||||
| // TODO | |||||
| break; | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| case effProcessEvents: | |||||
| if (ptr) | |||||
| { | |||||
| //VstEvents* events = (VstEvents*)ptr; | |||||
| // TODO | |||||
| } | |||||
| break; | |||||
| #endif | |||||
| case effCanBeAutomated: | |||||
| if (index < (int32_t)plugin.parameterCount()) | |||||
| { | |||||
| uint32_t hints = plugin.parameterHints(index); | |||||
| // must be automable, and not output | |||||
| if ((hints & PARAMETER_IS_AUTOMABLE) > 0 && (hints & PARAMETER_IS_OUTPUT) == 0) | |||||
| ret = 1; | |||||
| } | |||||
| break; | |||||
| case effCanDo: | |||||
| // TODO | |||||
| break; | |||||
| case effStartProcess: | |||||
| case effStopProcess: | |||||
| break; | |||||
| } | |||||
| return ret; | |||||
| // unused | |||||
| (void)opt; | |||||
| } | |||||
| float vst_getParameter(int32_t index) | |||||
| { | |||||
| const ParameterRanges* ranges = plugin.parameterRanges(index); | |||||
| float realValue = plugin.parameterValue(index); | |||||
| float perValue = (realValue - ranges->min) / (ranges->max - ranges->min); | |||||
| return perValue; | |||||
| } | |||||
| void vst_setParameter(int32_t index, float value) | |||||
| { | |||||
| const ParameterRanges* ranges = plugin.parameterRanges(index); | |||||
| float realValue = ranges->min + (ranges->max - ranges->min) * value; | |||||
| plugin.setParameterValue(index, realValue); | |||||
| if (vstui) | |||||
| vstui->setParameterValueFromPlugin(index, realValue); | |||||
| } | |||||
| void vst_processReplacing(float** inputs, float** outputs, int32_t sampleFrames) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| plugin.run((const float**)inputs, outputs, sampleFrames, midiEventCount, midiEvents); | |||||
| // TODO - send notes to UI | |||||
| #else | |||||
| plugin.run((const float**)inputs, outputs, sampleFrames, 0, nullptr); | |||||
| #endif | |||||
| } | |||||
| // --------------------------------------------- | |||||
| private: | |||||
| // VST stuff | |||||
| audioMasterCallback const m_audioMaster; | |||||
| AEffect* const m_effect; | |||||
| // Plugin | |||||
| PluginInternal plugin; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| // UI | |||||
| UIVst* vstui; | |||||
| ERect rect; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| int32_t curProgram; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| uint32_t midiEventCount; | |||||
| MidiEvent midiEvents[MAX_MIDI_EVENTS]; | |||||
| #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 == -1 && index == 0xdead && value == 0xf00d); | |||||
| if (doInternalInit) | |||||
| { | |||||
| d_lastBufferSize = 512; | |||||
| d_lastSampleRate = 44100.0; | |||||
| } | |||||
| // Create dummy plugin to get data from | |||||
| static PluginInternal plugin; | |||||
| if (doInternalInit) | |||||
| { | |||||
| d_lastBufferSize = 0; | |||||
| d_lastSampleRate = 0.0; | |||||
| *(PluginInternal**)ptr = &plugin; | |||||
| return 0; | |||||
| } | |||||
| // handle opcodes | |||||
| switch (opcode) | |||||
| { | |||||
| case effOpen: | |||||
| if (! effect->object) | |||||
| { | |||||
| audioMasterCallback audioMaster = (audioMasterCallback)effect->user; | |||||
| d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); | |||||
| d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); | |||||
| effect->object = new PluginVst(audioMaster, effect); | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| case effClose: | |||||
| if (effect->object) | |||||
| { | |||||
| delete (PluginVst*)effect->object; | |||||
| effect->object = nullptr; | |||||
| delete effect; | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| case effGetParamLabel: | |||||
| if (ptr && index < (int32_t)plugin.parameterCount()) | |||||
| { | |||||
| strncpy((char*)ptr, plugin.parameterUnit(index), kVstMaxParamStrLen); | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| case effGetParamName: | |||||
| if (ptr && index < (int32_t)plugin.parameterCount()) | |||||
| { | |||||
| strncpy((char*)ptr, plugin.parameterName(index), kVstMaxParamStrLen); | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| case effGetProgramNameIndexed: | |||||
| if (ptr && index < (int32_t)plugin.programCount()) | |||||
| { | |||||
| strncpy((char*)ptr, plugin.programName(index), kVstMaxProgNameLen); | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| #endif | |||||
| case effGetPlugCategory: | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| return kPlugCategSynth; | |||||
| #else | |||||
| return kPlugCategUnknown; | |||||
| #endif | |||||
| case effGetEffectName: | |||||
| if (ptr) | |||||
| { | |||||
| strncpy((char*)ptr, plugin.name(), kVstMaxProductStrLen); | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| case effGetVendorString: | |||||
| if (ptr) | |||||
| { | |||||
| strncpy((char*)ptr, plugin.maker(), kVstMaxVendorStrLen); | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| case effGetProductString: | |||||
| if (ptr) | |||||
| { | |||||
| strncpy((char*)ptr, plugin.label(), kVstMaxEffectNameLen); | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| case effGetVendorVersion: | |||||
| return plugin.version(); | |||||
| case effGetVstVersion: | |||||
| return kVstVersion; | |||||
| }; | |||||
| if (effect->object) | |||||
| { | |||||
| PluginVst* _this_ = (PluginVst*)effect->object; | |||||
| return _this_->vst_dispatcher(opcode, index, value, ptr, opt); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| static float vst_getParameterCallback(AEffect* effect, int32_t index) | |||||
| { | |||||
| PluginVst* _this_ = (PluginVst*)effect->object; | |||||
| assert(_this_); | |||||
| if (_this_) | |||||
| return _this_->vst_getParameter(index); | |||||
| return 0.0f; | |||||
| } | |||||
| static void vst_setParameterCallback(AEffect* effect, int32_t index, float value) | |||||
| { | |||||
| PluginVst* _this_ = (PluginVst*)effect->object; | |||||
| assert(_this_); | |||||
| if (_this_) | |||||
| _this_->vst_setParameter(index, value); | |||||
| } | |||||
| static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) | |||||
| { | |||||
| PluginVst* _this_ = (PluginVst*)effect->object; | |||||
| assert(_this_); | |||||
| if (_this_) | |||||
| _this_->vst_processReplacing(inputs, outputs, sampleFrames); | |||||
| } | |||||
| static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) | |||||
| { | |||||
| PluginVst* _this_ = (PluginVst*)effect->object; | |||||
| assert(_this_); | |||||
| if (_this_) | |||||
| _this_->vst_processReplacing(inputs, outputs, sampleFrames); | |||||
| } | |||||
| 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)) | |||||
| return nullptr; | |||||
| PluginInternal* plugin = nullptr; | |||||
| vst_dispatcherCallback(nullptr, -1, 0xdead, 0xf00d, &plugin, 0.0f); | |||||
| AEffect* effect = new AEffect; | |||||
| memset(effect, 0, sizeof(AEffect)); | |||||
| // vst fields | |||||
| effect->magic = kEffectMagic; | |||||
| effect->uniqueID = plugin->uniqueId(); | |||||
| effect->version = plugin->version(); | |||||
| // plugin fields | |||||
| effect->numParams = plugin->parameterCount(); | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| effect->numPrograms = plugin->programCount(); | |||||
| #endif | |||||
| effect->numInputs = DISTRHO_PLUGIN_NUM_INPUTS; | |||||
| effect->numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS; | |||||
| // static calls | |||||
| effect->dispatcher = vst_dispatcherCallback; | |||||
| effect->process = vst_processCallback; | |||||
| effect->getParameter = vst_getParameterCallback; | |||||
| effect->setParameter = vst_setParameterCallback; | |||||
| effect->processReplacing = vst_processReplacingCallback; | |||||
| effect->processDoubleReplacing = nullptr; | |||||
| // plugin flags | |||||
| effect->flags |= effFlagsCanReplacing; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| # ifdef DISTRHO_UI_QT4 | |||||
| if (QApplication::instance()) | |||||
| # endif | |||||
| effect->flags |= effFlagsHasEditor; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| effect->flags |= effFlagsProgramChunks; | |||||
| #endif | |||||
| // pointers | |||||
| effect->object = nullptr; | |||||
| effect->user = (void*)audioMaster; | |||||
| return effect; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_TARGET_VST | |||||
| @@ -0,0 +1,104 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "DistrhoUIInternal.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| void setLastUiSampleRate(double sampleRate) | |||||
| { | |||||
| d_lastUiSampleRate = sampleRate; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| UI::UI() | |||||
| { | |||||
| data = new UIPrivateData; | |||||
| #if (defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)) | |||||
| data->parameterOffset = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; | |||||
| # if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| data->parameterOffset += 1; | |||||
| # endif | |||||
| data->parameterOffset += 1; // sample-rate | |||||
| #endif | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| data->widget = (Qt4UI*)this; | |||||
| #endif | |||||
| } | |||||
| UI::~UI() | |||||
| { | |||||
| delete data; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| // Host DSP State | |||||
| double UI::d_sampleRate() const | |||||
| { | |||||
| return data->sampleRate; | |||||
| } | |||||
| void UI::d_setParameterValue(uint32_t index, float value) | |||||
| { | |||||
| data->setParamCallback(index + data->parameterOffset, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void UI::d_setState(const char* key, const char* value) | |||||
| { | |||||
| data->setStateCallback(key, value); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| // Host UI State | |||||
| void UI::d_uiEditParameter(uint32_t index, bool started) | |||||
| { | |||||
| data->uiEditParamCallback(index, started); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void UI::d_uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| data->uiSendNoteCallback(onOff, channel, note, velocity); | |||||
| } | |||||
| #endif | |||||
| void UI::d_uiResize(unsigned int width, unsigned int height) | |||||
| { | |||||
| data->uiResizeCallback(width, height); | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| // DSP Callbacks | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void UI::d_uiNoteReceived(bool, uint8_t, uint8_t, uint8_t) | |||||
| { | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,612 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "DistrhoDefines.h" | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI | |||||
| #include "DistrhoUIInternal.h" | |||||
| #include <QtCore/QSettings> | |||||
| #include <QtGui/QApplication> | |||||
| #include <QtGui/QMainWindow> | |||||
| #include <lo/lo.h> | |||||
| // ------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| struct OscData { | |||||
| lo_address addr; | |||||
| const char* path; | |||||
| lo_server server; | |||||
| }; | |||||
| struct StringData { | |||||
| d_string key; | |||||
| d_string value; | |||||
| }; | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void osc_send_configure(const OscData* oscData, const char* const key, const char* const value) | |||||
| { | |||||
| char targetPath[strlen(oscData->path)+11]; | |||||
| strcpy(targetPath, oscData->path); | |||||
| strcat(targetPath, "/configure"); | |||||
| lo_send(oscData->addr, targetPath, "ss", key, value); | |||||
| } | |||||
| #endif | |||||
| void osc_send_control(const OscData* oscData, const int32_t index, const float value) | |||||
| { | |||||
| char targetPath[strlen(oscData->path)+9]; | |||||
| strcpy(targetPath, oscData->path); | |||||
| strcat(targetPath, "/control"); | |||||
| lo_send(oscData->addr, targetPath, "if", index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void osc_send_midi(const OscData* oscData, unsigned char data[4]) | |||||
| { | |||||
| char targetPath[strlen(oscData->path)+6]; | |||||
| strcpy(targetPath, oscData->path); | |||||
| strcat(targetPath, "/midi"); | |||||
| lo_send(oscData->addr, targetPath, "m", data); | |||||
| } | |||||
| #endif | |||||
| void osc_send_update(const OscData* oscData, const char* const url) | |||||
| { | |||||
| char targetPath[strlen(oscData->path)+8]; | |||||
| strcpy(targetPath, oscData->path); | |||||
| strcat(targetPath, "/update"); | |||||
| lo_send(oscData->addr, targetPath, "s", url); | |||||
| } | |||||
| void osc_send_exiting(const OscData* oscData) | |||||
| { | |||||
| char targetPath[strlen(oscData->path)+9]; | |||||
| strcpy(targetPath, oscData->path); | |||||
| strcat(targetPath, "/exiting"); | |||||
| lo_send(oscData->addr, targetPath, ""); | |||||
| } | |||||
| // stuff that we might receive while waiting for sample-rate | |||||
| static bool globalShow = false; | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| static std::vector<StringData> globalConfigures; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static int32_t globalProgram[2] = { -1, -1 }; | |||||
| #endif | |||||
| static std::vector<float> globalControls; | |||||
| class UIDssi : public QMainWindow | |||||
| { | |||||
| public: | |||||
| UIDssi(const OscData* oscData_, const char* title) | |||||
| : QMainWindow(nullptr), | |||||
| widget(this), | |||||
| settings("DISTRHO", DISTRHO_PLUGIN_NAME), | |||||
| ui(this, widget.winId(), setParameterCallback, setStateCallback, nullptr, uiSendNoteCallback, uiResizeCallback), | |||||
| oscData(oscData_) | |||||
| { | |||||
| setCentralWidget(&widget); | |||||
| setFixedSize(ui.getWidth(), ui.getHeight()); | |||||
| setWindowTitle(title); | |||||
| uiTimer = startTimer(30); | |||||
| // load settings | |||||
| restoreGeometry(settings.value("Global/Geometry", QByteArray()).toByteArray()); | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| for (size_t i=0; i < globalConfigures.size(); i++) | |||||
| dssiui_configure(globalConfigures.at(i).key, globalConfigures.at(i).value); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| if (globalProgram[0] >= 0 && globalProgram[1] >= 0) | |||||
| dssiui_program(globalProgram[0], globalProgram[1]); | |||||
| #endif | |||||
| for (size_t i=0; i < globalControls.size(); i++) | |||||
| dssiui_control(i, globalControls.at(i)); | |||||
| if (globalShow) | |||||
| show(); | |||||
| } | |||||
| ~UIDssi() | |||||
| { | |||||
| // save settings | |||||
| settings.setValue("Global/Geometry", saveGeometry()); | |||||
| if (uiTimer) | |||||
| { | |||||
| killTimer(uiTimer); | |||||
| osc_send_exiting(oscData); | |||||
| } | |||||
| } | |||||
| // --------------------------------------------- | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void dssiui_configure(const char* key, const char* value) | |||||
| { | |||||
| ui.stateChanged(key, value); | |||||
| } | |||||
| #endif | |||||
| void dssiui_control(unsigned long index, float value) | |||||
| { | |||||
| ui.parameterChanged(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void dssiui_program(unsigned long bank, unsigned long program) | |||||
| { | |||||
| unsigned long index = bank * 128 + program; | |||||
| ui.programChanged(index); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void dssiui_midi(uint8_t data[4]) | |||||
| { | |||||
| uint8_t status = data[1] & 0xF0; | |||||
| uint8_t channel = data[1] & 0x0F; | |||||
| // fix bad note-off | |||||
| if (status == 0x90 && data[3] == 0) | |||||
| status -= 0x10; | |||||
| if (status == 0x80) | |||||
| { | |||||
| uint8_t note = data[2]; | |||||
| ui.noteReceived(false, channel, note, 0); | |||||
| } | |||||
| else if (status == 0x90) | |||||
| { | |||||
| uint8_t note = data[2]; | |||||
| uint8_t velo = data[3]; | |||||
| ui.noteReceived(true, channel, note, velo); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| void dssiui_quit() | |||||
| { | |||||
| if (uiTimer) | |||||
| { | |||||
| killTimer(uiTimer); | |||||
| uiTimer = 0; | |||||
| } | |||||
| close(); | |||||
| qApp->quit(); | |||||
| } | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| void setParameterValue(uint32_t rindex, float value) | |||||
| { | |||||
| osc_send_control(oscData, rindex, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void setState(const char* key, const char* value) | |||||
| { | |||||
| osc_send_configure(oscData, key, value); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| uint8_t mdata[4] = { 0, channel, note, velocity }; | |||||
| mdata[1] += onOff ? 0x90 : 0x80; | |||||
| osc_send_midi(oscData, mdata); | |||||
| } | |||||
| #endif | |||||
| void uiResize(unsigned int width, unsigned int height) | |||||
| { | |||||
| widget.setFixedSize(width, height); | |||||
| setFixedSize(width, height); | |||||
| } | |||||
| void timerEvent(QTimerEvent* event) | |||||
| { | |||||
| if (event->timerId() == uiTimer) | |||||
| { | |||||
| ui.idle(); | |||||
| while (lo_server_recv_noblock(oscData->server, 0) != 0) {} | |||||
| } | |||||
| QObject::timerEvent(event); | |||||
| } | |||||
| private: | |||||
| // Qt4 stuff | |||||
| int uiTimer; | |||||
| QWidget widget; | |||||
| QSettings settings; | |||||
| // Plugin UI | |||||
| UIInternal ui; | |||||
| const OscData* const oscData; | |||||
| // --------------------------------------------- | |||||
| // Callbacks | |||||
| static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||||
| { | |||||
| UIDssi* _this_ = (UIDssi*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setParameterValue(rindex, value); | |||||
| } | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| UIDssi* _this_ = (UIDssi*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setState(key, value); | |||||
| #else | |||||
| Q_UNUSED(ptr); | |||||
| Q_UNUSED(key); | |||||
| Q_UNUSED(value); | |||||
| #endif | |||||
| } | |||||
| static void uiSendNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| UIDssi* _this_ = (UIDssi*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiSendNote(onOff, channel, note, velocity); | |||||
| #else | |||||
| Q_UNUSED(ptr); | |||||
| Q_UNUSED(onOff); | |||||
| Q_UNUSED(channel); | |||||
| Q_UNUSED(note); | |||||
| Q_UNUSED(velocity); | |||||
| #endif | |||||
| } | |||||
| static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) | |||||
| { | |||||
| UIDssi* _this_ = (UIDssi*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiResize(width, height); | |||||
| } | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| static UIDssi* globalUI = nullptr; | |||||
| void osc_error_handler(int num, const char* msg, const char* path) | |||||
| { | |||||
| qCritical("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; | |||||
| qDebug("osc_configure_handler(\"%s\", \"%s\")", key, value); | |||||
| if (globalUI) | |||||
| { | |||||
| globalUI->dssiui_configure(key, value); | |||||
| } | |||||
| else | |||||
| { | |||||
| StringData data = { key, value }; | |||||
| globalConfigures.push_back(data); | |||||
| } | |||||
| 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; | |||||
| qDebug("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 | |||||
| // sample-rate | |||||
| index -= 1; | |||||
| if (index == -1) | |||||
| { | |||||
| setLastUiSampleRate(value); | |||||
| return 0; | |||||
| } | |||||
| if (index < 0) | |||||
| return 0; | |||||
| if (globalUI) | |||||
| { | |||||
| globalUI->dssiui_control(index, value); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (index >= (int32_t)globalControls.size()) | |||||
| { | |||||
| for (int32_t i=globalControls.size(); i < index; i++) | |||||
| globalControls.push_back(0.0f); | |||||
| } | |||||
| globalControls[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; | |||||
| qDebug("osc_program_handler(%i, %i)", bank, program); | |||||
| if (globalUI) | |||||
| { | |||||
| globalUI->dssiui_program(bank, program); | |||||
| } | |||||
| else | |||||
| { | |||||
| globalProgram[0] = bank; | |||||
| globalProgram[1] = program; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| int osc_midi_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||||
| { | |||||
| qDebug("osc_midi_handler()"); | |||||
| if (globalUI) | |||||
| globalUI->dssiui_midi(argv[0]->m); | |||||
| 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; | |||||
| qDebug("osc_sample_rate_handler(%i)", sampleRate); | |||||
| setLastUiSampleRate(sampleRate); | |||||
| return 0; | |||||
| } | |||||
| int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| qDebug("osc_show_handler()"); | |||||
| if (globalUI) | |||||
| globalUI->show(); | |||||
| else | |||||
| globalShow = true; | |||||
| return 0; | |||||
| } | |||||
| int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| qDebug("osc_hide_handler()"); | |||||
| if (globalUI) | |||||
| globalUI->hide(); | |||||
| else | |||||
| globalShow = false; | |||||
| return 0; | |||||
| } | |||||
| int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| qDebug("osc_quit_handler()"); | |||||
| if (globalUI) | |||||
| { | |||||
| globalUI->dssiui_quit(); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (QApplication* app = qApp) | |||||
| app->quit(); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| qDebug("osc_debug_handler(\"%s\")", path); | |||||
| return 0; | |||||
| } | |||||
| END_NAMESPACE_DISTRHO | |||||
| int main(int argc, char* argv[]) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| if (argc != 5) | |||||
| { | |||||
| qWarning("Usage: %s <osc-url> <so-file> <plugin-label> <instance-name>", argv[0]); | |||||
| return 1; | |||||
| } | |||||
| QApplication app(argc, argv, true); | |||||
| 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(nullptr, osc_error_handler); | |||||
| OscData oscData = { oscAddr, oscPath, oscServer }; | |||||
| #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); | |||||
| #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 | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| char oscPathMidi[oscPathSize+6]; | |||||
| strcpy(oscPathMidi, oscPath); | |||||
| strcat(oscPathMidi, "/midi"); | |||||
| lo_server_add_method(oscServer, oscPathMidi, "m", osc_midi_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); | |||||
| char* const serverPath = lo_server_get_url(oscServer); | |||||
| char* const pluginPath = strdup(QString("%1%2").arg(serverPath).arg(oscPathSize > 1 ? oscPath + 1 : oscPath).toUtf8().constData()); | |||||
| free(serverPath); | |||||
| // send update msg and wait for sample-rate | |||||
| osc_send_update(&oscData, pluginPath); | |||||
| // wait for sample-rate | |||||
| for (int i=0; i < 1000; i++) | |||||
| { | |||||
| if (d_lastUiSampleRate != 0.0) | |||||
| break; | |||||
| lo_server_recv(oscServer); | |||||
| d_msleep(50); | |||||
| } | |||||
| int ret; | |||||
| if (d_lastUiSampleRate != 0.0) | |||||
| { | |||||
| globalUI = new UIDssi(&oscData, uiTitle); | |||||
| ret = app.exec(); | |||||
| delete globalUI; | |||||
| globalUI = nullptr; | |||||
| } | |||||
| else | |||||
| { | |||||
| ret = 1; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| globalConfigures.clear(); | |||||
| #endif | |||||
| globalControls.clear(); | |||||
| #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 | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| lo_server_del_method(oscServer, oscPathMidi, "m"); | |||||
| #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); | |||||
| free(oscHost); | |||||
| free(oscPort); | |||||
| free(oscPath); | |||||
| free(pluginPath); | |||||
| lo_address_free(oscAddr); | |||||
| lo_server_free(oscServer); | |||||
| return ret; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_TARGET_DSSI && DISTRHO_PLUGIN_HAS_UI | |||||
| @@ -0,0 +1,548 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #ifndef __DISTRHO_UI_INTERNAL_H__ | |||||
| #define __DISTRHO_UI_INTERNAL_H__ | |||||
| #include "DistrhoUI.h" | |||||
| #ifdef DISTRHO_UI_OPENGL | |||||
| # include "DistrhoUIOpenGL.h" | |||||
| # include "pugl/pugl.h" | |||||
| #else | |||||
| # include "DistrhoUIQt4.h" | |||||
| # include <QtGui/QApplication> | |||||
| # include <QtGui/QMouseEvent> | |||||
| # include <QtGui/QSizeGrip> | |||||
| # include <QtGui/QVBoxLayout> | |||||
| # ifdef Q_WS_X11 | |||||
| # include <QtGui/QX11EmbedWidget> | |||||
| # endif | |||||
| #endif | |||||
| #include <cassert> | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| #ifdef DISTRHO_UI_OPENGL | |||||
| typedef PuglView* NativeWidget; | |||||
| #else | |||||
| typedef QWidget* NativeWidget; | |||||
| #endif | |||||
| typedef void (*setParamFunc) (void* ptr, uint32_t index, float value); | |||||
| typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); | |||||
| typedef void (*uiEditParamFunc) (void* ptr, uint32_t index, bool started); | |||||
| typedef void (*uiSendNoteFunc) (void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velo); | |||||
| typedef void (*uiResizeFunc) (void* ptr, unsigned int width, unsigned int height); | |||||
| static double d_lastUiSampleRate = 0.0; | |||||
| void setLastUiSampleRate(double sampleRate); | |||||
| // ------------------------------------------------- | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| # ifdef Q_WS_X11 | |||||
| class QEmbedWidget : public QX11EmbedWidget | |||||
| #else | |||||
| class QEmbedWidget : public QWidget | |||||
| #endif | |||||
| { | |||||
| public: | |||||
| QEmbedWidget(); | |||||
| ~QEmbedWidget(); | |||||
| void embedInto(WId id); | |||||
| WId containerWinId() const; | |||||
| }; | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| struct UIPrivateData { | |||||
| // DSP | |||||
| double sampleRate; | |||||
| uint32_t parameterOffset; | |||||
| // UI | |||||
| void* ptr; | |||||
| NativeWidget widget; | |||||
| // Callbacks | |||||
| setParamFunc setParamCallbackFunc; | |||||
| setStateFunc setStateCallbackFunc; | |||||
| uiEditParamFunc uiEditParamCallbackFunc; | |||||
| uiSendNoteFunc uiSendNoteCallbackFunc; | |||||
| uiResizeFunc uiResizeCallbackFunc; | |||||
| UIPrivateData() | |||||
| : sampleRate(d_lastUiSampleRate), | |||||
| parameterOffset(0), | |||||
| ptr(nullptr), | |||||
| widget(nullptr), | |||||
| setParamCallbackFunc(nullptr), | |||||
| setStateCallbackFunc(nullptr), | |||||
| uiEditParamCallbackFunc(nullptr), | |||||
| uiSendNoteCallbackFunc(nullptr), | |||||
| uiResizeCallbackFunc(nullptr) | |||||
| { | |||||
| assert(d_lastUiSampleRate != 0.0); | |||||
| } | |||||
| ~UIPrivateData() | |||||
| { | |||||
| } | |||||
| void setParamCallback(uint32_t rindex, float value) | |||||
| { | |||||
| if (setParamCallbackFunc) | |||||
| setParamCallbackFunc(ptr, rindex, value); | |||||
| } | |||||
| void setStateCallback(const char* key, const char* value) | |||||
| { | |||||
| if (setStateCallbackFunc) | |||||
| setStateCallbackFunc(ptr, key, value); | |||||
| } | |||||
| void uiEditParamCallback(uint32_t index, bool started) | |||||
| { | |||||
| if (uiEditParamCallbackFunc) | |||||
| uiEditParamCallbackFunc(ptr, index, started); | |||||
| } | |||||
| void uiSendNoteCallback(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| if (uiSendNoteCallbackFunc) | |||||
| uiSendNoteCallbackFunc(ptr, onOff, channel, note, velocity); | |||||
| } | |||||
| void uiResizeCallback(unsigned int width, unsigned int height) | |||||
| { | |||||
| if (uiResizeCallbackFunc) | |||||
| uiResizeCallbackFunc(ptr, width, height); | |||||
| } | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| class UIInternal : public QObject | |||||
| #else | |||||
| class UIInternal | |||||
| #endif | |||||
| { | |||||
| public: | |||||
| UIInternal(void* ptr, intptr_t winId, setParamFunc setParamCall, setStateFunc setStateCall, uiEditParamFunc uiEditParamCall, uiSendNoteFunc uiSendNoteCall, uiResizeFunc uiResizeCall) | |||||
| : ui(createUI()), | |||||
| data(nullptr) | |||||
| { | |||||
| assert(ui); | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| qt_grip = nullptr; | |||||
| qt_widget = nullptr; | |||||
| #else | |||||
| gl_initiated = false; | |||||
| #endif | |||||
| if (winId == 0 || ! ui) | |||||
| return; | |||||
| data = ui->data; | |||||
| data->ptr = ptr; | |||||
| data->setParamCallbackFunc = setParamCall; | |||||
| data->setStateCallbackFunc = setStateCall; | |||||
| data->uiEditParamCallbackFunc = uiEditParamCall; | |||||
| data->uiSendNoteCallbackFunc = uiSendNoteCall; | |||||
| data->uiResizeCallbackFunc = uiResizeCall; | |||||
| createWindow(winId); | |||||
| } | |||||
| ~UIInternal() | |||||
| { | |||||
| if (ui) | |||||
| { | |||||
| destroyWindow(); | |||||
| delete ui; | |||||
| } | |||||
| } | |||||
| // --------------------------------------------- | |||||
| void idle() | |||||
| { | |||||
| if (ui) | |||||
| ui->d_uiIdle(); | |||||
| } | |||||
| unsigned int getWidth() | |||||
| { | |||||
| return ui ? ui->d_width() : 0; | |||||
| } | |||||
| unsigned int getHeight() | |||||
| { | |||||
| return ui ? ui->d_height() : 0; | |||||
| } | |||||
| intptr_t getWindowId() | |||||
| { | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| return qt_widget ? qt_widget->winId() : 0; | |||||
| #else | |||||
| return (data && data->widget) ? puglGetNativeWindow(data->widget) : 0; | |||||
| #endif | |||||
| } | |||||
| // --------------------------------------------- | |||||
| void parameterChanged(uint32_t index, float value) | |||||
| { | |||||
| if (ui) | |||||
| ui->d_parameterChanged(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void programChanged(uint32_t index) | |||||
| { | |||||
| if (ui) | |||||
| ui->d_programChanged(index); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void stateChanged(const char* key, const char* value) | |||||
| { | |||||
| if (ui) | |||||
| ui->d_stateChanged(key, value); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void noteReceived(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| if (ui) | |||||
| ui->d_uiNoteReceived(onOff, channel, note, velocity); | |||||
| } | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| void createWindow(intptr_t parent) | |||||
| { | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| // create embedable widget | |||||
| qt_widget = new QEmbedWidget; | |||||
| // set layout | |||||
| qt_widget->setLayout(new QVBoxLayout(qt_widget)); | |||||
| qt_widget->layout()->addWidget(data->widget); | |||||
| qt_widget->layout()->setContentsMargins(0, 0, 0, 0); | |||||
| qt_widget->setFixedSize(ui->d_width(), ui->d_height()); | |||||
| // listen for resize on the plugin widget | |||||
| data->widget->installEventFilter(this); | |||||
| // set resize grip | |||||
| if (((Qt4UI*)ui)->d_resizable()) | |||||
| { | |||||
| qt_grip = new QSizeGrip(qt_widget); | |||||
| qt_grip->resize(qt_grip->sizeHint()); | |||||
| qt_grip->setCursor(Qt::SizeFDiagCursor); | |||||
| qt_grip->move(ui->d_width() - qt_grip->width(), ui->d_height() - qt_grip->height()); | |||||
| qt_grip->show(); | |||||
| qt_grip->raise(); | |||||
| qt_grip->installEventFilter(this); | |||||
| } | |||||
| // reparent widget | |||||
| qt_widget->embedInto(parent); | |||||
| // show it | |||||
| qt_widget->show(); | |||||
| #else | |||||
| if ((data && data->widget) || ! ui) | |||||
| return; | |||||
| data->widget = puglCreate(parent, DISTRHO_PLUGIN_NAME, ui->d_width(), ui->d_height(), false); | |||||
| assert(data->widget); | |||||
| if (! data->widget) | |||||
| return; | |||||
| puglSetHandle(data->widget, this); | |||||
| puglSetDisplayFunc(data->widget, gl_onDisplayCallback); | |||||
| puglSetKeyboardFunc(data->widget, gl_onKeyboardCallback); | |||||
| puglSetMotionFunc(data->widget, gl_onMotionCallback); | |||||
| puglSetMouseFunc(data->widget, gl_onMouseCallback); | |||||
| puglSetScrollFunc(data->widget, gl_onScrollCallback); | |||||
| puglSetSpecialFunc(data->widget, gl_onSpecialCallback); | |||||
| puglSetReshapeFunc(data->widget, gl_onReshapeCallback); | |||||
| puglSetCloseFunc(data->widget, gl_onCloseCallback); | |||||
| #endif | |||||
| } | |||||
| void destroyWindow() | |||||
| { | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| if (qt_widget) | |||||
| { | |||||
| // remove main widget, to prevent it from being auto-deleted | |||||
| qt_widget->layout()->removeWidget(data->widget); | |||||
| data->widget->close(); | |||||
| data->widget->setParent(nullptr); | |||||
| qt_widget->close(); | |||||
| if (qt_grip) | |||||
| delete qt_grip; | |||||
| delete qt_widget; | |||||
| } | |||||
| #else | |||||
| ((OpenGLUI*)ui)->d_onClose(); | |||||
| if (data && data->widget) | |||||
| { | |||||
| puglDestroy(data->widget); | |||||
| data->widget = nullptr; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| // --------------------------------------------- | |||||
| #ifdef DISTRHO_UI_OPENGL | |||||
| void gl_onDisplay() | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| uiGL->d_onDisplay(); | |||||
| } | |||||
| void gl_onKeyboard(bool press, uint32_t key) | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| uiGL->d_onKeyboard(press, key); | |||||
| } | |||||
| void gl_onMotion(int x, int y) | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| uiGL->d_onMotion(x, y); | |||||
| } | |||||
| void gl_onMouse(int button, bool press, int x, int y) | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| uiGL->d_onMouse(button, press, x, y); | |||||
| } | |||||
| void gl_onReshape(int width, int height) | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| { | |||||
| if (! gl_initiated) | |||||
| { | |||||
| uiGL->d_onInit(); | |||||
| gl_initiated = true; | |||||
| } | |||||
| else | |||||
| uiGL->d_onReshape(width, height); | |||||
| } | |||||
| } | |||||
| void gl_onScroll(float dx, float dy) | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| uiGL->d_onScroll(dx, dy); | |||||
| } | |||||
| void gl_onSpecial(bool press, Key key) | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| uiGL->d_onSpecial(press, key); | |||||
| } | |||||
| void gl_onClose() | |||||
| { | |||||
| OpenGLUI* uiGL = (OpenGLUI*)ui; | |||||
| assert(uiGL); | |||||
| if (uiGL) | |||||
| uiGL->d_onClose(); | |||||
| } | |||||
| // --------------------------------------------- | |||||
| static void gl_onDisplayCallback(PuglView* view) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onDisplay(); | |||||
| } | |||||
| static void gl_onKeyboardCallback(PuglView* view, bool press, uint32_t key) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onKeyboard(press, key); | |||||
| } | |||||
| static void gl_onMotionCallback(PuglView* view, int x, int y) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onMotion(x, y); | |||||
| } | |||||
| static void gl_onMouseCallback(PuglView* view, int button, bool press, int x, int y) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onMouse(button, press, x, y); | |||||
| } | |||||
| static void gl_onReshapeCallback(PuglView* view, int width, int height) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onReshape(width, height); | |||||
| } | |||||
| static void gl_onScrollCallback(PuglView* view, float dx, float dy) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onScroll(dx, dy); | |||||
| } | |||||
| static void gl_onSpecialCallback(PuglView* view, bool press, PuglKey key) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onSpecial(press, (Key)key); | |||||
| } | |||||
| static void gl_onCloseCallback(PuglView* view) | |||||
| { | |||||
| UIInternal* _this_ = (UIInternal*)puglGetHandle(view); | |||||
| _this_->gl_onClose(); | |||||
| } | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| UI* const ui; | |||||
| UIPrivateData* data; | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| bool eventFilter(QObject* obj, QEvent* event) | |||||
| { | |||||
| if (obj == qt_grip) | |||||
| { | |||||
| if (event->type() == QEvent::MouseButtonPress) | |||||
| { | |||||
| QMouseEvent* mEvent = (QMouseEvent*)event; | |||||
| if (mEvent->button() == Qt::LeftButton) | |||||
| qt_mouseDown = true; | |||||
| return true; | |||||
| } | |||||
| if (event->type() == QEvent::MouseMove) | |||||
| { | |||||
| if (qt_mouseDown) | |||||
| { | |||||
| Qt4UI* qt_ui = (Qt4UI*)ui; | |||||
| QMouseEvent* mEvent = (QMouseEvent*)event; | |||||
| int width = ui->d_width() + mEvent->x() - qt_grip->width(); | |||||
| int height = ui->d_height() + mEvent->y() - qt_grip->height(); | |||||
| if (width < qt_ui->d_minimumWidth()) | |||||
| width = qt_ui->d_minimumWidth(); | |||||
| if (height < qt_ui->d_minimumHeight()) | |||||
| height = qt_ui->d_minimumHeight(); | |||||
| data->widget->setFixedSize(width, height); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| if (event->type() == QEvent::MouseButtonRelease) | |||||
| { | |||||
| QMouseEvent* mEvent = (QMouseEvent*)event; | |||||
| if (mEvent->button() == Qt::LeftButton) | |||||
| qt_mouseDown = false; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| else if (data && obj == data->widget) | |||||
| { | |||||
| if (event->type() == QEvent::Resize) | |||||
| { | |||||
| QResizeEvent* rEvent = (QResizeEvent*)event; | |||||
| const QSize& size = rEvent->size(); | |||||
| qt_widget->setFixedSize(size.width(), size.height()); | |||||
| qt_grip->move(size.width() - qt_grip->width(), size.height() - qt_grip->height()); | |||||
| ui->d_uiResize(size.width(), size.height()); | |||||
| } | |||||
| } | |||||
| return QObject::eventFilter(obj, event); | |||||
| } | |||||
| #endif | |||||
| private: | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| bool qt_mouseDown; | |||||
| QSizeGrip* qt_grip; | |||||
| QEmbedWidget* qt_widget; | |||||
| #else | |||||
| bool gl_initiated; | |||||
| #endif | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // __DISTRHO_UI_INTERNAL_H__ | |||||
| @@ -0,0 +1,372 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "DistrhoDefines.h" | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_LV2) && DISTRHO_PLUGIN_HAS_UI | |||||
| #include "DistrhoUIInternal.h" | |||||
| #include "lv2-sdk/lv2.h" | |||||
| //#include "lv2-sdk/atom.h" | |||||
| //#include "lv2-sdk/atom-util.h" | |||||
| //#include "lv2-sdk/midi.h" | |||||
| #include "lv2-sdk/patch.h" | |||||
| #include "lv2-sdk/programs.h" | |||||
| //#include "lv2-sdk/state.h" | |||||
| #include "lv2-sdk/urid.h" | |||||
| #include "lv2-sdk/ui.h" | |||||
| #include <cassert> | |||||
| #include <cstring> | |||||
| #include <thread> | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| # include <QtCore/QTimerEvent> | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_URI | |||||
| # error DISTRHO_PLUGIN_URI undefined! | |||||
| #endif | |||||
| #define DISTRHO_LV2UI_USE_EXTENSION_DATA (DISTRHO_PLUGIN_WANT_PROGRAMS) | |||||
| // ------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| class UILv2 : public QObject | |||||
| #else | |||||
| class UILv2 | |||||
| #endif | |||||
| { | |||||
| public: | |||||
| UILv2(intptr_t parent, LV2UI_Controller controller, LV2UI_Write_Function writeFunction, LV2UI_Widget* widget, const LV2_Feature* const* features) | |||||
| : ui(this, parent, setParameterCallback, setStateCallback, uiNoteCallback, uiResizeCallback), | |||||
| lv2Controller(controller), | |||||
| lv2WriteFunction(writeFunction), | |||||
| lv2UiResize(nullptr), | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| uridIdPatchMessage(0), | |||||
| #endif | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| uiTimer(0) | |||||
| #else | |||||
| threadExitNow(false), | |||||
| threadRunning(false), | |||||
| thread(uiThreadCallback, this) | |||||
| #endif | |||||
| { | |||||
| // Get Features | |||||
| for (uint32_t i = 0; features[i]; i++) | |||||
| { | |||||
| if (strcmp(features[i]->URI, LV2_URID__map) == 0) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| LV2_URID_Map* uridMap = (LV2_URID_Map*)features[i]->data; | |||||
| uridIdPatchMessage = uridMap->map(uridMap->handle, LV2_PATCH__Message); | |||||
| #endif | |||||
| } | |||||
| else if (strcmp(features[i]->URI, LV2_UI__resize) == 0) | |||||
| lv2UiResize = (LV2UI_Resize*)features[i]->data; | |||||
| } | |||||
| #ifndef DISTRHO_UI_QT4 | |||||
| assert(parent); | |||||
| if (parent == 0) | |||||
| return; | |||||
| #endif | |||||
| if (lv2UiResize) | |||||
| lv2UiResize->ui_resize(lv2UiResize->handle, ui.getWidth(), ui.getHeight()); | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| uiTimer = startTimer(30); | |||||
| #endif | |||||
| *widget = (void*)ui.getWindowId(); | |||||
| } | |||||
| ~UILv2() | |||||
| { | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| if (uiTimer) | |||||
| killTimer(uiTimer); | |||||
| #else | |||||
| uiThreadClose(); | |||||
| #endif | |||||
| } | |||||
| // --------------------------------------------- | |||||
| void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) | |||||
| { | |||||
| if (format == 0) | |||||
| { | |||||
| if (bufferSize != sizeof(float)) | |||||
| return; | |||||
| if (int32_t(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS) < 0) | |||||
| return; | |||||
| float value = *(float*)buffer; | |||||
| ui.parameterChanged(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS, value); | |||||
| } | |||||
| // TODO - atom events | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void lv2ui_select_program(uint32_t bank, uint32_t program) | |||||
| { | |||||
| const uint32_t index = bank * 128 + program; | |||||
| ui.programChanged(index); | |||||
| } | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| void setParameterValue(uint32_t index, float value) | |||||
| { | |||||
| if (lv2WriteFunction) | |||||
| lv2WriteFunction(lv2Controller, DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + index, sizeof(float), 0, &value); | |||||
| } | |||||
| void setState(const char* key, const char* value) | |||||
| { | |||||
| // TODO | |||||
| (void)key; | |||||
| (void)value; | |||||
| //if (lv2WriteFunction && uridMap) | |||||
| { | |||||
| } | |||||
| } | |||||
| void uiResize(unsigned int width, unsigned int height) | |||||
| { | |||||
| if (lv2UiResize) | |||||
| lv2UiResize->ui_resize(lv2UiResize->handle, width, height); | |||||
| } | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| void timerEvent(QTimerEvent* event) | |||||
| { | |||||
| if (event->timerId() == uiTimer) | |||||
| ui.idle(); | |||||
| QObject::timerEvent(event); | |||||
| } | |||||
| #else | |||||
| void uiThreadRun() | |||||
| { | |||||
| threadRunning = true; | |||||
| while (! threadExitNow) | |||||
| { | |||||
| ui.idle(); | |||||
| d_msleep(1000 / 25); // 25 FPS | |||||
| } | |||||
| thread.detach(); | |||||
| threadRunning = false; | |||||
| } | |||||
| void uiThreadClose() | |||||
| { | |||||
| threadExitNow = true; | |||||
| while (threadRunning) | |||||
| d_msleep(1000 / 25); // 25 FPS | |||||
| } | |||||
| #endif | |||||
| private: | |||||
| UIInternal ui; | |||||
| // LV2 Stuff | |||||
| LV2UI_Controller const lv2Controller; | |||||
| LV2UI_Write_Function const lv2WriteFunction; | |||||
| const LV2UI_Resize* lv2UiResize; | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| LV2_URID uridIdPatchMessage; | |||||
| #endif | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| int uiTimer; | |||||
| #else | |||||
| // UI Thread | |||||
| bool threadExitNow; | |||||
| bool threadRunning; | |||||
| std::thread thread; | |||||
| #endif | |||||
| // --------------------------------------------- | |||||
| // Callbacks | |||||
| static void setParameterCallback(void* ptr, uint32_t index, float value) | |||||
| { | |||||
| UILv2* _this_ = (UILv2*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setParameterValue(index, value); | |||||
| } | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| UILv2* _this_ = (UILv2*)ptr; | |||||
| assert(_this_); | |||||
| _this_->setState(key, value); | |||||
| #else | |||||
| // unused | |||||
| (void)ptr; | |||||
| (void)key; | |||||
| (void)value; | |||||
| #endif | |||||
| } | |||||
| static void uiNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| UILv2* _this_ = (UILv2*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiNote(onOff, channel, note, velocity); | |||||
| #else | |||||
| // unused | |||||
| (void)ptr; | |||||
| (void)onOff; | |||||
| (void)channel; | |||||
| (void)note; | |||||
| (void)velocity; | |||||
| #endif | |||||
| } | |||||
| static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) | |||||
| { | |||||
| UILv2* _this_ = (UILv2*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiResize(width, height); | |||||
| } | |||||
| #ifndef DISTRHO_UI_QT4 | |||||
| static void uiThreadCallback(void* ptr) | |||||
| { | |||||
| UILv2* _this_ = (UILv2*)ptr; | |||||
| assert(_this_); | |||||
| _this_->uiThreadRun(); | |||||
| } | |||||
| #endif | |||||
| }; | |||||
| // ------------------------------------------------- | |||||
| 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 (strcmp(uri, DISTRHO_PLUGIN_URI) != 0) | |||||
| return nullptr; | |||||
| // Get parent | |||||
| intptr_t parent = 0; | |||||
| for (uint32_t i = 0; features[i]; i++) | |||||
| { | |||||
| if (strcmp(features[i]->URI, LV2_UI__parent) == 0) | |||||
| { | |||||
| parent = (intptr_t)features[i]->data; | |||||
| break; | |||||
| } | |||||
| } | |||||
| return new UILv2(parent, controller, writeFunction, widget, features); | |||||
| } | |||||
| static void lv2ui_cleanup(LV2UI_Handle instance) | |||||
| { | |||||
| UILv2* ui = (UILv2*)instance; | |||||
| assert(ui); | |||||
| delete ui; | |||||
| } | |||||
| static void lv2ui_port_event(LV2UI_Handle instance, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) | |||||
| { | |||||
| UILv2* ui = (UILv2*)instance; | |||||
| assert(ui); | |||||
| ui->lv2ui_port_event(portIndex, bufferSize, format, buffer); | |||||
| } | |||||
| #if DISTRHO_LV2UI_USE_EXTENSION_DATA | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static void lv2ui_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) | |||||
| { | |||||
| UILv2* ui = (UILv2*)instance; | |||||
| assert(ui); | |||||
| ui->lv2ui_select_program(bank, program); | |||||
| } | |||||
| # endif | |||||
| static const void* lv2ui_extension_data(const char* uri) | |||||
| { | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static const LV2_Programs_UI_Interface programs = { lv2ui_select_program }; | |||||
| if (strcmp(uri, LV2_PROGRAMS__UIInterface) == 0) | |||||
| return &programs; | |||||
| # endif | |||||
| return nullptr; | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------- | |||||
| static LV2UI_Descriptor uidescriptor = { | |||||
| DISTRHO_UI_URI, | |||||
| lv2ui_instantiate, | |||||
| lv2ui_cleanup, | |||||
| lv2ui_port_event, | |||||
| #if DISTRHO_LV2UI_USE_EXTENSION_DATA | |||||
| lv2ui_extension_data | |||||
| #else | |||||
| /* extension_data */ nullptr | |||||
| #endif | |||||
| }; | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| return (index == 0) ? &uidescriptor : nullptr; | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_TARGET_LV2 && DISTRHO_PLUGIN_HAS_UI | |||||
| @@ -0,0 +1,80 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "DistrhoDefines.h" | |||||
| #ifdef DISTRHO_UI_OPENGL | |||||
| #include "DistrhoUIInternal.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| OpenGLUI::OpenGLUI() | |||||
| : UI() | |||||
| { | |||||
| } | |||||
| OpenGLUI::~OpenGLUI() | |||||
| { | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| // Host UI State | |||||
| int OpenGLUI::d_uiGetModifiers() | |||||
| { | |||||
| if (data && data->widget) | |||||
| return puglGetModifiers(data->widget); | |||||
| return 0; | |||||
| } | |||||
| void OpenGLUI::d_uiIgnoreKeyRepeat(bool ignore) | |||||
| { | |||||
| if (data && data->widget) | |||||
| puglIgnoreKeyRepeat(data->widget, ignore); | |||||
| } | |||||
| void OpenGLUI::d_uiRepaint() | |||||
| { | |||||
| if (data && data->widget) | |||||
| puglPostRedisplay(data->widget); | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| // UI Callbacks | |||||
| void OpenGLUI::d_uiIdle() | |||||
| { | |||||
| if (data && data->widget) | |||||
| puglProcessEvents(data->widget); | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #if DISTRHO_OS_WINDOWS | |||||
| # include "pugl/pugl_win.cpp" | |||||
| #elif DISTRHO_OS_MAC | |||||
| # include "pugl/pugl_osx.m" | |||||
| #else | |||||
| # include "pugl/pugl_x11.c" | |||||
| #endif | |||||
| #endif // DISTRHO_UI_OPENGL | |||||
| @@ -0,0 +1,75 @@ | |||||
| /* | |||||
| * DISTHRO Plugin Toolkit (DPT) | |||||
| * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||||
| * | |||||
| * 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 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. | |||||
| * | |||||
| * For a full copy of the license see the GPL.txt file | |||||
| */ | |||||
| #include "DistrhoDefines.h" | |||||
| #ifdef DISTRHO_UI_QT4 | |||||
| #include "DistrhoUIInternal.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ------------------------------------------------- | |||||
| // QEmbedWidget | |||||
| QEmbedWidget::QEmbedWidget() | |||||
| { | |||||
| } | |||||
| QEmbedWidget::~QEmbedWidget() | |||||
| { | |||||
| } | |||||
| void QEmbedWidget::embedInto(WId id) | |||||
| { | |||||
| #ifdef Q_WS_X11 | |||||
| QX11EmbedWidget::embedInto(id); | |||||
| #endif | |||||
| } | |||||
| WId QEmbedWidget::containerWinId() const | |||||
| { | |||||
| #ifdef Q_WS_X11 | |||||
| return QX11EmbedWidget::containerWinId(); | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| // Qt4UI | |||||
| Qt4UI::Qt4UI() | |||||
| : UI(), | |||||
| QWidget(nullptr) | |||||
| { | |||||
| } | |||||
| Qt4UI::~Qt4UI() | |||||
| { | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| // UI Callbacks | |||||
| void Qt4UI::d_uiIdle() | |||||
| { | |||||
| } | |||||
| // ------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_UI_QT4 | |||||
| @@ -0,0 +1 @@ | |||||
| ../../../carla-includes/dssi | |||||
| @@ -0,0 +1 @@ | |||||
| ../../../carla-includes/ladspa | |||||
| @@ -0,0 +1 @@ | |||||
| ../../../carla-includes/lv2 | |||||
| @@ -0,0 +1,346 @@ | |||||
| /* | |||||
| 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. | |||||
| */ | |||||
| PUGL_API PuglView* | |||||
| puglCreate(PuglNativeWindow parent, | |||||
| const char* title, | |||||
| int width, | |||||
| int height, | |||||
| bool resizable, | |||||
| bool addToDesktop = true); | |||||
| /** | |||||
| 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); | |||||
| /** | |||||
| 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 */ | |||||
| @@ -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. | |||||
| */ | |||||
| /** | |||||
| @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 ignoreKeyRepeat; | |||||
| bool redisplay; | |||||
| }; | |||||
| void | |||||
| puglSetHandle(PuglView* view, PuglHandle handle) | |||||
| { | |||||
| view->handle = handle; | |||||
| } | |||||
| PuglHandle | |||||
| puglGetHandle(PuglView* view) | |||||
| { | |||||
| return view->handle; | |||||
| } | |||||
| int | |||||
| puglGetModifiers(PuglView* view) | |||||
| { | |||||
| return view->mods; | |||||
| } | |||||
| static inline 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(); | |||||
| (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; | |||||
| } | |||||
| @@ -0,0 +1,325 @@ | |||||
| /* | |||||
| 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 PuglOpenGLView : NSOpenGLView | |||||
| { | |||||
| int colorBits; | |||||
| int depthBits; | |||||
| @public | |||||
| PuglView* view; | |||||
| } | |||||
| - (id) initWithFrame:(NSRect)frame | |||||
| colorBits:(int)numColorBits | |||||
| depthBits:(int)numDepthBits; | |||||
| - (void) reshape; | |||||
| - (void) drawRect:(NSRect)rect; | |||||
| - (void) mouseMoved:(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 (view->reshapeFunc) { | |||||
| view->reshapeFunc(view, width, height); | |||||
| } else { | |||||
| puglDefaultReshape(view, width, height); | |||||
| } | |||||
| view->width = width; | |||||
| view->height = height; | |||||
| } | |||||
| - (void) drawRect:(NSRect)rect | |||||
| { | |||||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
| glLoadIdentity(); | |||||
| if (self->view->displayFunc) { | |||||
| self->view->displayFunc(self->view); | |||||
| } | |||||
| glFlush(); | |||||
| glSwapAPPLE(); | |||||
| } | |||||
| static int | |||||
| getModifiers(unsigned modifierFlags) | |||||
| { | |||||
| int 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) mouseMoved:(NSEvent*)event | |||||
| { | |||||
| if (view->motionFunc) { | |||||
| NSPoint loc = [event locationInWindow]; | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->motionFunc(view, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) mouseDown:(NSEvent*)event | |||||
| { | |||||
| if (view->mouseFunc) { | |||||
| NSPoint loc = [event locationInWindow]; | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->mouseFunc(view, 1, true, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) mouseUp:(NSEvent*)event | |||||
| { | |||||
| if (view->mouseFunc) { | |||||
| NSPoint loc = [event locationInWindow]; | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->mouseFunc(view, 1, false, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) rightMouseDown:(NSEvent*)event | |||||
| { | |||||
| if (view->mouseFunc) { | |||||
| NSPoint loc = [event locationInWindow]; | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->mouseFunc(view, 3, true, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) rightMouseUp:(NSEvent*)event | |||||
| { | |||||
| if (view->mouseFunc) { | |||||
| NSPoint loc = [event locationInWindow]; | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->mouseFunc(view, 3, false, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) scrollWheel:(NSEvent*)event | |||||
| { | |||||
| if (view->scrollFunc) { | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->scrollFunc(view, [event deltaX], [event deltaY]); | |||||
| } | |||||
| } | |||||
| - (void) keyDown:(NSEvent*)event | |||||
| { | |||||
| if (view->keyboardFunc && !(view->ignoreKeyRepeat && [event isARepeat])) { | |||||
| NSString* chars = [event characters]; | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->keyboardFunc(view, true, [chars characterAtIndex:0]); | |||||
| } | |||||
| } | |||||
| - (void) keyUp:(NSEvent*)event | |||||
| { | |||||
| if (view->keyboardFunc) { | |||||
| NSString* chars = [event characters]; | |||||
| view->mods = getModifiers([event modifierFlags]); | |||||
| view->keyboardFunc(view, false, [chars characterAtIndex:0]); | |||||
| } | |||||
| } | |||||
| - (void) flagsChanged:(NSEvent*)event | |||||
| { | |||||
| if (view->specialFunc) { | |||||
| int mods = getModifiers([event modifierFlags]); | |||||
| if ((mods & PUGL_MOD_SHIFT) != (view->mods & PUGL_MOD_SHIFT)) { | |||||
| view->specialFunc(view, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); | |||||
| } else if ((mods & PUGL_MOD_CTRL) != (view->mods & PUGL_MOD_CTRL)) { | |||||
| view->specialFunc(view, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); | |||||
| } else if ((mods & PUGL_MOD_ALT) != (view->mods & PUGL_MOD_ALT)) { | |||||
| view->specialFunc(view, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); | |||||
| } else if ((mods & PUGL_MOD_SUPER) != (view->mods & PUGL_MOD_SUPER)) { | |||||
| view->specialFunc(view, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); | |||||
| } | |||||
| view->mods = mods; | |||||
| } | |||||
| } | |||||
| @end | |||||
| struct PuglInternalsImpl { | |||||
| PuglOpenGLView* view; | |||||
| NSModalSession session; | |||||
| id window; | |||||
| }; | |||||
| PuglView* | |||||
| puglCreate(PuglNativeWindow parent, | |||||
| const char* title, | |||||
| int width, | |||||
| int height, | |||||
| bool resizable) | |||||
| { | |||||
| 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]; | |||||
| [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; | |||||
| NSString* titleString = [[NSString alloc] | |||||
| initWithBytes:title | |||||
| length:strlen(title) | |||||
| encoding:NSUTF8StringEncoding]; | |||||
| id window = [[[NSWindow alloc] | |||||
| initWithContentRect:NSMakeRect(0, 0, 512, 512) | |||||
| styleMask:NSTitledWindowMask | |||||
| backing:NSBackingStoreBuffered | |||||
| defer:NO] | |||||
| autorelease]; | |||||
| [window cascadeTopLeftFromPoint:NSMakePoint(20, 20)]; | |||||
| [window setTitle:titleString]; | |||||
| [window setAcceptsMouseMovedEvents:YES]; | |||||
| impl->view = [PuglOpenGLView new]; | |||||
| impl->window = window; | |||||
| impl->view->view = view; | |||||
| [window setContentView:impl->view]; | |||||
| [NSApp activateIgnoringOtherApps:YES]; | |||||
| [window makeFirstResponder:impl->view]; | |||||
| impl->session = [NSApp beginModalSessionForWindow:view->impl->window]; | |||||
| return view; | |||||
| } | |||||
| void | |||||
| puglDestroy(PuglView* view) | |||||
| { | |||||
| [NSApp endModalSession:view->impl->session]; | |||||
| [view->impl->view release]; | |||||
| free(view->impl); | |||||
| free(view); | |||||
| } | |||||
| void | |||||
| puglDisplay(PuglView* view) | |||||
| { | |||||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
| glLoadIdentity(); | |||||
| if (view->displayFunc) { | |||||
| view->displayFunc(view); | |||||
| } | |||||
| glFlush(); | |||||
| view->redisplay = false; | |||||
| } | |||||
| PuglStatus | |||||
| puglProcessEvents(PuglView* view) | |||||
| { | |||||
| NSInteger response = [NSApp runModalSession:view->impl->session]; | |||||
| if (response != NSRunContinuesResponse) { | |||||
| if (view->closeFunc) { | |||||
| view->closeFunc(view); | |||||
| } | |||||
| } | |||||
| if (view->redisplay) { | |||||
| puglDisplay(view); | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| void | |||||
| puglPostRedisplay(PuglView* view) | |||||
| { | |||||
| view->redisplay = true; | |||||
| } | |||||
| PuglNativeWindow | |||||
| puglGetNativeWindow(PuglView* view) | |||||
| { | |||||
| return (PuglNativeWindow)view->impl->view; | |||||
| } | |||||
| @@ -0,0 +1,339 @@ | |||||
| /* | |||||
| 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 "pugl_internal.h" | |||||
| #ifndef WM_MOUSEWHEEL | |||||
| # define WM_MOUSEWHEEL 0x020A | |||||
| #endif | |||||
| #ifndef WM_MOUSEHWHEEL | |||||
| # define WM_MOUSEHWHEEL 0x020E | |||||
| #endif | |||||
| struct PuglInternalsImpl { | |||||
| HWND hwnd; | |||||
| HDC hdc; | |||||
| HGLRC hglrc; | |||||
| }; | |||||
| LRESULT CALLBACK | |||||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||||
| PuglView* | |||||
| puglCreate(PuglNativeWindow parent, | |||||
| const char* title, | |||||
| int width, | |||||
| int height, | |||||
| bool resizable) | |||||
| { | |||||
| 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; | |||||
| WNDCLASS wc; | |||||
| wc.style = CS_OWNDC; | |||||
| wc.lpfnWndProc = wndProc; | |||||
| wc.cbClsExtra = 0; | |||||
| wc.cbWndExtra = 0; | |||||
| wc.hInstance = 0; | |||||
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |||||
| wc.hCursor = LoadCursor(NULL, IDC_ARROW); | |||||
| wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||||
| wc.lpszMenuName = NULL; | |||||
| wc.lpszClassName = "Pugl"; | |||||
| RegisterClass(&wc); | |||||
| impl->hwnd = CreateWindow( | |||||
| "Pugl", title, | |||||
| WS_VISIBLE | (parent ? WS_CHILD : (WS_POPUPWINDOW | WS_CAPTION)), | |||||
| 0, 0, width, height, | |||||
| (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); | |||||
| free(view->impl); | |||||
| free(view); | |||||
| } | |||||
| 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) | |||||
| { | |||||
| 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) | |||||
| { | |||||
| MSG msg; | |||||
| PAINTSTRUCT ps; | |||||
| PuglKey key; | |||||
| setModifiers(view); | |||||
| switch (message) { | |||||
| case WM_CREATE: | |||||
| case WM_SHOWWINDOW: | |||||
| case WM_SIZE: | |||||
| puglReshape(view, view->width, view->height); | |||||
| 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: | |||||
| 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: | |||||
| 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) { | |||||
| puglDisplay(view); | |||||
| } | |||||
| 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: | |||||
| PostQuitMessage(0); | |||||
| return 0; | |||||
| case WM_DESTROY: | |||||
| return 0; | |||||
| case WM_MOUSEWHEEL: | |||||
| case WM_MOUSEHWHEEL: | |||||
| PostMessage(hwnd, message, wParam, lParam); | |||||
| 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; | |||||
| } | |||||
| @@ -0,0 +1,384 @@ | |||||
| /* | |||||
| Copyright 2012 David Robillard <http://drobilla.net> | |||||
| Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||||
| 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" | |||||
| 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 addToDesktop) | |||||
| { | |||||
| 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; | |||||
| printf("singlebuffered rendering will be used, no doublebuffering available\n"); | |||||
| } else { | |||||
| impl->doubleBuffered = True; | |||||
| printf("doublebuffered rendering available\n"); | |||||
| } | |||||
| int glxMajor, glxMinor; | |||||
| glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||||
| printf("GLX-Version %d.%d\n", glxMajor, glxMinor); | |||||
| 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 | |||||
| | 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 (addToDesktop) | |||||
| XMapRaised(impl->display, impl->win); | |||||
| if (glXIsDirect(impl->display, impl->ctx)) { | |||||
| printf("DRI enabled\n"); | |||||
| } else { | |||||
| printf("no DRI available\n"); | |||||
| } | |||||
| 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, int xstate) | |||||
| { | |||||
| 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); | |||||
| view->redisplay = false; | |||||
| break; | |||||
| case MotionNotify: | |||||
| setModifiers(view, event.xmotion.state); | |||||
| if (view->motionFunc) { | |||||
| view->motionFunc(view, event.xmotion.x, event.xmotion.y); | |||||
| } | |||||
| break; | |||||
| case ButtonPress: | |||||
| setModifiers(view, event.xbutton.state); | |||||
| 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); | |||||
| 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); | |||||
| 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); | |||||
| 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 = XKeycodeToKeysym( | |||||
| view->impl->display, event.xkey.keycode, 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; | |||||
| 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; | |||||
| } | |||||