/* * Copyright (C) 2014-2015 Tim Mayberry * Copyright (C) 2014-2020 Paul Davis * Copyright (C) 2014-2019 Robin Gareus * * This program 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. * * This program 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 more 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "pbd/error.h" #include "ardour/port_engine_shared.h" #include "ardour/port_manager.h" #include "pbd/i18n.h" using namespace ARDOUR; BackendPort::BackendPort (PortEngineSharedImpl &b, const std::string& name, PortFlags flags) : _backend (b) , _name (name) , _flags (flags) { _capture_latency_range.min = 0; _capture_latency_range.max = 0; _playback_latency_range.min = 0; _playback_latency_range.max = 0; _backend.port_connect_add_remove_callback (); // XXX -> RT } BackendPort::~BackendPort () { _backend.port_connect_add_remove_callback (); // XXX -> RT assert (_connections.empty()); } int BackendPort::connect (BackendPortHandle port, BackendPortHandle self) { if (!port) { PBD::error << _("BackendPort::connect (): invalid (null) port") << endmsg; return -1; } if (type () != port->type ()) { PBD::error << string_compose (_("BackendPort::connect (): wrong port-type trying to connect %1 and %2"), name(), port->name()) << endmsg; return -1; } if (is_output () && port->is_output ()) { PBD::error << string_compose (_("BackendPort::connect (): cannot inter-connect output ports %1 and %2."), name(), port->name()) << endmsg; return -1; } if (is_input () && port->is_input ()) { PBD::error << string_compose (_("BackendPort::connect (): cannot inter-connect input ports."), name(), port->name()) << endmsg; return -1; } if (this == port.get()) { PBD::error << _("BackendPort::connect (): cannot self-connect ports.") << endmsg; return -1; } if (is_connected (port)) { #if 0 // don't bother to warn about this for now. just ignore it PBD::error << _("BackendPort::connect (): ports are already connected:") << " (" << name () << ") -> (" << port->name () << ")" << endmsg; #endif return 0; } store_connection (port); port->store_connection (self); _backend.port_connect_callback (name(), port->name(), true); return 0; } void BackendPort::store_connection (BackendPortHandle port) { _connections.insert (port); } int BackendPort::disconnect (BackendPortHandle port, BackendPortHandle self) { if (!port) { PBD::error << _("BackendPort::disconnect (): invalid (null) port") << endmsg; return -1; } if (!is_connected (port)) { PBD::error << _("BackendPort::disconnect (): ports are not connected:") << " (" << name () << ") -> (" << port->name () << ")" << endmsg; return -1; } remove_connection (port); port->remove_connection (self); _backend.port_connect_callback (name(), port->name(), false); return 0; } void BackendPort::remove_connection (BackendPortHandle port) { std::set::iterator it = _connections.find (port); assert (it != _connections.end ()); _connections.erase (it); } void BackendPort::disconnect_all (BackendPortHandle self) { while (!_connections.empty ()) { std::set::iterator it = _connections.begin (); (*it)->remove_connection (self); _backend.port_connect_callback (name(), (*it)->name(), false); _connections.erase (it); } } bool BackendPort::is_connected (BackendPortHandle port) const { return _connections.find (port) != _connections.end (); } bool BackendPort::is_physically_connected () const { for (std::set::const_iterator it = _connections.begin (); it != _connections.end (); ++it) { if ((*it)->is_physical ()) { return true; } } return false; } void BackendPort::set_latency_range (const LatencyRange &latency_range, bool for_playback) { LatencyRange& lr = for_playback ? _playback_latency_range : _capture_latency_range; if (lr == latency_range) { return; } lr = latency_range; for (std::set::const_iterator it = _connections.begin (); it != _connections.end (); ++it) { if ((*it)->is_physical ()) { (*it)->update_connected_latency (is_input ()); } } } void BackendPort::update_connected_latency (bool for_playback) { LatencyRange lr; lr.min = lr.max = 0; for (std::set::const_iterator it = _connections.begin (); it != _connections.end (); ++it) { LatencyRange l; l = (*it)->latency_range (for_playback); lr.min = std::max (lr.min, l.min); lr.max = std::max (lr.max, l.max); } set_latency_range (lr, for_playback); } bool BackendMIDIEvent::operator< (const BackendMIDIEvent &other) const { if (timestamp() == other.timestamp ()) { /* concurrent MIDI events, sort * - CC first (may include bank/patch) * - Program Change * - Note Off * - Note On * - Note Pressure * - Channel Pressure * - Pitch Bend * - SysEx/RT, etc * * see Evoral::Sequence