diff options
author | David Robillard <d@drobilla.net> | 2016-10-06 15:51:11 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2016-10-06 15:51:11 -0400 |
commit | 77f6e9e63ce9ad329b43c92e8a9556aff8e78f2f (patch) | |
tree | 50dca5900274ca2f95c3d06069b7fe0bd285e46b | |
parent | a513af4218d0a62a45960d04ff6ddeecb8d3d4f5 (diff) | |
download | ingen-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.hpp | 1 | ||||
-rw-r--r-- | ingen/URIs.hpp | 1 | ||||
-rw-r--r-- | src/Serialiser.cpp | 16 | ||||
-rw-r--r-- | src/URIs.cpp | 1 | ||||
-rw-r--r-- | src/gui/GraphBox.cpp | 31 | ||||
-rw-r--r-- | src/gui/GraphBox.hpp | 2 | ||||
-rw-r--r-- | src/server/BlockImpl.hpp | 2 | ||||
-rw-r--r-- | src/server/GraphPlugin.hpp | 3 | ||||
-rw-r--r-- | src/server/InternalBlock.cpp | 2 | ||||
-rw-r--r-- | src/server/InternalPlugin.cpp | 3 | ||||
-rw-r--r-- | src/server/InternalPlugin.hpp | 3 | ||||
-rw-r--r-- | src/server/LV2Block.cpp | 103 | ||||
-rw-r--r-- | src/server/LV2Block.hpp | 9 | ||||
-rw-r--r-- | src/server/LV2Plugin.cpp | 5 | ||||
-rw-r--r-- | src/server/LV2Plugin.hpp | 3 | ||||
-rw-r--r-- | src/server/PluginImpl.hpp | 3 | ||||
-rw-r--r-- | src/server/events/CreateBlock.cpp | 22 |
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); } } |