summaryrefslogtreecommitdiffstats
path: root/src/server
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-02-08 07:02:59 +0000
committerDavid Robillard <d@drobilla.net>2015-02-08 07:02:59 +0000
commit0f9c8151d5b42b243a499bb31a1e1f0b2e8c5f6f (patch)
tree1ed4df4df4c3f160120544d92c681f1b4519e1aa /src/server
parent8733afb7ae9a04f46ac6318667182da16eca9fe5 (diff)
downloadingen-0f9c8151d5b42b243a499bb31a1e1f0b2e8c5f6f.tar.gz
ingen-0f9c8151d5b42b243a499bb31a1e1f0b2e8c5f6f.tar.bz2
ingen-0f9c8151d5b42b243a499bb31a1e1f0b2e8c5f6f.zip
Server-side copy paste with LV2 state support.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@5541 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/server')
-rw-r--r--src/server/BlockImpl.hpp6
-rw-r--r--src/server/Broadcaster.hpp5
-rw-r--r--src/server/DuplexPort.cpp19
-rw-r--r--src/server/DuplexPort.hpp4
-rw-r--r--src/server/EventWriter.cpp9
-rw-r--r--src/server/EventWriter.hpp3
-rw-r--r--src/server/GraphImpl.cpp63
-rw-r--r--src/server/GraphImpl.hpp4
-rw-r--r--src/server/LV2Block.cpp41
-rw-r--r--src/server/LV2Block.hpp4
-rw-r--r--src/server/PortImpl.cpp2
-rw-r--r--src/server/events.hpp1
-rw-r--r--src/server/events/Copy.cpp130
-rw-r--r--src/server/events/Copy.hpp73
-rw-r--r--src/server/events/CreateBlock.cpp93
-rw-r--r--src/server/events/CreateBlock.hpp19
-rw-r--r--src/server/events/CreateGraph.cpp47
-rw-r--r--src/server/events/CreateGraph.hpp16
-rw-r--r--src/server/events/CreatePort.cpp30
-rw-r--r--src/server/events/Delta.cpp67
-rw-r--r--src/server/events/DisconnectAll.cpp3
-rw-r--r--src/server/events/Get.cpp20
-rw-r--r--src/server/events/Get.hpp15
-rw-r--r--src/server/ingen_lv2.cpp4
-rw-r--r--src/server/wscript1
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