ladish/grauph/src/libs/engine/InputPort.cpp

353 lines
11 KiB
C++

/* 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 <iostream>
#include <cstdlib>
#include <cassert>
#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 <typename T>
InputPort<T>::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size)
: PortBase<T>(node, name, index, poly, port_info, buffer_size)
{
assert(port_info->is_input() && !port_info->is_output());
}
template InputPort<sample>::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
template InputPort<MidiMessage>::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<typename T>
void
InputPort<T>::add_connection(ListNode<ConnectionBase<T>*>* 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<sample>::add_connection(ListNode<ConnectionBase<sample>*>* const c);
template void InputPort<MidiMessage>::add_connection(ListNode<ConnectionBase<MidiMessage>*>* const c);
/** Remove a connection. Realtime safe.
*/
template <typename T>
ListNode<ConnectionBase<T>*>*
InputPort<T>::remove_connection(const OutputPort<T>* 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<ConnectionBase<T>*>::iterator ConnectionBaseListIterator;
bool found = false;
ListNode<ConnectionBase<T>*>* 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<T>::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<ConnectionBase<sample>*>*
InputPort<sample>::remove_connection(const OutputPort<sample>* const src_port);
template ListNode<ConnectionBase<MidiMessage>*>*
InputPort<MidiMessage>::remove_connection(const OutputPort<MidiMessage>* 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 <typename T>
void
InputPort<T>::update_buffers()
{
for (size_t i=0; i < m_poly; ++i)
InputPort<T>::parent_node()->set_port_buffer(i, m_index, m_buffers.at(i)->data());
}
template void InputPort<sample>::update_buffers();
template void InputPort<MidiMessage>::update_buffers();
/** Returns whether this port is connected to the passed port.
*/
template <typename T>
bool
InputPort<T>::is_connected_to(const OutputPort<T>* const port) const
{
typedef typename List<ConnectionBase<T>*>::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<sample>::is_connected_to(const OutputPort<sample>* const port) const;
template bool InputPort<MidiMessage>::is_connected_to(const OutputPort<MidiMessage>* 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 <typename T>
void
InputPort<T>::tie(OutputPort<T>* 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<sample>::tie(OutputPort<sample>* const port);
template void InputPort<MidiMessage>::tie(OutputPort<MidiMessage>* const port);
/** Prepare buffer for access, mixing if necessary. Realtime safe.
* FIXME: nframes parameter not used,
*/
template<>
void
InputPort<sample>::prepare_buffers(size_t nframes)
{
assert(!m_is_tied || m_tied_port != NULL);
typedef List<ConnectionBase<sample>*>::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<MidiMessage>::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<ConnectionBase<MidiMessage>*>::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