diff options
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/BlockImpl.hpp | 6 | ||||
-rw-r--r-- | src/server/Broadcaster.hpp | 5 | ||||
-rw-r--r-- | src/server/DuplexPort.cpp | 19 | ||||
-rw-r--r-- | src/server/DuplexPort.hpp | 4 | ||||
-rw-r--r-- | src/server/EventWriter.cpp | 9 | ||||
-rw-r--r-- | src/server/EventWriter.hpp | 3 | ||||
-rw-r--r-- | src/server/GraphImpl.cpp | 63 | ||||
-rw-r--r-- | src/server/GraphImpl.hpp | 4 | ||||
-rw-r--r-- | src/server/LV2Block.cpp | 41 | ||||
-rw-r--r-- | src/server/LV2Block.hpp | 4 | ||||
-rw-r--r-- | src/server/PortImpl.cpp | 2 | ||||
-rw-r--r-- | src/server/events.hpp | 1 | ||||
-rw-r--r-- | src/server/events/Copy.cpp | 130 | ||||
-rw-r--r-- | src/server/events/Copy.hpp | 73 | ||||
-rw-r--r-- | src/server/events/CreateBlock.cpp | 93 | ||||
-rw-r--r-- | src/server/events/CreateBlock.hpp | 19 | ||||
-rw-r--r-- | src/server/events/CreateGraph.cpp | 47 | ||||
-rw-r--r-- | src/server/events/CreateGraph.hpp | 16 | ||||
-rw-r--r-- | src/server/events/CreatePort.cpp | 30 | ||||
-rw-r--r-- | src/server/events/Delta.cpp | 67 | ||||
-rw-r--r-- | src/server/events/DisconnectAll.cpp | 3 | ||||
-rw-r--r-- | src/server/events/Get.cpp | 20 | ||||
-rw-r--r-- | src/server/events/Get.hpp | 15 | ||||
-rw-r--r-- | src/server/ingen_lv2.cpp | 4 | ||||
-rw-r--r-- | src/server/wscript | 1 |
25 files changed, 543 insertions, 136 deletions
diff --git a/src/server/BlockImpl.hpp b/src/server/BlockImpl.hpp index 845cd7df..b3064168 100644 --- a/src/server/BlockImpl.hpp +++ b/src/server/BlockImpl.hpp @@ -42,6 +42,7 @@ namespace Server { class Buffer; class BufferFactory; class Context; +class Engine; class GraphImpl; class PluginImpl; class PortImpl; @@ -83,6 +84,11 @@ public: */ virtual void deactivate(); + /** Duplicate this Node. */ + virtual BlockImpl* duplicate(Engine& engine, + const Raul::Symbol& symbol, + GraphImpl* parent) { return NULL; } + /** Return true iff this block is activated */ bool activated() const { return _activated; } diff --git a/src/server/Broadcaster.hpp b/src/server/Broadcaster.hpp index b9e37c44..e33594a4 100644 --- a/src/server/Broadcaster.hpp +++ b/src/server/Broadcaster.hpp @@ -114,6 +114,11 @@ public: BROADCAST(delta, uri, remove, add); } + void copy(const Raul::Path& old_path, + const Raul::URI& new_uri) { + BROADCAST(copy, old_path, new_uri); + } + void move(const Raul::Path& old_path, const Raul::Path& new_path) { BROADCAST(move, old_path, new_path); diff --git a/src/server/DuplexPort.cpp b/src/server/DuplexPort.cpp index 1dd1672f..7b39a0de 100644 --- a/src/server/DuplexPort.cpp +++ b/src/server/DuplexPort.cpp @@ -67,6 +67,25 @@ DuplexPort::~DuplexPort() } } +DuplexPort* +DuplexPort::duplicate(Engine& engine, + const Raul::Symbol& symbol, + GraphImpl* parent) +{ + BufferFactory& bufs = *engine.buffer_factory(); + const Atom polyphonic = get_property(bufs.uris().ingen_polyphonic); + + DuplexPort* dup = new DuplexPort( + bufs, parent, symbol, _index, + polyphonic.type() == bufs.uris().atom_Bool && polyphonic.get<int32_t>(), + _poly, _type, _buffer_type, + _value, _buffer_size, _is_output); + + dup->set_properties(properties()); + + return dup; +} + void DuplexPort::inherit_neighbour(const PortImpl* port, Resource::Properties& remove, diff --git a/src/server/DuplexPort.hpp b/src/server/DuplexPort.hpp index a247841e..d8e01e42 100644 --- a/src/server/DuplexPort.hpp +++ b/src/server/DuplexPort.hpp @@ -56,6 +56,10 @@ public: virtual ~DuplexPort(); + DuplexPort* duplicate(Engine& engine, + const Raul::Symbol& symbol, + GraphImpl* parent); + void inherit_neighbour(const PortImpl* port, Resource::Properties& remove, Resource::Properties& add); diff --git a/src/server/EventWriter.cpp b/src/server/EventWriter.cpp index 8508fab3..5f3fe3c3 100644 --- a/src/server/EventWriter.cpp +++ b/src/server/EventWriter.cpp @@ -69,6 +69,15 @@ EventWriter::delta(const Raul::URI& uri, } void +EventWriter::copy(const Raul::Path& old_path, + const Raul::URI& new_uri) +{ + _engine.enqueue_event( + new Events::Copy(_engine, _respondee, _request_id, now(), + old_path, new_uri)); +} + +void EventWriter::move(const Raul::Path& old_path, const Raul::Path& new_path) { diff --git a/src/server/EventWriter.hpp b/src/server/EventWriter.hpp index 89493051..1c031961 100644 --- a/src/server/EventWriter.hpp +++ b/src/server/EventWriter.hpp @@ -64,6 +64,9 @@ public: const Resource::Properties& remove, const Resource::Properties& add); + virtual void copy(const Raul::Path& old_path, + const Raul::URI& new_uri); + virtual void move(const Raul::Path& old_path, const Raul::Path& new_path); diff --git a/src/server/GraphImpl.cpp b/src/server/GraphImpl.cpp index a8a44cf1..f9c0e767 100644 --- a/src/server/GraphImpl.cpp +++ b/src/server/GraphImpl.cpp @@ -15,6 +15,7 @@ */ #include <cassert> +#include <unordered_map> #include "ingen/Log.hpp" #include "ingen/URIs.hpp" @@ -24,6 +25,7 @@ #include "ArcImpl.hpp" #include "BlockImpl.hpp" #include "BufferFactory.hpp" +#include "Driver.hpp" #include "DuplexPort.hpp" #include "Engine.hpp" #include "GraphImpl.hpp" @@ -63,6 +65,67 @@ GraphImpl::~GraphImpl() delete _plugin; } +BlockImpl* +GraphImpl::duplicate(Engine& engine, + const Raul::Symbol& symbol, + GraphImpl* parent) +{ + BufferFactory& bufs = *engine.buffer_factory(); + const SampleRate rate = engine.driver()->sample_rate(); + + // Duplicate graph + GraphImpl* dup = new GraphImpl( + engine, symbol, _polyphony, parent, rate, _poly_process); + + Properties props = properties(); + props.erase(bufs.uris().lv2_symbol); + props.insert({bufs.uris().lv2_symbol, bufs.forge().alloc(symbol.c_str())}); + dup->set_properties(props); + + // We need a map of port duplicates to duplicate arcs + typedef std::unordered_map<PortImpl*, PortImpl*> PortMap; + PortMap port_map; + + // Add duplicates of all ports + dup->_ports = new Raul::Array<PortImpl*>(num_ports(), NULL); + for (Ports::iterator p = _inputs.begin(); p != _inputs.end(); ++p) { + DuplexPort* p_dup = p->duplicate(engine, p->symbol(), dup); + dup->_inputs.push_front(*p_dup); + (*dup->_ports)[p->index()] = p_dup; + port_map.insert({&*p, p_dup}); + } + for (Ports::iterator p = _outputs.begin(); p != _outputs.end(); ++p) { + DuplexPort* p_dup = p->duplicate(engine, p->symbol(), dup); + dup->_outputs.push_front(*p_dup); + (*dup->_ports)[p->index()] = p_dup; + port_map.insert({&*p, p_dup}); + } + + // Add duplicates of all blocks + for (auto& b : _blocks) { + BlockImpl* b_dup = b.duplicate(engine, b.symbol(), dup); + dup->add_block(*b_dup); + b_dup->activate(*engine.buffer_factory()); + for (uint32_t p = 0; p < b.num_ports(); ++p) { + port_map.insert({b.port_impl(p), b_dup->port_impl(p)}); + } + } + + // Add duplicates of all arcs + for (const auto& a : _arcs) { + SPtr<ArcImpl> arc = dynamic_ptr_cast<ArcImpl>(a.second); + if (arc) { + PortMap::iterator t = port_map.find(arc->tail()); + PortMap::iterator h = port_map.find(arc->head()); + if (t != port_map.end() && h != port_map.end()) { + dup->add_arc(SPtr<ArcImpl>(new ArcImpl(t->second, h->second))); + } + } + } + + return dup; +} + void GraphImpl::activate(BufferFactory& bufs) { diff --git a/src/server/GraphImpl.hpp b/src/server/GraphImpl.hpp index 61bbdba4..9601f9a1 100644 --- a/src/server/GraphImpl.hpp +++ b/src/server/GraphImpl.hpp @@ -60,6 +60,10 @@ public: virtual GraphType graph_type() const { return GraphType::GRAPH; } + BlockImpl* duplicate(Engine& engine, + const Raul::Symbol& symbol, + GraphImpl* parent); + void activate(BufferFactory& bufs); void deactivate(); diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp index 4369107e..3f6f4be1 100644 --- a/src/server/LV2Block.cpp +++ b/src/server/LV2Block.cpp @@ -22,6 +22,7 @@ #include "lv2/lv2plug.in/ns/ext/morph/morph.h" #include "lv2/lv2plug.in/ns/ext/options/options.h" #include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" +#include "lv2/lv2plug.in/ns/ext/state/state.h" #include "raul/Maid.hpp" #include "raul/Array.hpp" @@ -432,6 +433,46 @@ LV2Block::instantiate(BufferFactory& bufs) return ret; } +BlockImpl* +LV2Block::duplicate(Engine& engine, + const Raul::Symbol& symbol, + GraphImpl* parent) +{ + const SampleRate rate = engine.driver()->sample_rate(); + + // Duplicate and instantiate block + LV2Block* dup = new LV2Block(_lv2_plugin, symbol, _polyphonic, parent, rate); + if (!dup->instantiate(*engine.buffer_factory())) { + delete dup; + return NULL; + } + dup->set_properties(properties()); + + // Set duplicate port values and properties to the same as ours + for (uint32_t p = 0; p < num_ports(); ++p) { + const Atom& val = port_impl(p)->value(); + if (val.is_valid()) { + dup->port_impl(p)->set_value(val); + } + 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; +} + void LV2Block::activate(BufferFactory& bufs) { diff --git a/src/server/LV2Block.hpp b/src/server/LV2Block.hpp index 35a2d5c3..ec2f99f4 100644 --- a/src/server/LV2Block.hpp +++ b/src/server/LV2Block.hpp @@ -48,6 +48,10 @@ public: bool instantiate(BufferFactory& bufs); + BlockImpl* duplicate(Engine& engine, + const Raul::Symbol& symbol, + GraphImpl* parent); + bool prepare_poly(BufferFactory& bufs, uint32_t poly); bool apply_poly(ProcessContext& context, Raul::Maid& maid, uint32_t poly); diff --git a/src/server/PortImpl.cpp b/src/server/PortImpl.cpp index 2dafcf32..bf605856 100644 --- a/src/server/PortImpl.cpp +++ b/src/server/PortImpl.cpp @@ -383,7 +383,6 @@ void PortImpl::clear_buffers() { switch (_type.id()) { - case PortType::AUDIO: case PortType::CONTROL: case PortType::CV: for (uint32_t v = 0; v < _poly; ++v) { @@ -395,6 +394,7 @@ PortImpl::clear_buffers() state.time = 0; } break; + case PortType::AUDIO: default: for (uint32_t v = 0; v < _poly; ++v) { buffer(v)->clear(); diff --git a/src/server/events.hpp b/src/server/events.hpp index 9a1346b7..fb8509eb 100644 --- a/src/server/events.hpp +++ b/src/server/events.hpp @@ -27,6 +27,7 @@ #include "events/DisconnectAll.hpp" #include "events/Get.hpp" #include "events/Move.hpp" +#include "events/Copy.hpp" #include "events/SetPortValue.hpp" #endif // INGEN_ENGINE_EVENTS_HPP diff --git a/src/server/events/Copy.cpp b/src/server/events/Copy.cpp new file mode 100644 index 00000000..67ab4747 --- /dev/null +++ b/src/server/events/Copy.cpp @@ -0,0 +1,130 @@ +/* + 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/>. +*/ + +#include "ingen/Store.hpp" +#include "raul/Path.hpp" + +#include "BlockImpl.hpp" +#include "Broadcaster.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "EnginePort.hpp" +#include "GraphImpl.hpp" +#include "events/Copy.hpp" + +namespace Ingen { +namespace Server { +namespace Events { + +Copy::Copy(Engine& engine, + SPtr<Interface> client, + int32_t id, + SampleCount timestamp, + const Raul::Path& old_path, + const Raul::URI& new_uri) + : Event(engine, client, id, timestamp) + , _old_path(old_path) + , _new_uri(new_uri) + , _parent(NULL) + , _block(NULL) + , _compiled_graph(NULL) +{} + +bool +Copy::pre_process() +{ + if (_old_path.empty() || + !Node::uri_is_path(_new_uri) || + _new_uri == Node::root_uri()) { + return Event::pre_process_done(Status::BAD_REQUEST); + } + + // Only support a single source for now + const Raul::Path new_path = Node::uri_to_path(_new_uri); + if (!Raul::Symbol::is_valid(new_path.symbol())) { + return Event::pre_process_done(Status::BAD_REQUEST); + } + + std::unique_lock<std::mutex> lock(_engine.store()->mutex()); + + // Find the old node + const Store::iterator i = _engine.store()->find(_old_path); + if (i == _engine.store()->end()) { + return Event::pre_process_done(Status::NOT_FOUND, _old_path); + } + + // Ensure the new node doesn't already exists + if (_engine.store()->find(new_path) != _engine.store()->end()) { + return Event::pre_process_done(Status::EXISTS, new_path); + } + + // Get old node block, or fail (ports not supported for now) + BlockImpl* old_block = dynamic_cast<BlockImpl*>(i->second.get()); + if (!old_block) { + return Event::pre_process_done(Status::BAD_OBJECT_TYPE, _old_path); + } + + // Find new parent graph + const Raul::Path parent_path = new_path.parent(); + const Store::iterator p = _engine.store()->find(parent_path); + if (p == _engine.store()->end()) { + return Event::pre_process_done(Status::NOT_FOUND, parent_path); + } + if (!(_parent = dynamic_cast<GraphImpl*>(p->second.get()))) { + return Event::pre_process_done(Status::BAD_OBJECT_TYPE, parent_path); + } + + // Create new block + if (!(_block = dynamic_cast<BlockImpl*>( + old_block->duplicate(_engine, Raul::Symbol(new_path.symbol()), _parent)))) { + return Event::pre_process_done(Status::INTERNAL_ERROR); + } + + _block->activate(*_engine.buffer_factory()); + + // Add block to the store and the graph's pre-processor only block list + _parent->add_block(*_block); + _engine.store()->add(_block); + + // Compile graph with new block added for insertion in audio thread + if (_parent->enabled()) { + _compiled_graph = _parent->compile(); + } + + return Event::pre_process_done(Status::SUCCESS); +} + +void +Copy::execute(ProcessContext& context) +{ + if (_block) { + _parent->set_compiled_graph(_compiled_graph); + _compiled_graph = NULL; // Graph takes ownership + } +} + +void +Copy::post_process() +{ + Broadcaster::Transfer t(*_engine.broadcaster()); + if (respond() == Status::SUCCESS) { + _engine.broadcaster()->copy(_old_path, _new_uri); + } +} + +} // namespace Events +} // namespace Server +} // namespace Ingen diff --git a/src/server/events/Copy.hpp b/src/server/events/Copy.hpp new file mode 100644 index 00000000..26c0c815 --- /dev/null +++ b/src/server/events/Copy.hpp @@ -0,0 +1,73 @@ +/* + 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_EVENTS_COPY_HPP +#define INGEN_EVENTS_COPY_HPP + +#include <list> + +#include "ingen/Store.hpp" +#include "raul/Path.hpp" + +#include "Event.hpp" + +namespace Ingen { +namespace Server { + +class BlockImpl; +class CompiledGraph; +class GraphImpl; + +namespace Events { + +/** \page methods + * <h2>COPY</h2> + * As per WebDAV (RFC4918 S9.8). + * + * Copy an object from its current location and insert it at a new location + * in a single operation. + */ + +/** COPY a graph object to a new path (see \ref methods). + * \ingroup engine + */ +class Copy : public Event +{ +public: + Copy(Engine& engine, + SPtr<Interface> client, + int32_t id, + SampleCount timestamp, + const Raul::Path& old_path, + const Raul::URI& new_uri); + + bool pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const Raul::Path _old_path; + const Raul::URI _new_uri; + GraphImpl* _parent; + BlockImpl* _block; + CompiledGraph* _compiled_graph; +}; + +} // namespace Events +} // namespace Server +} // namespace Ingen + +#endif // INGEN_EVENTS_COPY_HPP diff --git a/src/server/events/CreateBlock.cpp b/src/server/events/CreateBlock.cpp index 1e1e2228..e14a3356 100644 --- a/src/server/events/CreateBlock.cpp +++ b/src/server/events/CreateBlock.cpp @@ -23,6 +23,7 @@ #include "BlockImpl.hpp" #include "Broadcaster.hpp" #include "CreateBlock.hpp" +#include "Driver.hpp" #include "Engine.hpp" #include "GraphImpl.hpp" #include "PluginImpl.hpp" @@ -54,58 +55,72 @@ CreateBlock::~CreateBlock() bool CreateBlock::pre_process() { - Ingen::URIs& uris = _engine.world()->uris(); - typedef Resource::Properties::const_iterator iterator; + const Ingen::URIs& uris = _engine.world()->uris(); + const SPtr<Store> store = _engine.store(); + + // Check sanity of target path if (_path.is_root()) { return Event::pre_process_done(Status::BAD_URI, _path); + } else if (store->get(_path)) { + return Event::pre_process_done(Status::EXISTS, _path); + } else if (!(_graph = dynamic_cast<GraphImpl*>(store->get(_path.parent())))) { + return Event::pre_process_done(Status::PARENT_NOT_FOUND, _path.parent()); } - std::string plugin_uri_str; + // Get prototype URI const iterator t = _properties.find(uris.ingen_prototype); - if (t != _properties.end() && t->second.type() == uris.forge.URI) { - plugin_uri_str = t->second.ptr<char>(); - } else { + if (t == _properties.end() || t->second.type() != uris.forge.URI) { return Event::pre_process_done(Status::BAD_REQUEST); } - if (_engine.store()->get(_path)) { - return Event::pre_process_done(Status::EXISTS, _path); - } - - _graph = dynamic_cast<GraphImpl*>(_engine.store()->get(_path.parent())); - if (!_graph) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, _path.parent()); - } - - const Raul::URI plugin_uri(plugin_uri_str); - PluginImpl* plugin = _engine.block_factory()->plugin(plugin_uri); - if (!plugin) { - return Event::pre_process_done(Status::PLUGIN_NOT_FOUND, - Raul::URI(plugin_uri)); - } + const Raul::URI prototype(t->second.ptr<char>()); + + // Find polyphony + const iterator p = _properties.find(uris.ingen_polyphonic); + const bool polyphonic = (p != _properties.end() && + p->second.type() == uris.forge.Bool && + p->second.get<int32_t>()); + + // Find and instantiate/duplicate prototype (plugin/existing node) + if (Node::uri_is_path(prototype)) { + // Prototype is an existing block + BlockImpl* const ancestor = dynamic_cast<BlockImpl*>( + store->get(Node::uri_to_path(prototype))); + if (!ancestor) { + return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); + } else if (!(_block = ancestor->duplicate( + _engine, Raul::Symbol(_path.symbol()), _graph))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } - const iterator p = _properties.find(uris.ingen_polyphonic); - const bool polyphonic = ( - p != _properties.end() && - p->second.type() == _engine.world()->forge().Bool && - p->second.get<int32_t>()); - - if (!(_block = plugin->instantiate(*_engine.buffer_factory(), - Raul::Symbol(_path.symbol()), - polyphonic, - _graph, - _engine))) { - return Event::pre_process_done(Status::CREATION_FAILED, _path); + /* Replace prototype with the ancestor's. This is less informative, + but the client expects an actual LV2 plugin as prototype. */ + _properties.erase(uris.ingen_prototype); + _properties.insert(std::make_pair(uris.ingen_prototype, + uris.forge.alloc_uri(ancestor->plugin()->uri()))); + } else { + // Prototype is a plugin + 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))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } } + // Activate block _block->properties().insert(_properties.begin(), _properties.end()); _block->activate(*_engine.buffer_factory()); // Add block to the store and the graph's pre-processor only block list _graph->add_block(*_block); - _engine.store()->add(_block); + store->add(_block); /* Compile graph with new block added for insertion in audio thread TODO: Since the block is not connected at this point, a full compilation @@ -114,11 +129,7 @@ CreateBlock::pre_process() _compiled_graph = _graph->compile(); } - _update.push_back(make_pair(_block->uri(), _block->properties())); - for (uint32_t i = 0; i < _block->num_ports(); ++i) { - const PortImpl* port = _block->port_impl(i); - _update.push_back(std::make_pair(port->uri(), port->properties())); - } + _update.put_block(_block); return Event::pre_process_done(Status::SUCCESS); } @@ -137,9 +148,7 @@ CreateBlock::post_process() { Broadcaster::Transfer t(*_engine.broadcaster()); if (respond() == Status::SUCCESS) { - for (const auto& u : _update) { - _engine.broadcaster()->put(u.first, u.second); - } + _update.send(_engine.broadcaster()); } } diff --git a/src/server/events/CreateBlock.hpp b/src/server/events/CreateBlock.hpp index 36e35775..6e0d7f4d 100644 --- a/src/server/events/CreateBlock.hpp +++ b/src/server/events/CreateBlock.hpp @@ -17,12 +17,10 @@ #ifndef INGEN_EVENTS_CREATEBLOCK_HPP #define INGEN_EVENTS_CREATEBLOCK_HPP -#include <list> -#include <utility> - #include "ingen/Resource.hpp" #include "Event.hpp" +#include "events/Get.hpp" namespace Ingen { namespace Server { @@ -54,15 +52,12 @@ public: void post_process(); private: - /// Update put message to broadcast to clients - typedef std::list< std::pair<Raul::URI, Resource::Properties> > Update; - - Raul::Path _path; - Resource::Properties _properties; - Update _update; - GraphImpl* _graph; - BlockImpl* _block; - CompiledGraph* _compiled_graph; + Raul::Path _path; + Resource::Properties _properties; + Events::Get::Response _update; + GraphImpl* _graph; + BlockImpl* _block; + CompiledGraph* _compiled_graph; }; } // namespace Events diff --git a/src/server/events/CreateGraph.cpp b/src/server/events/CreateGraph.cpp index 0355b0bb..06445a6b 100644 --- a/src/server/events/CreateGraph.cpp +++ b/src/server/events/CreateGraph.cpp @@ -41,8 +41,7 @@ CreateGraph::CreateGraph(Engine& engine, , _graph(NULL) , _parent(NULL) , _compiled_graph(NULL) -{ -} +{} bool CreateGraph::pre_process() @@ -76,13 +75,33 @@ CreateGraph::pre_process() } const Raul::Symbol symbol((_path.is_root()) ? "root" : _path.symbol()); - _graph = new GraphImpl(_engine, symbol, ext_poly, _parent, - _engine.driver()->sample_rate(), int_poly); - _graph->properties().insert(_properties.begin(), _properties.end()); - _graph->add_property(uris.rdf_type, uris.ingen_Graph); - _graph->add_property(uris.rdf_type, - Resource::Property(uris.ingen_Block, - Resource::Graph::EXTERNAL)); + + // Create graph based on prototype + const iterator t = _properties.find(uris.ingen_prototype); + if (t != _properties.end() && + Raul::URI::is_valid(t->second.ptr<char>()) && + Node::uri_is_path(Raul::URI(t->second.ptr<char>()))) { + // Create a duplicate of an existing graph + const Raul::URI prototype(t->second.ptr<char>()); + GraphImpl* ancestor = dynamic_cast<GraphImpl*>( + _engine.store()->get(Node::uri_to_path(prototype))); + if (!ancestor) { + return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); + } else if (!(_graph = dynamic_cast<GraphImpl*>( + ancestor->duplicate(_engine, symbol, _parent)))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } + } else { + // Create a new graph + _graph = new GraphImpl(_engine, symbol, ext_poly, _parent, + _engine.driver()->sample_rate(), int_poly); + _graph->add_property(uris.rdf_type, uris.ingen_Graph); + _graph->add_property(uris.rdf_type, + Resource::Property(uris.ingen_Block, + Resource::Graph::EXTERNAL)); + } + + _graph->set_properties(_properties); _parent->add_block(*_graph); if (_parent->enabled()) { @@ -92,10 +111,12 @@ CreateGraph::pre_process() _graph->activate(*_engine.buffer_factory()); - // Insert into Store + // Insert into store and build update to send to clients _engine.store()->add(_graph); - - _update = _graph->properties(); + _update.put_graph(_graph); + for (BlockImpl& block : _graph->blocks()) { + _engine.store()->add(&block); + } return Event::pre_process_done(Status::SUCCESS); } @@ -113,7 +134,7 @@ CreateGraph::post_process() { Broadcaster::Transfer t(*_engine.broadcaster()); if (respond() == Status::SUCCESS) { - _engine.broadcaster()->put(Node::path_to_uri(_path), _update); + _update.send(_engine.broadcaster()); } } diff --git a/src/server/events/CreateGraph.hpp b/src/server/events/CreateGraph.hpp index 64fb92bd..542a2921 100644 --- a/src/server/events/CreateGraph.hpp +++ b/src/server/events/CreateGraph.hpp @@ -17,9 +17,11 @@ #ifndef INGEN_EVENTS_CREATEGRAPH_HPP #define INGEN_EVENTS_CREATEGRAPH_HPP -#include "Event.hpp" #include "ingen/Resource.hpp" +#include "Event.hpp" +#include "events/Get.hpp" + namespace Ingen { namespace Server { @@ -47,12 +49,12 @@ public: void post_process(); private: - const Raul::Path _path; - Resource::Properties _properties; - Resource::Properties _update; - GraphImpl* _graph; - GraphImpl* _parent; - CompiledGraph* _compiled_graph; + const Raul::Path _path; + Resource::Properties _properties; + Events::Get::Response _update; + GraphImpl* _graph; + GraphImpl* _parent; + CompiledGraph* _compiled_graph; }; } // namespace Events diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp index 2a1a7994..21602df6 100644 --- a/src/server/events/CreatePort.cpp +++ b/src/server/events/CreatePort.cpp @@ -87,31 +87,23 @@ CreatePort::pre_process() { if (_port_type == PortType::UNKNOWN) { return Event::pre_process_done(Status::UNKNOWN_TYPE, _path); - } - - if (_path.is_root()) { + } else if (_path.is_root()) { return Event::pre_process_done(Status::BAD_URI, _path); - } - - if (_engine.store()->get(_path)) { + } else if (_engine.store()->get(_path)) { return Event::pre_process_done(_status, _path); } - Node* parent = _engine.store()->get(_path.parent()); + const Raul::Path parent_path = _path.parent(); + Node* const parent = _engine.store()->get(parent_path); if (!parent) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, - _path.parent()); + return Event::pre_process_done(Status::PARENT_NOT_FOUND, parent_path); + } else if (!(_graph = dynamic_cast<GraphImpl*>(parent))) { + return Event::pre_process_done(Status::INVALID_PARENT, parent_path); } - if (!(_graph = dynamic_cast<GraphImpl*>(parent))) { - return Event::pre_process_done(Status::INVALID_PARENT_PATH, - _path.parent()); - } - - const URIs& uris = _engine.world()->uris(); - const BufferFactory& buffer_factory = *_engine.buffer_factory(); - - const uint32_t buf_size = buffer_factory.default_size(_buf_type); + const URIs& uris = _engine.world()->uris(); + BufferFactory& bufs = *_engine.buffer_factory(); + const uint32_t buf_size = bufs.default_size(_buf_type); const int32_t old_n_ports = _graph->num_ports_non_rt(); typedef Resource::Properties::const_iterator PropIter; @@ -133,7 +125,7 @@ CreatePort::pre_process() poly_i->second.get<int32_t>()); if (!(_graph_port = _graph->create_port( - *_engine.buffer_factory(), Raul::Symbol(_path.symbol()), + bufs, Raul::Symbol(_path.symbol()), _port_type, _buf_type, buf_size, _is_output, polyphonic))) { return Event::pre_process_done(Status::CREATION_FAILED, _path); } diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index 23285b9b..02f53410 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -135,9 +135,11 @@ Delta::pre_process() path, is_output, _properties); } if (_create_event) { - _create_event->pre_process(); - // Grab the object for applying properties, if the create-event succeeded - _object = _engine.store()->get(path); + if (_create_event->pre_process()) { + _object = _engine.store()->get(path); // Get object for setting + } else { + return Event::pre_process_done(Status::CREATION_FAILED, _subject); + } } else { return Event::pre_process_done(Status::BAD_OBJECT_TYPE, _subject); } @@ -382,36 +384,39 @@ Delta::post_process() Broadcaster::Transfer t(*_engine.broadcaster()); - for (auto& s : _set_events) - s->post_process(); + if (_create_event) { + _create_event->post_process(); + if (_create_event->status() != Status::SUCCESS) { + return; // Creation failed, nothing else to do + } + } - if (_status == Status::SUCCESS) { - if (_create_event) { - _create_event->post_process(); - } else { - respond(); - switch (_type) { - case Type::SET: - /* Kludge to avoid feedback for set events only. The GUI - depends on put responses to e.g. initially place blocks. - Some more sensible way of controlling this is needed. */ - _engine.broadcaster()->set_ignore_client(_request_client); - _engine.broadcaster()->set_property( - _subject, - (*_properties.begin()).first, - (*_properties.begin()).second); - _engine.broadcaster()->clear_ignore_client(); - break; - case Type::PUT: - _engine.broadcaster()->put(_subject, _properties, _context); - break; - case Type::PATCH: - _engine.broadcaster()->delta(_subject, _remove, _properties); - break; - } + for (auto& s : _set_events) { + if (s->status() != Status::SUCCESS) { + s->post_process(); // Set failed, report error + } + } + + if (respond() == Status::SUCCESS) { + switch (_type) { + case Type::SET: + /* Kludge to avoid feedback for set events only. The GUI + depends on put responses to e.g. initially place blocks. + Some more sensible way of controlling this is needed. */ + _engine.broadcaster()->set_ignore_client(_request_client); + _engine.broadcaster()->set_property( + _subject, + (*_properties.begin()).first, + (*_properties.begin()).second); + _engine.broadcaster()->clear_ignore_client(); + break; + case Type::PUT: + _engine.broadcaster()->put(_subject, _properties, _context); + break; + case Type::PATCH: + _engine.broadcaster()->delta(_subject, _remove, _properties); + break; } - } else { - respond(); } } diff --git a/src/server/events/DisconnectAll.cpp b/src/server/events/DisconnectAll.cpp index 22560c7e..262bfea8 100644 --- a/src/server/events/DisconnectAll.cpp +++ b/src/server/events/DisconnectAll.cpp @@ -100,8 +100,7 @@ DisconnectAll::pre_process() if (object->parent_graph() != _parent && object->parent()->parent_graph() != _parent) { - return Event::pre_process_done(Status::INVALID_PARENT_PATH, - _parent_path); + return Event::pre_process_done(Status::INVALID_PARENT, _parent_path); } // Only one of these will succeed diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp index c984e576..8a9eb227 100644 --- a/src/server/events/Get.cpp +++ b/src/server/events/Get.cpp @@ -100,6 +100,19 @@ Get::Response::put_graph(const GraphImpl* graph) } } +void +Get::Response::send(Interface* dest) +{ + // Sort puts by URI so parents are sent first + std::sort(puts.begin(), puts.end()); + for (const Response::Put& put : puts) { + dest->put(put.uri, put.properties, put.ctx); + } + for (const Response::Connect& connect : connects) { + dest->connect(connect.tail, connect.head); + } +} + Get::Get(Engine& engine, SPtr<Interface> client, int32_t id, @@ -162,12 +175,7 @@ Get::post_process() uris.param_sampleRate, uris.forge.make(int32_t(_engine.driver()->sample_rate()))); } else { - for (const Response::Put& put : _response.puts) { - _request_client->put(put.uri, put.properties, put.ctx); - } - for (const Response::Connect& connect : _response.connects) { - _request_client->connect(connect.tail, connect.head); - } + _response.send(_request_client.get()); } } } diff --git a/src/server/events/Get.hpp b/src/server/events/Get.hpp index 7aea8c77..e0ed3483 100644 --- a/src/server/events/Get.hpp +++ b/src/server/events/Get.hpp @@ -17,6 +17,8 @@ #ifndef INGEN_EVENTS_GET_HPP #define INGEN_EVENTS_GET_HPP +#include <vector> + #include "Event.hpp" #include "BlockFactory.hpp" #include "types.hpp" @@ -24,7 +26,10 @@ namespace Ingen { namespace Server { +class BlockImpl; +class GraphImpl; class PluginImpl; +class PortImpl; namespace Events { @@ -45,7 +50,6 @@ public: void execute(ProcessContext& context) {} void post_process(); -private: /** A sequence of puts and connects to respond to client with. * This is constructed in the pre_process() and later sent in * post_process() to avoid the need to lock. @@ -63,11 +67,17 @@ private: void put_port(const PortImpl* port); void put_block(const BlockImpl* block); void put_graph(const GraphImpl* graph); - + + void send(Interface* dest); + struct Put { Raul::URI uri; Resource::Properties properties; Resource::Graph ctx; + + inline bool operator<(const Put& other) { + return uri < other.uri; + } }; struct Connect { @@ -79,6 +89,7 @@ private: std::vector<Connect> connects; }; +private: const Raul::URI _uri; const Node* _object; const PluginImpl* _plugin; diff --git a/src/server/ingen_lv2.cpp b/src/server/ingen_lv2.cpp index 56f106cb..065f42ac 100644 --- a/src/server/ingen_lv2.cpp +++ b/src/server/ingen_lv2.cpp @@ -713,7 +713,9 @@ ingen_save(LV2_Handle instance, char* state_path = map_path->abstract_path(map_path->handle, real_path); Ingen::Store::iterator root = plugin->world->store()->find(Raul::Path("/")); - plugin->world->serialiser()->to_file(root->second, real_path); + plugin->world->serialiser()->start_to_file(root->second->path(), real_path); + plugin->world->serialiser()->serialise(root->second); + plugin->world->serialiser()->finish(); store(handle, ingen_file, diff --git a/src/server/wscript b/src/server/wscript index 9d1238a4..16bca160 100644 --- a/src/server/wscript +++ b/src/server/wscript @@ -28,6 +28,7 @@ def build(bld): SocketListener.cpp Worker.cpp events/Connect.cpp + events/Copy.cpp events/CreateBlock.cpp events/CreateGraph.cpp events/CreatePort.cpp |