diff options
author | David Robillard <d@drobilla.net> | 2015-08-12 04:46:29 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2015-08-12 04:46:29 +0000 |
commit | dd79e76e41446833088482588456afed37231bff (patch) | |
tree | c0f3c5c2fc74b286d529df69ad2206e2fddd96f9 | |
parent | 44af7b7b66e2083819103c760ab3bf4980469f86 (diff) | |
download | ingen-dd79e76e41446833088482588456afed37231bff.tar.gz ingen-dd79e76e41446833088482588456afed37231bff.tar.bz2 ingen-dd79e76e41446833088482588456afed37231bff.zip |
Server-side presets.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@5703 a436a847-0d15-0410-975c-d299462d15a1
40 files changed, 557 insertions, 240 deletions
diff --git a/ingen/Forge.hpp b/ingen/Forge.hpp index 83a94319..683c0041 100644 --- a/ingen/Forge.hpp +++ b/ingen/Forge.hpp @@ -22,6 +22,7 @@ #include "ingen/Atom.hpp" #include "ingen/ingen.h" #include "lv2/lv2plug.in/ns/ext/atom/forge.h" +#include "raul/URI.hpp" namespace Ingen { @@ -46,6 +47,8 @@ public: Atom make_urid(int32_t v) { return Atom(sizeof(int32_t), URID, &v); } + Atom make_urid(const Raul::URI& u); + Atom alloc(uint32_t size, uint32_t type, const void* val) { return Atom(size, type, val); } diff --git a/ingen/Node.hpp b/ingen/Node.hpp index 181a9c53..4c7c453a 100644 --- a/ingen/Node.hpp +++ b/ingen/Node.hpp @@ -31,7 +31,6 @@ class Symbol; namespace Ingen { class Arc; -class Plugin; class Store; /** An object on the audio graph. @@ -63,9 +62,9 @@ public: const Arcs& arcs() const { return _arcs; } // Blocks and graphs only - virtual uint32_t num_ports() const { return 0; } - virtual Node* port(uint32_t index) const { return NULL; } - virtual const Plugin* plugin() const { return NULL; } + virtual uint32_t num_ports() const { return 0; } + virtual Node* port(uint32_t index) const { return NULL; } + virtual const Resource* plugin() const { return NULL; } // All objects virtual GraphType graph_type() const = 0; @@ -101,7 +100,7 @@ protected: friend class Store; virtual void set_path(const Raul::Path& p) = 0; - Node(URIs& uris, const Raul::Path& path) + Node(const URIs& uris, const Raul::Path& path) : Resource(uris, path_to_uri(path)) {} diff --git a/ingen/Plugin.hpp b/ingen/Plugin.hpp deleted file mode 100644 index cb873d20..00000000 --- a/ingen/Plugin.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - 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 <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_PLUGIN_HPP -#define INGEN_PLUGIN_HPP - -#include "raul/URI.hpp" - -#include "ingen/Resource.hpp" -#include "ingen/ingen.h" - -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -namespace Ingen { - -/** A plugin which instantiates to a Block. - * @ingroup Ingen - */ -class INGEN_API Plugin : public Resource -{ -public: - Plugin(URIs& uris, const Raul::URI& uri) - : Resource(uris, uri) - {} - - enum Type { NIL, LV2, Internal, Graph }; - - virtual Type type() const = 0; - - static inline const Raul::URI& type_uri(Type type) { - static const Raul::URI uris[] = { - Raul::URI("http://www.w3.org/2002/07/owl#Nothing"), - Raul::URI(LV2_CORE__Plugin), - Raul::URI(INGEN__Internal), - Raul::URI(INGEN__Graph) - }; - - return uris[type]; - } - - inline const Raul::URI& type_uri() const { return type_uri(type()); } - - static inline Type type_from_uri(const Raul::URI& uri) { - if (uri == type_uri(LV2)) - return LV2; - else if (uri == type_uri(Internal)) - return Internal; - else if (uri == type_uri(Graph)) - return Graph; - else - return NIL; - } -}; - -} // namespace Ingen - -#endif // INGEN_PLUGIN_HPP diff --git a/ingen/Resource.hpp b/ingen/Resource.hpp index c4626a13..d798b009 100644 --- a/ingen/Resource.hpp +++ b/ingen/Resource.hpp @@ -34,11 +34,20 @@ namespace Ingen { class INGEN_API Resource : public Raul::Deletable { public: - Resource(URIs& uris, const Raul::URI& uri) + Resource(const URIs& uris, const Raul::URI& uri) : _uris(uris) , _uri(uri) {} + Resource& operator=(const Resource& rhs) { + assert(&rhs._uris == &_uris); + if (&rhs != this) { + _uri = rhs._uri; + _properties = rhs._properties; + } + return *this; + } + enum class Graph { DEFAULT, EXTERNAL, @@ -76,6 +85,11 @@ public: , _ctx(ctx) {} + Property(const URIs::Quark& quark, Graph ctx=Graph::DEFAULT) + : Atom(quark.urid) + , _ctx(ctx) + {} + Graph context() const { return _ctx; } void set_context(Graph ctx) { _ctx = ctx; } @@ -104,6 +118,16 @@ public: const Atom& value, Graph ctx = Graph::DEFAULT); + /** Set (replace) a property value. + * + * This will first erase any properties with the given `uri`, so after + * this call exactly one property with predicate `uri` will be set. + */ + virtual const Atom& set_property( + const Raul::URI& uri, + const URIs::Quark& value, + Graph ctx = Graph::DEFAULT); + /** Add a property value. * * This will not remove any existing values, so if properties with @@ -122,10 +146,22 @@ public: virtual void remove_property(const Raul::URI& uri, const Atom& value); - /** Return true iff a property is set. */ + /** Remove a property. + * + * If `value` is patch:wildcard then any property with `uri` for a + * predicate will be removed. + */ + virtual void remove_property(const Raul::URI& uri, + const URIs::Quark& value); + + /** Return true iff a property is set with a specific value. */ virtual bool has_property(const Raul::URI& uri, const Atom& value) const; + /** Return true iff a property is set with a specific value. */ + virtual bool has_property(const Raul::URI& uri, + const URIs::Quark& value) const; + /** Set (replace) several properties at once. * * This will erase all properties with keys in `p`, though multiple values @@ -177,7 +213,7 @@ public: /** Get all the properties with a given context. */ Properties properties(Resource::Graph ctx) const; - URIs& uris() const { return _uris; } + const URIs& uris() const { return _uris; } const Raul::URI& uri() const { return _uri; } const Properties& properties() const { return _properties; } Properties& properties() { return _properties; } @@ -185,7 +221,7 @@ public: protected: const Atom& set_property(const Raul::URI& uri, const Atom& value) const; - URIs& _uris; + const URIs& _uris; private: Raul::URI _uri; diff --git a/ingen/URIs.hpp b/ingen/URIs.hpp index 3696aba2..a10cd30c 100644 --- a/ingen/URIs.hpp +++ b/ingen/URIs.hpp @@ -46,10 +46,21 @@ public: struct Quark : public Raul::URI { Quark(Ingen::Forge& forge, URIMap* map, const char* str); - operator LV2_URID() const { return id; } - operator Atom() const { return atom; } - uint32_t id; - Atom atom; + + operator LV2_URID() const { return urid.get<LV2_URID>(); } + operator Atom() const { return urid; } + + inline bool operator==(const Atom& rhs) const { + if (rhs.type() == urid.type()) { + return rhs == urid; + } else if (rhs.type() == uri.type()) { + return rhs == uri; + } + return false; + } + + Atom urid; + Atom uri; }; Ingen::Forge& forge; @@ -105,6 +116,7 @@ public: const Quark lv2_InputPort; const Quark lv2_OutputPort; const Quark lv2_Plugin; + const Quark lv2_appliesTo; const Quark lv2_binary; const Quark lv2_connectionOptional; const Quark lv2_default; @@ -151,10 +163,12 @@ public: const Quark patch_subject; const Quark patch_value; const Quark patch_wildcard; + const Quark pset_Preset; const Quark pset_preset; const Quark pprops_logarithmic; const Quark rdf_type; const Quark rdfs_Class; + const Quark rdfs_label; const Quark rdfs_seeAlso; const Quark rsz_minimumSize; const Quark time_Position; diff --git a/ingen/client/BlockModel.hpp b/ingen/client/BlockModel.hpp index 93dcb1a7..e689414c 100644 --- a/ingen/client/BlockModel.hpp +++ b/ingen/client/BlockModel.hpp @@ -59,8 +59,8 @@ public: Node* port(uint32_t index) const; const Raul::URI& plugin_uri() const { return _plugin_uri; } - const Plugin* plugin() const { return _plugin.get(); } - Plugin* plugin() { return _plugin.get(); } + const Resource* plugin() const { return _plugin.get(); } + Resource* plugin() { return _plugin.get(); } SPtr<PluginModel> plugin_model() const { return _plugin; } uint32_t num_ports() const { return _ports.size(); } const Ports& ports() const { return _ports; } diff --git a/ingen/client/ClientStore.hpp b/ingen/client/ClientStore.hpp index 9ba4aa26..e9fda479 100644 --- a/ingen/client/ClientStore.hpp +++ b/ingen/client/ClientStore.hpp @@ -113,6 +113,7 @@ public: private: SPtr<ObjectModel> _object(const Raul::Path& path); SPtr<PluginModel> _plugin(const Raul::URI& uri); + SPtr<PluginModel> _plugin(const Atom& uri); SPtr<Resource> _resource(const Raul::URI& uri); void add_object(SPtr<ObjectModel> object); diff --git a/ingen/client/PluginModel.hpp b/ingen/client/PluginModel.hpp index bbc6308d..e468aede 100644 --- a/ingen/client/PluginModel.hpp +++ b/ingen/client/PluginModel.hpp @@ -18,11 +18,10 @@ #define INGEN_CLIENT_PLUGINMODEL_HPP #include <list> +#include <map> #include <string> #include <utility> -#include "ingen/Plugin.hpp" -#include "ingen/Resource.hpp" #include "ingen/Resource.hpp" #include "ingen/World.hpp" #include "ingen/client/signal.hpp" @@ -46,15 +45,20 @@ class PluginUI; * * @ingroup IngenClient */ -class INGEN_API PluginModel : public Ingen::Plugin +class INGEN_API PluginModel : public Ingen::Resource { public: PluginModel(URIs& uris, const Raul::URI& uri, - const Raul::URI& type_uri, + const Atom& type, const Ingen::Resource::Properties& properties); - Type type() const { return _type; } + const Atom& type() const { return _type; } + const Raul::URI type_uri() const { + return Raul::URI(_type.is_valid() + ? _uris.forge.str(_type) + : "http://www.w3.org/2002/07/owl#Nothing"); + } virtual const Atom& get_property(const Raul::URI& key) const; @@ -65,6 +69,9 @@ public: typedef std::map<float, std::string> ScalePoints; ScalePoints port_scale_points(uint32_t i) const; + typedef std::map<Raul::URI, std::string> Presets; + const Presets& presets() const { return _presets; } + static LilvWorld* lilv_world() { return _lilv_world; } const LilvPlugin* lilv_plugin() const { return _lilv_plugin; } @@ -89,11 +96,17 @@ public: // Signals INGEN_SIGNAL(changed, void); INGEN_SIGNAL(property, void, const Raul::URI&, const Atom&); + INGEN_SIGNAL(preset, void, const Raul::URI&, const std::string&); + + bool fetched() const { return _fetched; } + void set_fetched(bool f) { _fetched = f; } protected: friend class ClientStore; void set(SPtr<PluginModel> p); + void add_preset(const Raul::URI& uri, const std::string& label); + private: std::string get_documentation(const LilvNode* subject, bool html) const; @@ -101,8 +114,10 @@ private: static LilvWorld* _lilv_world; static const LilvPlugins* _lilv_plugins; - Type _type; + Atom _type; const LilvPlugin* _lilv_plugin; + Presets _presets; + bool _fetched; }; } // namespace Client diff --git a/src/Forge.cpp b/src/Forge.cpp index 89c194f9..fff4295a 100644 --- a/src/Forge.cpp +++ b/src/Forge.cpp @@ -28,6 +28,13 @@ Forge::Forge(URIMap& map) lv2_atom_forge_init(this, &map.urid_map_feature()->urid_map); } +Atom +Forge::make_urid(const Raul::URI& u) +{ + const LV2_URID urid = _map.map_uri(u); + return Atom(sizeof(int32_t), URID, &urid); +} + std::string Forge::str(const Atom& atom, bool quoted) { diff --git a/src/Resource.cpp b/src/Resource.cpp index 5e1dd13a..cbb9c3b7 100644 --- a/src/Resource.cpp +++ b/src/Resource.cpp @@ -66,10 +66,18 @@ Resource::set_property(const Raul::URI& uri, return v; } +const Atom& +Resource::set_property(const Raul::URI& uri, + const URIs::Quark& value, + Resource::Graph ctx) +{ + return set_property(uri, value.urid, ctx); +} + void Resource::remove_property(const Raul::URI& uri, const Atom& value) { - if (value == _uris.patch_wildcard) { + if (_uris.patch_wildcard == value) { _properties.erase(uri); } else { for (Properties::iterator i = _properties.find(uri); @@ -84,6 +92,13 @@ Resource::remove_property(const Raul::URI& uri, const Atom& value) on_property_removed(uri, value); } +void +Resource::remove_property(const Raul::URI& uri, const URIs::Quark& value) +{ + remove_property(uri, value.urid); + remove_property(uri, value.uri); +} + bool Resource::has_property(const Raul::URI& uri, const Atom& value) const { @@ -96,6 +111,18 @@ Resource::has_property(const Raul::URI& uri, const Atom& value) const return false; } +bool +Resource::has_property(const Raul::URI& uri, const URIs::Quark& value) const +{ + Properties::const_iterator i = _properties.find(uri); + for (; (i != _properties.end()) && (i->first == uri); ++i) { + if (value == i->second) { + return true; + } + } + return false; +} + const Atom& Resource::set_property(const Raul::URI& uri, const Atom& value) const { @@ -128,14 +155,14 @@ Resource::type(const URIs& uris, continue; // Non-URI type, ignore garbage data } - if (atom == uris.ingen_Graph) { + if (uris.ingen_Graph == atom) { graph = true; - } else if (atom == uris.ingen_Block) { + } else if (uris.ingen_Block == atom) { block = true; - } else if (atom == uris.lv2_InputPort) { + } else if (uris.lv2_InputPort == atom) { port = true; is_output = false; - } else if (atom == uris.lv2_OutputPort) { + } else if (uris.lv2_OutputPort == atom) { port = true; is_output = true; } @@ -164,7 +191,7 @@ Resource::set_properties(const Properties& props) // Erase existing properties with matching keys for (const auto& p : props) { _properties.erase(p.first); - on_property_removed(p.first, _uris.patch_wildcard); + on_property_removed(p.first, _uris.patch_wildcard.urid); } // Set new properties diff --git a/src/Serialiser.cpp b/src/Serialiser.cpp index 4ef8eaed..79660fa6 100644 --- a/src/Serialiser.cpp +++ b/src/Serialiser.cpp @@ -33,7 +33,6 @@ #include "ingen/Interface.hpp" #include "ingen/Log.hpp" #include "ingen/Node.hpp" -#include "ingen/Plugin.hpp" #include "ingen/Resource.hpp" #include "ingen/Serialiser.hpp" #include "ingen/Store.hpp" diff --git a/src/URIs.cpp b/src/URIs.cpp index 25698b93..195b1a9d 100644 --- a/src/URIs.cpp +++ b/src/URIs.cpp @@ -34,8 +34,8 @@ namespace Ingen { URIs::Quark::Quark(Forge& forge, URIMap* map, const char* c_str) : Raul::URI(c_str) - , id(map->map_uri(c_str)) - , atom(forge.alloc_uri(c_str)) + , urid(forge.make_urid(Raul::URI(c_str))) + , uri(forge.alloc_uri(c_str)) { } @@ -95,6 +95,7 @@ URIs::URIs(Forge& f, URIMap* map) , lv2_InputPort (forge, map, LV2_CORE__InputPort) , lv2_OutputPort (forge, map, LV2_CORE__OutputPort) , lv2_Plugin (forge, map, LV2_CORE__Plugin) + , lv2_appliesTo (forge, map, LV2_CORE__appliesTo) , lv2_binary (forge, map, LV2_CORE__binary) , lv2_connectionOptional(forge, map, LV2_CORE__connectionOptional) , lv2_default (forge, map, LV2_CORE__default) @@ -141,10 +142,12 @@ URIs::URIs(Forge& f, URIMap* map) , patch_subject (forge, map, LV2_PATCH__subject) , patch_value (forge, map, LV2_PATCH__value) , patch_wildcard (forge, map, LV2_PATCH__wildcard) + , pset_Preset (forge, map, LV2_PRESETS__Preset) , pset_preset (forge, map, LV2_PRESETS__preset) , pprops_logarithmic (forge, map, LV2_PORT_PROPS__logarithmic) , rdf_type (forge, map, NS_RDF "type") , rdfs_Class (forge, map, NS_RDFS "Class") + , rdfs_label (forge, map, NS_RDFS "label") , rdfs_seeAlso (forge, map, NS_RDFS "seeAlso") , rsz_minimumSize (forge, map, LV2_RESIZE_PORT__minimumSize) , time_Position (forge, map, LV2_TIME__Position) diff --git a/src/client/BlockModel.cpp b/src/client/BlockModel.cpp index 13b15012..67261dba 100644 --- a/src/client/BlockModel.cpp +++ b/src/client/BlockModel.cpp @@ -174,7 +174,7 @@ BlockModel::default_port_value_range(SPtr<const PortModel> port, max = 1.0; // Get range from client-side LV2 data - if (_plugin && _plugin->type() == PluginModel::LV2 && _plugin->lilv_plugin()) { + if (_plugin && _plugin->lilv_plugin()) { if (!_min_values) { _num_values = lilv_plugin_get_num_ports(_plugin->lilv_plugin()); _min_values = new float[_num_values]; @@ -243,7 +243,7 @@ BlockModel::port_label(SPtr<const PortModel> port) const return name.ptr<char>(); } - if (_plugin && _plugin->type() == PluginModel::LV2) { + if (_plugin && _plugin->lilv_plugin()) { LilvWorld* w = _plugin->lilv_world(); const LilvPlugin* plug = _plugin->lilv_plugin(); LilvNode* sym = lilv_new_string(w, port->symbol().c_str()); diff --git a/src/client/ClientStore.cpp b/src/client/ClientStore.cpp index 31ef4d47..5adf7f52 100644 --- a/src/client/ClientStore.cpp +++ b/src/client/ClientStore.cpp @@ -136,11 +136,18 @@ ClientStore::remove_object(const Raul::Path& path) SPtr<PluginModel> ClientStore::_plugin(const Raul::URI& uri) { - Plugins::iterator i = _plugins->find(uri); - if (i == _plugins->end()) - return SPtr<PluginModel>(); - else - return (*i).second; + const Plugins::iterator i = _plugins->find(uri); + return (i == _plugins->end()) ? SPtr<PluginModel>() : (*i).second; +} + +SPtr<PluginModel> +ClientStore::_plugin(const Atom& uri) +{ + /* FIXME: SHould probably be stored with URIs rather than strings, to make this + a fast case. */ + + const Plugins::iterator i = _plugins->find(Raul::URI(_uris.forge.str(uri, false))); + return (i == _plugins->end()) ? SPtr<PluginModel>() : (*i).second; } SPtr<const PluginModel> @@ -241,17 +248,31 @@ ClientStore::put(const Raul::URI& uri, Resource::type(uris(), properties, is_graph, is_block, is_port, is_output); - // Check if uri is a plugin - Iterator t = properties.find(_uris.rdf_type); - if (t != properties.end() && t->second.type() == _uris.forge.URI) { - const Atom& type(t->second); - const Raul::URI type_uri(type.ptr<char>()); - const Plugin::Type plugin_type(Plugin::type_from_uri(type_uri)); - if (plugin_type == Plugin::Graph) { + // Check for specially handled types + const Iterator t = properties.find(_uris.rdf_type); + if (t != properties.end()) { + const Atom& type(t->second); + if (_uris.pset_Preset == type) { + const Iterator p = properties.find(_uris.lv2_appliesTo); + const Iterator l = properties.find(_uris.rdfs_label); + SPtr<PluginModel> plug; + if (p == properties.end()) { + _log.error(fmt("Preset <%1%> with no plugin\n") % uri.c_str()); + } else if (l == properties.end()) { + _log.error(fmt("Preset <%1%> with no label\n") % uri.c_str()); + } else if (l->second.type() != _uris.forge.String) { + _log.error(fmt("Preset <%1%> label is not a string\n") % uri.c_str()); + } else if (!(plug = _plugin(p->second))) { + _log.error(fmt("Preset <%1%> for unknown plugin %2%\n") + % uri.c_str() % _uris.forge.str(p->second)); + } else { + plug->add_preset(uri, l->second.ptr<char>()); + } + return; + } else if (_uris.ingen_Graph == type) { is_graph = true; - } else if (plugin_type != Plugin::NIL) { - SPtr<PluginModel> p( - new PluginModel(uris(), uri, type_uri, properties)); + } else if (_uris.ingen_Internal == type || _uris.lv2_Plugin == type) { + SPtr<PluginModel> p(new PluginModel(uris(), uri, type, properties)); add_plugin(p); return; } @@ -292,7 +313,7 @@ ClientStore::put(const Raul::URI& uri, new PluginModel( uris(), Raul::URI(p->second.ptr<char>()), - Raul::URI("http://www.w3.org/2002/07/owl#Nothing"), + Atom(), Resource::Properties())); add_plugin(plug); } diff --git a/src/client/PluginModel.cpp b/src/client/PluginModel.cpp index f228863c..3a1f3a9a 100644 --- a/src/client/PluginModel.cpp +++ b/src/client/PluginModel.cpp @@ -42,28 +42,28 @@ Sord::World* PluginModel::_rdf_world = NULL; PluginModel::PluginModel(URIs& uris, const Raul::URI& uri, - const Raul::URI& type_uri, + const Atom& type, const Resource::Properties& properties) - : Plugin(uris, uri) - , _type(type_from_uri(type_uri)) + : Resource(uris, uri) + , _type(type) + , _fetched(false) { - if (_type == NIL) { + if (!_type.is_valid()) { if (uri.find("ingen-internals") != string::npos) { - _type = Internal; + _type = uris.ingen_Internal; } else { - _type = LV2; // Assume LV2 and hope Lilv can tell us something + _type = uris.lv2_Plugin; // Assume LV2 and hope for the best... } } + + add_property(uris.rdf_type, type); add_properties(properties); - assert(_rdf_world); - add_property(uris.rdf_type, - uris.forge.alloc_uri(this->type_uri())); 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 (_type == Internal) { + if (uris.ingen_Internal == _type) { set_property(uris.doap_name, uris.forge.alloc(uri.substr(uri.find_last_of('#') + 1))); } @@ -166,6 +166,13 @@ PluginModel::set(SPtr<PluginModel> p) _signal_changed.emit(); } +void +PluginModel::add_preset(const Raul::URI& uri, const std::string& label) +{ + _presets.insert(std::make_pair(uri, label)); + _signal_preset.emit(uri, label); +} + Raul::Symbol PluginModel::default_block_symbol() const { diff --git a/src/client/PluginUI.cpp b/src/client/PluginUI.cpp index f959fd3b..69e9eb95 100644 --- a/src/client/PluginUI.cpp +++ b/src/client/PluginUI.cpp @@ -75,7 +75,7 @@ lv2_ui_write(SuilController controller, uris.ingen_value, ui->world()->forge().make(value)); - } else if (format == uris.atom_eventTransfer.id) { + } else if (format == uris.atom_eventTransfer.urid.get<LV2_URID>()) { const LV2_Atom* atom = (const LV2_Atom*)buffer; Atom val = ui->world()->forge().alloc( atom->size, atom->type, LV2_ATOM_BODY_CONST(atom)); diff --git a/src/gui/GraphCanvas.cpp b/src/gui/GraphCanvas.cpp index 9987e0b8..e4e5c120 100644 --- a/src/gui/GraphCanvas.cpp +++ b/src/gui/GraphCanvas.cpp @@ -297,7 +297,7 @@ GraphCanvas::show_port_names(bool b) void GraphCanvas::add_plugin(SPtr<PluginModel> p) { - if (_internal_menu && p->type() == Plugin::Internal) { + if (_internal_menu && _app.uris().ingen_Internal == p->type()) { _internal_menu->items().push_back( Gtk::Menu_Helpers::MenuElem( std::string("_") + p->human_name(), diff --git a/src/gui/LoadPluginWindow.cpp b/src/gui/LoadPluginWindow.cpp index eb604b91..073936a8 100644 --- a/src/gui/LoadPluginWindow.cpp +++ b/src/gui/LoadPluginWindow.cpp @@ -276,29 +276,24 @@ LoadPluginWindow::set_row(Gtk::TreeModel::Row& row, if (name.is_valid() && name.type() == uris.forge.String) row[_plugins_columns._col_name] = name.ptr<char>(); - switch (plugin->type()) { - case Plugin::NIL: - row[_plugins_columns._col_type] = ""; - break; - case Plugin::LV2: + if (uris.lv2_Plugin == plugin->type()) { row[_plugins_columns._col_type] = lilv_node_as_string( lilv_plugin_class_get_label( lilv_plugin_get_class(plugin->lilv_plugin()))); row[_plugins_columns._col_project] = get_project_name(plugin); row[_plugins_columns._col_author] = get_author_name(plugin); - break; - case Plugin::Internal: + } else if (uris.ingen_Internal == plugin->type()) { row[_plugins_columns._col_type] = "Internal"; row[_plugins_columns._col_project] = "Ingen"; row[_plugins_columns._col_author] = "David Robillard"; - break; - case Plugin::Graph: + } else if (uris.ingen_Graph == plugin->type()) { row[_plugins_columns._col_type] = "Graph"; - break; + } else { + row[_plugins_columns._col_type] = ""; } - row[_plugins_columns._col_uri] = plugin->uri(); + row[_plugins_columns._col_uri] = plugin->uri(); row[_plugins_columns._col_plugin] = plugin; } diff --git a/src/gui/NodeMenu.cpp b/src/gui/NodeMenu.cpp index 1012f7eb..0acce065 100644 --- a/src/gui/NodeMenu.cpp +++ b/src/gui/NodeMenu.cpp @@ -16,6 +16,8 @@ #include <string> +#include <gtkmm/entry.h> +#include <gtkmm/filechooserdialog.h> #include <gtkmm/image.h> #include <gtkmm/stock.h> @@ -66,8 +68,19 @@ NodeMenu::init(App& app, SPtr<const Client::BlockModel> block) _randomize_menuitem->signal_activate().connect( sigc::mem_fun(this, &NodeMenu::on_menu_randomize)); - const PluginModel* plugin = dynamic_cast<const PluginModel*>(block->plugin()); - if (plugin && plugin->type() == PluginModel::LV2 && plugin->has_ui()) { + SPtr<PluginModel> plugin = block->plugin_model(); + if (plugin) { + // Get the plugin to receive related presets + _preset_connection = plugin->signal_preset().connect( + sigc::mem_fun(this, &NodeMenu::add_preset)); + + if (!plugin->fetched()) { + _app->interface()->get(plugin->uri()); + plugin->set_fetched(true); + } + } + + if (plugin && plugin->has_ui()) { _popup_gui_menuitem->show(); _embed_gui_menuitem->show(); const Atom& ui_embedded = block->get_property( @@ -82,53 +95,25 @@ NodeMenu::init(App& app, SPtr<const Client::BlockModel> block) const Atom& enabled = block->get_property(_app->uris().ingen_enabled); _enabled_menuitem->set_active(!enabled.is_valid() || enabled.get<int32_t>()); - if (plugin && plugin->type() == PluginModel::LV2) { - - LilvNode* pset_Preset = lilv_new_uri(plugin->lilv_world(), - LV2_PRESETS__Preset); - LilvNode* rdfs_label = lilv_new_uri(plugin->lilv_world(), - LILV_NS_RDFS "label"); - LilvNodes* presets = lilv_plugin_get_related(plugin->lilv_plugin(), - pset_Preset); - if (presets) { - _presets_menu = Gtk::manage(new Gtk::Menu()); - - unsigned n_presets = 0; - LILV_FOREACH(nodes, i, presets) { - const LilvNode* preset = lilv_nodes_get(presets, i); - lilv_world_load_resource(plugin->lilv_world(), preset); - LilvNodes* labels = lilv_world_find_nodes( - plugin->lilv_world(), preset, rdfs_label, NULL); - if (labels) { - const LilvNode* label = lilv_nodes_get_first(labels); - _presets_menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - lilv_node_as_string(label), - sigc::bind( - sigc::mem_fun(this, &NodeMenu::on_preset_activated), - string(lilv_node_as_string(preset))))); - - lilv_nodes_free(labels); - ++n_presets; - } else { - app.log().error( - fmt("Preset <%1%> has no rdfs:label\n") - % lilv_node_as_string(lilv_nodes_get(presets, i))); - } - } - - if (n_presets > 0) { - items().push_front( - Gtk::Menu_Helpers::ImageMenuElem( - "_Presets", - *(manage(new Gtk::Image(Gtk::Stock::INDEX, Gtk::ICON_SIZE_MENU))))); - Gtk::MenuItem* presets_menu_item = &(items().front()); - presets_menu_item->set_submenu(*_presets_menu); - } - lilv_nodes_free(presets); + if (plugin && _app->uris().lv2_Plugin == plugin->type()) { + _presets_menu = Gtk::manage(new Gtk::Menu()); + _presets_menu->items().push_back( + Gtk::Menu_Helpers::MenuElem( + "_Save Preset...", + sigc::mem_fun(this, &NodeMenu::on_save_preset_activated))); + _presets_menu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); + + for (const auto& p : plugin->presets()) { + add_preset(p.first, p.second); } - lilv_node_free(pset_Preset); - lilv_node_free(rdfs_label); + + items().push_front( + Gtk::Menu_Helpers::ImageMenuElem( + "_Presets", + *(manage(new Gtk::Image(Gtk::Stock::INDEX, Gtk::ICON_SIZE_MENU))))); + + Gtk::MenuItem* presets_menu_item = &(items().front()); + presets_menu_item->set_submenu(*_presets_menu); } if (has_control_inputs()) @@ -152,6 +137,18 @@ NodeMenu::init(App& app, SPtr<const Client::BlockModel> block) } void +NodeMenu::add_preset(const Raul::URI& uri, const std::string& label) +{ + if (_presets_menu) { + _presets_menu->items().push_back( + Gtk::Menu_Helpers::MenuElem( + label, + sigc::bind(sigc::mem_fun(this, &NodeMenu::on_preset_activated), + uri))); + } +} + +void NodeMenu::on_menu_embed_gui() { signal_embed_gui.emit(_embed_gui_menuitem->get_active()); @@ -192,12 +189,52 @@ NodeMenu::on_menu_disconnect() } void +NodeMenu::on_save_preset_activated() +{ + Gtk::FileChooserDialog dialog("Save Preset", Gtk::FILE_CHOOSER_ACTION_SAVE); + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + dialog.set_default_response(Gtk::RESPONSE_OK); + dialog.set_current_folder(Glib::build_filename(Glib::get_home_dir(), ".lv2")); + + Gtk::HBox* extra = Gtk::manage(new Gtk::HBox()); + Gtk::Label* label = Gtk::manage(new Gtk::Label("URI (Optional): ")); + Gtk::Entry* entry = Gtk::manage(new Gtk::Entry()); + extra->pack_start(*label, false, true, 4); + extra->pack_start(*entry, true, true, 4); + extra->show_all(); + dialog.set_extra_widget(*Gtk::manage(extra)); + + if (dialog.run() == Gtk::RESPONSE_OK) { + const std::string user_uri = dialog.get_uri(); + const std::string user_path = Glib::filename_from_uri(user_uri); + const std::string dirname = Glib::path_get_dirname(user_path); + const std::string basename = Glib::path_get_basename(user_path); + const std::string sym = Raul::Symbol::symbolify(basename); + const std::string plugname = block()->plugin_model()->human_name(); + const std::string prefix = Raul::Symbol::symbolify(plugname); + const std::string bundle = prefix + "_" + sym + ".preset.lv2/"; + const std::string file = sym + ".ttl"; + const std::string real_path = Glib::build_filename(dirname, bundle, file); + const std::string real_uri = Glib::filename_to_uri(real_path); + + Resource::Properties props; + props.emplace(_app->uris().rdf_type, + _app->uris().pset_Preset); + props.emplace(_app->uris().rdfs_label, + _app->forge().alloc(basename)); + props.emplace(_app->uris().lv2_prototype, + _app->forge().alloc_uri(block()->uri())); + _app->interface()->put(Raul::URI(real_uri), props); + } +} + +void NodeMenu::on_preset_activated(const std::string& uri) { _app->set_property(block()->uri(), _app->uris().pset_preset, _app->forge().alloc_uri(uri)); - } bool diff --git a/src/gui/NodeMenu.hpp b/src/gui/NodeMenu.hpp index bcfe68a9..c8ac3daf 100644 --- a/src/gui/NodeMenu.hpp +++ b/src/gui/NodeMenu.hpp @@ -52,10 +52,13 @@ protected: return dynamic_ptr_cast<const Client::BlockModel>(_object); } + void add_preset(const Raul::URI& uri, const std::string& label); + void on_menu_disconnect(); void on_menu_embed_gui(); void on_menu_enabled(); void on_menu_randomize(); + void on_save_preset_activated(); void on_preset_activated(const std::string& uri); Gtk::MenuItem* _popup_gui_menuitem; @@ -63,6 +66,7 @@ protected: Gtk::CheckMenuItem* _enabled_menuitem; Gtk::MenuItem* _randomize_menuitem; Gtk::Menu* _presets_menu; + sigc::connection _preset_connection; }; } // namespace GUI diff --git a/src/gui/NodeModule.cpp b/src/gui/NodeModule.cpp index 37c8e617..46f388d2 100644 --- a/src/gui/NodeModule.cpp +++ b/src/gui/NodeModule.cpp @@ -307,7 +307,7 @@ NodeModule::delete_port_view(SPtr<const PortModel> model) bool NodeModule::popup_gui() { - if (_block->plugin() && _block->plugin()->type() == PluginModel::LV2) { + if (_block->plugin() && app().uris().lv2_Plugin == _block->plugin_model()->type()) { if (_plugin_ui) { app().log().warn("LV2 GUI already embedded, cannot pop up\n"); return false; diff --git a/src/server/BlockImpl.cpp b/src/server/BlockImpl.cpp index ecc73090..103747fe 100644 --- a/src/server/BlockImpl.cpp +++ b/src/server/BlockImpl.cpp @@ -71,12 +71,18 @@ BlockImpl::port(uint32_t index) const return (*_ports)[index]; } -const Plugin* +const Resource* BlockImpl::plugin() const { return _plugin; } +const PluginImpl* +BlockImpl::plugin_impl() const +{ + return _plugin; +} + void BlockImpl::activate(BufferFactory& bufs) { diff --git a/src/server/BlockImpl.hpp b/src/server/BlockImpl.hpp index 44271cc3..2dcc4762 100644 --- a/src/server/BlockImpl.hpp +++ b/src/server/BlockImpl.hpp @@ -20,6 +20,7 @@ #include <list> #include <boost/intrusive/slist.hpp> +#include <boost/optional.hpp> #include "lilv/lilv.h" @@ -28,6 +29,7 @@ #include "BufferRef.hpp" #include "Context.hpp" #include "NodeImpl.hpp" +#include "PluginImpl.hpp" #include "PortType.hpp" #include "types.hpp" @@ -36,9 +38,6 @@ class Maid; } namespace Ingen { - -class Plugin; - namespace Server { class Buffer; @@ -106,6 +105,11 @@ public: /** Restore `state`. */ virtual void apply_state(LilvState* state) {} + /** Save current state as preset. */ + virtual boost::optional<Resource> + save_preset(const Raul::URI& bundle, + const Properties& props) { return boost::optional<Resource>(); } + /** Learn the next incoming MIDI event (for internals) */ virtual void learn() {} @@ -152,14 +156,14 @@ public: ProcessContext& context, Raul::Maid& maid, uint32_t poly); /** Information about the Plugin this Block is an instance of. - * Not the best name - not all blocks come from plugins (e.g. Graph) + * Not the best name - not all blocks come from plugins (ie Graph) */ - virtual PluginImpl* plugin_impl() const { return _plugin; } + virtual const Resource* plugin() const; /** Information about the Plugin this Block is an instance of. * Not the best name - not all blocks come from plugins (ie Graph) */ - virtual const Plugin* plugin() const; + virtual const PluginImpl* plugin_impl() const; virtual void plugin(PluginImpl* pi) { _plugin = pi; } diff --git a/src/server/ControlBindings.cpp b/src/server/ControlBindings.cpp index 40255fff..c4a95476 100644 --- a/src/server/ControlBindings.cpp +++ b/src/server/ControlBindings.cpp @@ -180,7 +180,7 @@ ControlBindings::port_value_changed(ProcessContext& context, break; } if (size > 0) { - _feedback->append_event(0, size, uris.midi_MidiEvent.id, buf); + _feedback->append_event(0, size, (LV2_URID)uris.midi_MidiEvent, buf); } } } diff --git a/src/server/DuplexPort.cpp b/src/server/DuplexPort.cpp index 0a928a3f..f681e250 100644 --- a/src/server/DuplexPort.cpp +++ b/src/server/DuplexPort.cpp @@ -91,28 +91,25 @@ DuplexPort::inherit_neighbour(const PortImpl* port, Resource::Properties& remove, Resource::Properties& add) { + const URIs& uris = _bufs.uris(); + /* TODO: This needs to become more sophisticated, and correct the situation if the port is disconnected. */ if (_type == PortType::CONTROL || _type == PortType::CV) { if (port->minimum().get<float>() < _min.get<float>()) { _min = port->minimum(); - remove.insert(std::make_pair(_bufs.uris().lv2_minimum, - Property(_bufs.uris().patch_wildcard))); - add.insert(std::make_pair(_bufs.uris().lv2_minimum, - port->minimum())); + remove.emplace(uris.lv2_minimum, uris.patch_wildcard); + add.emplace(uris.lv2_minimum, port->minimum()); } if (port->maximum().get<float>() > _max.get<float>()) { _max = port->maximum(); - remove.insert(std::make_pair(_bufs.uris().lv2_maximum, - Property(_bufs.uris().patch_wildcard))); - add.insert(std::make_pair(_bufs.uris().lv2_maximum, - port->maximum())); + remove.emplace(uris.lv2_maximum, uris.patch_wildcard); + add.emplace(uris.lv2_maximum, port->maximum()); } } else if (_type == PortType::ATOM) { for (Resource::Properties::const_iterator i = port->properties().find( - _bufs.uris().atom_supports); - i != port->properties().end() && - i->first == _bufs.uris().atom_supports; + uris.atom_supports); + i != port->properties().end() && i->first == uris.atom_supports; ++i) { set_property(i->first, i->second); add.insert(*i); diff --git a/src/server/EventWriter.cpp b/src/server/EventWriter.cpp index 1ec5e1a7..6c04c73d 100644 --- a/src/server/EventWriter.cpp +++ b/src/server/EventWriter.cpp @@ -127,11 +127,11 @@ EventWriter::set_property(const Raul::URI& uri, const Atom& value) { Resource::Properties remove; - remove.insert( - make_pair(predicate, - Resource::Property(_engine.world()->uris().patch_wildcard))); + remove.emplace(predicate, + _engine.world()->uris().patch_wildcard.urid); + Resource::Properties add; - add.insert(make_pair(predicate, value)); + add.emplace(predicate, value); _engine.enqueue_event( new Events::Delta(_engine, _respondee, _request_id, now(), Events::Delta::Type::SET, Resource::Graph::DEFAULT, diff --git a/src/server/GraphPlugin.hpp b/src/server/GraphPlugin.hpp index 8cd08c1b..e18e173a 100644 --- a/src/server/GraphPlugin.hpp +++ b/src/server/GraphPlugin.hpp @@ -36,7 +36,7 @@ public: const Raul::URI& uri, const Raul::Symbol& symbol, const std::string& name) - : PluginImpl(uris, Plugin::Graph, uri) + : PluginImpl(uris, uris.ingen_Graph, uri) {} BlockImpl* instantiate(BufferFactory& bufs, diff --git a/src/server/InputPort.cpp b/src/server/InputPort.cpp index 974fe3ab..f2aacea1 100644 --- a/src/server/InputPort.cpp +++ b/src/server/InputPort.cpp @@ -51,7 +51,7 @@ InputPort::InputPort(BufferFactory& bufs, const Ingen::URIs& uris = bufs.uris(); if (parent->graph_type() != Node::GraphType::GRAPH) { - add_property(uris.rdf_type, uris.lv2_InputPort); + add_property(uris.rdf_type, uris.lv2_InputPort.urid); } } diff --git a/src/server/InternalPlugin.cpp b/src/server/InternalPlugin.cpp index f1ee9024..647823f7 100644 --- a/src/server/InternalPlugin.cpp +++ b/src/server/InternalPlugin.cpp @@ -35,7 +35,7 @@ using namespace Internals; InternalPlugin::InternalPlugin(URIs& uris, const Raul::URI& uri, const Raul::Symbol& symbol) - : PluginImpl(uris, Plugin::Internal, uri) + : PluginImpl(uris, uris.ingen_Internal, uri) , _symbol(symbol) { set_property(uris.rdf_type, uris.ingen_Internal); diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp index 30d1f090..6b644e5c 100644 --- a/src/server/LV2Block.cpp +++ b/src/server/LV2Block.cpp @@ -19,6 +19,9 @@ #include <cassert> #include <cmath> +#include <glibmm/miscutils.h> +#include <glibmm/convert.h> + #include "lv2/lv2plug.in/ns/ext/morph/morph.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/options/options.h" @@ -577,6 +580,74 @@ LV2Block::apply_state(LilvState* state) } } +static const void* +get_port_value(const char* port_symbol, + void* user_data, + uint32_t* size, + uint32_t* type) +{ + LV2Block* const block = (LV2Block*)user_data; + PortImpl* const port = block->port_by_symbol(port_symbol); + + if (port && port->is_input() && port->value().is_valid()) { + *size = port->value().size(); + *type = port->value().type(); + return port->value().get_body(); + } + + return NULL; +} + +boost::optional<Resource> +LV2Block::save_preset(const Raul::URI& uri, + const Properties& props) +{ + World* world = parent_graph()->engine().world(); + LilvWorld* lworld = _lv2_plugin->lv2_info()->lv2_world(); + LV2_URID_Map* lmap = &world->uri_map().urid_map_feature()->urid_map; + LV2_URID_Unmap* lunmap = &world->uri_map().urid_unmap_feature()->urid_unmap; + + const std::string path = Glib::filename_from_uri(uri); + const std::string dirname = Glib::path_get_dirname(path); + const std::string basename = Glib::path_get_basename(path); + + LilvState* state = lilv_state_new_from_instance( + _lv2_plugin->lilv_plugin(), instance(0), lmap, + NULL, NULL, NULL, path.c_str(), + get_port_value, this, LV2_STATE_IS_NATIVE, NULL); + + if (state) { + const Properties::const_iterator l = props.find(_uris.rdfs_label); + if (l != props.end() && l->second.type() == _uris.atom_String) { + lilv_state_set_label(state, l->second.ptr<char>()); + } + + lilv_state_save(lworld, lmap, lunmap, state, NULL, + dirname.c_str(), basename.c_str()); + + const Raul::URI uri(lilv_node_as_uri(lilv_state_get_uri(state))); + const std::string label(lilv_state_get_label(state) + ? lilv_state_get_label(state) + : basename); + lilv_state_free(state); + + Resource preset(_uris, uri); + preset.set_property(_uris.rdf_type, _uris.pset_Preset); + preset.set_property(_uris.rdfs_label, world->forge().alloc(label)); + preset.set_property(_uris.lv2_appliesTo, + world->forge().make_urid(_lv2_plugin->uri())); + + LilvNode* lbundle = lilv_new_uri( + lworld, Glib::filename_to_uri(dirname + "/").c_str()); + lilv_world_load_bundle(lworld, lbundle); + lilv_node_free(lbundle); + + return preset; + } + + return boost::optional<Resource>(); +} + void LV2Block::set_port_buffer(uint32_t voice, uint32_t port_num, diff --git a/src/server/LV2Block.hpp b/src/server/LV2Block.hpp index f07f845f..ffb86d0a 100644 --- a/src/server/LV2Block.hpp +++ b/src/server/LV2Block.hpp @@ -67,6 +67,9 @@ public: void apply_state(LilvState* state); + boost::optional<Resource> save_preset(const Raul::URI& bundle, + const Properties& props); + void set_port_buffer(uint32_t voice, uint32_t port_num, BufferRef buf, diff --git a/src/server/LV2Plugin.cpp b/src/server/LV2Plugin.cpp index 58491f4c..d88689ca 100644 --- a/src/server/LV2Plugin.cpp +++ b/src/server/LV2Plugin.cpp @@ -16,7 +16,9 @@ #include <string> +#include "ingen/Log.hpp" #include "ingen/URIs.hpp" +#include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "Driver.hpp" #include "Engine.hpp" @@ -29,7 +31,9 @@ namespace Ingen { namespace Server { LV2Plugin::LV2Plugin(SPtr<LV2Info> lv2_info, const Raul::URI& uri) - : PluginImpl(lv2_info->world().uris(), Plugin::LV2, uri) + : PluginImpl(lv2_info->world().uris(), + lv2_info->world().uris().lv2_Plugin, + uri) , _lilv_plugin(NULL) , _lv2_info(lv2_info) { @@ -80,5 +84,43 @@ LV2Plugin::lilv_plugin(const LilvPlugin* p) _lilv_plugin = p; } +void +LV2Plugin::load_presets() +{ + LilvWorld* lworld = _lv2_info->world().lilv_world(); + LilvNode* pset_Preset = lilv_new_uri(lworld, LV2_PRESETS__Preset); + LilvNode* rdfs_label = lilv_new_uri(lworld, LILV_NS_RDFS "label"); + LilvNodes* presets = lilv_plugin_get_related(_lilv_plugin, pset_Preset); + + if (presets) { + LILV_FOREACH(nodes, i, presets) { + const LilvNode* preset = lilv_nodes_get(presets, i); + lilv_world_load_resource(lworld, preset); + + LilvNodes* labels = lilv_world_find_nodes( + lworld, preset, rdfs_label, NULL); + if (labels) { + const LilvNode* label = lilv_nodes_get_first(labels); + + _presets.emplace(Raul::URI(lilv_node_as_uri(preset)), + lilv_node_as_string(label)); + + lilv_nodes_free(labels); + } else { + _lv2_info->world().log().error( + fmt("Preset <%1%> has no rdfs:label\n") + % lilv_node_as_string(lilv_nodes_get(presets, i))); + } + } + + lilv_nodes_free(presets); + } + + lilv_node_free(rdfs_label); + lilv_node_free(pset_Preset); + + PluginImpl::load_presets(); +} + } // namespace Server } // namespace Ingen diff --git a/src/server/LV2Plugin.hpp b/src/server/LV2Plugin.hpp index 0dde120c..63946071 100644 --- a/src/server/LV2Plugin.hpp +++ b/src/server/LV2Plugin.hpp @@ -52,6 +52,8 @@ public: const LilvPlugin* lilv_plugin() const { return _lilv_plugin; } void lilv_plugin(const LilvPlugin* p); + void load_presets(); + private: const LilvPlugin* _lilv_plugin; SPtr<LV2Info> _lv2_info; diff --git a/src/server/NodeImpl.cpp b/src/server/NodeImpl.cpp index 27d1ba4e..c95f3c5e 100644 --- a/src/server/NodeImpl.cpp +++ b/src/server/NodeImpl.cpp @@ -23,7 +23,7 @@ using namespace std; namespace Ingen { namespace Server { -NodeImpl::NodeImpl(Ingen::URIs& uris, +NodeImpl::NodeImpl(const Ingen::URIs& uris, NodeImpl* parent, const Raul::Symbol& symbol) : Node(uris, parent ? parent->path().child(symbol) : Raul::Path("/")) diff --git a/src/server/NodeImpl.hpp b/src/server/NodeImpl.hpp index 17238a74..457834f2 100644 --- a/src/server/NodeImpl.hpp +++ b/src/server/NodeImpl.hpp @@ -92,7 +92,7 @@ public: ProcessContext& context, Raul::Maid& maid, uint32_t poly) = 0; protected: - NodeImpl(Ingen::URIs& uris, + NodeImpl(const Ingen::URIs& uris, NodeImpl* parent, const Raul::Symbol& symbol); diff --git a/src/server/PluginImpl.hpp b/src/server/PluginImpl.hpp index 92338b70..414bd1f1 100644 --- a/src/server/PluginImpl.hpp +++ b/src/server/PluginImpl.hpp @@ -21,7 +21,6 @@ #include <boost/utility.hpp> -#include "ingen/Plugin.hpp" #include "ingen/Resource.hpp" #include "raul/Symbol.hpp" #include "raul/URI.hpp" @@ -41,17 +40,19 @@ class GraphImpl; * * Conceptually, a Block is an instance of this. */ -class PluginImpl : public Plugin +class PluginImpl : public Resource , public boost::noncopyable { public: PluginImpl(Ingen::URIs& uris, - Type type, + const Atom& type, const Raul::URI& uri) - : Plugin(uris, uri) + : Resource(uris, uri) , _type(type) {} + virtual ~PluginImpl() {} + virtual BlockImpl* instantiate(BufferFactory& bufs, const Raul::Symbol& symbol, bool polyphonic, @@ -60,11 +61,26 @@ public: virtual const Raul::Symbol symbol() const = 0; - Plugin::Type type() const { return _type; } - void type(Plugin::Type t) { _type = t; } + const Atom& type() const { return _type; } + void set_type(const Atom& t) { _type = t; } + + typedef std::pair<Raul::URI, std::string> Preset; + typedef std::map<Raul::URI, std::string> Presets; + + const Presets& presets(bool force_reload=false) { + if (!_presets_loaded || force_reload) { + load_presets(); + } + + return _presets; + } + + virtual void load_presets() { _presets_loaded = true; } protected: - Plugin::Type _type; + Atom _type; + Presets _presets; + bool _presets_loaded; }; } // namespace Server diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index 7d2f896d..dc6759b0 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -34,6 +34,7 @@ #include "PortImpl.hpp" #include "PortType.hpp" #include "SetPortValue.hpp" +#include "events/Get.hpp" // #define DUMP 1 // #include "ingen/URIMap.hpp" @@ -134,10 +135,45 @@ s_add_set_event(const char* port_symbol, bool Delta::pre_process() { + const Ingen::URIs& uris = _engine.world()->uris(); + const bool is_graph_object = Node::uri_is_path(_subject); const bool is_client = (_subject == "ingen:/clients/this"); + const bool is_file = (_subject.substr(0, 5) == "file:"); bool poly_changed = false; + if (_type == Type::PUT && is_file) { + // Ensure type is Preset, the only supported file put + const auto t = _properties.find(uris.rdf_type); + if (t == _properties.end() || t->second != uris.pset_Preset) { + return Event::pre_process_done(Status::BAD_REQUEST, _subject); + } + + // Get "prototype" for preset (node to save state for) + const auto p = _properties.find(uris.lv2_prototype); + if (p == _properties.end()) { + return Event::pre_process_done(Status::BAD_REQUEST, _subject); + } + + const Raul::URI prot(_engine.world()->forge().str(p->second, false)); + + Node* node = _engine.store()->get(Node::uri_to_path(Raul::URI(prot))); + if (!node) { + return Event::pre_process_done(Status::NOT_FOUND, prot); + } + + BlockImpl* block = dynamic_cast<BlockImpl*>(node); + if (!block) { + return Event::pre_process_done(Status::BAD_OBJECT_TYPE, prot); + } + + if ((_preset = block->save_preset(_subject, _properties))) { + return Event::pre_process_done(Status::SUCCESS); + } else { + return Event::pre_process_done(Status::FAILURE); + } + } + // Take a writer lock while we modify the store std::unique_lock<std::mutex> lock(_engine.store()->mutex()); @@ -149,8 +185,6 @@ Delta::pre_process() return Event::pre_process_done(Status::NOT_FOUND, _subject); } - const Ingen::URIs& uris = _engine.world()->uris(); - if (is_graph_object && !_object) { Raul::Path path(Node::uri_to_path(_subject)); bool is_graph = false, is_block = false, is_port = false, is_output = false; @@ -258,6 +292,8 @@ Delta::pre_process() if ((_state = block->load_preset(Raul::URI(str)))) { lilv_state_emit_port_values( _state, s_add_set_event, this); + } else { + _engine.log().warn(fmt("Failed to load preset <%1%>\n") % str); } } else { _status = Status::BAD_VALUE; @@ -341,7 +377,7 @@ Delta::pre_process() void Delta::execute(ProcessContext& context) { - if (_status != Status::SUCCESS) { + if (_status != Status::SUCCESS || _preset) { return; } @@ -406,7 +442,7 @@ Delta::execute(ProcessContext& context) if (port) { _engine.control_bindings()->port_binding_changed(context, port, value); } else if (block) { - if (block->plugin_impl()->type() == Plugin::Internal) { + if (uris.ingen_Internal == block->plugin_impl()->type()) { block->learn(); } } @@ -472,7 +508,15 @@ Delta::post_process() _engine.broadcaster()->clear_ignore_client(); break; case Type::PUT: - _engine.broadcaster()->put(_subject, _properties, _context); + if (_type == Type::PUT && _subject.substr(0, 5) == "file:") { + // Preset save + Get::Response response; + response.put(_preset->uri(), _preset->properties()); + response.send(_engine.broadcaster()); + } else { + // Graph object put + _engine.broadcaster()->put(_subject, _properties, _context); + } break; case Type::PATCH: _engine.broadcaster()->delta(_subject, _remove, _properties); diff --git a/src/server/events/Delta.hpp b/src/server/events/Delta.hpp index 0b5934ac..7c303fc2 100644 --- a/src/server/events/Delta.hpp +++ b/src/server/events/Delta.hpp @@ -19,10 +19,13 @@ #include <vector> +#include <boost/optional.hpp> + #include "lilv/lilv.h" #include "raul/URI.hpp" +#include "PluginImpl.hpp" #include "ControlBindings.hpp" #include "Event.hpp" @@ -128,6 +131,8 @@ private: SPtr<ControlBindings::Bindings> _old_bindings; + boost::optional<Resource> _preset; + std::unique_lock<std::mutex> _poly_lock; ///< Long-term lock for poly changes }; diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp index bba95485..185f275a 100644 --- a/src/server/events/Get.cpp +++ b/src/server/events/Get.cpp @@ -60,8 +60,10 @@ Get::Response::put_port(const PortImpl* port) void Get::Response::put_block(const BlockImpl* block) { - PluginImpl* const plugin = block->plugin_impl(); - if (plugin->type() == Plugin::Graph) { + const PluginImpl* const plugin = block->plugin_impl(); + const URIs& uris = plugin->uris(); + + if (uris.ingen_Graph == plugin->type()) { put_graph((const GraphImpl*)block); } else { put(block->uri(), block->properties()); @@ -82,7 +84,7 @@ Get::Response::put_graph(const GraphImpl* graph) graph->properties(Resource::Graph::EXTERNAL), Resource::Graph::EXTERNAL); - // Enqueue locks + // Enqueue blocks for (const auto& b : graph->blocks()) { put_block(&b); } @@ -100,6 +102,29 @@ Get::Response::put_graph(const GraphImpl* graph) } } +void +Get::Response::put_plugin(PluginImpl* plugin) +{ + put(plugin->uri(), plugin->properties()); + + for (const auto& p : plugin->presets()) { + put_preset(plugin->uris(), plugin->uri(), p.first, p.second); + } +} + +void +Get::Response::put_preset(const URIs& uris, + const Raul::URI& plugin, + const Raul::URI& preset, + const std::string& label) +{ + Resource::Properties props; + props.emplace(uris.rdf_type, uris.pset_Preset.urid); + props.emplace(uris.rdfs_label, uris.forge.alloc(label)); + props.emplace(uris.lv2_appliesTo, uris.forge.make_urid(plugin)); + put(preset, props); +} + /** Returns true if a is closer to the root than b. */ static inline bool put_higher_than(const Get::Response::Put& a, const Get::Response::Put& b) @@ -159,11 +184,10 @@ Get::pre_process() return Event::pre_process_done(Status::SUCCESS); } return Event::pre_process_done(Status::NOT_FOUND, _uri); + } else if ((_plugin = _engine.block_factory()->plugin(_uri))) { + _response.put_plugin(_plugin); + return Event::pre_process_done(Status::SUCCESS); } else { - if ((_plugin = _engine.block_factory()->plugin(_uri))) { - _response.put(_uri, _plugin->properties()); - return Event::pre_process_done(Status::SUCCESS); - } return Event::pre_process_done(Status::NOT_FOUND, _uri); } } diff --git a/src/server/events/Get.hpp b/src/server/events/Get.hpp index 5a4bde23..f24e42e0 100644 --- a/src/server/events/Get.hpp +++ b/src/server/events/Get.hpp @@ -67,6 +67,11 @@ public: void put_port(const PortImpl* port); void put_block(const BlockImpl* block); void put_graph(const GraphImpl* graph); + void put_plugin(PluginImpl* plugin); + void put_preset(const URIs& uris, + const Raul::URI& plugin, + const Raul::URI& preset, + const std::string& label); void send(Interface* dest); @@ -88,7 +93,7 @@ public: private: const Raul::URI _uri; const Node* _object; - const PluginImpl* _plugin; + PluginImpl* _plugin; BlockFactory::Plugins _plugins; Response _response; }; |