From acbda29f838280ba98cf9e9e539e9d8a6e8fc6ad Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 9 Jun 2006 15:07:31 +0000 Subject: Added Om aka Graph aka god knows what git-svn-id: http://svn.drobilla.net/lad/grauph@9 a436a847-0d15-0410-975c-d299462d15a1 --- src/engine/Patch.cpp | 356 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 src/engine/Patch.cpp (limited to 'src/engine/Patch.cpp') diff --git a/src/engine/Patch.cpp b/src/engine/Patch.cpp new file mode 100644 index 00000000..be938cae --- /dev/null +++ b/src/engine/Patch.cpp @@ -0,0 +1,356 @@ +/* This file is part of Om. Copyright (C) 2006 Dave Robillard. + * + * Om 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. + * + * Om 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "Node.h" +#include "Patch.h" +#include "Plugin.h" +#include "Port.h" +#include "PortInfo.h" +#include "ClientBroadcaster.h" +#include "InternalNode.h" +#include "Connection.h" +#include "Om.h" +#include "OmApp.h" +#include "PortBase.h" +#include "ObjectStore.h" +#include "interface/ClientInterface.h" + +using std::cerr; using std::cout; using std::endl; + +namespace Om { + + +Patch::Patch(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size, size_t internal_poly) +: NodeBase(path, poly, parent, srate, buffer_size), + m_internal_poly(internal_poly), + m_process_order(NULL), + m_process(false) +{ + assert(internal_poly >= 1); + + m_plugin.type(Plugin::Patch); + m_plugin.lib_path(""); + m_plugin.plug_label("om_patch"); + m_plugin.name("Om patch"); + + //std::cerr << "Creating patch " << m_name << ", poly = " << poly + // << ", internal poly = " << internal_poly << std::endl; +} + + +Patch::~Patch() +{ + assert(!m_activated); + + for (List::iterator i = m_connections.begin(); i != m_connections.end(); ++i) { + delete (*i); + delete m_connections.remove(i); + } + + for (List::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) { + assert(!(*i)->activated()); + delete (*i); + delete m_nodes.remove(i); + } + + delete m_process_order; +} + + +void +Patch::activate() +{ + NodeBase::activate(); + + for (List::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) + (*i)->activate(); + + assert(m_activated); +} + + +void +Patch::deactivate() +{ + if (m_activated) { + + NodeBase::deactivate(); + + for (List::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) { + if ((*i)->activated()) + (*i)->deactivate(); + assert(!(*i)->activated()); + } + } + assert(!m_activated); +} + + +void +Patch::process(bool b) +{ + if (!b) { + // Write output buffers to 0 + for (List::iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i) { + assert((*i)->as_port() != NULL); + if ((*i)->as_port()->port_info()->is_output()) + (*i)->as_port()->clear_buffers(); + } + } + m_process = b; +} + + +/** Run the patch for the specified number of frames. + * + * Calls all Nodes in the order m_process_order specifies. + */ +inline void +Patch::run(size_t nframes) +{ + if (m_process_order == NULL || !m_process) + return; + + // Prepare all ports + for (List::iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i) + (*i)->as_port()->prepare_buffers(nframes); + + // Run all nodes + for (size_t i=0; i < m_process_order->size(); ++i) { + // Could be a gap due to a node removal event (see RemoveNodeEvent.cpp) + // If you're thinking this isn't very nice, you're right. + if (m_process_order->at(i) != NULL) + m_process_order->at(i)->run(nframes); + } +} + + +/** Returns the number of ports. + * + * Needs to override the NodeBase implementation since a Patch's ports are really + * just it's input and output nodes' ports. + */ +size_t +Patch::num_ports() const +{ + return m_bridge_nodes.size(); +} + + +#if 0 +void +Patch::send_creation_messages(ClientInterface* client) const +{ + cerr << "FIXME: creation\n"; + + om->client_broadcaster()->send_patch_to(client, this); + + for (List::const_iterator j = m_nodes.begin(); j != m_nodes.end(); ++j) { + Node* node = (*j); + Port* port = node->as_port(); // NULL unless a bridge node + node->send_creation_messages(client); + + usleep(100); + + // If this is a bridge (input/output) node, send the patch control value as well + if (port != NULL && port->port_info()->is_control()) + om->client_broadcaster()->send_control_change_to(client, port->path(), + ((PortBase*)port)->buffer(0)->value_at(0)); + } + + for (List::const_iterator j = m_connections.begin(); j != m_connections.end(); ++j) { + om->client_broadcaster()->send_connection_to(client, *j); + } + + // Send port information + /*for (size_t i=0; i < m_ports.size(); ++i) { + Port* const port = m_ports.at(i); + + // Send metadata + const map& data = port->metadata(); + for (map::const_iterator i = data.begin(); i != data.end(); ++i) + om->client_broadcaster()->send_metadata_update_to(client, port->path(), (*i).first, (*i).second); + + if (port->port_info()->is_control()) + om->client_broadcaster()->send_control_change_to(client, port->path(), + ((PortBase*)port)->buffer(0)->value_at(0)); + }*/ +} +#endif + + +void +Patch::add_to_store() +{ + // Add self and ports + NodeBase::add_to_store(); + + // Add nodes + for (List::iterator j = m_nodes.begin(); j != m_nodes.end(); ++j) + (*j)->add_to_store(); +} + + +void +Patch::remove_from_store() +{ + // Remove self and ports + NodeBase::remove_from_store(); + + // Remove nodes + for (List::iterator j = m_nodes.begin(); j != m_nodes.end(); ++j) { + (*j)->remove_from_store(); + assert(om->object_store()->find((*j)->path()) == NULL); + } +} + + +// Patch specific stuff + + +void +Patch::add_node(ListNode* ln) +{ + assert(ln != NULL); + assert(ln->elem() != NULL); + assert(ln->elem()->parent_patch() == this); + assert(ln->elem()->poly() == m_internal_poly || ln->elem()->poly() == 1); + + m_nodes.push_back(ln); +} + + +ListNode* +Patch::remove_node(const string& name) +{ + for (List::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) + if ((*i)->name() == name) + return m_nodes.remove(i); + + return NULL; +} + + +/** Remove a connection. Realtime safe. + */ +ListNode* +Patch::remove_connection(const Port* src_port, const Port* dst_port) +{ + bool found = false; + ListNode* connection = NULL; + for (List::iterator i = m_connections.begin(); i != m_connections.end(); ++i) { + if ((*i)->src_port() == src_port && (*i)->dst_port() == dst_port) { + connection = m_connections.remove(i); + found = true; + } + } + + if ( ! found) + cerr << "WARNING: [Patch::remove_connection] Connection not found !" << endl; + + return connection; +} + + +/** Remove a bridge_node. Realtime safe. + */ +ListNode* +Patch::remove_bridge_node(const InternalNode* node) +{ + bool found = false; + ListNode* bridge_node = NULL; + for (List::iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i) { + if ((*i) == node) { + bridge_node = m_bridge_nodes.remove(i); + found = true; + } + } + + if ( ! found) + cerr << "WARNING: [Patch::remove_bridge_node] InternalNode not found !" << endl; + + return bridge_node; +} + + +/** Find the process order for this Patch. + * + * The process order is a flat list that the patch will execute in order + * when it's run() method is called. Return value is a newly allocated list + * which the caller is reponsible to delete. Note that this function does + * NOT actually set the process order, it is returned so it can be inserted + * at the beginning of an audio cycle (by various Events). + * + * This function is not realtime safe, due to the use of the List iterator + */ +Array* +Patch::build_process_order() const +{ + Node* node = NULL; + Array* const process_order = new Array(m_nodes.size()); + + for (List::const_iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) + (*i)->traversed(false); + + // Traverse backwards starting at outputs + for (List::const_iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i) { + node = (*i); + assert(node->as_port() != NULL); + + // If the output node has been disconnected and has no connections left, don't traverse + // into it so it's not in the process order (and can be removed w/o flaming segfault death) + if (node->as_port()->port_info()->is_output() && node->providers()->size() > 0) + build_process_order_recursive(node, process_order); + } + + // Add any nodes that weren't hit by the traversal (disjoint nodes) + for (List::const_iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) { + node = (*i); + if ( ! node->traversed()) { + process_order->push_back(*i); + node->traversed(true); + } + } + + assert(process_order->size() == m_nodes.size()); + + return process_order; +} + + +/** Rename this Patch. + * + * This is responsible for updating the ObjectStore so the Patch can be + * found at it's new path, as well as all it's children. + */ +void +Patch::set_path(const Path& new_path) +{ + const Path old_path = path(); + + // Update nodes + for (List::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) + (*i)->set_path(new_path.base_path() + (*i)->name()); + + // Update self + NodeBase::set_path(new_path); +} + + +} // namespace Om -- cgit v1.2.1