Browse Source

Import DISTRHO-Plugin-Toolkit code, will be used for Carla plugin

tags/v0.9.0
falkTX 13 years ago
parent
commit
d144aae730
33 changed files with 9487 additions and 0 deletions
  1. +35
    -0
      c++/carla-backend/DistrhoPluginInfo.h
  2. +188
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoPlugin.h
  3. +28
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoPluginMain.cpp
  4. +92
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoUI.h
  5. +35
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoUIMain.cpp
  6. +131
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGL.h
  7. +306
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGLExt.h
  8. +74
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoUIQt4.h
  9. +256
    -0
      c++/carla-backend/distrho-plugin-toolkit/DistrhoUtils.h
  10. +289
    -0
      c++/carla-backend/distrho-plugin-toolkit/distrho.doxygen
  11. +91
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoDefines.h
  12. +95
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPlugin.cpp
  13. +337
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginInternal.h
  14. +366
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginJACK.cpp
  15. +690
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLADSPA+DSSI.cpp
  16. +597
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2.cpp
  17. +347
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2export.cpp
  18. +724
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginVST.cpp
  19. +104
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUI.cpp
  20. +612
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIDSSI.cpp
  21. +548
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIInternal.h
  22. +372
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUILV2.cpp
  23. +80
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGL.cpp
  24. +1486
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGLExt.cpp
  25. +75
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIQt4.cpp
  26. +1
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/dssi
  27. +1
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/ladspa
  28. +1
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/lv2-sdk
  29. +346
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl.h
  30. +132
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_internal.h
  31. +325
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_osx.m
  32. +339
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_win.cpp
  33. +384
    -0
      c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_x11.c

+ 35
- 0
c++/carla-backend/DistrhoPluginInfo.h View File

@@ -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__

+ 188
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoPlugin.h View File

@@ -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__

+ 28
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoPluginMain.cpp View File

@@ -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

+ 92
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoUI.h View File

@@ -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__

+ 35
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoUIMain.cpp View File

@@ -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

+ 131
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGL.h View File

@@ -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__

+ 306
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGLExt.h View File

@@ -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__

+ 74
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoUIQt4.h View File

@@ -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__

+ 256
- 0
c++/carla-backend/distrho-plugin-toolkit/DistrhoUtils.h View File

@@ -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__

+ 289
- 0
c++/carla-backend/distrho-plugin-toolkit/distrho.doxygen View File

@@ -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

+ 91
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoDefines.h View File

@@ -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__

+ 95
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPlugin.cpp View File

@@ -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

+ 337
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginInternal.h View File

@@ -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__

+ 366
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginJACK.cpp View File

@@ -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

+ 690
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLADSPA+DSSI.cpp View File

@@ -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

+ 597
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2.cpp View File

@@ -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

+ 347
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2export.cpp View File

@@ -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

+ 724
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginVST.cpp View File

@@ -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 = &rect;
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

+ 104
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUI.cpp View File

@@ -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

+ 612
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIDSSI.cpp View File

@@ -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

+ 548
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIInternal.h View File

@@ -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__

+ 372
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUILV2.cpp View File

@@ -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

+ 80
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGL.cpp View File

@@ -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

+ 1486
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGLExt.cpp
File diff suppressed because it is too large
View File


+ 75
- 0
c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIQt4.cpp View File

@@ -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

+ 1
- 0
c++/carla-backend/distrho-plugin-toolkit/src/dssi View File

@@ -0,0 +1 @@
../../../carla-includes/dssi

+ 1
- 0
c++/carla-backend/distrho-plugin-toolkit/src/ladspa View File

@@ -0,0 +1 @@
../../../carla-includes/ladspa

+ 1
- 0
c++/carla-backend/distrho-plugin-toolkit/src/lv2-sdk View File

@@ -0,0 +1 @@
../../../carla-includes/lv2

+ 346
- 0
c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl.h View File

@@ -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 */

+ 132
- 0
c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_internal.h View File

@@ -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;
}

+ 325
- 0
c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_osx.m View File

@@ -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;
}

+ 339
- 0
c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_win.cpp View File

@@ -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;
}

+ 384
- 0
c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_x11.c View File

@@ -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;
}

Loading…
Cancel
Save