diff options
author | David Robillard <d@drobilla.net> | 2011-04-20 16:26:40 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2011-04-20 16:26:40 +0000 |
commit | 138a87e915ad3aff184730415105f94c874174bf (patch) | |
tree | 0d942bdddfdbcc3d969b4fce5592e770ab851b86 /src/server/events | |
parent | 1f1758f4dda0ddaf01c0b1d3a756f9db8ddc979d (diff) | |
download | ingen-138a87e915ad3aff184730415105f94c874174bf.tar.gz ingen-138a87e915ad3aff184730415105f94c874174bf.tar.bz2 ingen-138a87e915ad3aff184730415105f94c874174bf.zip |
Rename Ingen::Engine to Ingen::Server (hopefully avoid odd name clases and fix #675).
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@3184 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/server/events')
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 |