summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-08-12 04:46:29 +0000
committerDavid Robillard <d@drobilla.net>2015-08-12 04:46:29 +0000
commitdd79e76e41446833088482588456afed37231bff (patch)
treec0f3c5c2fc74b286d529df69ad2206e2fddd96f9
parent44af7b7b66e2083819103c760ab3bf4980469f86 (diff)
downloadingen-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
-rw-r--r--ingen/Forge.hpp3
-rw-r--r--ingen/Node.hpp9
-rw-r--r--ingen/Plugin.hpp70
-rw-r--r--ingen/Resource.hpp44
-rw-r--r--ingen/URIs.hpp22
-rw-r--r--ingen/client/BlockModel.hpp4
-rw-r--r--ingen/client/ClientStore.hpp1
-rw-r--r--ingen/client/PluginModel.hpp27
-rw-r--r--src/Forge.cpp7
-rw-r--r--src/Resource.cpp39
-rw-r--r--src/Serialiser.cpp1
-rw-r--r--src/URIs.cpp7
-rw-r--r--src/client/BlockModel.cpp4
-rw-r--r--src/client/ClientStore.cpp53
-rw-r--r--src/client/PluginModel.cpp27
-rw-r--r--src/client/PluginUI.cpp2
-rw-r--r--src/gui/GraphCanvas.cpp2
-rw-r--r--src/gui/LoadPluginWindow.cpp17
-rw-r--r--src/gui/NodeMenu.cpp135
-rw-r--r--src/gui/NodeMenu.hpp4
-rw-r--r--src/gui/NodeModule.cpp2
-rw-r--r--src/server/BlockImpl.cpp8
-rw-r--r--src/server/BlockImpl.hpp16
-rw-r--r--src/server/ControlBindings.cpp2
-rw-r--r--src/server/DuplexPort.cpp19
-rw-r--r--src/server/EventWriter.cpp8
-rw-r--r--src/server/GraphPlugin.hpp2
-rw-r--r--src/server/InputPort.cpp2
-rw-r--r--src/server/InternalPlugin.cpp2
-rw-r--r--src/server/LV2Block.cpp71
-rw-r--r--src/server/LV2Block.hpp3
-rw-r--r--src/server/LV2Plugin.cpp44
-rw-r--r--src/server/LV2Plugin.hpp2
-rw-r--r--src/server/NodeImpl.cpp2
-rw-r--r--src/server/NodeImpl.hpp2
-rw-r--r--src/server/PluginImpl.hpp30
-rw-r--r--src/server/events/Delta.cpp54
-rw-r--r--src/server/events/Delta.hpp5
-rw-r--r--src/server/events/Get.cpp38
-rw-r--r--src/server/events/Get.hpp7
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;
};