summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2019-04-17 19:51:34 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2019-04-17 19:51:34 +0200
commitfb8588d8a0013b8da78b796f84ddcccf2bc9238e (patch)
tree0333d12df5a9e813904e4f5b4a42de7d4dba7443
parent34bfc5046bd297311a38dace8914c51905ca9d2e (diff)
Added ttlgen dynamic ttl manifest generator for LV2 plugins.
-rw-r--r--Makefile7
-rw-r--r--manifest.ttl112
-rw-r--r--plugin.h9
-rw-r--r--pluginlv2.h2
-rw-r--r--plugintest.cc14
-rw-r--r--plugintest.h2
-rw-r--r--ttlgen.cc255
7 files changed, 285 insertions, 116 deletions
diff --git a/Makefile b/Makefile
index 22e18ed..b2a5055 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,8 @@
PLUGIN_NAME=plugintest
PLUGIN_URI=http://example.org/plugintest
-CXXFLAGS = -DX11 -I. -fPIC -std=c++11 -Wall -shared -Wl,--no-undefined
+CXXFLAGS = -DX11 -I. -fPIC -std=c++11 -Wall -shared -Wl,--no-undefined \
+ -fvisibility=hidden
LV2_CXXFLAGS = -DLV2 -g -Werror -DLV2_PLUGIN_URI=\"$(PLUGIN_URI)\"
LV2_SRC = \
@@ -61,3 +62,7 @@ test: test-lv2 test-vst
all: $(PLUGIN_NAME)_lv2.so $(PLUGIN_NAME)_vst.so
clean: clean-lv2 clean-vst
+
+ttlgen: ttlgen.cc $(PLUGIN_NAME)_lv2.so
+ g++ -I. -std=c++11 ttlgen.cc -ldl -o ttlgen
+ ./ttlgen ./$(PLUGIN_NAME)_lv2.so manifest.ttl
diff --git a/manifest.ttl b/manifest.ttl
deleted file mode 100644
index 1468215..0000000
--- a/manifest.ttl
+++ /dev/null
@@ -1,112 +0,0 @@
-# LV2 Plugin
-# Copyright 2016 Bent Bisballe Nyeng <deva@aasimon.org>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-@prefix doap: <http://usefulinc.com/ns/doap#> .
-@prefix foaf: <http://xmlns.com/foaf/0.1/> .
-@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
-@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
-@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
-@prefix state: <http://lv2plug.in/ns/ext/state#> .
-@prefix pprops: <http://lv2plug.in/ns/ext/port-props#> .
-@prefix idpy: <http://harrisonconsoles.com/lv2/inlinedisplay#> .
-
-@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-
-<http://example.org/plugintest#ui>
- a ui:X11UI ;
- lv2:requiredFeature ui:resize ;
- lv2:extensionData ui:resize ;
- lv2:requiredFeature ui:idleInterface ;
- lv2:extensionData ui:idleInterface ;
- ui:binary <plugintest_lv2.so> .
-
-<http://example.org/plugintest>
- a lv2:Plugin ;
- lv2:optionalFeature idpy:queue_draw ;
- lv2:binary <plugintest_lv2.so> ;
- a lv2:InstrumentPlugin ;
- doap:name "LV2Test" ;
- ui:ui <http://example.org/plugintest#ui> ;
- doap:maintainer [
- foaf:name "LV2Test" ;
- foaf:homepage <http://www.example.org/plugintest> ;
- ] ;
- doap:license <http://usefulinc.com/doap/licenses/gpl> ;
- doap:license <http://opensource.org/licenses/gpl-3.0> ;
- lv2:optionalFeature <http://lv2plug.in/ns/ext/uri-map> ;
- lv2:optionalFeature <http://lv2plug.in/ns/ext/event> ;
- lv2:extensionData state:interface ;
- lv2:port [
- a lv2:InputPort, lv2:ControlPort ;
- lv2:index 0 ;
- lv2:symbol "lv2_freewheel" ;
- lv2:name "Freewheel" ;
- lv2:default 0.0 ;
- lv2:minimum 0.0 ;
- lv2:maximum 1.0 ;
- lv2:designation <http://lv2plug.in/ns/lv2core#freeWheeling> ;
- lv2:portProperty lv2:toggled ;
- lv2:portProperty pprops:hasStrictBounds;
- ] , [
- a lv2:OutputPort, lv2:ControlPort ;
- lv2:designation <http://lv2plug.in/ns/lv2core#latency>;
- lv2:index 1;
- lv2:symbol "latency";
- lv2:name "Latency";
- lv2:minimum 0;
- lv2:maximum 192000;
- lv2:portProperty lv2:reportsLatency, lv2:integer;
- ] , [
- a atom:AtomPort ,
- lv2:InputPort;
- atom:bufferType atom:Sequence ;
- atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;
- lv2:index 2 ;
- lv2:symbol "control" ;
- lv2:name "Control"
- ] , [
- a atom:AtomPort ,
- lv2:OutputPort;
- atom:bufferType atom:Sequence ;
- atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;
- lv2:index 3 ;
- lv2:symbol "control" ;
- lv2:name "Control"
- ] , [
- a lv2:AudioPort ,
- lv2:InputPort ;
- lv2:index 4 ;
- lv2:symbol "in1" ;
- lv2:name "In1"
- ] , [
- a lv2:AudioPort ,
- lv2:InputPort ;
- lv2:index 5 ;
- lv2:symbol "in2" ;
- lv2:name "In2"
- ] , [
- a lv2:AudioPort ,
- lv2:OutputPort ;
- lv2:index 6 ;
- lv2:symbol "out1" ;
- lv2:name "Out1"
- ], [
- a lv2:AudioPort ,
- lv2:OutputPort ;
- lv2:index 7 ;
- lv2:symbol "out2" ;
- lv2:name "Out2"
- ] .
diff --git a/plugin.h b/plugin.h
index e1d8d69..4c22943 100644
--- a/plugin.h
+++ b/plugin.h
@@ -31,6 +31,13 @@
#include <cstdlib>
+
+#if defined(WIN32)
+#define PG_EXPORT extern "C" __declspec(dllexport)
+#else
+#define PG_EXPORT extern "C" __attribute__((visibility("default")))
+#endif
+
class MidiEvent;
//! Plugin categories.
@@ -127,9 +134,11 @@ public:
virtual std::string getId() = 0;
// Functions used to set plugin information.
+ virtual std::string getURI() = 0;
virtual std::string getEffectName() = 0;
virtual std::string getVendorString() = 0;
virtual std::string getProductString() = 0;
+ virtual std::string getHomepage() = 0;
virtual PluginCategory getPluginCategory() = 0;
//! Process callback.
diff --git a/pluginlv2.h b/pluginlv2.h
index 2ff2226..26bc627 100644
--- a/pluginlv2.h
+++ b/pluginlv2.h
@@ -283,4 +283,4 @@ private:
LV2UI_Resize* resize{nullptr};
};
-PluginLV2* createEffectInstance();
+PG_EXPORT PluginLV2* createEffectInstance();
diff --git a/plugintest.cc b/plugintest.cc
index b485400..70bb9ea 100644
--- a/plugintest.cc
+++ b/plugintest.cc
@@ -37,7 +37,7 @@
#ifdef LV2
// Entry point for lv2 plugin instantiation.
-PluginLV2* createEffectInstance()
+PG_EXPORT PluginLV2* createEffectInstance()
{
return new PluginTest();
}
@@ -45,7 +45,7 @@ PluginLV2* createEffectInstance()
#ifdef VST
// Entry point for vst plugin instantiation.
-AudioEffect* createEffectInstance(audioMasterCallback audioMaster)
+PG_EXPORT AudioEffect* createEffectInstance(audioMasterCallback audioMaster)
{
return new PluginTest(audioMaster);
}
@@ -112,6 +112,11 @@ std::string PluginTest::getEffectName()
return "Test plugin";
}
+std::string PluginTest::getURI()
+{
+ return "http://example.com/plugintest";
+}
+
std::string PluginTest::getVendorString()
{
return "Test vendor";
@@ -122,6 +127,11 @@ std::string PluginTest::getProductString()
return "Test product";
}
+std::string PluginTest::getHomepage()
+{
+ return "https://www.example.com/pluginwebsite";
+}
+
PluginCategory PluginTest::getPluginCategory()
{
return PluginCategory::Synth;
diff --git a/plugintest.h b/plugintest.h
index f2bf8d5..b8897e6 100644
--- a/plugintest.h
+++ b/plugintest.h
@@ -75,8 +75,10 @@ public:
std::string getId() override;
std::string getEffectName() override;
+ std::string getURI() override;
std::string getVendorString() override;
std::string getProductString() override;
+ std::string getHomepage() override;
PluginCategory getPluginCategory() override;
void process(size_t pos,
diff --git a/ttlgen.cc b/ttlgen.cc
new file mode 100644
index 0000000..77780d0
--- /dev/null
+++ b/ttlgen.cc
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * ttlgen.cc
+ *
+ * Fri Jul 15 09:27:19 CEST 2016
+ * Copyright 2016 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of PluginGizmo.
+ *
+ * PluginGizmo is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PluginGizmo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with PluginGizmo; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include <iostream>
+#include <fstream>
+#include <dlfcn.h>
+
+#include "pluginlv2.h"
+
+typedef PluginLV2* create_t();
+
+static void header(std::ostream& output)
+{
+ output << "\
+# LV2 Plugin\n\
+# Copyright 2019 Bent Bisballe Nyeng <deva@aasimon.org>\n\
+#\n\
+# Permission to use, copy, modify, and/or distribute this software for any\n\
+# purpose with or without fee is hereby granted, provided that the above\n\
+# copyright notice and this permission notice appear in all copies.\n\
+#\n\
+# THIS SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n\
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n\
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n\
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n\
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n\
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n\
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\
+";
+}
+
+static void includes(std::ostream& output)
+{
+ output << "\
+@prefix doap: <http://usefulinc.com/ns/doap#> .\n\
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n\
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n\
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .\n\
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n\
+@prefix state: <http://lv2plug.in/ns/ext/state#> .\n\
+@prefix pprops: <http://lv2plug.in/ns/ext/port-props#> .\n\
+@prefix idpy: <http://harrisonconsoles.com/lv2/inlinedisplay#> .\n\
+@prefix time: <http://lv2plug.in/ns/ext/time#> .\n\
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n\
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n\
+";
+}
+
+static void ui(Plugin& plugin, const std::string& pluginfile, std::ostream& output)
+{
+ if(!plugin.hasGUI())
+ {
+ return;
+ }
+
+ output << "\
+<" << plugin.getURI() << "/lv2#ui>\n\
+ a ui:X11UI ;\n\
+ lv2:requiredFeature ui:resize ;\n\
+ lv2:extensionData ui:resize ;\n\
+ lv2:requiredFeature ui:idleInterface ;\n\
+ lv2:extensionData ui:idleInterface ;\n\
+ lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> ;\n\
+ ui:binary <" << pluginfile << "> .\n";
+}
+
+static void ports(Plugin& plugin, std::ostream& output)
+{
+ std::size_t port_index = 2;
+ for(std::size_t i = 0; i < plugin.getNumberOfMidiInputs(); ++i)
+ {
+ output << "\
+ , [\n\
+ a atom:AtomPort ,\n\
+ lv2:InputPort;\n\
+ atom:bufferType atom:Sequence ;\n\
+ atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n\
+ lv2:index " << port_index << " ;\n\
+ lv2:symbol \"control\" ;\n\
+ lv2:name \"Control\"\n\
+ ]";
+ ++port_index;
+ }
+
+ for(std::size_t i = 0; i < plugin.getNumberOfMidiOutputs(); ++i)
+ {
+ output << "\
+ , [\n\
+ a atom:AtomPort ,\n\
+ lv2:OutputPort;\n\
+ atom:bufferType atom:Sequence ;\n\
+ atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n\
+ lv2:index " << port_index << " ;\n\
+ lv2:symbol \"control\" ;\n\
+ lv2:name \"Control\"\n\
+ ]";
+ ++port_index;
+ }
+
+ std::size_t input_port_index = 1;
+ for(std::size_t i = 0; i < plugin.getNumberOfAudioInputs(); ++i)
+ {
+ output << "\
+ , [\n\
+ a lv2:AudioPort ,\n\
+ lv2:InputPort ;\n\
+ lv2:index " << port_index << " ;\n\
+ lv2:symbol \"in" << input_port_index << "\" ;\n\
+ lv2:name \"In" << input_port_index << "\"\n\
+ ]";
+ ++port_index;
+ ++input_port_index;
+ }
+
+ std::size_t output_port_index = 1;
+ for(std::size_t i = 0; i < plugin.getNumberOfAudioOutputs(); ++i)
+ {
+ output << "\
+ , [\n\
+ a lv2:AudioPort ,\n\
+ lv2:OutputPort ;\n\
+ lv2:index " << port_index << " ;\n\
+ lv2:symbol \"out" << output_port_index << "\" ;\n\
+ lv2:name \"Out" << output_port_index << "\"\n\
+ ]";
+ ++port_index;
+ ++output_port_index;
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ if(argc != 3)
+ {
+ std::cerr << "Missing argument.\n";
+ std::cout << "Usage: " << argv[0] << " <plugin binary> <output file>\n";
+ return 1;
+ }
+
+ std::string library = argv[1];
+ auto seppos = library.rfind("/");
+ std::string binary = library;
+ if(seppos != std::string::npos)
+ {
+ binary = library.substr(seppos + 1);
+ }
+
+ // load the plugin library
+ void* plugin = dlopen(library.data(), RTLD_LAZY);
+ if(!plugin)
+ {
+ std::cerr << "Cannot load library: " << dlerror() << std::endl;
+ std::cerr << "Library must have absolute path or prefix ./myplugin.so.\n";
+ return 1;
+ }
+
+ // reset errors
+ dlerror();
+
+ // load the symbols
+ create_t* create_plugin = (create_t*) dlsym(plugin, "createEffectInstance");
+ const char* dlsym_error = dlerror();
+ if(dlsym_error)
+ {
+ std::cerr << "Cannot load symbol create: " << dlsym_error << '\n';
+ return 1;
+ }
+
+ std::ofstream output(argv[2]);
+
+ // create an instance of the class
+ Plugin* p = create_plugin();
+
+ header(output);
+ output << std::endl;
+ includes(output);
+ output << std::endl;
+ ui(*p, binary, output);
+ output << std::endl;
+
+ output << "\
+<" << p->getURI() << "/lv2>\n\
+ a lv2:Plugin ;\n\
+ lv2:binary <" << binary << "> ;\n \
+ a lv2:InstrumentPlugin ;\n\
+ doap:name \"" << p->getEffectName() << "\" ;\n\
+ doap:maintainer [\n\
+ foaf:name \"" << p->getVendorString() << "\" ;\n\
+ foaf:homepage <" << p->getHomepage() << "> ;\n\
+ ] ;\n\
+ doap:license <http://usefulinc.com/doap/licenses/gpl> ;\n\
+ ui:ui <" << p->getURI() << "/lv2#ui> ;\n\
+ doap:license <http://opensource.org/licenses/gpl-3.0> ;\n\
+ lv2:optionalFeature <http://lv2plug.in/ns/ext/uri-map> ;\n\
+ lv2:optionalFeature <http://lv2plug.in/ns/ext/event> ;\n\
+ lv2:optionalFeature idpy:queue_draw ;\n\
+ lv2:extensionData state:interface ;\n\
+ lv2:port [\n\
+ a lv2:InputPort, lv2:ControlPort ;\n\
+ lv2:index 0 ;\n\
+ lv2:symbol \"lv2_freewheel\" ;\n\
+ lv2:name \"Freewheel\" ;\n\
+ lv2:default 0.0 ;\n\
+ lv2:minimum 0.0 ;\n\
+ lv2:maximum 1.0 ;\n\
+ lv2:designation <http://lv2plug.in/ns/lv2core#freeWheeling> ;\n\
+ lv2:portProperty <http://lv2plug.in/ns/lv2core#freeWheeling> ;\n\
+ lv2:portProperty lv2:toggled ;\n\
+ lv2:portProperty pprops:hasStrictBounds;\n\
+ ] , [\n\
+ a lv2:OutputPort, lv2:ControlPort ;\n\
+ lv2:designation <http://lv2plug.in/ns/lv2core#latency>;\n\
+ lv2:index 1;\n\
+ lv2:symbol \"latency\";\n\
+ lv2:name \"Latency\";\n\
+ lv2:minimum 0;\n\
+ lv2:maximum 192000;\n\
+ lv2:portProperty lv2:reportsLatency, lv2:integer;\n\
+ ]";
+
+ ports(*p, output);
+
+ output << " .\n";
+
+ // FIXME: The plugin is never deleted, but since we're terminating now anyway
+ // it doesn't matter much...
+
+ // unload the plugin library
+ dlclose(plugin);
+
+ return 0;
+}