/* 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