summaryrefslogtreecommitdiffstats
path: root/src/server/events
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/events')
-rw-r--r--src/server/events/Connect.cpp204
-rw-r--r--src/server/events/Connect.hpp88
-rw-r--r--src/server/events/CreateNode.cpp146
-rw-r--r--src/server/events/CreateNode.hpp71
-rw-r--r--src/server/events/CreatePatch.cpp164
-rw-r--r--src/server/events/CreatePatch.hpp67
-rw-r--r--src/server/events/CreatePort.cpp192
-rw-r--r--src/server/events/CreatePort.hpp81
-rw-r--r--src/server/events/Deactivate.hpp49
-rw-r--r--src/server/events/Delete.cpp213
-rw-r--r--src/server/events/Delete.hpp95
-rw-r--r--src/server/events/Disconnect.cpp269
-rw-r--r--src/server/events/Disconnect.hpp106
-rw-r--r--src/server/events/DisconnectAll.cpp189
-rw-r--r--src/server/events/DisconnectAll.hpp93
-rw-r--r--src/server/events/Get.cpp84
-rw-r--r--src/server/events/Get.hpp60
-rw-r--r--src/server/events/Move.cpp130
-rw-r--r--src/server/events/Move.hpp79
-rw-r--r--src/server/events/Ping.hpp51
-rw-r--r--src/server/events/RegisterClient.cpp57
-rw-r--r--src/server/events/RegisterClient.hpp54
-rw-r--r--src/server/events/RequestMetadata.cpp137
-rw-r--r--src/server/events/RequestMetadata.hpp79
-rw-r--r--src/server/events/SendBinding.cpp55
-rw-r--r--src/server/events/SendBinding.hpp86
-rw-r--r--src/server/events/SendPortActivity.cpp36
-rw-r--r--src/server/events/SendPortActivity.hpp69
-rw-r--r--src/server/events/SendPortValue.cpp42
-rw-r--r--src/server/events/SendPortValue.hpp82
-rw-r--r--src/server/events/SetMetadata.cpp380
-rw-r--r--src/server/events/SetMetadata.hpp124
-rw-r--r--src/server/events/SetPortValue.cpp223
-rw-r--r--src/server/events/SetPortValue.hpp83
-rw-r--r--src/server/events/UnregisterClient.cpp48
-rw-r--r--src/server/events/UnregisterClient.hpp50
36 files changed, 4036 insertions, 0 deletions
diff --git a/src/server/events/Connect.cpp b/src/server/events/Connect.cpp
new file mode 100644
index 00000000..e0f09a3d
--- /dev/null
+++ b/src/server/events/Connect.cpp
@@ -0,0 +1,204 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string>
+#include <boost/format.hpp>
+#include "raul/Maid.hpp"
+#include "raul/Path.hpp"
+#include "ClientBroadcaster.hpp"
+#include "Connect.hpp"
+#include "ConnectionImpl.hpp"
+#include "DuplexPort.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "InputPort.hpp"
+#include "OutputPort.hpp"
+#include "PatchImpl.hpp"
+#include "PortImpl.hpp"
+#include "ProcessContext.hpp"
+#include "Request.hpp"
+#include "types.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+Connect::Connect(Engine& engine, SharedPtr<Request> request, SampleCount timestamp, const Path& src_port_path, const Path& dst_port_path)
+ : QueuedEvent(engine, request, timestamp)
+ , _src_port_path(src_port_path)
+ , _dst_port_path(dst_port_path)
+ , _patch(NULL)
+ , _src_output_port(NULL)
+ , _dst_input_port(NULL)
+ , _compiled_patch(NULL)
+ , _port_listnode(NULL)
+ , _buffers(NULL)
+{
+}
+
+void
+Connect::pre_process()
+{
+ PortImpl* src_port = _engine.engine_store()->find_port(_src_port_path);
+ PortImpl* dst_port = _engine.engine_store()->find_port(_dst_port_path);
+ if (!src_port || !dst_port) {
+ _error = PORT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _dst_input_port = dynamic_cast<InputPort*>(dst_port);
+ _src_output_port = dynamic_cast<OutputPort*>(src_port);
+ if (!_dst_input_port || !_src_output_port) {
+ _error = DIRECTION_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ NodeImpl* const src_node = src_port->parent_node();
+ NodeImpl* const dst_node = dst_port->parent_node();
+ if (!src_node || !dst_node) {
+ _error = PARENTS_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (src_node->parent() != dst_node->parent()
+ && src_node != dst_node->parent()
+ && src_node->parent() != dst_node) {
+ _error = PARENT_PATCH_DIFFERENT;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (!ConnectionImpl::can_connect(_src_output_port, _dst_input_port)) {
+ _error = TYPE_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ // Connection to a patch port from inside the patch
+ if (src_node->parent_patch() != dst_node->parent_patch()) {
+ assert(src_node->parent() == dst_node || dst_node->parent() == src_node);
+ if (src_node->parent() == dst_node)
+ _patch = dynamic_cast<PatchImpl*>(dst_node);
+ else
+ _patch = dynamic_cast<PatchImpl*>(src_node);
+
+ // Connection from a patch input to a patch output (pass through)
+ } else if (src_node == dst_node && dynamic_cast<PatchImpl*>(src_node)) {
+ _patch = dynamic_cast<PatchImpl*>(src_node);
+
+ // Normal connection between nodes with the same parent
+ } else {
+ _patch = src_node->parent_patch();
+ }
+
+ if (_patch->has_connection(_src_output_port, _dst_input_port)) {
+ _error = ALREADY_CONNECTED;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _connection = SharedPtr<ConnectionImpl>(
+ new ConnectionImpl(*_engine.buffer_factory(), _src_output_port, _dst_input_port));
+
+ _port_listnode = new InputPort::Connections::Node(_connection);
+
+ // Need to be careful about patch port connections here and adding a node's
+ // parent as a dependant/provider, or adding a patch as it's own provider...
+ if (src_node != dst_node && src_node->parent() == dst_node->parent()) {
+ dst_node->providers()->push_back(new Raul::List<NodeImpl*>::Node(src_node));
+ src_node->dependants()->push_back(new Raul::List<NodeImpl*>::Node(dst_node));
+ }
+
+ _patch->add_connection(_connection);
+ _dst_input_port->increment_num_connections();
+
+ /*if ((_dst_input_port->num_connections() == 1
+ && (_connection->must_mix() || _connection->must_queue()))
+ || _dst_input_port->num_connections() == 2) {*/
+ _buffers = new Raul::Array<BufferFactory::Ref>(_dst_input_port->poly());
+ _dst_input_port->get_buffers(*_engine.buffer_factory(),
+ _buffers, _dst_input_port->poly());
+ //}
+
+ if (_patch->enabled())
+ _compiled_patch = _patch->compile();
+
+ QueuedEvent::pre_process();
+}
+
+void
+Connect::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ if (_error == NO_ERROR) {
+ // This must be inserted here, since they're actually used by the audio thread
+ _dst_input_port->add_connection(_port_listnode);
+ assert(_buffers);
+ //if (_buffers)
+ _engine.maid()->push(_dst_input_port->set_buffers(_buffers));
+ //else
+ // _dst_input_port->setup_buffers(*_engine.buffer_factory(), _dst_input_port->poly());
+ _dst_input_port->connect_buffers();
+ _engine.maid()->push(_patch->compiled_patch());
+ _patch->compiled_patch(_compiled_patch);
+ }
+}
+
+void
+Connect::post_process()
+{
+ std::ostringstream ss;
+ if (_error == NO_ERROR) {
+ _request->respond_ok();
+ _engine.broadcaster()->connect(_src_port_path, _dst_port_path);
+ return;
+ }
+
+ ss << boost::format("Unable to make connection %1% -> %2% (")
+ % _src_port_path.chop_scheme() % _dst_port_path.chop_scheme();
+
+ switch (_error) {
+ case PARENT_PATCH_DIFFERENT:
+ ss << "Ports have mismatched parents"; break;
+ case PORT_NOT_FOUND:
+ ss << "Port not found"; break;
+ case TYPE_MISMATCH:
+ ss << "Type mismatch"; break;
+ case DIRECTION_MISMATCH:
+ ss << "Direction mismatch"; break;
+ case ALREADY_CONNECTED:
+ ss << "Already connected"; break;
+ case PARENTS_NOT_FOUND:
+ ss << "Parents not found"; break;
+ default:
+ ss << "Unknown error";
+ }
+ ss << ")";
+ _request->respond_error(ss.str());
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/Connect.hpp b/src/server/events/Connect.hpp
new file mode 100644
index 00000000..1cc98729
--- /dev/null
+++ b/src/server/events/Connect.hpp
@@ -0,0 +1,88 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_CONNECT_HPP
+#define INGEN_EVENTS_CONNECT_HPP
+
+#include "raul/Path.hpp"
+#include "QueuedEvent.hpp"
+#include "PatchImpl.hpp"
+#include "InputPort.hpp"
+#include "types.hpp"
+
+namespace Raul {
+ template <typename T> class ListNode;
+ template <typename T> class Array;
+}
+
+namespace Ingen {
+namespace Server {
+
+class PatchImpl;
+class NodeImpl;
+class ConnectionImpl;
+class PortImpl;
+class InputPort;
+class OutputPort;
+class CompiledPatch;
+
+namespace Events {
+
+/** Make a Connection between two Ports.
+ *
+ * \ingroup engine
+ */
+class Connect : public QueuedEvent
+{
+public:
+ Connect(Engine& engine, SharedPtr<Request> request, SampleCount timestamp, const Raul::Path& src_port_path, const Raul::Path& dst_port_path);
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType {
+ NO_ERROR,
+ PARENT_PATCH_DIFFERENT,
+ PORT_NOT_FOUND,
+ TYPE_MISMATCH,
+ DIRECTION_MISMATCH,
+ ALREADY_CONNECTED,
+ PARENTS_NOT_FOUND
+ };
+
+ Raul::Path _src_port_path;
+ Raul::Path _dst_port_path;
+
+ PatchImpl* _patch;
+ OutputPort* _src_output_port;
+ InputPort* _dst_input_port;
+
+ CompiledPatch* _compiled_patch; ///< New process order for Patch
+
+ SharedPtr<ConnectionImpl> _connection;
+ InputPort::Connections::Node* _port_listnode;
+
+ Raul::Array<BufferFactory::Ref>* _buffers;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_CONNECT_HPP
diff --git a/src/server/events/CreateNode.cpp b/src/server/events/CreateNode.cpp
new file mode 100644
index 00000000..01d4f285
--- /dev/null
+++ b/src/server/events/CreateNode.cpp
@@ -0,0 +1,146 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "raul/log.hpp"
+#include "raul/Maid.hpp"
+#include "raul/Path.hpp"
+#include "sord/sordmm.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "CreateNode.hpp"
+#include "Request.hpp"
+#include "PatchImpl.hpp"
+#include "NodeImpl.hpp"
+#include "PluginImpl.hpp"
+#include "Engine.hpp"
+#include "PatchImpl.hpp"
+#include "NodeFactory.hpp"
+#include "ClientBroadcaster.hpp"
+#include "EngineStore.hpp"
+#include "PortImpl.hpp"
+#include "Driver.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+CreateNode::CreateNode(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Path& path,
+ const URI& plugin_uri,
+ const Resource::Properties& properties)
+ : QueuedEvent(engine, request, timestamp)
+ , _path(path)
+ , _plugin_uri(plugin_uri)
+ , _patch(NULL)
+ , _plugin(NULL)
+ , _node(NULL)
+ , _compiled_patch(NULL)
+ , _node_already_exists(false)
+ , _polyphonic(false)
+ , _properties(properties)
+{
+ const Resource::Properties::const_iterator p = properties.find(
+ engine.world()->uris()->ingen_polyphonic);
+ if (p != properties.end() && p->second.type() == Raul::Atom::BOOL
+ && p->second.get_bool())
+ _polyphonic = true;
+}
+
+void
+CreateNode::pre_process()
+{
+ if (_engine.engine_store()->find_object(_path) != NULL) {
+ _node_already_exists = true;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _patch = _engine.engine_store()->find_patch(_path.parent());
+ _plugin = _engine.node_factory()->plugin(_plugin_uri.str());
+
+ if (_patch && _plugin) {
+
+ _node = _plugin->instantiate(*_engine.buffer_factory(), _path.symbol(), _polyphonic, _patch, _engine);
+
+ if (_node != NULL) {
+ _node->properties().insert(_properties.begin(), _properties.end());
+ _node->activate(*_engine.buffer_factory());
+
+ // This can be done here because the audio thread doesn't touch the
+ // node tree - just the process order array
+ _patch->add_node(new PatchImpl::Nodes::Node(_node));
+ _engine.engine_store()->add(_node);
+
+ // FIXME: not really necessary to build process order since it's not connected,
+ // just append to the list
+ if (_patch->enabled())
+ _compiled_patch = _patch->compile();
+ }
+ }
+
+ if (!_node)
+ _error = 1;
+
+ QueuedEvent::pre_process();
+}
+
+void
+CreateNode::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ if (_node) {
+ _engine.maid()->push(_patch->compiled_patch());
+ _patch->compiled_patch(_compiled_patch);
+ }
+}
+
+void
+CreateNode::post_process()
+{
+ if (!_request)
+ return;
+
+ string msg;
+ if (_node_already_exists) {
+ msg = string("Could not create node - ").append(_path.str());// + " already exists.";
+ _request->respond_error(msg);
+ } else if (_patch == NULL) {
+ msg = "Could not find patch '" + _path.parent().str() +"' to add node.";
+ _request->respond_error(msg);
+ } else if (_plugin == NULL) {
+ msg = "Unable to load node ";
+ msg += _path.str() + " (you're missing the plugin " + _plugin_uri.str() + ")";
+ _request->respond_error(msg);
+ } else if (_node == NULL) {
+ msg = "Failed to instantiate plugin " + _plugin_uri.str();
+ _request->respond_error(msg);
+ } else {
+ _request->respond_ok();
+ _engine.broadcaster()->send_object(_node, true); // yes, send ports
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/CreateNode.hpp b/src/server/events/CreateNode.hpp
new file mode 100644
index 00000000..bbaf830b
--- /dev/null
+++ b/src/server/events/CreateNode.hpp
@@ -0,0 +1,71 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_CREATENODE_HPP
+#define INGEN_EVENTS_CREATENODE_HPP
+
+#include <string>
+#include "QueuedEvent.hpp"
+#include "ingen/Resource.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PatchImpl;
+class PluginImpl;
+class NodeImpl;
+class CompiledPatch;
+
+namespace Events {
+
+/** An event to load a Node and insert it into a Patch.
+ *
+ * \ingroup engine
+ */
+class CreateNode : public QueuedEvent
+{
+public:
+ CreateNode(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& node_path,
+ const Raul::URI& plugin_uri,
+ const Resource::Properties& properties);
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ Raul::Path _path;
+ Raul::URI _plugin_uri;
+ PatchImpl* _patch;
+ PluginImpl* _plugin;
+ NodeImpl* _node;
+ CompiledPatch* _compiled_patch; ///< Patch's new process order
+ bool _node_already_exists;
+ bool _polyphonic;
+
+ Resource::Properties _properties;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_CREATENODE_HPP
diff --git a/src/server/events/CreatePatch.cpp b/src/server/events/CreatePatch.cpp
new file mode 100644
index 00000000..a5c75adf
--- /dev/null
+++ b/src/server/events/CreatePatch.cpp
@@ -0,0 +1,164 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "raul/Maid.hpp"
+#include "raul/Path.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "events/CreatePatch.hpp"
+#include "Request.hpp"
+#include "PatchImpl.hpp"
+#include "NodeImpl.hpp"
+#include "PluginImpl.hpp"
+#include "Engine.hpp"
+#include "ClientBroadcaster.hpp"
+#include "Driver.hpp"
+#include "EngineStore.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+CreatePatch::CreatePatch(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& path,
+ int poly,
+ const Resource::Properties& properties)
+ : QueuedEvent(engine, request, timestamp)
+ , _path(path)
+ , _patch(NULL)
+ , _parent(NULL)
+ , _compiled_patch(NULL)
+ , _poly(poly)
+ , _properties(properties)
+{
+}
+
+void
+CreatePatch::pre_process()
+{
+ if (_path.is_root() || _engine.engine_store()->find_object(_path) != NULL) {
+ _error = OBJECT_EXISTS;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (_poly < 1) {
+ _error = INVALID_POLY;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ const Path& path = (const Path&)_path;
+
+ _parent = _engine.engine_store()->find_patch(path.parent());
+ if (_parent == NULL) {
+ _error = PARENT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ uint32_t poly = 1;
+ if (_parent != NULL && _poly > 1 && _poly == static_cast<int>(_parent->internal_poly()))
+ poly = _poly;
+
+ const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get();
+
+ _patch = new PatchImpl(_engine, path.symbol(), poly, _parent,
+ _engine.driver()->sample_rate(), _poly);
+ _patch->properties().insert(_properties.begin(), _properties.end());
+ _patch->add_property(uris.rdf_type, uris.ingen_Patch);
+ _patch->add_property(uris.rdf_type,
+ Resource::Property(uris.ingen_Node, Resource::EXTERNAL));
+
+ if (_parent != NULL) {
+ _parent->add_node(new PatchImpl::Nodes::Node(_patch));
+
+ if (_parent->enabled())
+ _compiled_patch = _parent->compile();
+ }
+
+ _patch->activate(*_engine.buffer_factory());
+
+ // Insert into EngineStore
+ //_patch->add_to_store(_engine.engine_store());
+ _engine.engine_store()->add(_patch);
+
+ QueuedEvent::pre_process();
+}
+
+void
+CreatePatch::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ if (_patch) {
+ if (!_parent) {
+ assert(_path.is_root());
+ assert(_patch->parent_patch() == NULL);
+ _engine.driver()->set_root_patch(_patch);
+ } else {
+ assert(_parent);
+ assert(!_path.is_root());
+ _engine.maid()->push(_parent->compiled_patch());
+ _parent->compiled_patch(_compiled_patch);
+ }
+ }
+}
+
+void
+CreatePatch::post_process()
+{
+ string msg;
+ if (_request) {
+ switch (_error) {
+ case NO_ERROR:
+ _request->respond_ok();
+ // Don't send ports/nodes that have been added since prepare()
+ // (otherwise they would be sent twice)
+ _engine.broadcaster()->send_object(_patch, false);
+ break;
+ case OBJECT_EXISTS:
+ _request->respond_ok();
+ /*string msg = "Unable to create patch: ";
+ msg.append(_path).append(" already exists.");
+ _request->respond_error(msg);*/
+ break;
+ case PARENT_NOT_FOUND:
+ msg = "Unable to create patch: Parent ";
+ msg.append(Path(_path).parent().str()).append(" not found.");
+ _request->respond_error(msg);
+ break;
+ case INVALID_POLY:
+ msg = "Unable to create patch ";
+ msg.append(_path.str()).append(": ").append("Invalid polyphony requested.");
+ _request->respond_error(msg);
+ break;
+ default:
+ _request->respond_error("Unable to load patch.");
+ }
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/CreatePatch.hpp b/src/server/events/CreatePatch.hpp
new file mode 100644
index 00000000..e3afde5f
--- /dev/null
+++ b/src/server/events/CreatePatch.hpp
@@ -0,0 +1,67 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_CREATEPATCH_HPP
+#define INGEN_EVENTS_CREATEPATCH_HPP
+
+#include "QueuedEvent.hpp"
+#include "ingen/Resource.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PatchImpl;
+class CompiledPatch;
+
+namespace Events {
+
+/** Creates a new Patch.
+ *
+ * \ingroup engine
+ */
+class CreatePatch : public QueuedEvent
+{
+public:
+ CreatePatch(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& path,
+ int poly,
+ const Resource::Properties& properties);
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType { NO_ERROR, OBJECT_EXISTS, PARENT_NOT_FOUND, INVALID_POLY };
+
+ const Raul::Path _path;
+ PatchImpl* _patch;
+ PatchImpl* _parent;
+ CompiledPatch* _compiled_patch;
+ int _poly;
+
+ Resource::Properties _properties;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_CREATEPATCH_HPP
diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp
new file mode 100644
index 00000000..ba46a35d
--- /dev/null
+++ b/src/server/events/CreatePort.cpp
@@ -0,0 +1,192 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "raul/Array.hpp"
+#include "raul/Atom.hpp"
+#include "raul/List.hpp"
+#include "raul/Maid.hpp"
+#include "raul/Path.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "ClientBroadcaster.hpp"
+#include "ControlBindings.hpp"
+#include "CreatePort.hpp"
+#include "Driver.hpp"
+#include "DuplexPort.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "PatchImpl.hpp"
+#include "PatchImpl.hpp"
+#include "PluginImpl.hpp"
+#include "PortImpl.hpp"
+#include "Request.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+CreatePort::CreatePort(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& path,
+ const Raul::URI& type,
+ bool is_output,
+ const Resource::Properties& properties)
+ : QueuedEvent(engine, request, timestamp, bool(request))
+ , _path(path)
+ , _type(type)
+ , _is_output(is_output)
+ , _data_type(type)
+ , _patch(NULL)
+ , _patch_port(NULL)
+ , _driver_port(NULL)
+ , _properties(properties)
+{
+ /* This is blocking because of the two different sets of Patch ports, the array used in the
+ * audio thread (inherited from NodeImpl), and the arrays used in the pre processor thread.
+ * If two add port events arrive in the same cycle and the second pre processes before the
+ * first executes, bad things happen (ports are lost).
+ *
+ * TODO: fix this using RCU?
+ */
+
+ if (_data_type == PortType::UNKNOWN)
+ _error = UNKNOWN_TYPE;
+}
+
+void
+CreatePort::pre_process()
+{
+ if (_error == UNKNOWN_TYPE || _engine.engine_store()->find_object(_path)) {
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _patch = _engine.engine_store()->find_patch(_path.parent());
+
+ const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get();
+
+ if (_patch != NULL) {
+ assert(_patch->path() == _path.parent());
+
+ size_t buffer_size = _engine.buffer_factory()->default_buffer_size(_data_type);
+
+ const uint32_t old_num_ports = (_patch->external_ports())
+ ? _patch->external_ports()->size()
+ : 0;
+
+ Resource::Properties::const_iterator index_i = _properties.find(uris.lv2_index);
+ if (index_i == _properties.end()) {
+ index_i = _properties.insert(make_pair(uris.lv2_index, (int)old_num_ports));
+ } else if (index_i->second.type() != Atom::INT
+ || index_i->second.get_int32() != static_cast<int32_t>(old_num_ports)) {
+ QueuedEvent::pre_process();
+ _error = BAD_INDEX;
+ return;
+ }
+
+ Resource::Properties::const_iterator poly_i = _properties.find(uris.ingen_polyphonic);
+ bool polyphonic = (poly_i != _properties.end() && poly_i->second.type() == Atom::BOOL
+ && poly_i->second.get_bool());
+
+ _patch_port = _patch->create_port(*_engine.buffer_factory(), _path.symbol(), _data_type, buffer_size, _is_output, polyphonic);
+
+ _patch_port->properties().insert(_properties.begin(), _properties.end());
+
+ assert(index_i->second == Atom((int)_patch_port->index()));
+
+ if (_patch_port) {
+
+ if (_is_output)
+ _patch->add_output(new Raul::List<PortImpl*>::Node(_patch_port));
+ else
+ _patch->add_input(new Raul::List<PortImpl*>::Node(_patch_port));
+
+ if (_patch->external_ports())
+ _ports_array = new Raul::Array<PortImpl*>(old_num_ports + 1, *_patch->external_ports(), NULL);
+ else
+ _ports_array = new Raul::Array<PortImpl*>(old_num_ports + 1, NULL);
+
+ _ports_array->at(old_num_ports) = _patch_port;
+ _engine.engine_store()->add(_patch_port);
+
+ if (!_patch->parent())
+ _driver_port = _engine.driver()->create_port(
+ dynamic_cast<DuplexPort*>(_patch_port));
+
+ assert(_ports_array->size() == _patch->num_ports());
+
+ } else {
+ _error = CREATION_FAILED;
+ }
+ }
+ QueuedEvent::pre_process();
+}
+
+void
+CreatePort::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ if (_patch_port) {
+ _engine.maid()->push(_patch->external_ports());
+ _patch->external_ports(_ports_array);
+ _engine.control_bindings()->port_binding_changed(context, _patch_port);
+ }
+
+ if (_driver_port) {
+ _engine.driver()->add_port(_driver_port);
+ }
+
+ if (_request)
+ _request->unblock();
+}
+
+void
+CreatePort::post_process()
+{
+ if (!_request)
+ return;
+
+ string msg;
+ switch (_error) {
+ case NO_ERROR:
+ _request->respond_ok();
+ _engine.broadcaster()->send_object(_patch_port, true);
+ break;
+ case BAD_INDEX:
+ msg = string("Could not create port ") + _path.str() + " (Illegal index given)";
+ _request->respond_error(msg);
+ break;
+ case UNKNOWN_TYPE:
+ msg = string("Could not create port ") + _path.str() + " (Unknown type)";
+ _request->respond_error(msg);
+ break;
+ case CREATION_FAILED:
+ msg = string("Could not create port ") + _path.str() + " (Creation failed)";
+ _request->respond_error(msg);
+ break;
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/CreatePort.hpp b/src/server/events/CreatePort.hpp
new file mode 100644
index 00000000..ae44e2f1
--- /dev/null
+++ b/src/server/events/CreatePort.hpp
@@ -0,0 +1,81 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_CREATEPORT_HPP
+#define INGEN_EVENTS_CREATEPORT_HPP
+
+#include "QueuedEvent.hpp"
+#include "raul/Path.hpp"
+#include "raul/Array.hpp"
+#include "ingen/PortType.hpp"
+#include "ingen/Resource.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PatchImpl;
+class PortImpl;
+class DriverPort;
+
+namespace Events {
+
+/** An event to add a Port to a Patch.
+ *
+ * \ingroup engine
+ */
+class CreatePort : public QueuedEvent
+{
+public:
+ CreatePort(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& path,
+ const Raul::URI& type,
+ bool is_output,
+ const Resource::Properties& properties);
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType {
+ NO_ERROR,
+ UNKNOWN_TYPE,
+ BAD_INDEX,
+ CREATION_FAILED
+ };
+
+ Raul::Path _path;
+ Raul::URI _type;
+ bool _is_output;
+ PortType _data_type;
+ PatchImpl* _patch;
+ PortImpl* _patch_port;
+ Raul::Array<PortImpl*>* _ports_array; ///< New (external) ports array for Patch
+ DriverPort* _driver_port; ///< Driver (eg Jack) port if this is a toplevel port
+ bool _succeeded;
+
+ Resource::Properties _properties;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_CREATEPORT_HPP
diff --git a/src/server/events/Deactivate.hpp b/src/server/events/Deactivate.hpp
new file mode 100644
index 00000000..779ba54c
--- /dev/null
+++ b/src/server/events/Deactivate.hpp
@@ -0,0 +1,49 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_DEACTIVATE_HPP
+#define INGEN_EVENTS_DEACTIVATE_HPP
+
+#include "QueuedEvent.hpp"
+#include "Engine.hpp"
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+/** Deactivates the engine.
+ *
+ * \ingroup engine
+ */
+class Deactivate : public QueuedEvent
+{
+public:
+ Deactivate(Engine& engine, SharedPtr<Request> request, SampleCount timestamp)
+ : QueuedEvent(engine, request, timestamp)
+ {}
+
+ void post_process() {
+ _request->respond_ok();
+ _engine.deactivate();
+ }
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_DEACTIVATE_HPP
diff --git a/src/server/events/Delete.cpp b/src/server/events/Delete.cpp
new file mode 100644
index 00000000..b1dc1558
--- /dev/null
+++ b/src/server/events/Delete.cpp
@@ -0,0 +1,213 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "raul/Maid.hpp"
+#include "raul/Path.hpp"
+#include "ClientBroadcaster.hpp"
+#include "ControlBindings.hpp"
+#include "Delete.hpp"
+#include "DisconnectAll.hpp"
+#include "Driver.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "NodeImpl.hpp"
+#include "PatchImpl.hpp"
+#include "PluginImpl.hpp"
+#include "PortImpl.hpp"
+#include "Request.hpp"
+
+using namespace std;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+Delete::Delete(Engine& engine,
+ SharedPtr<Request> request,
+ FrameTime time,
+ const Raul::URI& uri)
+ : QueuedEvent(engine, request, time, true)
+ , _uri(uri)
+ , _store_iterator(engine.engine_store()->end())
+ , _garbage(NULL)
+ , _driver_port(NULL)
+ , _patch_node_listnode(NULL)
+ , _patch_port_listnode(NULL)
+ , _ports_array(NULL)
+ , _compiled_patch(NULL)
+ , _disconnect_event(NULL)
+{
+ assert(request);
+ assert(request->source());
+
+ if (Raul::Path::is_path(uri))
+ _path = Raul::Path(uri.str());
+}
+
+Delete::~Delete()
+{
+ delete _disconnect_event;
+}
+
+void
+Delete::pre_process()
+{
+ if (_path.is_root() || _path == "path:/control_in" || _path == "path:/control_out") {
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _removed_bindings = _engine.control_bindings()->remove(_path);
+
+ _store_iterator = _engine.engine_store()->find(_path);
+
+ if (_store_iterator != _engine.engine_store()->end()) {
+ _node = PtrCast<NodeImpl>(_store_iterator->second);
+
+ if (!_node)
+ _port = PtrCast<PortImpl>(_store_iterator->second);
+ }
+
+ if (_store_iterator != _engine.engine_store()->end()) {
+ _removed_table = _engine.engine_store()->remove(_store_iterator);
+ }
+
+ if (_node && !_path.is_root()) {
+ assert(_node->parent_patch());
+ _patch_node_listnode = _node->parent_patch()->remove_node(_path.symbol());
+ if (_patch_node_listnode) {
+ assert(_patch_node_listnode->elem() == _node.get());
+
+ _disconnect_event = new DisconnectAll(_engine, _node->parent_patch(), _node.get());
+ _disconnect_event->pre_process();
+
+ if (_node->parent_patch()->enabled()) {
+ // FIXME: is this called multiple times?
+ _compiled_patch = _node->parent_patch()->compile();
+#ifndef NDEBUG
+ // Be sure node is removed from process order, so it can be deleted
+ for (size_t i=0; i < _compiled_patch->size(); ++i) {
+ assert(_compiled_patch->at(i).node() != _node.get());
+ // FIXME: check providers/dependants too
+ }
+#endif
+ }
+ }
+ } else if (_port) {
+ assert(_port->parent_patch());
+ _patch_port_listnode = _port->parent_patch()->remove_port(_path.symbol());
+ if (_patch_port_listnode) {
+ assert(_patch_port_listnode->elem() == _port.get());
+
+ _disconnect_event = new DisconnectAll(_engine, _port->parent_patch(), _port.get());
+ _disconnect_event->pre_process();
+
+ if (_port->parent_patch()->enabled()) {
+ // FIXME: is this called multiple times?
+ _compiled_patch = _port->parent_patch()->compile();
+ _ports_array = _port->parent_patch()->build_ports_array();
+ assert(_ports_array->size() == _port->parent_patch()->num_ports());
+ }
+ }
+
+ }
+
+ QueuedEvent::pre_process();
+}
+
+void
+Delete::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ PatchImpl* parent_patch = NULL;
+
+ if (_patch_node_listnode) {
+ assert(_node);
+
+ if (_disconnect_event)
+ _disconnect_event->execute(context);
+
+ parent_patch = _node->parent_patch();
+
+ } else if (_patch_port_listnode) {
+ assert(_port);
+
+ if (_disconnect_event)
+ _disconnect_event->execute(context);
+
+ parent_patch = _port->parent_patch();
+
+ _engine.maid()->push(_port->parent_patch()->external_ports());
+ _port->parent_patch()->external_ports(_ports_array);
+
+ if ( ! _port->parent_patch()->parent())
+ _garbage = _engine.driver()->remove_port(_port->path(), &_driver_port);
+ }
+
+ if (parent_patch) {
+ _engine.maid()->push(parent_patch->compiled_patch());
+ parent_patch->compiled_patch(_compiled_patch);
+ }
+
+ _request->unblock();
+}
+
+void
+Delete::post_process()
+{
+ _removed_bindings.reset();
+
+ if (!Raul::Path::is_path(_uri)
+ || _path.is_root() || _path == "path:/control_in" || _path == "path:/control_out") {
+ // XXX: Just ignore?
+ //_request->respond_error(_path.chop_scheme() + " can not be deleted");
+ } else if (!_node && !_port) {
+ string msg = string("Could not find object ") + _path.chop_scheme() + " to delete";
+ _request->respond_error(msg);
+ } else if (_patch_node_listnode) {
+ assert(_node);
+ _node->deactivate();
+ _request->respond_ok();
+ _engine.broadcaster()->bundle_begin();
+ if (_disconnect_event)
+ _disconnect_event->post_process();
+ _engine.broadcaster()->del(_path);
+ _engine.broadcaster()->bundle_end();
+ _engine.maid()->push(_patch_node_listnode);
+ } else if (_patch_port_listnode) {
+ assert(_port);
+ _request->respond_ok();
+ _engine.broadcaster()->bundle_begin();
+ if (_disconnect_event)
+ _disconnect_event->post_process();
+ _engine.broadcaster()->del(_path);
+ _engine.broadcaster()->bundle_end();
+ _engine.maid()->push(_patch_port_listnode);
+ } else {
+ _request->respond_error("Unable to delete object " + _path.chop_scheme());
+ }
+
+ if (_driver_port)
+ _driver_port->destroy();
+
+ _engine.maid()->push(_garbage);
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
diff --git a/src/server/events/Delete.hpp b/src/server/events/Delete.hpp
new file mode 100644
index 00000000..ba256d87
--- /dev/null
+++ b/src/server/events/Delete.hpp
@@ -0,0 +1,95 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_DELETE_HPP
+#define INGEN_EVENTS_DELETE_HPP
+
+#include "QueuedEvent.hpp"
+#include "EngineStore.hpp"
+#include "PatchImpl.hpp"
+#include "ControlBindings.hpp"
+
+namespace Raul {
+ template<typename T> class Array;
+ template<typename T> class ListNode;
+}
+
+namespace Ingen {
+namespace Server {
+
+class GraphObjectImpl;
+class NodeImpl;
+class PortImpl;
+class DriverPort;
+class CompiledPatch;
+
+namespace Events {
+
+class DisconnectAll;
+
+/** \page methods
+ * <h2>DELETE</h2>
+ * As per WebDAV (RFC4918 S9.6).
+ *
+ * Remove an object from the engine and destroy it.
+ *
+ * \li All properties of the object are lost
+ * \li All references to the object are lost (e.g. the parent's reference to
+ * this child is lost, any connections to the object are removed, etc.)
+ */
+
+/** DELETE a graph object (see \ref methods).
+ * \ingroup engine
+ */
+class Delete : public QueuedEvent
+{
+public:
+ Delete(Engine& engine,
+ SharedPtr<Request> request,
+ FrameTime timestamp,
+ const Raul::URI& uri);
+
+ ~Delete();
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ Raul::URI _uri;
+ Raul::Path _path;
+ EngineStore::iterator _store_iterator;
+ SharedPtr<NodeImpl> _node; ///< Non-NULL iff a node
+ SharedPtr<PortImpl> _port; ///< Non-NULL iff a port
+ Raul::Deletable* _garbage;
+ DriverPort* _driver_port;
+ PatchImpl::Nodes::Node* _patch_node_listnode;
+ Raul::List<PortImpl*>::Node* _patch_port_listnode;
+ Raul::Array<PortImpl*>* _ports_array; ///< New (external) ports for Patch
+ CompiledPatch* _compiled_patch; ///< Patch's new process order
+ DisconnectAll* _disconnect_event;
+
+ SharedPtr<ControlBindings::Bindings> _removed_bindings;
+
+ SharedPtr< Raul::Table<Raul::Path, SharedPtr<GraphObject> > > _removed_table;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_DELETE_HPP
diff --git a/src/server/events/Disconnect.cpp b/src/server/events/Disconnect.cpp
new file mode 100644
index 00000000..e9374648
--- /dev/null
+++ b/src/server/events/Disconnect.cpp
@@ -0,0 +1,269 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "raul/log.hpp"
+#include "raul/Maid.hpp"
+#include "raul/Path.hpp"
+#include "events/Disconnect.hpp"
+#include "AudioBuffer.hpp"
+#include "ClientBroadcaster.hpp"
+#include "ConnectionImpl.hpp"
+#include "DuplexPort.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "InputPort.hpp"
+#include "OutputPort.hpp"
+#include "PatchImpl.hpp"
+#include "PortImpl.hpp"
+#include "ProcessContext.hpp"
+#include "Request.hpp"
+#include "ThreadManager.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+Disconnect::Disconnect(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& src_port_path,
+ const Raul::Path& dst_port_path)
+ : QueuedEvent(engine, request, timestamp)
+ , _src_port_path(src_port_path)
+ , _dst_port_path(dst_port_path)
+ , _patch(NULL)
+ , _src_port(NULL)
+ , _dst_port(NULL)
+ , _impl(NULL)
+ , _compiled_patch(NULL)
+{
+}
+
+Disconnect::Impl::Impl(Engine& e,
+ PatchImpl* patch,
+ OutputPort* s,
+ InputPort* d)
+ : _engine(e)
+ , _src_output_port(s)
+ , _dst_input_port(d)
+ , _patch(patch)
+ , _connection(patch->remove_connection(_src_output_port, _dst_input_port))
+ , _buffers(NULL)
+{
+ ThreadManager::assert_thread(THREAD_PRE_PROCESS);
+
+ NodeImpl* const src_node = _src_output_port->parent_node();
+ NodeImpl* const dst_node = _dst_input_port->parent_node();
+
+ for (Raul::List<NodeImpl*>::iterator i = dst_node->providers()->begin();
+ i != dst_node->providers()->end(); ++i) {
+ if ((*i) == src_node) {
+ delete dst_node->providers()->erase(i);
+ break;
+ }
+ }
+
+ for (Raul::List<NodeImpl*>::iterator i = src_node->dependants()->begin();
+ i != src_node->dependants()->end(); ++i) {
+ if ((*i) == dst_node) {
+ delete src_node->dependants()->erase(i);
+ break;
+ }
+ }
+
+ _dst_input_port->decrement_num_connections();
+
+ if (_dst_input_port->num_connections() == 0) {
+ _buffers = new Raul::Array<BufferFactory::Ref>(_dst_input_port->poly());
+ _dst_input_port->get_buffers(*_engine.buffer_factory(),
+ _buffers, _dst_input_port->poly());
+
+ const bool is_control = _dst_input_port->is_a(PortType::CONTROL);
+ const float value = is_control ? _dst_input_port->value().get_float() : 0;
+ for (uint32_t i = 0; i < _buffers->size(); ++i) {
+ if (is_control) {
+ PtrCast<AudioBuffer>(_buffers->at(i))->set_value(value, 0, 0);
+ } else {
+ _buffers->at(i)->clear();
+ }
+ }
+ }
+
+ _connection->pending_disconnection(true);
+}
+
+void
+Disconnect::pre_process()
+{
+ if (_src_port_path.parent().parent() != _dst_port_path.parent().parent()
+ && _src_port_path.parent() != _dst_port_path.parent().parent()
+ && _src_port_path.parent().parent() != _dst_port_path.parent()) {
+ _error = PARENT_PATCH_DIFFERENT;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _src_port = _engine.engine_store()->find_port(_src_port_path);
+ _dst_port = _engine.engine_store()->find_port(_dst_port_path);
+
+ if (_src_port == NULL || _dst_port == NULL) {
+ _error = PORT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ NodeImpl* const src_node = _src_port->parent_node();
+ NodeImpl* const dst_node = _dst_port->parent_node();
+
+ // Connection to a patch port from inside the patch
+ if (src_node->parent_patch() != dst_node->parent_patch()) {
+
+ assert(src_node->parent() == dst_node || dst_node->parent() == src_node);
+ if (src_node->parent() == dst_node)
+ _patch = dynamic_cast<PatchImpl*>(dst_node);
+ else
+ _patch = dynamic_cast<PatchImpl*>(src_node);
+
+ // Connection from a patch input to a patch output (pass through)
+ } else if (src_node == dst_node && dynamic_cast<PatchImpl*>(src_node)) {
+ _patch = dynamic_cast<PatchImpl*>(src_node);
+
+ // Normal connection between nodes with the same parent
+ } else {
+ _patch = src_node->parent_patch();
+ }
+
+ assert(_patch);
+
+ if (!_patch->has_connection(_src_port, _dst_port)) {
+ _error = NOT_CONNECTED;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (src_node == NULL || dst_node == NULL) {
+ _error = PARENTS_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _impl = new Impl(_engine,
+ _patch,
+ dynamic_cast<OutputPort*>(_src_port),
+ dynamic_cast<InputPort*>(_dst_port));
+
+ if (_patch->enabled())
+ _compiled_patch = _patch->compile();
+
+ QueuedEvent::pre_process();
+}
+
+bool
+Disconnect::Impl::execute(ProcessContext& context, bool set_dst_buffers)
+{
+ ThreadManager::assert_thread(THREAD_PROCESS);
+
+ InputPort::Connections::Node* const port_connections_node
+ = _dst_input_port->remove_connection(context, _src_output_port);
+ if (!port_connections_node) {
+ return false;
+ }
+
+ if (set_dst_buffers) {
+ if (_buffers) {
+ _engine.maid()->push(_dst_input_port->set_buffers(_buffers));
+ } else {
+ _dst_input_port->setup_buffers(*_engine.buffer_factory(),
+ _dst_input_port->poly());
+ }
+ _dst_input_port->connect_buffers();
+ } else {
+ _dst_input_port->recycle_buffers();
+ }
+
+ assert(_connection);
+ assert(port_connections_node->elem() == _connection);
+
+ _engine.maid()->push(port_connections_node);
+ return true;
+}
+
+void
+Disconnect::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ if (_error == NO_ERROR) {
+ if (!_impl->execute(context, true)) {
+ _error = CONNECTION_NOT_FOUND;
+ return;
+ }
+
+ _engine.maid()->push(_patch->compiled_patch());
+ _patch->compiled_patch(_compiled_patch);
+ }
+}
+
+void
+Disconnect::post_process()
+{
+ if (_error == NO_ERROR) {
+ if (_request)
+ _request->respond_ok();
+ _engine.broadcaster()->disconnect(_src_port->path(), _dst_port->path());
+ } else {
+ string msg("Unable to disconnect ");
+ msg.append(_src_port_path.str() + " => " + _dst_port_path.str());
+ msg.append(" (");
+ switch (_error) {
+ case PARENT_PATCH_DIFFERENT:
+ msg.append("Ports exist in different patches");
+ break;
+ case PORT_NOT_FOUND:
+ msg.append("Port not found");
+ break;
+ case TYPE_MISMATCH:
+ msg.append("Ports have incompatible types");
+ break;
+ case NOT_CONNECTED:
+ msg.append("Ports are not connected");
+ break;
+ case PARENTS_NOT_FOUND:
+ msg.append("Parent node not found");
+ break;
+ case CONNECTION_NOT_FOUND:
+ msg.append("Connection not found");
+ break;
+ default:
+ break;
+ }
+ msg.append(")");
+ if (_request)
+ _request->respond_error(msg);
+ }
+
+ delete _impl;
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/Disconnect.hpp b/src/server/events/Disconnect.hpp
new file mode 100644
index 00000000..a553fe79
--- /dev/null
+++ b/src/server/events/Disconnect.hpp
@@ -0,0 +1,106 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_DISCONNECT_HPP
+#define INGEN_EVENTS_DISCONNECT_HPP
+
+#include "raul/Path.hpp"
+#include "QueuedEvent.hpp"
+#include "types.hpp"
+#include "PatchImpl.hpp"
+#include "BufferFactory.hpp"
+
+namespace Raul {
+ template <typename T> class ListNode;
+ template <typename T> class Array;
+}
+
+namespace Ingen {
+namespace Server {
+
+class CompiledPatch;
+class InputPort;
+class OutputPort;
+class PortImpl;
+
+namespace Events {
+
+/** Make a Connection between two Ports.
+ *
+ * \ingroup engine
+ */
+class Disconnect : public QueuedEvent
+{
+public:
+ Disconnect(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& src_port_path,
+ const Raul::Path& dst_port_path);
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+ class Impl {
+ public:
+ Impl(Engine& e,
+ PatchImpl* patch,
+ OutputPort* s,
+ InputPort* d);
+
+ bool execute(ProcessContext& context, bool set_dst_buffers);
+
+ InputPort* dst_port() { return _dst_input_port; }
+
+ private:
+ Engine& _engine;
+ OutputPort* _src_output_port;
+ InputPort* _dst_input_port;
+ PatchImpl* _patch;
+ SharedPtr<ConnectionImpl> _connection;
+ Raul::Array<BufferFactory::Ref>* _buffers;
+ };
+
+private:
+ enum ErrorType {
+ NO_ERROR,
+ PARENT_PATCH_DIFFERENT,
+ PORT_NOT_FOUND,
+ TYPE_MISMATCH,
+ NOT_CONNECTED,
+ PARENTS_NOT_FOUND,
+ CONNECTION_NOT_FOUND
+ };
+
+ const Raul::Path _src_port_path;
+ const Raul::Path _dst_port_path;
+
+ PatchImpl* _patch;
+ PortImpl* _src_port;
+ PortImpl* _dst_port;
+
+ Impl* _impl;
+ CompiledPatch* _compiled_patch;
+};
+
+} // namespace Events
+} // namespace Server
+} // namespace Ingen
+
+#endif // INGEN_EVENTS_DISCONNECT_HPP
diff --git a/src/server/events/DisconnectAll.cpp b/src/server/events/DisconnectAll.cpp
new file mode 100644
index 00000000..dd694810
--- /dev/null
+++ b/src/server/events/DisconnectAll.cpp
@@ -0,0 +1,189 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <boost/format.hpp>
+
+#include "raul/Array.hpp"
+#include "raul/Maid.hpp"
+#include "raul/Path.hpp"
+
+#include "ClientBroadcaster.hpp"
+#include "ConnectionImpl.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "InputPort.hpp"
+#include "NodeImpl.hpp"
+#include "OutputPort.hpp"
+#include "PatchImpl.hpp"
+#include "PortImpl.hpp"
+#include "Request.hpp"
+#include "events/Disconnect.hpp"
+#include "events/DisconnectAll.hpp"
+#include "util.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+DisconnectAll::DisconnectAll(Engine& engine, SharedPtr<Request> request, SampleCount timestamp, const Path& parent_path, const Path& node_path)
+ : QueuedEvent(engine, request, timestamp)
+ , _parent_path(parent_path)
+ , _path(node_path)
+ , _parent(NULL)
+ , _node(NULL)
+ , _port(NULL)
+ , _compiled_patch(NULL)
+ , _deleting(false)
+{
+}
+
+/** Internal version for use by other events.
+ */
+DisconnectAll::DisconnectAll(Engine& engine, PatchImpl* parent, GraphObjectImpl* object)
+ : QueuedEvent(engine)
+ , _parent_path(parent->path())
+ , _path(object->path())
+ , _parent(parent)
+ , _node(dynamic_cast<NodeImpl*>(object))
+ , _port(dynamic_cast<PortImpl*>(object))
+ , _compiled_patch(NULL)
+ , _deleting(true)
+{
+}
+
+DisconnectAll::~DisconnectAll()
+{
+ for (Impls::iterator i = _impls.begin(); i != _impls.end(); ++i)
+ delete (*i);
+}
+
+void
+DisconnectAll::maybe_remove_connection(ConnectionImpl* c)
+{
+ if (c->pending_disconnection())
+ return;
+
+ OutputPort* src = dynamic_cast<OutputPort*>(c->src_port());
+ InputPort* dst = dynamic_cast<InputPort*>(c->dst_port());
+
+ if (_node) {
+ if (src->parent_node() == _node || dst->parent_node() == _node) {
+ _impls.push_back(new Disconnect::Impl(_engine, _parent, src, dst));
+ }
+ } else {
+ assert(_port);
+ if (src == _port || dst == _port) {
+ _impls.push_back(new Disconnect::Impl(_engine, _parent, src, dst));
+ }
+ }
+}
+
+void
+DisconnectAll::pre_process()
+{
+ if (!_deleting) {
+ _parent = _engine.engine_store()->find_patch(_parent_path);
+
+ if (_parent == NULL) {
+ _error = PARENT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ GraphObjectImpl* object = _engine.engine_store()->find_object(_path);
+
+ if (object == NULL) {
+ _error = OBJECT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (object->parent_patch() != _parent && object->parent()->parent_patch() != _parent) {
+ _error = INVALID_PARENT_PATH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ // Only one of these will succeed
+ _node = dynamic_cast<NodeImpl*>(object);
+ _port = dynamic_cast<PortImpl*>(object);
+
+ assert((_node || _port) && !(_node && _port));
+ }
+
+ for (Patch::Connections::const_iterator i = _parent->connections().begin();
+ i != _parent->connections().end(); ++i) {
+ maybe_remove_connection((ConnectionImpl*)i->second.get());
+ }
+
+ if (!_deleting && _parent->enabled())
+ _compiled_patch = _parent->compile();
+
+ QueuedEvent::pre_process();
+}
+
+void
+DisconnectAll::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ if (_error == NO_ERROR) {
+ for (Impls::iterator i = _impls.begin(); i != _impls.end(); ++i) {
+ (*i)->execute(context,
+ !_deleting || ((*i)->dst_port()->parent_node() != _node));
+ }
+ }
+
+ _engine.maid()->push(_parent->compiled_patch());
+ _parent->compiled_patch(_compiled_patch);
+}
+
+void
+DisconnectAll::post_process()
+{
+ if (_error == NO_ERROR) {
+ if (_request)
+ _request->respond_ok();
+ _engine.broadcaster()->disconnect_all(_parent_path, _path);
+ } else {
+ if (_request) {
+ boost::format fmt("Unable to disconnect %1% (%2%)");
+ fmt % _path;
+ switch (_error) {
+ case INVALID_PARENT_PATH:
+ fmt % string("Invalid parent path: ").append(_parent_path.str());
+ break;
+ case PARENT_NOT_FOUND:
+ fmt % string("Unable to find parent: ").append(_parent_path.str());
+ break;
+ case OBJECT_NOT_FOUND:
+ fmt % string("Unable to find object");
+ default:
+ break;
+ }
+ _request->respond_error(fmt.str());
+ }
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/DisconnectAll.hpp b/src/server/events/DisconnectAll.hpp
new file mode 100644
index 00000000..d53d1114
--- /dev/null
+++ b/src/server/events/DisconnectAll.hpp
@@ -0,0 +1,93 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_DISCONNECTALL_HPP
+#define INGEN_EVENTS_DISCONNECTALL_HPP
+
+#include <list>
+
+#include "raul/Path.hpp"
+
+#include "Disconnect.hpp"
+#include "QueuedEvent.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class CompiledPatch;
+class NodeImpl;
+class PatchImpl;
+class PortImpl;
+
+namespace Events {
+
+class Disconnect;
+
+/** An event to disconnect all connections to a Node.
+ *
+ * \ingroup engine
+ */
+class DisconnectAll : public QueuedEvent
+{
+public:
+ DisconnectAll(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& parent,
+ const Raul::Path& object);
+
+ DisconnectAll(
+ Engine& engine,
+ PatchImpl* parent,
+ GraphObjectImpl* object);
+
+ ~DisconnectAll();
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType {
+ NO_ERROR,
+ INVALID_PARENT_PATH,
+ PARENT_NOT_FOUND,
+ OBJECT_NOT_FOUND,
+ };
+
+ void maybe_remove_connection(ConnectionImpl* c);
+
+ Raul::Path _parent_path;
+ Raul::Path _path;
+ PatchImpl* _parent;
+ NodeImpl* _node;
+ PortImpl* _port;
+
+ typedef std::list<Disconnect::Impl*> Impls;
+ Impls _impls;
+
+ CompiledPatch* _compiled_patch; ///< New process order for Patch
+
+ bool _deleting;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_DISCONNECTALL_HPP
diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp
new file mode 100644
index 00000000..058f0b63
--- /dev/null
+++ b/src/server/events/Get.cpp
@@ -0,0 +1,84 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "Get.hpp"
+#include "ingen/ClientInterface.hpp"
+#include "Request.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "ClientBroadcaster.hpp"
+#include "PatchImpl.hpp"
+#include "NodeImpl.hpp"
+#include "PortImpl.hpp"
+#include "ObjectSender.hpp"
+#include "ProcessContext.hpp"
+
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+Get::Get(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const URI& uri)
+ : QueuedEvent(engine, request, timestamp)
+ , _uri(uri)
+ , _object(NULL)
+ , _plugin(NULL)
+{
+}
+
+void
+Get::pre_process()
+{
+ if (_uri == "ingen:plugins") {
+ _plugins = _engine.node_factory()->plugins();
+ } else if (Path::is_valid(_uri.str())) {
+ _object = _engine.engine_store()->find_object(Path(_uri.str()));
+ } else {
+ _plugin = _engine.node_factory()->plugin(_uri);
+ }
+
+ QueuedEvent::pre_process();
+}
+
+void
+Get::post_process()
+{
+ if (_uri == "ingen:plugins") {
+ _request->respond_ok();
+ _engine.broadcaster()->send_plugins_to(_request->client(), _plugins);
+ } else if (!_object && !_plugin) {
+ _request->respond_error("Unable to find object requested.");
+ } else if (_request->client()) {
+ _request->respond_ok();
+ if (_object)
+ ObjectSender::send_object(_request->client(), _object, true);
+ else if (_plugin)
+ _request->client()->put(_uri, _plugin->properties());
+ } else {
+ _request->respond_error("Unable to find client to send object.");
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/Get.hpp b/src/server/events/Get.hpp
new file mode 100644
index 00000000..ed68e3c0
--- /dev/null
+++ b/src/server/events/Get.hpp
@@ -0,0 +1,60 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_GET_HPP
+#define INGEN_EVENTS_GET_HPP
+
+#include "QueuedEvent.hpp"
+#include "NodeFactory.hpp"
+#include "types.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class GraphObjectImpl;
+class PluginImpl;
+
+namespace Events {
+
+/** A request from a client to send an object.
+ *
+ * \ingroup engine
+ */
+class Get : public QueuedEvent
+{
+public:
+ Get(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::URI& uri);
+
+ void pre_process();
+ void post_process();
+
+private:
+ const Raul::URI _uri;
+ GraphObjectImpl* _object;
+ const PluginImpl* _plugin;
+ NodeFactory::Plugins _plugins;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_GET_HPP
diff --git a/src/server/events/Move.cpp b/src/server/events/Move.cpp
new file mode 100644
index 00000000..2e006b8c
--- /dev/null
+++ b/src/server/events/Move.cpp
@@ -0,0 +1,130 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "raul/Path.hpp"
+#include "events/Move.hpp"
+#include "ClientBroadcaster.hpp"
+#include "Engine.hpp"
+#include "NodeImpl.hpp"
+#include "EngineStore.hpp"
+#include "PatchImpl.hpp"
+#include "Request.hpp"
+#include "Driver.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+Move::Move(Engine& engine, SharedPtr<Request> request, SampleCount timestamp, const Path& path, const Path& new_path)
+ : QueuedEvent(engine, request, timestamp)
+ , _old_path(path)
+ , _new_path(new_path)
+ , _parent_patch(NULL)
+ , _store_iterator(engine.engine_store()->end())
+{
+}
+
+Move::~Move()
+{
+}
+
+void
+Move::pre_process()
+{
+ if (!_old_path.parent().is_parent_of(_new_path)) {
+ _error = PARENT_DIFFERS;
+ QueuedEvent::pre_process();
+ return;
+ }
+ _store_iterator = _engine.engine_store()->find(_old_path);
+ if (_store_iterator == _engine.engine_store()->end()) {
+ _error = OBJECT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (_engine.engine_store()->find_object(_new_path)) {
+ _error = OBJECT_EXISTS;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ SharedPtr< Table<Path, SharedPtr<GraphObject> > > removed
+ = _engine.engine_store()->remove(_store_iterator);
+
+ assert(removed->size() > 0);
+
+ for (Table<Path, SharedPtr<GraphObject> >::iterator i = removed->begin(); i != removed->end(); ++i) {
+ const Path& child_old_path = i->first;
+ assert(Path::descendant_comparator(_old_path, child_old_path));
+
+ Path child_new_path;
+ if (child_old_path == _old_path)
+ child_new_path = _new_path;
+ else
+ child_new_path = Path(_new_path).base() + child_old_path.substr(_old_path.length()+1);
+
+ PtrCast<GraphObjectImpl>(i->second)->set_path(child_new_path);
+ i->first = child_new_path;
+ }
+
+ _engine.engine_store()->add(*removed.get());
+
+ QueuedEvent::pre_process();
+}
+
+void
+Move::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+
+ SharedPtr<PortImpl> port = PtrCast<PortImpl>(_store_iterator->second);
+ if (port && port->parent()->parent() == NULL) {
+ DriverPort* driver_port = _engine.driver()->driver_port(_new_path);
+ if (driver_port)
+ driver_port->move(_new_path);
+ }
+}
+
+void
+Move::post_process()
+{
+ string msg = "Unable to rename object - ";
+
+ if (_error == NO_ERROR) {
+ _request->respond_ok();
+ _engine.broadcaster()->move(_old_path, _new_path);
+ } else {
+ if (_error == OBJECT_EXISTS)
+ msg.append("Object already exists at ").append(_new_path.str());
+ else if (_error == OBJECT_NOT_FOUND)
+ msg.append("Could not find object ").append(_old_path.str());
+ else if (_error == OBJECT_NOT_RENAMABLE)
+ msg.append(_old_path.str()).append(" is not renamable");
+ else if (_error == PARENT_DIFFERS)
+ msg.append(_new_path.str()).append(" is a child of a different patch");
+
+ _request->respond_error(msg);
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
diff --git a/src/server/events/Move.hpp b/src/server/events/Move.hpp
new file mode 100644
index 00000000..4286f583
--- /dev/null
+++ b/src/server/events/Move.hpp
@@ -0,0 +1,79 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_MOVE_HPP
+#define INGEN_EVENTS_MOVE_HPP
+
+#include "raul/Path.hpp"
+#include "QueuedEvent.hpp"
+#include "EngineStore.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PatchImpl;
+
+namespace Events {
+
+/** \page methods
+ * <h2>MOVE</h2>
+ * As per WebDAV (RFC4918 S9.9).
+ *
+ * Move an object from its current location and insert it at a new location
+ * in a single operation.
+ *
+ * MOVE to a path with a different parent is currently not supported.
+ */
+
+/** MOVE a graph object to a new path (see \ref methods).
+ * \ingroup engine
+ */
+class Move : public QueuedEvent
+{
+public:
+ Move(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::Path& old_path,
+ const Raul::Path& new_path);
+ ~Move();
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType {
+ NO_ERROR,
+ OBJECT_NOT_FOUND,
+ OBJECT_EXISTS,
+ OBJECT_NOT_RENAMABLE,
+ PARENT_DIFFERS
+ };
+
+ Raul::Path _old_path;
+ Raul::Path _new_path;
+ PatchImpl* _parent_patch;
+ EngineStore::iterator _store_iterator;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_MOVE_HPP
diff --git a/src/server/events/Ping.hpp b/src/server/events/Ping.hpp
new file mode 100644
index 00000000..2353e496
--- /dev/null
+++ b/src/server/events/Ping.hpp
@@ -0,0 +1,51 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_PING_HPP
+#define INGEN_EVENTS_PING_HPP
+
+#include "QueuedEvent.hpp"
+#include "types.hpp"
+#include "Request.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PortImpl;
+
+namespace Events {
+
+/** A ping that travels through the pre-processed event queue before responding
+ * (useful for the order guarantee).
+ *
+ * \ingroup engine
+ */
+class Ping : public QueuedEvent
+{
+public:
+ Ping(Engine& engine, SharedPtr<Request> request, SampleCount timestamp)
+ : QueuedEvent(engine, request, timestamp)
+ {}
+
+ void post_process() { _request->respond_ok(); }
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_PING_HPP
diff --git a/src/server/events/RegisterClient.cpp b/src/server/events/RegisterClient.cpp
new file mode 100644
index 00000000..71ec26bc
--- /dev/null
+++ b/src/server/events/RegisterClient.cpp
@@ -0,0 +1,57 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "Request.hpp"
+#include "events/RegisterClient.hpp"
+#include "Engine.hpp"
+#include "ClientBroadcaster.hpp"
+
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+RegisterClient::RegisterClient(Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const URI& uri,
+ ClientInterface* client)
+ : QueuedEvent(engine, request, timestamp)
+ , _uri(uri)
+ , _client(client)
+{
+}
+
+void
+RegisterClient::pre_process()
+{
+ _engine.broadcaster()->register_client(_uri, _client);
+
+ QueuedEvent::pre_process();
+}
+
+void
+RegisterClient::post_process()
+{
+ _request->respond_ok();
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/RegisterClient.hpp b/src/server/events/RegisterClient.hpp
new file mode 100644
index 00000000..ec2d0809
--- /dev/null
+++ b/src/server/events/RegisterClient.hpp
@@ -0,0 +1,54 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_REGISTERCLIENT_HPP
+#define INGEN_EVENTS_REGISTERCLIENT_HPP
+
+#include "raul/URI.hpp"
+#include "ingen/ClientInterface.hpp"
+#include "QueuedEvent.hpp"
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+/** Registers a new client with the OSC system, so it can receive updates.
+ *
+ * \ingroup engine
+ */
+class RegisterClient : public QueuedEvent
+{
+public:
+ RegisterClient(Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::URI& uri,
+ ClientInterface* client);
+
+ void pre_process();
+ void post_process();
+
+private:
+ Raul::URI _uri;
+ ClientInterface* _client;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_REGISTERCLIENT_HPP
diff --git a/src/server/events/RequestMetadata.cpp b/src/server/events/RequestMetadata.cpp
new file mode 100644
index 00000000..156fb51d
--- /dev/null
+++ b/src/server/events/RequestMetadata.cpp
@@ -0,0 +1,137 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "raul/IntrusivePtr.hpp"
+#include "ingen/ClientInterface.hpp"
+#include "events/RequestMetadata.hpp"
+#include "shared/LV2Atom.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "AudioBuffer.hpp"
+#include "ClientBroadcaster.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "GraphObjectImpl.hpp"
+#include "ObjectBuffer.hpp"
+#include "PluginImpl.hpp"
+#include "PortImpl.hpp"
+#include "ProcessContext.hpp"
+#include "Request.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+RequestMetadata::RequestMetadata(Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ Resource::Graph ctx,
+ const URI& subject,
+ const URI& key)
+ : QueuedEvent(engine, request, timestamp)
+ , _special_type(NONE)
+ , _uri(subject)
+ , _key(key)
+ , _resource(0)
+ , _context(ctx)
+{
+}
+
+void
+RequestMetadata::pre_process()
+{
+ const bool is_object = Path::is_path(_uri);
+ if (_request->client()) {
+ if (is_object)
+ _resource = _engine.engine_store()->find_object(Path(_uri.str()));
+ else
+ _resource = _engine.node_factory()->plugin(_uri);
+
+ if (!_resource) {
+ QueuedEvent::pre_process();
+ return;
+ }
+ }
+
+ GraphObjectImpl* obj = dynamic_cast<GraphObjectImpl*>(_resource);
+ if (obj) {
+ if (_key == _engine.world()->uris()->ingen_value)
+ _special_type = PORT_VALUE;
+ else
+ _value = obj->get_property(_key);
+ } else {
+ _value = _resource->get_property(_key);
+ }
+
+ QueuedEvent::pre_process();
+}
+
+void
+RequestMetadata::execute(ProcessContext& context)
+{
+ QueuedEvent::execute(context);
+ if (_special_type == PORT_VALUE) {
+ PortImpl* port = dynamic_cast<PortImpl*>(_resource);
+ if (port) {
+ IntrusivePtr<AudioBuffer> abuf = PtrCast<AudioBuffer>(port->buffer(0));
+ if (abuf) {
+ _value = abuf->value_at(0);
+ } else {
+ IntrusivePtr<ObjectBuffer> obuf = PtrCast<ObjectBuffer>(port->buffer(0));
+ if (obuf) {
+ Ingen::Shared::LV2Atom::to_atom(*_engine.world()->uris().get(),
+ obuf->atom(),
+ _value);
+ }
+ }
+ } else {
+ _resource = 0;
+ }
+ }
+}
+
+void
+RequestMetadata::post_process()
+{
+ if (_request->client()) {
+ if (_special_type == PORT_VALUE) {
+ if (_resource) {
+ _request->respond_ok();
+ _request->client()->set_property(_uri.str(),
+ _engine.world()->uris()->ingen_value, _value);
+ } else {
+ const string msg = "Get value for non-port " + _uri.str();
+ _request->respond_error(msg);
+ }
+ } else if (!_resource) {
+ const string msg = "Unable to find subject " + _uri.str();
+ _request->respond_error(msg);
+ } else {
+ _request->respond_ok();
+ _request->client()->set_property(_uri, _key, _value);
+ }
+ } else {
+ _request->respond_error("Unknown client");
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/RequestMetadata.hpp b/src/server/events/RequestMetadata.hpp
new file mode 100644
index 00000000..3f8311ef
--- /dev/null
+++ b/src/server/events/RequestMetadata.hpp
@@ -0,0 +1,79 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_REQUESTMETADATA_HPP
+#define INGEN_EVENTS_REQUESTMETADATA_HPP
+
+#include "raul/Atom.hpp"
+#include "raul/URI.hpp"
+
+#include "QueuedEvent.hpp"
+
+namespace Ingen {
+
+namespace Shared { class ResourceImpl; }
+
+namespace Server {
+
+class GraphObjectImpl;
+
+namespace Events {
+
+/** \page methods
+ * <h2>GET</h2>
+ * As per HTTP (RFC2616 S9.3).
+ *
+ * Get the description of a graph object.
+ */
+
+/** GET an object (see \ref methods).
+ *
+ * \ingroup engine
+ */
+class RequestMetadata : public QueuedEvent
+{
+public:
+ RequestMetadata(Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ Resource::Graph context,
+ const Raul::URI& subject,
+ const Raul::URI& key);
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType { NO_ERROR, NOT_FOUND };
+ enum {
+ NONE,
+ PORT_VALUE
+ } _special_type;
+
+ Raul::URI _uri;
+ Raul::URI _key;
+ Raul::Atom _value;
+ Ingen::Shared::ResourceImpl* _resource;
+ Resource::Graph _context;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_REQUESTMETADATA_HPP
diff --git a/src/server/events/SendBinding.cpp b/src/server/events/SendBinding.cpp
new file mode 100644
index 00000000..ebfa21dd
--- /dev/null
+++ b/src/server/events/SendBinding.cpp
@@ -0,0 +1,55 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <sstream>
+#include "events/SendBinding.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "Engine.hpp"
+#include "PortImpl.hpp"
+#include "ClientBroadcaster.hpp"
+
+using namespace std;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+void
+SendBinding::post_process()
+{
+ const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get();
+ Raul::Atom::DictValue dict;
+ if (_type == ControlBindings::MIDI_CC) {
+ dict[uris.rdf_type] = uris.midi_Controller;
+ dict[uris.midi_controllerNumber] = _num;
+ } else if (_type == ControlBindings::MIDI_BENDER) {
+ dict[uris.rdf_type] = uris.midi_Bender;
+ } else if (_type == ControlBindings::MIDI_CHANNEL_PRESSURE) {
+ dict[uris.rdf_type] = uris.midi_ChannelPressure;
+ } else if (_type == ControlBindings::MIDI_NOTE) {
+ dict[uris.rdf_type] = uris.midi_Note;
+ dict[uris.midi_noteNumber] = _num;
+ }
+ // TODO: other event types
+ _port->set_property(uris.ingen_controlBinding, dict); // FIXME: thread unsafe
+ _engine.broadcaster()->set_property(_port->path(), uris.ingen_controlBinding, dict);
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/SendBinding.hpp b/src/server/events/SendBinding.hpp
new file mode 100644
index 00000000..f3727ece
--- /dev/null
+++ b/src/server/events/SendBinding.hpp
@@ -0,0 +1,86 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_SENDBINDING_HPP
+#define INGEN_EVENTS_SENDBINDING_HPP
+
+#include "server/Event.hpp"
+#include "server/ControlBindings.hpp"
+#include "server/types.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PortImpl;
+
+namespace Events {
+
+/** A special event used internally to send control bindings from the audio thread.
+ *
+ * See SendPortValue documentation for details.
+ *
+ * \ingroup engine
+ */
+class SendBinding : public Event
+{
+public:
+ inline SendBinding(
+ Engine& engine,
+ SampleCount timestamp,
+ PortImpl* port,
+ ControlBindings::Type type,
+ int16_t num)
+ : Event(engine, SharedPtr<Request>(), timestamp)
+ , _port(port)
+ , _type(type)
+ , _num(num)
+ {
+ assert(_port);
+ switch (type) {
+ case ControlBindings::MIDI_CC:
+ assert(num >= 0 && num < 128);
+ break;
+ case ControlBindings::MIDI_RPN:
+ assert(num >= 0 && num < 16384);
+ break;
+ case ControlBindings::MIDI_NRPN:
+ assert(num >= 0 && num < 16384);
+ default:
+ break;
+ }
+ }
+
+ inline SendBinding& operator=(const SendBinding& ev) {
+ _port = ev._port;
+ _type = ev._type;
+ _num = ev._num;
+ return *this;
+ }
+
+ void post_process();
+
+private:
+ PortImpl* _port;
+ ControlBindings::Type _type;
+ int16_t _num;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_SENDBINDING_HPP
diff --git a/src/server/events/SendPortActivity.cpp b/src/server/events/SendPortActivity.cpp
new file mode 100644
index 00000000..8ff960e2
--- /dev/null
+++ b/src/server/events/SendPortActivity.cpp
@@ -0,0 +1,36 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "events/SendPortActivity.hpp"
+#include "Engine.hpp"
+#include "PortImpl.hpp"
+#include "ClientBroadcaster.hpp"
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+void
+SendPortActivity::post_process()
+{
+ _engine.broadcaster()->activity(_port->path());
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/SendPortActivity.hpp b/src/server/events/SendPortActivity.hpp
new file mode 100644
index 00000000..dd74a68a
--- /dev/null
+++ b/src/server/events/SendPortActivity.hpp
@@ -0,0 +1,69 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_SENDPORTACTIVITY_HPP
+#define INGEN_EVENTS_SENDPORTACTIVITY_HPP
+
+#include "Event.hpp"
+#include "types.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PortImpl;
+
+namespace Events {
+
+/** A special event used internally to send port activity notification (e.g.
+ * MIDI event activity) from the audio thread.
+ *
+ * This is created in the audio thread (directly in a ringbuffer, not new'd)
+ * for processing in the post processing thread (unlike normal events which
+ * are created in the pre-processor thread then run through the audio
+ * thread). This event's job is done entirely in post_process.
+ *
+ * This only really makes sense for message ports.
+ *
+ * \ingroup engine
+ */
+class SendPortActivity : public Event
+{
+public:
+ inline SendPortActivity(Engine& engine,
+ SampleCount timestamp,
+ PortImpl* port)
+ : Event(engine, SharedPtr<Request>(), timestamp)
+ , _port(port)
+ {
+ }
+
+ inline SendPortActivity& operator=(const SendPortActivity& ev) {
+ _port = ev._port;
+ return *this;
+ }
+
+ void post_process();
+
+private:
+ PortImpl* _port;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_SENDPORTACTIVITY_HPP
diff --git a/src/server/events/SendPortValue.cpp b/src/server/events/SendPortValue.cpp
new file mode 100644
index 00000000..1364b692
--- /dev/null
+++ b/src/server/events/SendPortValue.cpp
@@ -0,0 +1,42 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <sstream>
+#include "events/SendPortValue.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "Engine.hpp"
+#include "PortImpl.hpp"
+#include "ClientBroadcaster.hpp"
+
+using namespace std;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+void
+SendPortValue::post_process()
+{
+ _engine.broadcaster()->set_property(
+ _port->path(),
+ _engine.world()->uris()->ingen_value, _value);
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/SendPortValue.hpp b/src/server/events/SendPortValue.hpp
new file mode 100644
index 00000000..4465ef00
--- /dev/null
+++ b/src/server/events/SendPortValue.hpp
@@ -0,0 +1,82 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_SENDPORTVALUE_HPP
+#define INGEN_EVENTS_SENDPORTVALUE_HPP
+
+#include "raul/Atom.hpp"
+#include "server/Event.hpp"
+#include "server/types.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PortImpl;
+
+namespace Events {
+
+/** A special event used internally to send port values from the audio thread.
+ *
+ * This is created in the audio thread (using in-place new on a preallocated
+ * buffer) for processing in the post processing thread (unlike normal events
+ * which are created in the pre-processor thread then run through the audio
+ * thread). This event's job is done entirely in post_process.
+ *
+ * This only works for control ports right now.
+ *
+ * \ingroup engine
+ */
+class SendPortValue : public Event
+{
+public:
+ inline SendPortValue(
+ Engine& engine,
+ SampleCount timestamp,
+ PortImpl* port,
+ bool omni,
+ uint32_t voice_num,
+ const Raul::Atom& value)
+ : Event(engine, SharedPtr<Request>(), timestamp)
+ , _port(port)
+ , _omni(omni)
+ , _voice_num(voice_num)
+ , _value(value)
+ {
+ }
+
+ inline SendPortValue& operator=(const SendPortValue& ev) {
+ _port = ev._port;
+ _omni = ev._omni;
+ _voice_num = ev._voice_num;
+ _value = ev._value;
+ return *this;
+ }
+
+ void post_process();
+
+private:
+ PortImpl* _port;
+ bool _omni;
+ uint32_t _voice_num;
+ Raul::Atom _value;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_SENDPORTVALUE_HPP
diff --git a/src/server/events/SetMetadata.cpp b/src/server/events/SetMetadata.cpp
new file mode 100644
index 00000000..1812ad91
--- /dev/null
+++ b/src/server/events/SetMetadata.cpp
@@ -0,0 +1,380 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string>
+#include <boost/format.hpp>
+#include "raul/log.hpp"
+#include "raul/Maid.hpp"
+#include "ingen/PortType.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "ClientBroadcaster.hpp"
+#include "ControlBindings.hpp"
+#include "CreateNode.hpp"
+#include "CreatePatch.hpp"
+#include "CreatePort.hpp"
+#include "Driver.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "GraphObjectImpl.hpp"
+#include "PatchImpl.hpp"
+#include "PluginImpl.hpp"
+#include "PortImpl.hpp"
+#include "Request.hpp"
+#include "SetMetadata.hpp"
+#include "SetPortValue.hpp"
+
+#define LOG(s) s << "[SetMetadata] "
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+typedef Resource::Properties Properties;
+
+SetMetadata::SetMetadata(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ bool create,
+ Resource::Graph context,
+ const URI& subject,
+ const Properties& properties,
+ const Properties& remove)
+ : QueuedEvent(engine, request, timestamp, false)
+ , _create_event(NULL)
+ , _subject(subject)
+ , _properties(properties)
+ , _remove(remove)
+ , _object(NULL)
+ , _patch(NULL)
+ , _compiled_patch(NULL)
+ , _create(create)
+ , _context(context)
+{
+ if (context != Resource::DEFAULT) {
+ Resource::set_context(_properties, context);
+ }
+
+ /*
+ LOG(info) << "Set " << subject << " : " << context << " {" << endl;
+ typedef Resource::Properties::const_iterator iterator;
+ for (iterator i = properties.begin(); i != properties.end(); ++i)
+ LOG(info) << " " << i->first << " = " << i->second << " :: " << i->second.type() << endl;
+ LOG(info) << "}" << endl;
+
+ LOG(info) << "Unset " << subject << " {" << endl;
+ typedef Resource::Properties::const_iterator iterator;
+ for (iterator i = remove.begin(); i != remove.end(); ++i)
+ LOG(info) << " " << i->first << " = " << i->second << " :: " << i->second.type() << endl;
+ LOG(info) << "}" << endl;
+ */
+}
+
+SetMetadata::~SetMetadata()
+{
+ for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i)
+ delete *i;
+
+ delete _create_event;
+}
+
+void
+SetMetadata::pre_process()
+{
+ typedef Properties::const_iterator iterator;
+
+ const bool is_graph_object = Path::is_path(_subject);
+
+ _object = is_graph_object
+ ? _engine.engine_store()->find_object(Path(_subject.str()))
+ : static_cast<Shared::ResourceImpl*>(_engine.node_factory()->plugin(_subject));
+
+ if (!_object && (!is_graph_object || !_create)) {
+ _error = NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get();
+
+ if (is_graph_object && !_object) {
+ Path path(_subject.str());
+ bool is_patch = false, is_node = false, is_port = false, is_output = false;
+ PortType data_type(PortType::UNKNOWN);
+ Shared::ResourceImpl::type(uris, _properties, is_patch, is_node, is_port, is_output, data_type);
+
+ // Create a separate request without a source so EventSource isn't unblocked twice
+ SharedPtr<Request> sub_request(new Request(NULL, _request->client(), _request->id()));
+
+ if (is_patch) {
+ uint32_t poly = 1;
+ iterator p = _properties.find(uris.ingen_polyphony);
+ if (p != _properties.end() && p->second.is_valid() && p->second.type() == Atom::INT)
+ poly = p->second.get_int32();
+ _create_event = new CreatePatch(_engine, sub_request, _time,
+ path, poly, _properties);
+ } else if (is_node) {
+ const iterator p = _properties.find(uris.rdf_instanceOf);
+ _create_event = new CreateNode(_engine, sub_request, _time,
+ path, p->second.get_uri(), _properties);
+ } else if (is_port) {
+ _blocking = bool(_request);
+ _create_event = new CreatePort(_engine, sub_request, _time,
+ path, data_type.uri(), is_output, _properties);
+ }
+ if (_create_event)
+ _create_event->pre_process();
+ else
+ _error = BAD_OBJECT_TYPE;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ _types.reserve(_properties.size());
+
+ GraphObjectImpl* obj = dynamic_cast<GraphObjectImpl*>(_object);
+
+#if 0
+ // If we're replacing (i.e. this is a PUT, not a POST), first remove all properties
+ // with keys we will later set. This must be done first so a PUT with several properties
+ // of the same predicate (e.g. rdf:type) retains the multiple values. Only previously
+ // existing properties should be replaced
+ if (_replace)
+ for (Properties::iterator p = _properties.begin(); p != _properties.end(); ++p)
+ obj->properties().erase(p->first);
+#endif
+
+ for (Properties::const_iterator p = _remove.begin(); p != _remove.end(); ++p) {
+ const Raul::URI& key = p->first;
+ const Raul::Atom& value = p->second;
+ if (key == uris.ingen_controlBinding && value == uris.wildcard) {
+ PortImpl* port = dynamic_cast<PortImpl*>(_object);
+ if (port)
+ _old_bindings = _engine.control_bindings()->remove(port);
+ }
+ _object->remove_property(key, value);
+ }
+
+ for (Properties::iterator p = _properties.begin(); p != _properties.end(); ++p) {
+ const Raul::URI& key = p->first;
+ const Raul::Atom& value = p->second;
+ SpecialType op = NONE;
+ if (obj) {
+ Resource& resource = *obj;
+ resource.add_property(key, value);
+
+ PortImpl* port = dynamic_cast<PortImpl*>(_object);
+ if (port) {
+ if (key == uris.ingen_broadcast) {
+ if (value.type() == Atom::BOOL) {
+ op = ENABLE_BROADCAST;
+ } else {
+ _error = BAD_VALUE_TYPE;
+ }
+ } else if (key == uris.ingen_value) {
+ SetPortValue* ev = new SetPortValue(_engine, _request, _time, port, value);
+ ev->pre_process();
+ _set_events.push_back(ev);
+ } else if (key == uris.ingen_controlBinding) {
+ if (port->is_a(PortType::CONTROL)) {
+ if (value == uris.wildcard) {
+ _engine.control_bindings()->learn(port);
+ } else if (value.type() == Atom::DICT) {
+ op = CONTROL_BINDING;
+ } else {
+ _error = BAD_VALUE_TYPE;
+ }
+ } else {
+ _error = BAD_OBJECT_TYPE;
+ }
+ }
+ } else if ((_patch = dynamic_cast<PatchImpl*>(_object))) {
+ if (key == uris.ingen_enabled) {
+ if (value.type() == Atom::BOOL) {
+ op = ENABLE;
+ // FIXME: defer this until all other metadata has been processed
+ if (value.get_bool() && !_patch->enabled())
+ _compiled_patch = _patch->compile();
+ } else {
+ _error = BAD_VALUE_TYPE;
+ }
+ } else if (key == uris.ingen_polyphony) {
+ if (value.type() == Atom::INT) {
+ op = POLYPHONY;
+ _blocking = true;
+ _patch->prepare_internal_poly(*_engine.buffer_factory(), value.get_int32());
+ } else {
+ _error = BAD_VALUE_TYPE;
+ }
+ }
+ } else if (key == uris.ingen_polyphonic) {
+ PatchImpl* parent = dynamic_cast<PatchImpl*>(obj->parent());
+ if (parent) {
+ if (value.type() == Atom::BOOL) {
+ op = POLYPHONIC;
+ _blocking = true;
+ obj->set_property(key, value.get_bool());
+ NodeImpl* node = dynamic_cast<NodeImpl*>(obj);
+ if (node)
+ node->set_polyphonic(value.get_bool());
+ if (value.get_bool()) {
+ obj->prepare_poly(*_engine.buffer_factory(), parent->internal_poly());
+ } else {
+ obj->prepare_poly(*_engine.buffer_factory(), 1);
+ }
+ } else {
+ _error = BAD_VALUE_TYPE;
+ }
+ } else {
+ _error = BAD_OBJECT_TYPE;
+ }
+ }
+ }
+
+ if (_error != NO_ERROR) {
+ _error_predicate += key.str();
+ break;
+ }
+
+ _types.push_back(op);
+ }
+
+ QueuedEvent::pre_process();
+}
+
+void
+SetMetadata::execute(ProcessContext& context)
+{
+ if (_error != NO_ERROR) {
+ QueuedEvent::execute(context);
+ return;
+ }
+
+ if (_create_event) {
+ QueuedEvent::execute(context);
+ _create_event->execute(context);
+ if (_blocking)
+ _request->unblock();
+ return;
+ }
+
+ for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i)
+ (*i)->execute(context);
+
+ GraphObjectImpl* const object = dynamic_cast<GraphObjectImpl*>(_object);
+ NodeImpl* const node = dynamic_cast<NodeImpl*>(_object);
+ PortImpl* const port = dynamic_cast<PortImpl*>(_object);
+
+ std::vector<SpecialType>::const_iterator t = _types.begin();
+ for (Properties::const_iterator p = _properties.begin(); p != _properties.end(); ++p, ++t) {
+ const Raul::Atom& value = p->second;
+ switch (*t) {
+ case ENABLE_BROADCAST:
+ if (port)
+ port->broadcast(value.get_bool());
+ break;
+ case ENABLE:
+ if (value.get_bool()) {
+ if (_compiled_patch) {
+ _engine.maid()->push(_patch->compiled_patch());
+ _patch->compiled_patch(_compiled_patch);
+ }
+ _patch->enable();
+ } else {
+ _patch->disable();
+ }
+ break;
+ case POLYPHONIC:
+ {
+ PatchImpl* parent = reinterpret_cast<PatchImpl*>(object->parent());
+ if (value.get_bool())
+ object->apply_poly(*_engine.maid(), parent->internal_poly());
+ else
+ object->apply_poly(*_engine.maid(), 1);
+ }
+ break;
+ case POLYPHONY:
+ if (_patch->internal_poly() != static_cast<uint32_t>(value.get_int32()) &&
+ !_patch->apply_internal_poly(_engine.driver()->context(),
+ *_engine.buffer_factory(),
+ *_engine.maid(), value.get_int32())) {
+ _error = INTERNAL;
+ }
+ break;
+ case CONTROL_BINDING:
+ if (port) {
+ _engine.control_bindings()->port_binding_changed(context, port);
+ } else if (node) {
+ if (node->plugin_impl()->type() == Plugin::Internal) {
+ node->learn();
+ }
+ }
+ break;
+ case NONE:
+ break;
+ }
+ }
+
+ QueuedEvent::execute(context);
+
+ if (_blocking)
+ _request->unblock();
+}
+
+void
+SetMetadata::post_process()
+{
+ for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i)
+ (*i)->post_process();
+
+ switch (_error) {
+ case NO_ERROR:
+ if (_create_event)
+ _create_event->post_process();
+ else
+ _request->respond_ok();
+ if (_create)
+ _engine.broadcaster()->put(_subject, _properties, _context);
+ else
+ _engine.broadcaster()->delta(_subject, _remove, _properties);
+ break;
+ case NOT_FOUND:
+ _request->respond_error((boost::format(
+ "Unable to find object `%1%'") % _subject).str());
+ break;
+ case INTERNAL:
+ _request->respond_error("Internal error");
+ break;
+ case BAD_OBJECT_TYPE:
+ _request->respond_error((boost::format(
+ "Bad type for object `%1%'") % _subject).str());
+ break;
+ case BAD_VALUE_TYPE:
+ _request->respond_error((boost::format(
+ "Bad metadata value type for subject `%1%' predicate `%2%'")
+ % _subject % _error_predicate).str());
+ break;
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/SetMetadata.hpp b/src/server/events/SetMetadata.hpp
new file mode 100644
index 00000000..59856730
--- /dev/null
+++ b/src/server/events/SetMetadata.hpp
@@ -0,0 +1,124 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_SETMETADATA_HPP
+#define INGEN_EVENTS_SETMETADATA_HPP
+
+#include <vector>
+#include "raul/URI.hpp"
+#include "shared/ResourceImpl.hpp"
+#include "QueuedEvent.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class GraphObjectImpl;
+class PatchImpl;
+class CompiledPatch;
+
+namespace Events {
+
+/** \page methods
+ * <h2>POST</h2>
+ * As per HTTP (RFC2616 S9.5).
+ *
+ * Append properties to a graph object.
+ *
+ * An object can have several properties with a single predicate.
+ * POST appends properties without modifying or removing existing properties.
+ */
+
+/** \page methods
+ * <h2>PUT</h2>
+ * As per HTTP (RFC2616 S9.6).
+ *
+ * Set properties of a graph object, or create an object.
+ *
+ * An object can have several properties with a single predicate.
+ * \li If the object does not yet exist, the message must contain sufficient
+ * information to create the object (e.g. known rdf:type properties, etc.)
+ * \li If the object does exist, a PUT removes all existing object properties
+ * with predicates that match any property in the message, then adds all
+ * properties from the message.
+ */
+
+class SetPortValue;
+
+/** Set properties of a graph object.
+ * \ingroup engine
+ */
+class SetMetadata : public QueuedEvent
+{
+public:
+ SetMetadata(
+ Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ bool create,
+ Resource::Graph context,
+ const Raul::URI& subject,
+ const Resource::Properties& properties,
+ const Resource::Properties& remove = Resource::Properties());
+
+ ~SetMetadata();
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType {
+ NO_ERROR,
+ NOT_FOUND,
+ INTERNAL,
+ BAD_OBJECT_TYPE,
+ BAD_VALUE_TYPE
+ };
+
+ enum SpecialType {
+ NONE,
+ ENABLE,
+ ENABLE_BROADCAST,
+ POLYPHONY,
+ POLYPHONIC,
+ CONTROL_BINDING
+ };
+
+ typedef std::vector<SetPortValue*> SetEvents;
+
+ QueuedEvent* _create_event;
+ SetEvents _set_events;
+ std::vector<SpecialType> _types;
+ std::vector<SpecialType> _remove_types;
+ Raul::URI _subject;
+ Resource::Properties _properties;
+ Resource::Properties _remove;
+ Ingen::Shared::ResourceImpl* _object;
+ PatchImpl* _patch;
+ CompiledPatch* _compiled_patch;
+ std::string _error_predicate;
+ bool _create;
+ Resource::Graph _context;
+
+ SharedPtr<ControlBindings::Bindings> _old_bindings;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_SETMETADATA_HPP
diff --git a/src/server/events/SetPortValue.cpp b/src/server/events/SetPortValue.cpp
new file mode 100644
index 00000000..4af14d44
--- /dev/null
+++ b/src/server/events/SetPortValue.cpp
@@ -0,0 +1,223 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <sstream>
+#include "raul/log.hpp"
+#include "lv2/lv2plug.in/ns/ext/event/event.h"
+#include "shared/LV2URIMap.hpp"
+#include "shared/LV2Features.hpp"
+#include "shared/LV2Atom.hpp"
+#include "shared/World.hpp"
+#include "AudioBuffer.hpp"
+#include "ClientBroadcaster.hpp"
+#include "ControlBindings.hpp"
+#include "Driver.hpp"
+#include "Engine.hpp"
+#include "EngineStore.hpp"
+#include "EventBuffer.hpp"
+#include "MessageContext.hpp"
+#include "NodeImpl.hpp"
+#include "ObjectBuffer.hpp"
+#include "PortImpl.hpp"
+#include "ProcessContext.hpp"
+#include "ProcessContext.hpp"
+#include "Request.hpp"
+#include "SetPortValue.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+SetPortValue::SetPortValue(Engine& engine,
+ SharedPtr<Request> request,
+ bool queued,
+ SampleCount timestamp,
+ const Raul::Path& port_path,
+ const Raul::Atom& value)
+ : QueuedEvent(engine, request, timestamp)
+ , _queued(queued)
+ , _port_path(port_path)
+ , _value(value)
+ , _port(NULL)
+{
+}
+
+/** Internal */
+SetPortValue::SetPortValue(Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ PortImpl* port,
+ const Raul::Atom& value)
+ : QueuedEvent(engine, request, timestamp)
+ , _queued(false)
+ , _port_path(port->path())
+ , _value(value)
+ , _port(port)
+{
+}
+
+SetPortValue::~SetPortValue()
+{
+}
+
+void
+SetPortValue::pre_process()
+{
+ if (_queued) {
+ if (_port == NULL)
+ _port = _engine.engine_store()->find_port(_port_path);
+ if (_port == NULL)
+ _error = PORT_NOT_FOUND;
+ }
+
+ // Port is a message context port, set its value and
+ // call the plugin's message run function once
+ if (_port && _port->context() == Context::MESSAGE) {
+ apply(*_engine.message_context());
+ _port->parent_node()->set_port_valid(_port->index());
+ _engine.message_context()->run(_port->parent_node(),
+ _engine.driver()->frame_time() + _engine.driver()->block_length());
+ }
+
+ if (_port) {
+ _port->set_value(_value);
+ _port->set_property(_engine.world()->uris()->ingen_value, _value);
+ }
+
+ QueuedEvent::pre_process();
+}
+
+void
+SetPortValue::execute(ProcessContext& context)
+{
+ Event::execute(context);
+ assert(_time >= context.start() && _time <= context.end());
+
+ if (_port && _port->context() == Context::MESSAGE)
+ return;
+
+ apply(context);
+ _engine.control_bindings()->port_value_changed(context, _port);
+}
+
+void
+SetPortValue::apply(Context& context)
+{
+ uint32_t start = context.start();
+ if (_error == NO_ERROR && !_port)
+ _port = _engine.engine_store()->find_port(_port_path);
+
+ if (!_port) {
+ if (_error == NO_ERROR)
+ _error = PORT_NOT_FOUND;
+ /*} else if (_port->buffer(0)->capacity() < _data_size) {
+ _error = NO_SPACE;*/
+ } else {
+ Buffer* const buf = _port->buffer(0).get();
+ AudioBuffer* const abuf = dynamic_cast<AudioBuffer*>(buf);
+ if (abuf) {
+ if (_value.type() != Atom::FLOAT) {
+ _error = TYPE_MISMATCH;
+ return;
+ }
+
+ for (uint32_t v = 0; v < _port->poly(); ++v) {
+ ((AudioBuffer*)_port->buffer(v).get())->set_value(
+ _value.get_float(), start, _time);
+ }
+ return;
+ }
+
+ Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get();
+
+ EventBuffer* const ebuf = dynamic_cast<EventBuffer*>(buf);
+ if (ebuf && _value.type() == Atom::BLOB) {
+ const uint32_t frames = std::max(uint32_t(_time - start), ebuf->latest_frames());
+
+ // Size 0 event, pass it along to the plugin as a typed but empty event
+ if (_value.data_size() == 0) {
+ const uint32_t type_id = uris.uri_to_id(NULL, _value.get_blob_type());
+ ebuf->append(frames, 0, type_id, 0, NULL);
+ _port->raise_set_by_user_flag();
+ return;
+
+ } else if (!strcmp(_value.get_blob_type(),
+ "http://lv2plug.in/ns/ext/midi#MidiEvent")) {
+ ebuf->prepare_write(context);
+ ebuf->append(frames, 0,
+ uris.global_to_event(uris.midi_MidiEvent.id).second,
+ _value.data_size(),
+ (const uint8_t*)_value.get_blob());
+ _port->raise_set_by_user_flag();
+ return;
+ }
+ }
+
+ ObjectBuffer* const obuf = dynamic_cast<ObjectBuffer*>(buf);
+ if (obuf) {
+ obuf->atom()->size = obuf->size() - sizeof(LV2_Atom);
+ if (Ingen::Shared::LV2Atom::from_atom(uris, _value, obuf->atom())) {
+ debug << "Converted atom " << _value << " :: " << obuf->atom()->type
+ << " * " << obuf->atom()->size << " @ " << obuf->atom() << endl;
+ return;
+ } else {
+ warn << "Failed to convert atom to LV2 object" << endl;
+ }
+ }
+
+ warn << "Unknown value type " << (int)_value.type() << endl;
+ }
+}
+
+void
+SetPortValue::post_process()
+{
+ string msg;
+ std::ostringstream ss;
+ switch (_error) {
+ case NO_ERROR:
+ assert(_port != NULL);
+ _request->respond_ok();
+ _engine.broadcaster()->set_property(_port_path,
+ _engine.world()->uris()->ingen_value, _value);
+ break;
+ case TYPE_MISMATCH:
+ ss << "Illegal value type " << _value.type()
+ << " for port " << _port_path << endl;
+ _request->respond_error(ss.str());
+ break;
+ case PORT_NOT_FOUND:
+ msg = "Unable to find port ";
+ msg.append(_port_path.str()).append(" to set value");
+ _request->respond_error(msg);
+ break;
+ case NO_SPACE:
+ ss << "Attempt to write " << _value.data_size() << " bytes to "
+ << _port_path.str() << ", with capacity "
+ << _port->buffer_size() << endl;
+ _request->respond_error(ss.str());
+ break;
+ }
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/SetPortValue.hpp b/src/server/events/SetPortValue.hpp
new file mode 100644
index 00000000..6f3cde87
--- /dev/null
+++ b/src/server/events/SetPortValue.hpp
@@ -0,0 +1,83 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_SETPORTVALUE_HPP
+#define INGEN_EVENTS_SETPORTVALUE_HPP
+
+#include "raul/Atom.hpp"
+#include "QueuedEvent.hpp"
+#include "types.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class PortImpl;
+
+namespace Events {
+
+/** An event to change the value of a port.
+ *
+ * This event can either be queued or immediate, depending on the queued
+ * parameter passed to the constructor. It must be passed to the appropriate
+ * place (ie queued event passed to the event queue and non-queued event
+ * processed in the audio thread) or nasty things will happen.
+ *
+ * \ingroup engine
+ */
+class SetPortValue : public QueuedEvent
+{
+public:
+ SetPortValue(Engine& engine,
+ SharedPtr<Request> request,
+ bool queued,
+ SampleCount timestamp,
+ const Raul::Path& port_path,
+ const Raul::Atom& value);
+
+ SetPortValue(Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ PortImpl* port,
+ const Raul::Atom& value);
+
+ ~SetPortValue();
+
+ void pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ enum ErrorType {
+ NO_ERROR,
+ PORT_NOT_FOUND,
+ NO_SPACE,
+ TYPE_MISMATCH
+ };
+
+ void apply(Context& context);
+
+ bool _queued;
+ const Raul::Path _port_path;
+ const Raul::Atom _value;
+ PortImpl* _port;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_SETPORTVALUE_HPP
diff --git a/src/server/events/UnregisterClient.cpp b/src/server/events/UnregisterClient.cpp
new file mode 100644
index 00000000..b87bfa81
--- /dev/null
+++ b/src/server/events/UnregisterClient.cpp
@@ -0,0 +1,48 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "ingen/ClientInterface.hpp"
+#include "Request.hpp"
+#include "UnregisterClient.hpp"
+#include "Engine.hpp"
+#include "ClientBroadcaster.hpp"
+
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+UnregisterClient::UnregisterClient(Engine& engine, SharedPtr<Request> request, SampleCount timestamp, const URI& uri)
+ : QueuedEvent(engine, request, timestamp)
+ , _uri(uri)
+{
+}
+
+void
+UnregisterClient::post_process()
+{
+ if (_engine.broadcaster()->unregister_client(_uri))
+ _request->respond_ok();
+ else
+ _request->respond_error("Unable to unregister client");
+}
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
diff --git a/src/server/events/UnregisterClient.hpp b/src/server/events/UnregisterClient.hpp
new file mode 100644
index 00000000..eefc6318
--- /dev/null
+++ b/src/server/events/UnregisterClient.hpp
@@ -0,0 +1,50 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_EVENTS_UNREGISTERCLIENT_HPP
+#define INGEN_EVENTS_UNREGISTERCLIENT_HPP
+
+#include "QueuedEvent.hpp"
+#include "raul/URI.hpp"
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+/** Unregisters an OSC client so it no longer receives notifications.
+ *
+ * \ingroup engine
+ */
+class UnregisterClient : public QueuedEvent
+{
+public:
+ UnregisterClient(Engine& engine,
+ SharedPtr<Request> request,
+ SampleCount timestamp,
+ const Raul::URI& uri);
+
+ void post_process();
+
+private:
+ Raul::URI _uri;
+};
+
+} // namespace Server
+} // namespace Ingen
+} // namespace Events
+
+#endif // INGEN_EVENTS_UNREGISTERCLIENT_HPP