From fb8588d8a0013b8da78b796f84ddcccf2bc9238e Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Wed, 17 Apr 2019 19:51:34 +0200 Subject: Added ttlgen dynamic ttl manifest generator for LV2 plugins. --- Makefile | 7 +- manifest.ttl | 112 -------------------------- plugin.h | 9 +++ pluginlv2.h | 2 +- plugintest.cc | 14 +++- plugintest.h | 2 + ttlgen.cc | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 285 insertions(+), 116 deletions(-) delete mode 100644 manifest.ttl create mode 100644 ttlgen.cc 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 -# -# 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: . -@prefix foaf: . -@prefix lv2: . -@prefix atom: . -@prefix ui: . -@prefix state: . -@prefix pprops: . -@prefix idpy: . - -@prefix lv2: . -@prefix rdfs: . - - - a ui:X11UI ; - lv2:requiredFeature ui:resize ; - lv2:extensionData ui:resize ; - lv2:requiredFeature ui:idleInterface ; - lv2:extensionData ui:idleInterface ; - ui:binary . - - - a lv2:Plugin ; - lv2:optionalFeature idpy:queue_draw ; - lv2:binary ; - a lv2:InstrumentPlugin ; - doap:name "LV2Test" ; - ui:ui ; - doap:maintainer [ - foaf:name "LV2Test" ; - foaf:homepage ; - ] ; - doap:license ; - doap:license ; - lv2:optionalFeature ; - lv2:optionalFeature ; - 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 ; - lv2:portProperty lv2:toggled ; - lv2:portProperty pprops:hasStrictBounds; - ] , [ - a lv2:OutputPort, lv2:ControlPort ; - lv2:designation ; - 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 ; - lv2:index 2 ; - lv2:symbol "control" ; - lv2:name "Control" - ] , [ - a atom:AtomPort , - lv2:OutputPort; - atom:bufferType atom:Sequence ; - atom:supports ; - 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 + +#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 +#include +#include + +#include "pluginlv2.h" + +typedef PluginLV2* create_t(); + +static void header(std::ostream& output) +{ + output << "\ +# LV2 Plugin\n\ +# Copyright 2019 Bent Bisballe Nyeng \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: .\n\ +@prefix foaf: .\n\ +@prefix lv2: .\n\ +@prefix atom: .\n\ +@prefix ui: .\n\ +@prefix state: .\n\ +@prefix pprops: .\n\ +@prefix idpy: .\n\ +@prefix time: .\n\ +@prefix lv2: .\n\ +@prefix rdfs: .\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 ;\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 ;\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 ;\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] << " \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 ;\n\ + ui:ui <" << p->getURI() << "/lv2#ui> ;\n\ + doap:license ;\n\ + lv2:optionalFeature ;\n\ + lv2:optionalFeature ;\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 ;\n\ + lv2:portProperty ;\n\ + lv2:portProperty lv2:toggled ;\n\ + lv2:portProperty pprops:hasStrictBounds;\n\ + ] , [\n\ + a lv2:OutputPort, lv2:ControlPort ;\n\ + lv2:designation ;\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; +} -- cgit v1.2.3