summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2016-10-06 15:51:11 -0400
committerDavid Robillard <d@drobilla.net>2016-10-06 15:51:11 -0400
commit77f6e9e63ce9ad329b43c92e8a9556aff8e78f2f (patch)
tree50dca5900274ca2f95c3d06069b7fe0bd285e46b
parenta513af4218d0a62a45960d04ff6ddeecb8d3d4f5 (diff)
downloadingen-77f6e9e63ce9ad329b43c92e8a9556aff8e78f2f.tar.gz
ingen-77f6e9e63ce9ad329b43c92e8a9556aff8e78f2f.tar.bz2
ingen-77f6e9e63ce9ad329b43c92e8a9556aff8e78f2f.zip
Add plugin state saving
This only works with a server-side save, so the GUI now uses that if the server is not running remotely, where "remotely" is defined as "via TCP". This isn't perfect, since running ingen via TCP locally is a perfectly valid thing to do, but it will do for now.
-rw-r--r--ingen/Node.hpp1
-rw-r--r--ingen/URIs.hpp1
-rw-r--r--src/Serialiser.cpp16
-rw-r--r--src/URIs.cpp1
-rw-r--r--src/gui/GraphBox.cpp31
-rw-r--r--src/gui/GraphBox.hpp2
-rw-r--r--src/server/BlockImpl.hpp2
-rw-r--r--src/server/GraphPlugin.hpp3
-rw-r--r--src/server/InternalBlock.cpp2
-rw-r--r--src/server/InternalPlugin.cpp3
-rw-r--r--src/server/InternalPlugin.hpp3
-rw-r--r--src/server/LV2Block.cpp103
-rw-r--r--src/server/LV2Block.hpp9
-rw-r--r--src/server/LV2Plugin.cpp5
-rw-r--r--src/server/LV2Plugin.hpp3
-rw-r--r--src/server/PluginImpl.hpp3
-rw-r--r--src/server/events/CreateBlock.cpp22
17 files changed, 150 insertions, 60 deletions
diff --git a/ingen/Node.hpp b/ingen/Node.hpp
index fb5fc985..d6253cc4 100644
--- a/ingen/Node.hpp
+++ b/ingen/Node.hpp
@@ -69,6 +69,7 @@ public:
// Plugin blocks only
virtual LilvInstance* instance() { return NULL; }
+ virtual bool save_state(const std::string& dir) const { return false; }
// All objects
virtual GraphType graph_type() const = 0;
diff --git a/ingen/URIs.hpp b/ingen/URIs.hpp
index 6a691ba2..44f1056e 100644
--- a/ingen/URIs.hpp
+++ b/ingen/URIs.hpp
@@ -203,6 +203,7 @@ public:
const Quark rdfs_seeAlso;
const Quark rsz_minimumSize;
const Quark state_loadDefaultState;
+ const Quark state_state;
const Quark time_Position;
const Quark time_bar;
const Quark time_barBeat;
diff --git a/src/Serialiser.cpp b/src/Serialiser.cpp
index 37bd20c0..da88559c 100644
--- a/src/Serialiser.cpp
+++ b/src/Serialiser.cpp
@@ -445,9 +445,23 @@ Serialiser::Impl::serialise_block(SPtr<const Node> block,
Sord::URI(_model->world(), uris.lv2_prototype),
class_id);
- const Node::Properties props = block->properties();
+ // Serialise properties, but remove possibly stale state:state (set again below)
+ Node::Properties props = block->properties();
+ props.erase(uris.state_state);
serialise_properties(block_id, props);
+ if (_base_uri.substr(0, 5) == "file:") {
+ const std::string base = Glib::filename_from_uri(_base_uri);
+ const std::string graph_dir = Glib::path_get_dirname(base);
+ const std::string state_dir = Glib::build_filename(graph_dir, block->symbol());
+ const std::string state_file = Glib::build_filename(state_dir, "state.ttl");
+ if (block->save_state(state_dir)) {
+ _model->add_statement(block_id,
+ Sord::URI(_model->world(), uris.state_state),
+ Sord::URI(_model->world(), Glib::filename_to_uri(state_file)));
+ }
+ }
+
for (uint32_t i = 0; i < block->num_ports(); ++i) {
Node* const p = block->port(i);
const Sord::Node port_id = path_rdf_node(p->path());
diff --git a/src/URIs.cpp b/src/URIs.cpp
index 7eedd208..1c0a6b23 100644
--- a/src/URIs.cpp
+++ b/src/URIs.cpp
@@ -186,6 +186,7 @@ URIs::URIs(Forge& f, URIMap* map, LilvWorld* lworld)
, rdfs_seeAlso (forge, map, lworld, NS_RDFS "seeAlso")
, rsz_minimumSize (forge, map, lworld, LV2_RESIZE_PORT__minimumSize)
, state_loadDefaultState(forge, map, lworld, LV2_STATE__loadDefaultState)
+ , state_state (forge, map, lworld, LV2_STATE__state)
, time_Position (forge, map, lworld, LV2_TIME__Position)
, time_bar (forge, map, lworld, LV2_TIME__bar)
, time_barBeat (forge, map, lworld, LV2_TIME__barBeat)
diff --git a/src/gui/GraphBox.cpp b/src/gui/GraphBox.cpp
index 692e378a..6423c016 100644
--- a/src/gui/GraphBox.cpp
+++ b/src/gui/GraphBox.cpp
@@ -493,11 +493,7 @@ GraphBox::event_save()
if (!document.is_valid() || document.type() != _app->uris().forge.URI) {
event_save_as();
} else {
- _app->loader()->save_graph(_graph, document.ptr<char>());
- _status_bar->push(
- (boost::format("Saved %1% to %2%") % _graph->path().c_str()
- % document.ptr<char>()).str(),
- STATUS_CONTEXT_GRAPH);
+ save_graph(Raul::URI(document.ptr<char>()));
}
}
@@ -528,6 +524,24 @@ GraphBox::confirm(const Glib::ustring& message,
}
void
+GraphBox::save_graph(const Raul::URI& uri)
+{
+ if (_app->interface()->uri().substr(0, 3) == "tcp") {
+ _status_bar->push(
+ (boost::format("Saved %1% to %2% on client")
+ % _graph->path() % uri).str(),
+ STATUS_CONTEXT_GRAPH);
+ _app->loader()->save_graph(_graph, uri);
+ } else {
+ _status_bar->push(
+ (boost::format("Saved %1% to %2% on server")
+ % _graph->path() % uri).str(),
+ STATUS_CONTEXT_GRAPH);
+ _app->interface()->copy(_graph->uri(), uri);
+ }
+}
+
+void
GraphBox::event_save_as()
{
const URIs& uris = _app->uris();
@@ -613,14 +627,11 @@ GraphBox::event_save_as()
if (confirmed) {
const Glib::ustring uri = Glib::filename_to_uri(filename);
- _app->loader()->save_graph(_graph, uri);
+ save_graph(Raul::URI(uri));
+
const_cast<GraphModel*>(_graph.get())->set_property(
uris.ingen_file,
_app->forge().alloc_uri(uri.c_str()));
- _status_bar->push(
- (boost::format("Saved %1% to %2%") % _graph->path().c_str()
- % filename).str(),
- STATUS_CONTEXT_GRAPH);
}
_app->world()->conf().set(
diff --git a/src/gui/GraphBox.hpp b/src/gui/GraphBox.hpp
index 3ffd1f91..20e0a764 100644
--- a/src/gui/GraphBox.hpp
+++ b/src/gui/GraphBox.hpp
@@ -106,6 +106,8 @@ private:
bool confirm(const Glib::ustring& message,
const Glib::ustring& secondary_text="");
+ void save_graph(const Raul::URI& uri);
+
void event_import();
void event_save();
void event_save_as();
diff --git a/src/server/BlockImpl.hpp b/src/server/BlockImpl.hpp
index 2d7211ab..8f937dc5 100644
--- a/src/server/BlockImpl.hpp
+++ b/src/server/BlockImpl.hpp
@@ -103,7 +103,7 @@ public:
virtual LilvState* load_preset(const Raul::URI& uri) { return NULL; }
/** Restore `state`. */
- virtual void apply_state(Worker* worker, LilvState* state) {}
+ virtual void apply_state(Worker* worker, const LilvState* state) {}
/** Save current state as preset. */
virtual boost::optional<Resource>
diff --git a/src/server/GraphPlugin.hpp b/src/server/GraphPlugin.hpp
index 7d365383..9100b058 100644
--- a/src/server/GraphPlugin.hpp
+++ b/src/server/GraphPlugin.hpp
@@ -43,7 +43,8 @@ public:
const Raul::Symbol& symbol,
bool polyphonic,
GraphImpl* parent,
- Engine& engine)
+ Engine& engine,
+ const LilvState* state)
{
return NULL;
}
diff --git a/src/server/InternalBlock.cpp b/src/server/InternalBlock.cpp
index e26bdc10..ffb5163f 100644
--- a/src/server/InternalBlock.cpp
+++ b/src/server/InternalBlock.cpp
@@ -39,7 +39,7 @@ InternalBlock::duplicate(Engine& engine,
BufferFactory& bufs = *engine.buffer_factory();
BlockImpl* copy = reinterpret_cast<InternalPlugin*>(_plugin)->instantiate(
- bufs, symbol, _polyphonic, parent_graph(), engine);
+ bufs, symbol, _polyphonic, parent_graph(), engine, NULL);
for (size_t i = 0; i < num_ports(); ++i) {
const Atom& value = port_impl(i)->value();
diff --git a/src/server/InternalPlugin.cpp b/src/server/InternalPlugin.cpp
index 3d065fe3..1d397f14 100644
--- a/src/server/InternalPlugin.cpp
+++ b/src/server/InternalPlugin.cpp
@@ -46,7 +46,8 @@ InternalPlugin::instantiate(BufferFactory& bufs,
const Raul::Symbol& symbol,
bool polyphonic,
GraphImpl* parent,
- Engine& engine)
+ Engine& engine,
+ const LilvState* state)
{
const SampleCount srate = engine.driver()->sample_rate();
diff --git a/src/server/InternalPlugin.hpp b/src/server/InternalPlugin.hpp
index bcbfe71a..d95afa1a 100644
--- a/src/server/InternalPlugin.hpp
+++ b/src/server/InternalPlugin.hpp
@@ -43,7 +43,8 @@ public:
const Raul::Symbol& symbol,
bool polyphonic,
GraphImpl* parent,
- Engine& engine);
+ Engine& engine,
+ const LilvState* state);
const Raul::Symbol symbol() const { return _symbol; }
diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp
index 309b68da..c20abfda 100644
--- a/src/server/LV2Block.cpp
+++ b/src/server/LV2Block.cpp
@@ -75,20 +75,6 @@ LV2Block::~LV2Block()
delete _instances;
}
-void
-LV2Block::load_default_state(Worker* worker)
-{
- const LilvPlugin* lplug = _lv2_plugin->lilv_plugin();
- const LilvNode* uri_node = lilv_plugin_get_uri(lplug);
- const Raul::URI uri(lilv_node_as_string(uri_node));
-
- LilvState* default_state = load_preset(_lv2_plugin->uri());
- if (default_state) {
- apply_state(worker, default_state);
- lilv_state_free(default_state);
- }
-}
-
SPtr<LilvInstance>
LV2Block::make_instance(URIs& uris,
SampleRate rate,
@@ -229,7 +215,7 @@ LV2Block::apply_poly(RunContext& context, Raul::Maid& maid, uint32_t poly)
* value is false, this object may not be used.
*/
bool
-LV2Block::instantiate(BufferFactory& bufs)
+LV2Block::instantiate(BufferFactory& bufs, const LilvState* state)
{
const Ingen::URIs& uris = bufs.uris();
Ingen::World* world = bufs.engine().world();
@@ -446,7 +432,20 @@ LV2Block::instantiate(BufferFactory& bufs)
}
}
- load_default_state(NULL);
+ // Load initial state if no state is explicitly given
+ LilvState* default_state = NULL;
+ if (!state) {
+ state = default_state = load_preset(_lv2_plugin->uri());
+ }
+
+ // Apply state
+ if (state) {
+ apply_state(NULL, state);
+ }
+
+ if (default_state) {
+ lilv_state_free(default_state);
+ }
// FIXME: Polyphony + worker?
if (lilv_plugin_has_feature(plug, uris.work_schedule)) {
@@ -458,6 +457,38 @@ LV2Block::instantiate(BufferFactory& bufs)
return ret;
}
+bool
+LV2Block::save_state(const std::string& dir) const
+{
+ World* world = _lv2_plugin->world();
+ LilvWorld* lworld = world->lilv_world();
+
+ LilvState* state = lilv_state_new_from_instance(
+ _lv2_plugin->lilv_plugin(), const_cast<LV2Block*>(this)->instance(0),
+ &world->uri_map().urid_map_feature()->urid_map,
+ NULL, dir.c_str(), dir.c_str(), dir.c_str(), NULL, NULL,
+ LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, NULL);
+
+ if (!state) {
+ return false;
+ } else if (lilv_state_get_num_properties(state) == 0) {
+ lilv_state_free(state);
+ return false;
+ }
+
+ lilv_state_save(lworld,
+ &world->uri_map().urid_map_feature()->urid_map,
+ &world->uri_map().urid_unmap_feature()->urid_unmap,
+ state,
+ NULL,
+ dir.c_str(),
+ "state.ttl");
+
+ lilv_state_free(state);
+
+ return true;
+}
+
BlockImpl*
LV2Block::duplicate(Engine& engine,
const Raul::Symbol& symbol,
@@ -465,9 +496,15 @@ LV2Block::duplicate(Engine& engine,
{
const SampleRate rate = engine.driver()->sample_rate();
+ // Get current state
+ LilvState* state = lilv_state_new_from_instance(
+ _lv2_plugin->lilv_plugin(), instance(0),
+ &engine.world()->uri_map().urid_map_feature()->urid_map,
+ NULL, NULL, NULL, NULL, NULL, NULL, LV2_STATE_IS_NATIVE, NULL);
+
// Duplicate and instantiate block
LV2Block* dup = new LV2Block(_lv2_plugin, symbol, _polyphonic, parent, rate);
- if (!dup->instantiate(*engine.buffer_factory())) {
+ if (!dup->instantiate(*engine.buffer_factory(), state)) {
delete dup;
return NULL;
}
@@ -482,19 +519,6 @@ LV2Block::duplicate(Engine& engine,
dup->port_impl(p)->set_properties(port_impl(p)->properties());
}
- // Copy internal plugin state
- for (uint32_t v = 0; v < _polyphony; ++v) {
- LilvState* state = lilv_state_new_from_instance(
- _lv2_plugin->lilv_plugin(), instance(v),
- &engine.world()->uri_map().urid_map_feature()->urid_map,
- NULL, NULL, NULL, NULL, NULL, NULL, LV2_STATE_IS_NATIVE, NULL);
- if (state) {
- lilv_state_restore(state, dup->instance(v),
- NULL, NULL, LV2_STATE_IS_NATIVE, NULL);
- lilv_state_free(state);
- }
- }
-
return dup;
}
@@ -593,8 +617,25 @@ LV2Block::load_preset(const Raul::URI& uri)
return state;
}
+LilvState*
+LV2Block::load_state(World* world, const std::string& path)
+{
+ LilvWorld* lworld = world->lilv_world();
+ const std::string uri = Glib::filename_to_uri(path);
+ LilvNode* subject = lilv_new_uri(lworld, uri.c_str());
+
+ LilvState* state = lilv_state_new_from_file(
+ lworld,
+ &world->uri_map().urid_map_feature()->urid_map,
+ subject,
+ path.c_str());
+
+ lilv_node_free(subject);
+ return state;
+}
+
void
-LV2Block::apply_state(Worker* worker, LilvState* state)
+LV2Block::apply_state(Worker* worker, const LilvState* state)
{
World* world = parent_graph()->engine().world();
SPtr<LV2_Feature> sched;
diff --git a/src/server/LV2Block.hpp b/src/server/LV2Block.hpp
index b8438d9e..b6be8ccf 100644
--- a/src/server/LV2Block.hpp
+++ b/src/server/LV2Block.hpp
@@ -48,9 +48,10 @@ public:
~LV2Block();
- bool instantiate(BufferFactory& bufs);
+ bool instantiate(BufferFactory& bufs, const LilvState* state);
LilvInstance* instance() { return instance(0); }
+ bool save_state(const std::string& dir) const;
BlockImpl* duplicate(Engine& engine,
const Raul::Symbol& symbol,
@@ -69,7 +70,7 @@ public:
LilvState* load_preset(const Raul::URI& uri);
- void apply_state(Worker* worker, LilvState* state);
+ void apply_state(Worker* worker, const LilvState* state);
boost::optional<Resource> save_preset(const Raul::URI& bundle,
const Properties& props);
@@ -79,14 +80,14 @@ public:
BufferRef buf,
SampleCount offset);
+ static LilvState* load_state(World* world, const std::string& path);
+
protected:
SPtr<LilvInstance> make_instance(URIs& uris,
SampleRate rate,
uint32_t voice,
bool preparing);
- void load_default_state(Worker* worker);
-
inline LilvInstance* instance(uint32_t voice) {
return (LilvInstance*)(*_instances)[voice].get();
}
diff --git a/src/server/LV2Plugin.cpp b/src/server/LV2Plugin.cpp
index f8a0ecd8..8e820b8e 100644
--- a/src/server/LV2Plugin.cpp
+++ b/src/server/LV2Plugin.cpp
@@ -90,12 +90,13 @@ LV2Plugin::instantiate(BufferFactory& bufs,
const Raul::Symbol& symbol,
bool polyphonic,
GraphImpl* parent,
- Engine& engine)
+ Engine& engine,
+ const LilvState* state)
{
LV2Block* b = new LV2Block(
this, symbol, polyphonic, parent, engine.driver()->sample_rate());
- if (!b->instantiate(bufs)) {
+ if (!b->instantiate(bufs, state)) {
delete b;
return NULL;
} else {
diff --git a/src/server/LV2Plugin.hpp b/src/server/LV2Plugin.hpp
index aa10d90a..f490bbfd 100644
--- a/src/server/LV2Plugin.hpp
+++ b/src/server/LV2Plugin.hpp
@@ -45,7 +45,8 @@ public:
const Raul::Symbol& symbol,
bool polyphonic,
GraphImpl* parent,
- Engine& engine);
+ Engine& engine,
+ const LilvState* state);
const Raul::Symbol symbol() const;
diff --git a/src/server/PluginImpl.hpp b/src/server/PluginImpl.hpp
index 29daba7b..9728bf36 100644
--- a/src/server/PluginImpl.hpp
+++ b/src/server/PluginImpl.hpp
@@ -58,7 +58,8 @@ public:
const Raul::Symbol& symbol,
bool polyphonic,
GraphImpl* parent,
- Engine& engine) = 0;
+ Engine& engine,
+ const LilvState* state) = 0;
virtual const Raul::Symbol symbol() const = 0;
diff --git a/src/server/events/CreateBlock.cpp b/src/server/events/CreateBlock.cpp
index 231df4e2..a43a8bf0 100644
--- a/src/server/events/CreateBlock.cpp
+++ b/src/server/events/CreateBlock.cpp
@@ -29,6 +29,7 @@
#include "PluginImpl.hpp"
#include "PortImpl.hpp"
#include "PreProcessContext.hpp"
+#include "LV2Block.hpp"
namespace Ingen {
namespace Server {
@@ -118,11 +119,22 @@ CreateBlock::pre_process(PreProcessContext& ctx)
PluginImpl* const plugin = _engine.block_factory()->plugin(prototype);
if (!plugin) {
return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype);
- } else if (!(_block = plugin->instantiate(*_engine.buffer_factory(),
- Raul::Symbol(_path.symbol()),
- polyphonic,
- _graph,
- _engine))) {
+ }
+
+ // Load state from directory if given in properties
+ LilvState* state = NULL;
+ Resource::Properties::iterator s = _properties.find(uris.state_state);
+ if (s != _properties.end() && s->second.type() == uris.forge.Path) {
+ state = LV2Block::load_state(_engine.world(), s->second.ptr<char>());
+ }
+
+ // Instantiate plugin
+ if (!(_block = plugin->instantiate(*_engine.buffer_factory(),
+ Raul::Symbol(_path.symbol()),
+ polyphonic,
+ _graph,
+ _engine,
+ state))) {
return Event::pre_process_done(Status::CREATION_FAILED, _path);
}
}