From 98fe0e7056e6697396249531785d3899f94d79be Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 10 Jun 2006 01:52:02 +0000 Subject: More juggling git-svn-id: http://svn.drobilla.net/lad/grauph@15 a436a847-0d15-0410-975c-d299462d15a1 --- src/libs/engine/InputPort.cpp | 352 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 src/libs/engine/InputPort.cpp (limited to 'src/libs/engine/InputPort.cpp') diff --git a/src/libs/engine/InputPort.cpp b/src/libs/engine/InputPort.cpp new file mode 100644 index 00000000..3837da1b --- /dev/null +++ b/src/libs/engine/InputPort.cpp @@ -0,0 +1,352 @@ +/* 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 "InputPort.h" +#include +#include +#include +#include "ConnectionBase.h" +#include "OutputPort.h" +#include "PortInfo.h" +#include "Node.h" +#include "Om.h" +#include "util.h" + +using std::cerr; using std::cout; using std::endl; + + +namespace Om { + + +template +InputPort::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size) +: PortBase(node, name, index, poly, port_info, buffer_size) +{ + assert(port_info->is_input() && !port_info->is_output()); +} +template InputPort::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size); +template InputPort::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size); + + +/** Add a connection. Realtime safe. + * + * The buffer of this port will be set directly to the connection's buffer + * if there is only one connection, since no mixing needs to take place. + */ +template +void +InputPort::add_connection(ListNode*>* const c) +{ + m_connections.push_back(c); + + bool modify_buffers = !m_fixed_buffers; + if (modify_buffers && m_is_tied) + modify_buffers = !m_tied_port->fixed_buffers(); + + if (modify_buffers) { + if (m_connections.size() == 1) { + // Use buffer directly to avoid copying + for (size_t i=0; i < m_poly; ++i) { + m_buffers.at(i)->join(c->elem()->buffer(i)); + if (m_is_tied) + m_tied_port->buffer(i)->join(m_buffers.at(i)); + assert(m_buffers.at(i)->data() == c->elem()->buffer(i)->data()); + } + } else if (m_connections.size() == 2) { + // Used to directly use single connection buffer, now there's two + // so have to use local ones again and mix down + for (size_t i=0; i < m_poly; ++i) { + m_buffers.at(i)->unjoin(); + if (m_is_tied) + m_tied_port->buffer(i)->join(m_buffers.at(i)); + } + } + update_buffers(); + } + + assert( ! m_is_tied || m_tied_port != NULL); + assert( ! m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data()); +} +template void InputPort::add_connection(ListNode*>* const c); +template void InputPort::add_connection(ListNode*>* const c); + + +/** Remove a connection. Realtime safe. + */ +template +ListNode*>* +InputPort::remove_connection(const OutputPort* const src_port) +{ + bool modify_buffers = !m_fixed_buffers; + if (modify_buffers && m_is_tied) + modify_buffers = !m_tied_port->fixed_buffers(); + + typedef typename List*>::iterator ConnectionBaseListIterator; + bool found = false; + ListNode*>* connection = NULL; + for (ConnectionBaseListIterator i = m_connections.begin(); i != m_connections.end(); ++i) { + if ((*i)->src_port()->path() == src_port->path()) { + connection = m_connections.remove(i); + found = true; + } + } + + if ( ! found) { + cerr << "WARNING: [InputPort::remove_connection] Connection not found !" << endl; + exit(EXIT_FAILURE); + } else { + if (m_connections.size() == 0) { + for (size_t i=0; i < m_poly; ++i) { + // Use a local buffer + if (modify_buffers && m_buffers.at(i)->is_joined()) + m_buffers.at(i)->unjoin(); + m_buffers.at(i)->clear(); // Write silence + if (m_is_tied) + m_tied_port->buffer(i)->join(m_buffers.at(i)); + } + } else if (modify_buffers && m_connections.size() == 1) { + // Share a buffer + for (size_t i=0; i < m_poly; ++i) { + m_buffers.at(i)->join((*m_connections.begin())->buffer(i)); + if (m_is_tied) + m_tied_port->buffer(i)->join(m_buffers.at(i)); + } + } + } + + if (modify_buffers) + update_buffers(); + + assert( ! m_is_tied || m_tied_port != NULL); + assert( ! m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data()); + + return connection; +} +template ListNode*>* +InputPort::remove_connection(const OutputPort* const src_port); +template ListNode*>* +InputPort::remove_connection(const OutputPort* const src_port); + + +/** Update any changed buffers with the plugin this is a port on. + * + * This calls ie the LADSPA connect_port function when buffers have been changed + * due to a connection or disconnection. + */ +template +void +InputPort::update_buffers() +{ + for (size_t i=0; i < m_poly; ++i) + InputPort::parent_node()->set_port_buffer(i, m_index, m_buffers.at(i)->data()); +} +template void InputPort::update_buffers(); +template void InputPort::update_buffers(); + + +/** Returns whether this port is connected to the passed port. + */ +template +bool +InputPort::is_connected_to(const OutputPort* const port) const +{ + typedef typename List*>::const_iterator ConnectionBaseListIterator; + for (ConnectionBaseListIterator i = m_connections.begin(); i != m_connections.end(); ++i) + if ((*i)->src_port() == port) + return true; + + return false; +} +template bool InputPort::is_connected_to(const OutputPort* const port) const; +template bool InputPort::is_connected_to(const OutputPort* const port) const; + + +/** "Ties" this port to an OutputPort, so they share the same buffer. + * + * This is used by OutputNode and InputNode to provide two different ports + * (internal and external) that share a buffer. + */ +template +void +InputPort::tie(OutputPort* const port) +{ + assert((Port*)port != (Port*)this); + assert(port->poly() == this->poly()); + assert(!m_is_tied); + assert(m_tied_port == NULL); + + if (Port::parent_node() != NULL) { + assert(m_poly == port->poly()); + + for (size_t i=0; i < m_poly; ++i) + port->buffer(i)->join(m_buffers.at(i)); + } + m_is_tied = true; + m_tied_port = port; + port->set_tied_port(this); + + assert(m_buffers.at(0)->data() == port->buffer(0)->data()); + + //cerr << "*** Tied " << this->path() << " <-> " << port->path() << endl; +} +template void InputPort::tie(OutputPort* const port); +template void InputPort::tie(OutputPort* const port); + + +/** Prepare buffer for access, mixing if necessary. Realtime safe. + * FIXME: nframes parameter not used, + */ +template<> +void +InputPort::prepare_buffers(size_t nframes) +{ + assert(!m_is_tied || m_tied_port != NULL); + + typedef List*>::iterator ConnectionBaseListIterator; + bool do_mixdown = true; + + if (m_connections.size() == 0) return; + + for (ConnectionBaseListIterator c = m_connections.begin(); c != m_connections.end(); ++c) + (*c)->prepare_buffers(); + + // If only one connection, buffer is (maybe) used directly (no copying) + if (m_connections.size() == 1) { + // Buffer changed since connection + if (m_buffers.at(0)->data() != (*m_connections.begin())->buffer(0)->data()) { + if (m_fixed_buffers || (m_is_tied && m_tied_port->fixed_buffers())) { + // can't change buffer, must copy + do_mixdown = true; + } else { + // zero-copy + assert(m_buffers.at(0)->is_joined()); + m_buffers.at(0)->join((*m_connections.begin())->buffer(0)); + do_mixdown = false; + } + } else { + do_mixdown = false; + } + update_buffers(); + } + + if (!do_mixdown) { + assert(m_buffers.at(0)->data() == (*m_connections.begin())->buffer(0)->data()); + return; + } + + assert(!m_is_tied || m_tied_port != NULL); + assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data()); + + for (size_t voice=0; voice < m_poly; ++voice) { + m_buffers.at(voice)->copy((*m_connections.begin())->buffer(voice), 0, m_buffer_size-1); + + if (m_connections.size() > 1) { + // Copy first connection + ConnectionBaseListIterator c = m_connections.begin(); + + // Add all other connections + for (++c; c != m_connections.end(); ++c) + m_buffers.at(voice)->accumulate((*c)->buffer(voice), 0, m_buffer_size-1); + } + } +} + + +/** Prepare buffer for access, realtime safe. + * + * MIDI mixing not yet implemented. + */ +template <> +void +InputPort::prepare_buffers(size_t nframes) +{ + assert(!m_is_tied || m_tied_port != NULL); + + const size_t num_ins = m_connections.size(); + bool do_mixdown = true; + + assert(num_ins == 0 || num_ins == 1); + + typedef List*>::iterator ConnectionBaseListIterator; + assert(m_poly == 1); + + for (ConnectionBaseListIterator c = m_connections.begin(); c != m_connections.end(); ++c) + (*c)->prepare_buffers(); + + + // If only one connection, buffer is used directly (no copying) + if (num_ins == 1) { + // Buffer changed since connection + if (m_buffers.at(0) != (*m_connections.begin())->buffer(0)) { + if (m_fixed_buffers || (m_is_tied && m_tied_port->fixed_buffers())) { + // can't change buffer, must copy + do_mixdown = true; + } else { + // zero-copy + assert(m_buffers.at(0)->is_joined()); + m_buffers.at(0)->join((*m_connections.begin())->buffer(0)); + if (m_is_tied) + m_tied_port->buffer(0)->join(m_buffers.at(0)); + do_mixdown = false; + } + update_buffers(); + } else { + do_mixdown = false; + } + assert(!m_is_tied || m_tied_port != NULL); + assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data()); + assert(!m_is_tied || m_buffers.at(0)->filled_size() == m_tied_port->buffer(0)->filled_size()); + assert(do_mixdown || m_buffers.at(0)->filled_size() == + (*m_connections.begin())->src_port()->buffer(0)->filled_size()); + } + + // Get valid buffer size from inbound connections, unless a port on a top-level + // patch (which will be fed by the MidiDriver) + if (m_parent->parent() != NULL) { + if (num_ins == 1) { + m_buffers.at(0)->filled_size( + (*m_connections.begin())->src_port()->buffer(0)->filled_size()); + + if (m_is_tied) + m_tied_port->buffer(0)->filled_size(m_buffers.at(0)->filled_size()); + + assert(m_buffers.at(0)->filled_size() == + (*m_connections.begin())->src_port()->buffer(0)->filled_size()); + } else { + // Mixing not implemented + m_buffers.at(0)->clear(); + } + } + + assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data()); + + if (!do_mixdown || m_buffers.at(0)->filled_size() == 0 || num_ins == 0) + return; + + //cerr << path() << " - Copying MIDI buffer" << endl; + + // Be sure buffers are the same as tied port's, if joined + assert(!m_is_tied || m_tied_port != NULL); + assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data()); + + if (num_ins > 0) + for (size_t i=0; i < m_buffers.at(0)->filled_size(); ++i) + m_buffers.at(0)[i] = (*m_connections.begin())->buffer(0)[i]; +} + + +} // namespace Om + -- cgit v1.2.1