/* -*- 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(); enum class UIType { CocoaUI, Gtk3UI, GtkUI, Qt4UI, Qt5UI, WindowsUI, X11UI, }; 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\ "; } // If UIClass was not explicitly set already, try to autodetect it based on // operating system. #ifndef UIClass #ifdef __linux__ #define UIClass "X11UI" #elif _WIN32 #define UIClass "WindowsUI" #elif __APPLE__ #define UIClass "CocoaUI" #elif __FreeBSD__ #define UIClass "X11UI" #elif __unix__ // All other unices (*BSD etc) #define UIClass "X11UI" #endif #endif static void ui(Plugin& plugin, const std::string& pluginfile, UIType uitype, std::ostream& output) { if(!plugin.hasGUI()) { return; } output << "<" << plugin.getURI() << "/lv2#ui>\n"; switch(uitype) { case UIType::CocoaUI: output << " a ui:CocoaUI ;\n"; break; case UIType::Gtk3UI: output << " a ui:Gtk3UI ;\n"; break; case UIType::GtkUI: output << " a ui:GtkUI ;\n"; break; case UIType::Qt4UI: output << " a ui:Qt4UI ;\n"; break; case UIType::Qt5UI: output << " a ui:Qt5UI ;\n"; break; case UIType::WindowsUI: output << " a ui:WindowsUI ;\n"; break; case UIType::X11UI: output << " a ui:X11UI ;\n"; break; } output << "\ 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; } } void usage(const char* app) { std::cout << "Usage: " << app << " [uitype]\n"; std::cout << "where uitype can be one of:\n"; std::cout << " CocoaUI\n"; std::cout << " Gtk3UI\n"; std::cout << " GtkUI\n"; std::cout << " Qt4UI\n"; std::cout << " Qt5UI\n"; std::cout << " WindowsUI\n"; std::cout << " X11UI\n"; std::cout << "default is " UIClass "\n"; } UIType fromString(const std::string& t) { if(t == "CocoaUI") { return UIType::CocoaUI; } if(t == "Gtk3UI") { return UIType::Gtk3UI; } if(t == "GtkUI") { return UIType::GtkUI; } if(t == "Qt4UI") { return UIType::Qt4UI; } if(t == "Qt5UI") { return UIType::Qt5UI; } if(t == "WindowsUI") { return UIType::WindowsUI; } if(t == "X11UI") { return UIType::X11UI; } std::cerr << "Bad uitype: '" << t << "'\n"; exit(1); } int main(int argc, char* argv[]) { if(argc < 3 || argc > 4) { std::cerr << "Missing argument.\n"; usage(argv[0]); return 1; } UIType uitype = UIType::X11UI; if(argc == 4) { uitype = fromString(argv[3]); } 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, uitype, 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; }