aboutsummaryrefslogtreecommitdiffstats
path: root/lvz
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2008-08-12 00:20:16 +0000
committerDavid Robillard <d@drobilla.net>2008-08-12 00:20:16 +0000
commit102e899c331bd2ed9902467a077164e209c918f9 (patch)
treeb7fe5ec873582cc8a0fc0862f9da045d12b2259a /lvz
parent2b679f152e1c3104ac178b6c78ac0b1edf954ff6 (diff)
downloadmda.lv2-102e899c331bd2ed9902467a077164e209c918f9.tar.gz
mda.lv2-102e899c331bd2ed9902467a077164e209c918f9.tar.bz2
mda.lv2-102e899c331bd2ed9902467a077164e209c918f9.zip
VSTUI X11 port and embeddable GTK wrapper.
Build mdaSpecMeter and GUI. git-svn-id: http://svn.drobilla.net/lad/mda-lv2@1340 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'lvz')
-rw-r--r--lvz/AEffEditor.hpp30
l---------lvz/AudioEffect.hpp1
-rw-r--r--lvz/audioeffectx.h40
-rw-r--r--lvz/gendata.cpp100
-rw-r--r--lvz/gui_wrapper.cpp211
-rw-r--r--lvz/wrapper.cpp47
6 files changed, 389 insertions, 40 deletions
diff --git a/lvz/AEffEditor.hpp b/lvz/AEffEditor.hpp
new file mode 100644
index 0000000..2d5152c
--- /dev/null
+++ b/lvz/AEffEditor.hpp
@@ -0,0 +1,30 @@
+#ifndef __LVZ_AUDIOEFFECT_HPP
+#define __LVZ_AUDIOEFFECT_HPP
+
+class AudioEffect;
+
+class AEffEditor {
+public:
+ AEffEditor (AudioEffect* eff)
+ : effect(eff)
+ , URI("NULL")
+ , pluginURI("NULL")
+ {}
+
+ virtual bool open(void* ptr) { return true; }
+
+ virtual void idle() {}
+
+ virtual const char* getURI() { return URI; }
+ virtual void setURI(const char* u) { URI = u; }
+
+ virtual const char* getPluginURI() { return pluginURI; }
+ virtual void setPluginURI(const char* u) { pluginURI = u; }
+
+protected:
+ AudioEffect* effect;
+ const char* URI;
+ const char* pluginURI;
+};
+
+#endif // __LVZ_AUDIOEFFECT_HPP
diff --git a/lvz/AudioEffect.hpp b/lvz/AudioEffect.hpp
new file mode 120000
index 0000000..df90333
--- /dev/null
+++ b/lvz/AudioEffect.hpp
@@ -0,0 +1 @@
+audioeffectx.h \ No newline at end of file
diff --git a/lvz/audioeffectx.h b/lvz/audioeffectx.h
index 288d1cd..a1f975a 100644
--- a/lvz/audioeffectx.h
+++ b/lvz/audioeffectx.h
@@ -22,10 +22,29 @@
#include <stdint.h>
#include <string.h>
+// Some plugins seem to use these names...
+#ifndef VstInt32
+# define VstInt32 LvzInt32
+# define VstInt16 LvzInt16
+#endif
+#define VstEvents LvzEvents
+#define VstMidiEvent LvzMidiEvent
+#define VstPinProperty LvzPinProperty
+
typedef int16_t LvzInt16;
typedef int32_t LvzInt32;
typedef int (*audioMasterCallback)(int, int ver, int, int, int, int);
+class AEffEditor;
+
+struct VstFileSelect {
+ int reserved;
+ char* returnPath;
+ size_t sizeReturnPath;
+ char** returnMultiplePaths;
+ long nbReturnPath;
+};
+
enum LvzPinFlags {
kLvzPinIsActive = 1<<0,
kLvzPinIsStereo = 1<<1
@@ -59,9 +78,16 @@ struct LvzEvents {
class AudioEffect {
public:
+ AudioEffect() : editor(NULL) {}
virtual ~AudioEffect() {}
+
+ void setEditor(AEffEditor* e) { editor = e; }
+ virtual void masterIdle() {}
+protected:
+ AEffEditor* editor;
};
+
class AudioEffectX : public AudioEffect {
public:
AudioEffectX(audioMasterCallback audioMaster, LvzInt32 progs, LvzInt32 params)
@@ -91,10 +117,11 @@ public:
virtual void getParameterName(LvzInt32 index, char *label) = 0;
virtual bool getProductString(char* text) = 0;
- virtual void canMono() {}
- virtual void canProcessReplacing() {}
- virtual void isSynth() {}
- virtual void wantEvents() {}
+ virtual bool canHostDo(const char* act) { return false; }
+ virtual void canMono() {}
+ virtual void canProcessReplacing() {}
+ virtual void isSynth() {}
+ virtual void wantEvents() {}
virtual void setBlockSize(LvzInt32 size) {}
virtual void setNumInputs(LvzInt32 num) { numInputs = num; }
@@ -104,6 +131,11 @@ public:
virtual void setURI(const char* uri) { URI = uri; }
virtual void setUniqueID(const char* id) { uniqueID = id; }
virtual void suspend() {}
+ virtual void beginEdit(VstInt32 index) {}
+ virtual void endEdit(VstInt32 index) {}
+
+ virtual bool openFileSelector (VstFileSelect* sel) { return false; }
+ virtual bool closeFileSelector (VstFileSelect* sel) { return false; }
protected:
const char* URI;
diff --git a/lvz/gendata.cpp b/lvz/gendata.cpp
index 7f1d501..bf9c9ab 100644
--- a/lvz/gendata.cpp
+++ b/lvz/gendata.cpp
@@ -17,12 +17,14 @@
*/
#include <list>
+#include <map>
#include <string>
#include <cassert>
#include <iostream>
#include <fstream>
#include <dlfcn.h>
#include "audioeffectx.h"
+#include "AEffEditor.hpp"
using namespace std;
@@ -34,13 +36,16 @@ char name_buf[MAX_NAME_LENGTH];
struct Record {
- Record(const string& u, const string& n) : uri(u), base_name(n) {}
- string uri;
+ Record(const string& n) : base_name(n) {}
string base_name;
+ typedef list<string> UIs;
+ UIs uis;
};
-typedef std::list<Record> Manifest;
+typedef std::map<string, Record> Manifest;
Manifest manifest;
+typedef std::map<string, Record> GUIManifest;
+GUIManifest gui_manifest;
string
@@ -85,8 +90,8 @@ symbolify(const char* name)
void
write_plugin(AudioEffectX* effect, const string& lib_file_name)
{
- string base_name = lib_file_name.substr(0, lib_file_name.find_last_of("."));
- string data_file_name = base_name + ".ttl";
+ const string base_name = lib_file_name.substr(0, lib_file_name.find_last_of("."));
+ const string data_file_name = base_name + ".ttl";
fstream os(data_file_name.c_str(), ios::out);
effect->getProductString(name_buf);
@@ -141,7 +146,33 @@ write_plugin(AudioEffectX* effect, const string& lib_file_name)
os.close();
- manifest.push_back(Record(effect->getURI(), base_name));
+ Manifest::iterator i = manifest.find(effect->getURI());
+ if (i != manifest.end()) {
+ i->second.base_name = base_name;
+ } else {
+ manifest.insert(std::make_pair(effect->getURI(), Record(base_name)));
+ }
+}
+
+
+void
+write_gui(AEffEditor* gui, const string& lib_file_name)
+{
+ const string base_name = lib_file_name.substr(0, lib_file_name.find_last_of("."));
+ assert(gui_manifest.find(gui->getURI()) == gui_manifest.end());
+ gui_manifest.insert(std::make_pair(gui->getURI(), Record(base_name)));
+ Manifest::iterator plugin_record = manifest.find(lib_file_name);
+ if (plugin_record != manifest.end()) {
+ plugin_record->second.uis.push_back(gui->getPluginURI());
+ }
+ Manifest::iterator i = manifest.find(gui->getPluginURI());
+ if (i != manifest.end()) {
+ i->second.uis.push_back(gui->getURI());
+ } else {
+ Record r("ERRNOBASE");
+ r.uis.push_back(gui->getURI());
+ manifest.insert(std::make_pair(gui->getPluginURI(), r));
+ }
}
@@ -149,11 +180,23 @@ void
write_manifest(ostream& os)
{
os << "@prefix : <http://lv2plug.in/ns/lv2core#> ." << endl;
- os << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> ." << endl << endl;
+ os << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> ." << endl;
+ os << "@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> ." << endl << endl;
for (Manifest::iterator i = manifest.begin(); i != manifest.end(); ++i) {
- os << "<" << i->uri << "> a :Plugin ;" << endl;
- os << "\trdfs:seeAlso <" << i->base_name << ".ttl> ;" << endl;
- os << "\t:binary <" << i->base_name << ".so> ." << endl << endl;
+ Record& r = i->second;
+ os << "<" << i->first << "> a :Plugin ;" << endl;
+ os << "\trdfs:seeAlso <" << r.base_name << ".ttl> ;" << endl;
+ os << "\t:binary <" << r.base_name << ".so> ";
+ for (Record::UIs::iterator j = r.uis.begin(); j != r.uis.end(); ++j)
+ os << ";" << endl << "\tuiext:ui <" << *j << "> ";
+ os << "." << endl << endl;
+ }
+
+ for (GUIManifest::iterator i = gui_manifest.begin(); i != gui_manifest.end(); ++i) {
+ Record& r = i->second;
+ os << "<" << i->first << "> a uiext:GtkUI ;" << endl;
+ os << "\trdfs:seeAlso <" << r.base_name << ".ttl> ;" << endl;
+ os << "\tuiext:binary <" << r.base_name << ".so> ." << endl << endl;
}
}
@@ -171,33 +214,42 @@ main(int argc, char** argv)
}
typedef AudioEffectX* (*new_effect_func)();
+ typedef AEffEditor* (*new_gui_func)();
typedef AudioEffectX* (*plugin_uri_func)();
- new_effect_func constructor = NULL;
- AudioEffectX* effect = NULL;
+ new_effect_func constructor = NULL;
+ new_gui_func gui_constructor = NULL;
+ AudioEffectX* effect = NULL;
+ AEffEditor* gui = NULL;
for (int i = 1; i < argc; ++i) {
- void* handle = dlopen(argv[i], RTLD_NOW);
+ void* handle = dlopen(argv[i], RTLD_LAZY);
if (handle == NULL) {
- cerr << "ERROR: " << argv[i] << " is not a shared library, ignoring" << endl;
+ cerr << "ERROR: " << argv[i] << ": " << dlerror() << " (ignoring)" << endl;
continue;
}
- constructor = (new_effect_func)dlsym(handle, "lvz_new_audioeffectx");
- if (constructor == NULL) {
- dlclose(handle);
- cerr << "ERROR: " << argv[i] << " is not an LVZ plugin library, ignoring" << endl;
- continue;
- }
-
- effect = constructor();
string lib_path = argv[i];
size_t last_slash = lib_path.find_last_of("/");
if (last_slash != string::npos)
lib_path = lib_path.substr(last_slash + 1);
- write_plugin(effect, lib_path);
-
+ constructor = (new_effect_func)dlsym(handle, "lvz_new_audioeffectx");
+ if (constructor != NULL) {
+ effect = constructor();
+ write_plugin(effect, lib_path);
+ }
+
+ gui_constructor = (new_gui_func)dlsym(handle, "lvz_new_aeffeditor");
+ if (gui_constructor != NULL) {
+ gui = gui_constructor();
+ write_gui(gui, lib_path);
+ }
+
+ if (constructor == NULL && gui_constructor == NULL) {
+ cerr << "ERROR: " << argv[i] << ": not an LVZ plugin library, ignoring" << endl;
+ }
+
dlclose(handle);
}
diff --git a/lvz/gui_wrapper.cpp b/lvz/gui_wrapper.cpp
new file mode 100644
index 0000000..ca2b894
--- /dev/null
+++ b/lvz/gui_wrapper.cpp
@@ -0,0 +1,211 @@
+/* LVZ - A C++ interface for writing LV2 plugins.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef UI_CLASS
+#error "This file requires UI_CLASS to be defined"
+#endif
+#ifndef URI_PREFIX
+#error "This file requires URI_PREFIX to be defined"
+#endif
+#ifndef UI_URI_SUFFIX
+#error "This file requires UI_URI_SUFFIX to be defined"
+#endif
+
+#include <stdlib.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <cassert>
+#include "AEffEditor.hpp"
+#include "lv2_ui.h"
+#include UI_HEADER
+#include PLUGIN_HEADER
+
+extern "C" {
+
+/* UI */
+
+typedef struct {
+ bool stolen;
+ UI_CLASS* ui;
+ GtkSocket* socket;
+ Window x_window;
+ AudioEffectX* effect;
+} MDAPluginUI;
+
+
+static gboolean
+mda_ui_idle(void* data)
+{
+ MDAPluginUI* ui = (MDAPluginUI*)data;
+ if (!ui->stolen) {
+ //gtk_socket_add_id(GTK_SOCKET(ui->socket), ui->x_window);
+ ui->x_window = (Window)gtk_socket_get_id(GTK_SOCKET(ui->socket));
+ bool success = ui->ui->open((void*)ui->x_window);
+ if (!success)
+ fprintf(stderr, "FAILED TO OPEN GUI\n");
+ ui->ui->getFrame()->draw();
+ ui->stolen = true;
+ }
+
+ ui->ui->idle();
+ return true;
+}
+
+
+static LV2UI_Handle
+mda_ui_instantiate(const struct _LV2UI_Descriptor* descriptor,
+ const char* plugin_uri,
+ const char* bundle_path,
+ LV2UI_Write_Function write_function,
+ LV2UI_Controller controller,
+ LV2UI_Widget* widget,
+ const LV2_Feature* const* features)
+{
+ /* Some extensions need to be defined for this to work. Hosts must:
+ * 1) Pass a pointer to the plugin (VST is crap and has no UI separation)
+ * 2) Call idle on the UI periodically (VST is crap and uses polling)
+ *
+ * Thoughts:
+ *
+ * 1) This wrapper could be written explicitly in GTK and just get at
+ * the idle handler that way. Less burden on the host...
+ *
+ * 2) A better idea is to have a 'get plugin extension data' feature
+ * the UI (or anything else) can use to ask for any URI-identified
+ * piece of extension data (failing in this case if plugin is remote)
+ */
+
+ MDAPluginUI* ui = (MDAPluginUI*)malloc(sizeof(MDAPluginUI));
+ ui->effect = NULL;
+
+ typedef struct { const void* (*extension_data)(const char* uri); } LV2_ExtensionData;
+
+ typedef const void* (*extension_data_func)(const char* uri);
+ typedef const AudioEffectX* (*get_effect_func)(LV2_Handle instance);
+
+ LV2_Handle instance = NULL;
+ get_effect_func get_effect = NULL;
+
+
+ if (features != NULL) {
+ const LV2_Feature* feature = features[0];
+ for (size_t i = 0; (feature = features[i]) != NULL; ++i) {
+ if (!strcmp(feature->URI, "http://lv2plug.in/ns/ext/dev/plugin-instance")) {
+ instance = (LV2_Handle)feature->data;
+ } else if (!strcmp(feature->URI, "http://lv2plug.in/ns/ext/dev/extension-data")) {
+ LV2_ExtensionData* ext_data = (LV2_ExtensionData*)feature->data;
+ extension_data_func func = (extension_data_func)feature->data;
+ get_effect = (get_effect_func)ext_data->extension_data(
+ "http://lv2plug.in/ns/ext/dev/vstgui");
+ }
+ }
+ }
+
+ if (instance && get_effect) {
+ ui->effect = (AudioEffectX*)get_effect(instance);
+ } else {
+ fprintf(stderr, "Host does not support required features, aborting.\n");
+ return NULL;
+ }
+
+ ui->ui = new UI_CLASS(ui->effect);
+ ui->ui->setBundlePath(bundle_path);
+ ui->stolen = false;
+
+ ui->socket = GTK_SOCKET(gtk_socket_new());
+ gtk_widget_show_all(GTK_WIDGET(ui->socket));
+
+ *widget = ui->socket;
+ g_timeout_add(30, mda_ui_idle, ui);
+
+ return ui;
+}
+
+
+static void
+mda_ui_cleanup(LV2UI_Handle instance)
+{
+ g_idle_remove_by_data(instance);
+}
+
+
+static void
+mda_ui_port_event(LV2UI_Handle ui,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t format,
+ const void* buffer)
+{
+ // VST UIs seem to not use this at all, it's all polling
+ // The shit the proprietary people come up (and get away) with...
+}
+
+
+static const void*
+mda_ui_extension_data(const char* uri)
+{
+ return NULL;
+}
+
+
+/* Library */
+
+static LV2UI_Descriptor *mda_ui_descriptor = NULL;
+
+static void
+init_ui_descriptor()
+{
+ mda_ui_descriptor = (LV2UI_Descriptor*)malloc(sizeof(LV2UI_Descriptor));
+
+ mda_ui_descriptor->URI = URI_PREFIX UI_URI_SUFFIX;
+ mda_ui_descriptor->instantiate = mda_ui_instantiate;
+ mda_ui_descriptor->cleanup = mda_ui_cleanup;
+ mda_ui_descriptor->port_event = mda_ui_port_event;
+ mda_ui_descriptor->extension_data = mda_ui_extension_data;
+}
+
+
+LV2_SYMBOL_EXPORT
+const LV2UI_Descriptor*
+lv2ui_descriptor(uint32_t index)
+{
+ if (!mda_ui_descriptor)
+ init_ui_descriptor();
+
+ switch (index) {
+ case 0:
+ return mda_ui_descriptor;
+ default:
+ return NULL;
+ }
+}
+
+
+LV2_SYMBOL_EXPORT
+AEffEditor*
+lvz_new_aeffeditor(AudioEffect* effect)
+{
+ UI_CLASS* ui = new UI_CLASS(effect);
+ ui->setURI(URI_PREFIX UI_URI_SUFFIX);
+ ui->setPluginURI(URI_PREFIX PLUGIN_URI_SUFFIX);
+ return ui;
+}
+
+
+} // extern "C"
+
diff --git a/lvz/wrapper.cpp b/lvz/wrapper.cpp
index 4ab99df..82a6989 100644
--- a/lvz/wrapper.cpp
+++ b/lvz/wrapper.cpp
@@ -19,8 +19,8 @@
#ifndef PLUGIN_CLASS
#error "This file requires PLUGIN_CLASS to be defined"
#endif
-#ifndef PLUGIN_URI_PREFIX
-#error "This file requires PLUGIN_URI_PREFIX to be defined"
+#ifndef URI_PREFIX
+#error "This file requires URI_PREFIX to be defined"
#endif
#ifndef PLUGIN_URI_SUFFIX
#error "This file requires PLUGIN_URI_SUFFIX to be defined"
@@ -74,6 +74,7 @@ mda_connect_port(LV2_Handle instance, uint32_t port, void* data)
static int
master_callback(int, int ver, int, int, int, int)
{
+ return 0;
}
@@ -84,7 +85,7 @@ mda_instantiate(const LV2_Descriptor* descriptor,
const LV2_Feature*const* features)
{
PLUGIN_CLASS* effect = new PLUGIN_CLASS(master_callback);
- effect->setURI(PLUGIN_URI_PREFIX PLUGIN_URI_SUFFIX);
+ effect->setURI(URI_PREFIX PLUGIN_URI_SUFFIX);
effect->setSampleRate(rate);
uint32_t num_params = effect->getNumParameters();
@@ -97,7 +98,7 @@ mda_instantiate(const LV2_Descriptor* descriptor,
if (num_params > 0) {
plugin->controls = (float*)malloc(sizeof(float) * num_params);
plugin->control_buffers = (float**)malloc(sizeof(float*) * num_params);
- for (int32_t i = 0; i < num_params; ++i) {
+ for (uint32_t i = 0; i < num_params; ++i) {
plugin->controls[i] = effect->getParameter(i);
plugin->control_buffers[i] = NULL;
}
@@ -108,7 +109,7 @@ mda_instantiate(const LV2_Descriptor* descriptor,
if (num_inputs > 0) {
plugin->inputs = (float**)malloc(sizeof(float*) * num_inputs);
- for (int32_t i = 0; i < num_inputs; ++i)
+ for (uint32_t i = 0; i < num_inputs; ++i)
plugin->inputs[i] = NULL;
} else {
plugin->inputs = NULL;
@@ -116,7 +117,7 @@ mda_instantiate(const LV2_Descriptor* descriptor,
if (num_outputs > 0) {
plugin->outputs = (float**)malloc(sizeof(float*) * num_outputs);
- for (int32_t i = 0; i < num_outputs; ++i)
+ for (uint32_t i = 0; i < num_outputs; ++i)
plugin->outputs[i] = NULL;
} else {
plugin->outputs = NULL;
@@ -141,6 +142,26 @@ mda_run(LV2_Handle instance, uint32_t sample_count)
plugin->effect->processReplacing(plugin->inputs, plugin->outputs, sample_count);
}
+
+
+static const AudioEffectX*
+mda_get_audioeffectx(LV2_Handle instance)
+{
+ MDAPlugin* plugin = (MDAPlugin*)instance;
+ return plugin->effect;
+}
+
+
+static const void*
+mda_extension_data(const char* uri)
+{
+ if (!strcmp(uri, "http://lv2plug.in/ns/ext/dev/vstgui")) {
+ // FIXME: shouldn't return function pointers directly
+ return (const void*)mda_get_audioeffectx;
+ } else {
+ return NULL;
+ }
+}
static void
@@ -160,13 +181,14 @@ init_descriptor()
{
mda_descriptor = (LV2_Descriptor*)malloc(sizeof(LV2_Descriptor));
- mda_descriptor->URI = PLUGIN_URI_PREFIX PLUGIN_URI_SUFFIX;
- mda_descriptor->activate = NULL;
- mda_descriptor->cleanup = mda_cleanup;
- mda_descriptor->connect_port = mda_connect_port;
- mda_descriptor->deactivate = mda_deactivate;
+ mda_descriptor->URI = URI_PREFIX PLUGIN_URI_SUFFIX;
mda_descriptor->instantiate = mda_instantiate;
+ mda_descriptor->connect_port = mda_connect_port;
+ mda_descriptor->activate = NULL;
mda_descriptor->run = mda_run;
+ mda_descriptor->deactivate = mda_deactivate;
+ mda_descriptor->cleanup = mda_cleanup;
+ mda_descriptor->extension_data = mda_extension_data;
}
@@ -191,9 +213,10 @@ AudioEffectX*
lvz_new_audioeffectx()
{
PLUGIN_CLASS* effect = new PLUGIN_CLASS(master_callback);
- effect->setURI(PLUGIN_URI_PREFIX PLUGIN_URI_SUFFIX);
+ effect->setURI(URI_PREFIX PLUGIN_URI_SUFFIX);
return effect;
}
} // extern "C"
+