Do a topological sort of the route list before passing it to
the graph, as the graph's feedback detection algorithm depends on the input route list being sorted in such a way. Fixes #3924. git-svn-id: svn://localhost/ardour2/branches/3.0@10471 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
b7f195d375
commit
ae94dfda5f
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright (C) 2011 Paul Davis
|
||||
Author: Carl Hetherington <cth@carlh.net>
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
/** A list of edges for a directed acyclic graph for routes */
|
||||
class DAGEdges
|
||||
{
|
||||
public:
|
||||
typedef std::map<boost::shared_ptr<Route>, std::set<boost::shared_ptr<Route> > > EdgeMap;
|
||||
|
||||
void add (boost::shared_ptr<Route> from, boost::shared_ptr<Route> to);
|
||||
std::set<boost::shared_ptr<Route> > from (boost::shared_ptr<Route> r) const;
|
||||
void remove (boost::shared_ptr<Route> from, boost::shared_ptr<Route> to);
|
||||
bool has_none_to (boost::shared_ptr<Route> to) const;
|
||||
bool empty () const;
|
||||
void dump () const;
|
||||
|
||||
private:
|
||||
void insert (EdgeMap& e, boost::shared_ptr<Route> a, boost::shared_ptr<Route> b);
|
||||
|
||||
/* Keep a map in both directions to speed lookups */
|
||||
|
||||
/** map of edges with from as `first' and to as `second' */
|
||||
EdgeMap _from_to;
|
||||
/** map of the same edges with to as `first' and from as `second' */
|
||||
EdgeMap _to_from;
|
||||
};
|
||||
|
||||
boost::shared_ptr<RouteList> topographical_sort (
|
||||
boost::shared_ptr<RouteList>,
|
||||
DAGEdges
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
Copyright (C) 2011 Paul Davis
|
||||
Author: Carl Hetherington <cth@carlh.net>
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "ardour/route.h"
|
||||
#include "ardour/route_dag.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
|
||||
void
|
||||
DAGEdges::add (boost::shared_ptr<Route> from, boost::shared_ptr<Route> to)
|
||||
{
|
||||
insert (_from_to, from, to);
|
||||
insert (_to_from, to, from);
|
||||
|
||||
EdgeMap::iterator i = _from_to.find (from);
|
||||
if (i != _from_to.end ()) {
|
||||
i->second.insert (to);
|
||||
} else {
|
||||
set<boost::shared_ptr<Route> > v;
|
||||
v.insert (to);
|
||||
_from_to.insert (make_pair (from, v));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set<boost::shared_ptr<Route> >
|
||||
DAGEdges::from (boost::shared_ptr<Route> r) const
|
||||
{
|
||||
EdgeMap::const_iterator i = _from_to.find (r);
|
||||
if (i == _from_to.end ()) {
|
||||
return set<boost::shared_ptr<Route> > ();
|
||||
}
|
||||
|
||||
return i->second;
|
||||
}
|
||||
|
||||
void
|
||||
DAGEdges::remove (boost::shared_ptr<Route> from, boost::shared_ptr<Route> to)
|
||||
{
|
||||
EdgeMap::iterator i = _from_to.find (from);
|
||||
assert (i != _from_to.end ());
|
||||
i->second.erase (to);
|
||||
if (i->second.empty ()) {
|
||||
_from_to.erase (i);
|
||||
}
|
||||
|
||||
EdgeMap::iterator j = _to_from.find (to);
|
||||
assert (j != _to_from.end ());
|
||||
j->second.erase (from);
|
||||
if (j->second.empty ()) {
|
||||
_to_from.erase (j);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param to `To' route.
|
||||
* @return true if there are no edges going to `to'.
|
||||
*/
|
||||
|
||||
bool
|
||||
DAGEdges::has_none_to (boost::shared_ptr<Route> to) const
|
||||
{
|
||||
return _to_from.find (to) == _to_from.end ();
|
||||
}
|
||||
|
||||
bool
|
||||
DAGEdges::empty () const
|
||||
{
|
||||
assert (_from_to.empty () == _to_from.empty ());
|
||||
return _from_to.empty ();
|
||||
}
|
||||
|
||||
void
|
||||
DAGEdges::dump () const
|
||||
{
|
||||
for (EdgeMap::const_iterator i = _from_to.begin(); i != _from_to.end(); ++i) {
|
||||
cout << "FROM: " << i->first->name() << " ";
|
||||
for (set<boost::shared_ptr<Route> >::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << (*j)->name() << " ";
|
||||
}
|
||||
cout << "\n";
|
||||
}
|
||||
|
||||
for (EdgeMap::const_iterator i = _to_from.begin(); i != _to_from.end(); ++i) {
|
||||
cout << "TO: " << i->first->name() << " ";
|
||||
for (set<boost::shared_ptr<Route> >::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << (*j)->name() << " ";
|
||||
}
|
||||
cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DAGEdges::insert (EdgeMap& e, boost::shared_ptr<Route> a, boost::shared_ptr<Route> b)
|
||||
{
|
||||
EdgeMap::iterator i = e.find (a);
|
||||
if (i != e.end ()) {
|
||||
i->second.insert (b);
|
||||
} else {
|
||||
set<boost::shared_ptr<Route> > v;
|
||||
v.insert (b);
|
||||
e.insert (make_pair (a, v));
|
||||
}
|
||||
}
|
||||
|
||||
struct RouteRecEnabledComparator
|
||||
{
|
||||
bool operator () (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> r2) const
|
||||
{
|
||||
if (r1->record_enabled()) {
|
||||
if (r2->record_enabled()) {
|
||||
/* both rec-enabled, just use signal order */
|
||||
return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
|
||||
} else {
|
||||
/* r1 rec-enabled, r2 not rec-enabled, run r2 early */
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (r2->record_enabled()) {
|
||||
/* r2 rec-enabled, r1 not rec-enabled, run r1 early */
|
||||
return true;
|
||||
} else {
|
||||
/* neither rec-enabled, use signal order */
|
||||
return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
boost::shared_ptr<RouteList>
|
||||
ARDOUR::topographical_sort (
|
||||
boost::shared_ptr<RouteList> routes,
|
||||
DAGEdges edges
|
||||
)
|
||||
{
|
||||
boost::shared_ptr<RouteList> sorted_routes (new RouteList);
|
||||
|
||||
/* queue of routes to process */
|
||||
RouteList queue;
|
||||
|
||||
/* initial queue has routes that are not fed by anything */
|
||||
for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
|
||||
if ((*i)->not_fed ()) {
|
||||
queue.push_back (*i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort the initial queue so that non-rec-enabled routes are run first */
|
||||
queue.sort (RouteRecEnabledComparator ());
|
||||
|
||||
/* Do the sort: algorithm is Kahn's from Wikipedia.
|
||||
`Topological sorting of large networks', Communications of the ACM 5(11):558-562.
|
||||
*/
|
||||
|
||||
while (!queue.empty ()) {
|
||||
boost::shared_ptr<Route> r = queue.front ();
|
||||
queue.pop_front ();
|
||||
sorted_routes->push_back (r);
|
||||
set<boost::shared_ptr<Route> > e = edges.from (r);
|
||||
for (set<boost::shared_ptr<Route> >::iterator i = e.begin(); i != e.end(); ++i) {
|
||||
edges.remove (r, *i);
|
||||
if (edges.has_none_to (*i)) {
|
||||
queue.push_back (*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!edges.empty ()) {
|
||||
cout << "Feedback detected.\n";
|
||||
}
|
||||
|
||||
return sorted_routes;
|
||||
}
|
|
@ -86,6 +86,7 @@
|
|||
#include "ardour/recent_sessions.h"
|
||||
#include "ardour/region_factory.h"
|
||||
#include "ardour/return.h"
|
||||
#include "ardour/route_dag.h"
|
||||
#include "ardour/route_group.h"
|
||||
#include "ardour/send.h"
|
||||
#include "ardour/session.h"
|
||||
|
@ -1222,56 +1223,6 @@ Session::set_block_size (pframes_t nframes)
|
|||
}
|
||||
}
|
||||
|
||||
struct RouteSorter {
|
||||
/** @return true to run r1 before r2, otherwise false */
|
||||
bool sort_by_rec_enabled (const boost::shared_ptr<Route>& r1, const boost::shared_ptr<Route>& r2) {
|
||||
if (r1->record_enabled()) {
|
||||
if (r2->record_enabled()) {
|
||||
/* both rec-enabled, just use signal order */
|
||||
return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
|
||||
} else {
|
||||
/* r1 rec-enabled, r2 not rec-enabled, run r2 early */
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (r2->record_enabled()) {
|
||||
/* r2 rec-enabled, r1 not rec-enabled, run r1 early */
|
||||
return true;
|
||||
} else {
|
||||
/* neither rec-enabled, use signal order */
|
||||
return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool operator() (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> r2) {
|
||||
if (r2->feeds (r1)) {
|
||||
/* r1 fed by r2; run r2 early */
|
||||
return false;
|
||||
} else if (r1->feeds (r2)) {
|
||||
/* r2 fed by r1; run r1 early */
|
||||
return true;
|
||||
} else {
|
||||
if (r1->not_fed ()) {
|
||||
if (r2->not_fed ()) {
|
||||
/* no ardour-based connections inbound to either route. */
|
||||
return sort_by_rec_enabled (r1, r2);
|
||||
} else {
|
||||
/* r2 has connections, r1 does not; run r1 early */
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (r2->not_fed()) {
|
||||
/* r1 has connections, r2 does not; run r2 early */
|
||||
return false;
|
||||
} else {
|
||||
/* both r1 and r2 have connections, but not to each other. just use signal order */
|
||||
return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
|
||||
|
@ -1361,16 +1312,17 @@ Session::resort_routes ()
|
|||
#endif
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Session::resort_routes_using (boost::shared_ptr<RouteList> r)
|
||||
{
|
||||
RouteList::iterator i, j;
|
||||
DAGEdges edges;
|
||||
|
||||
for (i = r->begin(); i != r->end(); ++i) {
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
|
||||
(*i)->clear_fed_by ();
|
||||
|
||||
for (j = r->begin(); j != r->end(); ++j) {
|
||||
for (RouteList::iterator j = r->begin(); j != r->end(); ++j) {
|
||||
|
||||
/* although routes can feed themselves, it will
|
||||
cause an endless recursive descent if we
|
||||
|
@ -1385,23 +1337,22 @@ Session::resort_routes_using (boost::shared_ptr<RouteList> r)
|
|||
bool via_sends_only;
|
||||
|
||||
if ((*j)->direct_feeds (*i, &via_sends_only)) {
|
||||
edges.add (*j, *i);
|
||||
(*i)->add_fed_by (*j, via_sends_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = r->begin(); i != r->end(); ++i) {
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
trace_terminal (*i, *i);
|
||||
}
|
||||
|
||||
RouteSorter cmp;
|
||||
r->sort (cmp);
|
||||
|
||||
route_graph->rechain (r);
|
||||
boost::shared_ptr<RouteList> sorted_routes = topographical_sort (r, edges);
|
||||
route_graph->rechain (sorted_routes);
|
||||
|
||||
#ifndef NDEBUG
|
||||
DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
for (RouteList::iterator i = sorted_routes->begin(); i != sorted_routes->end(); ++i) {
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n",
|
||||
(*i)->name(), (*i)->order_key ("signal")));
|
||||
}
|
||||
|
|
|
@ -170,6 +170,7 @@ libardour_sources = [
|
|||
'return.cc',
|
||||
'reverse.cc',
|
||||
'route.cc',
|
||||
'route_dag.cc',
|
||||
'route_group.cc',
|
||||
'route_group_member.cc',
|
||||
'rb_effect.cc',
|
||||
|
|
Loading…
Reference in New Issue