/* This file is part of Ingen. Copyright 2007-2015 David Robillard Ingen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. Ingen 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 Affero General Public License for details. You should have received a copy of the GNU Affero General Public License along with Ingen. If not, see . */ #include "ingen/client/PluginModel.hpp" #include "ingen/Atom.hpp" #include "ingen/client/PluginUI.hpp" #include "lilv/lilv.h" #include "lv2/core/lv2.h" #include "raul/Symbol.hpp" #include #include #include #include #include using std::string; namespace ingen::client { LilvWorld* PluginModel::_lilv_world = nullptr; const LilvPlugins* PluginModel::_lilv_plugins = nullptr; Sord::World* PluginModel::_rdf_world = nullptr; PluginModel::PluginModel(URIs& uris, const URI& uri, const Atom& type, const Properties& properties) : Resource(uris, uri) , _type(type) { if (!_type.is_valid()) { if (uri.string().find("ingen-internals") != string::npos) { _type = uris.ingen_Internal.urid_atom(); } else { _type = uris.lv2_Plugin.urid_atom(); // Assume LV2 and hope for the best... } } add_property(uris.rdf_type, type); add_properties(properties); LilvNode* plugin_uri = lilv_new_uri(_lilv_world, uri.c_str()); _lilv_plugin = lilv_plugins_get_by_uri(_lilv_plugins, plugin_uri); lilv_node_free(plugin_uri); if (uris.ingen_Internal == _type) { set_property(uris.doap_name, uris.forge.alloc(std::string(uri.fragment().substr(1)))); } } static size_t last_uri_delim(const std::string& str) { for (size_t i = str.length() - 1; i > 0; --i) { switch (str[i]) { case ':': case '/': case '?': case '#': return i; } } return string::npos; } static bool contains_alpha_after(const std::string& str, size_t begin) { for (size_t i = begin; i < str.length(); ++i) { if (isalpha(str[i])) { return true; } } return false; } const Atom& PluginModel::get_property(const URI& key) const { static const Atom nil; const Atom& val = Resource::get_property(key); if (val.is_valid()) { return val; } // No lv2:symbol from data or engine, invent one if (key == _uris.lv2_symbol) { string str = this->uri(); size_t last_delim = last_uri_delim(str); while (last_delim != string::npos && !contains_alpha_after(str, last_delim)) { str.resize(last_delim); last_delim = last_uri_delim(str); } str = str.substr(last_delim + 1); const std::string symbol = raul::Symbol::symbolify(str); set_property(_uris.lv2_symbol, _uris.forge.alloc(symbol)); return get_property(key); } if (_lilv_plugin) { const Atom* ret = nullptr; LilvNode* lv2_pred = lilv_new_uri(_lilv_world, key.c_str()); LilvNodes* values = lilv_plugin_get_value(_lilv_plugin, lv2_pred); lilv_node_free(lv2_pred); LILV_FOREACH (nodes, i, values) { const LilvNode* value = lilv_nodes_get(values, i); if (lilv_node_is_uri(value)) { ret = &set_property( key, _uris.forge.make_urid(URI(lilv_node_as_uri(value)))); break; } if (lilv_node_is_string(value)) { ret = &set_property( key, _uris.forge.alloc(lilv_node_as_string(value))); break; } if (lilv_node_is_float(value)) { ret = &set_property( key, _uris.forge.make(lilv_node_as_float(value))); break; } if (lilv_node_is_int(value)) { ret = &set_property( key, _uris.forge.make(lilv_node_as_int(value))); break; } } lilv_nodes_free(values); if (ret) { return *ret; } } return nil; } void PluginModel::set(const std::shared_ptr& p) { _type = p->_type; if (p->_lilv_plugin) { _lilv_plugin = p->_lilv_plugin; } for (const auto& v : p->properties()) { Resource::set_property(v.first, v.second); _signal_property.emit(v.first, v.second); } _signal_changed.emit(); } void PluginModel::add_preset(const URI& uri, const std::string& label) { _presets.emplace(uri, label); _signal_preset.emit(uri, label); } raul::Symbol PluginModel::default_block_symbol() const { const Atom& name_atom = get_property(_uris.lv2_symbol); if (name_atom.is_valid() && name_atom.type() == _uris.forge.String) { return raul::Symbol::symbolify(name_atom.ptr()); } return raul::Symbol("_"); } string PluginModel::human_name() const { const Atom& name_atom = get_property(_uris.doap_name); if (name_atom.type() == _uris.forge.String) { return name_atom.ptr(); } return default_block_symbol().c_str(); } string PluginModel::port_human_name(const uint32_t index) const { if (_lilv_plugin) { const LilvPort* port = lilv_plugin_get_port_by_index(_lilv_plugin, index); LilvNode* name = lilv_port_get_name(_lilv_plugin, port); string ret(lilv_node_as_string(name)); lilv_node_free(name); return ret; } return ""; } PluginModel::ScalePoints PluginModel::port_scale_points(const uint32_t index) const { // TODO: Non-float scale points ScalePoints points; if (_lilv_plugin) { const LilvPort* port = lilv_plugin_get_port_by_index(_lilv_plugin, index); LilvScalePoints* sp = lilv_port_get_scale_points(_lilv_plugin, port); LILV_FOREACH (scale_points, i, sp) { const LilvScalePoint* p = lilv_scale_points_get(sp, i); points.emplace( lilv_node_as_float(lilv_scale_point_get_value(p)), lilv_node_as_string(lilv_scale_point_get_label(p))); } } return points; } bool PluginModel::has_ui() const { if (_lilv_plugin) { LilvUIs* uis = lilv_plugin_get_uis(_lilv_plugin); const bool ret = (lilv_nodes_size(uis) > 0); lilv_uis_free(uis); return ret; } return false; } std::shared_ptr PluginModel::ui(ingen::World& world, const std::shared_ptr& block) const { if (!_lilv_plugin) { return nullptr; } return PluginUI::create(world, block, _lilv_plugin); } static std::string heading(const std::string& text, bool html, unsigned level) { if (html) { const std::string tag = std::string("h") + std::to_string(level); return std::string("<") + tag + ">" + text + "\n"; } return text + ":\n\n"; } static std::string link(const std::string& addr, bool html) { if (html) { return std::string("" + addr + ""; } return addr; } std::string PluginModel::get_documentation(const LilvNode* subject, bool html) { std::string doc; LilvNode* lv2_documentation = lilv_new_uri(_lilv_world, LV2_CORE__documentation); LilvNode* rdfs_comment = lilv_new_uri(_lilv_world, LILV_NS_RDFS "comment"); LilvNodes* vals = lilv_world_find_nodes( _lilv_world, subject, lv2_documentation, nullptr); const bool doc_is_html = vals; if (!vals) { vals = lilv_world_find_nodes( _lilv_world, subject, rdfs_comment, nullptr); } if (vals) { const LilvNode* val = lilv_nodes_get_first(vals); if (lilv_node_is_string(val)) { doc += lilv_node_as_string(val); } } if (html && !doc_is_html) { for (std::size_t i = 0; i < doc.size(); ++i) { if (doc.substr(i, 2) == "\n\n") { doc.replace(i, 2, "

"); i += strlen("

"); } } } lilv_node_free(rdfs_comment); lilv_node_free(lv2_documentation); return doc; } std::string PluginModel::documentation(const bool html) const { LilvNode* subject = (_lilv_plugin) ? lilv_node_duplicate(lilv_plugin_get_uri(_lilv_plugin)) : lilv_new_uri(_lilv_world, uri().c_str()); const std::string doc(get_documentation(subject, html)); lilv_node_free(subject); return (heading(human_name(), html, 2) + link(uri(), html) + (html ? "

" : "\n\n") + doc); } std::string PluginModel::port_documentation(uint32_t index, bool html) const { if (!_lilv_plugin) { return ""; } const LilvPort* port = lilv_plugin_get_port_by_index(_lilv_plugin, index); if (!port) { return ""; } return (heading(port_human_name(index), html, 2) + get_documentation(lilv_port_get_node(_lilv_plugin, port), html)); } const LilvPort* PluginModel::lilv_port(uint32_t index) const { return lilv_plugin_get_port_by_index(_lilv_plugin, index); } void PluginModel::set_lilv_world(LilvWorld* world) { _lilv_world = world; _lilv_plugins = lilv_world_get_all_plugins(_lilv_world); } } // namespace ingen::client