xthickness = 0
ythickness = 0
}
+
+style "mixer_solo_button_alternate2" = "solo_button_alternate2"
+{
+ font_name = "@FONT_SMALLER@"
+ xthickness = 0
+ ythickness = 0
+}
+
+
style "mixer_solo_button_active" = "solo_button_active"
{
font_name = "@FONT_SMALLER@"
widget "*SoloButton-active" style:highest "solo_button_active"
widget "*MixerSoloButton" style:highest "mixer_solo_button"
widget "*MixerSoloButton-alternate" style:highest "mixer_solo_button_alternate"
+widget "*MixerSoloButton-alternate2" style:highest "mixer_solo_button_alternate2"
widget "*MixerSoloButton-active" style:highest "mixer_solo_button_active"
widget "*TrackLoopButton*" style:highest "track_loop_button"
widget "*PanAutomationLineSelector*" style:highest "multiline_combo"
void
ARDOUR_UI::soloing_changed (bool onoff)
{
- cerr << "solo change, " << onoff << endl;
-
if (solo_alert_button.get_active() != onoff) {
solo_alert_button.set_active (onoff);
}
}
(*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
- (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this), gui_context());
+ (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
(*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
(*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
}
update_rec_display ();
update_mute_display ();
- update_solo_display ();
+ update_solo_display (true);
update_solo_isolate_display ();
update_solo_safe_display ();
resume_redisplay ();
}
void
-EditorRoutes::update_solo_display ()
+EditorRoutes::update_solo_display (bool /* selfsoloed */)
{
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator i;
void
EditorRoutes::solo_changed_so_update_mute ()
{
- ENSURE_GUI_THREAD (*this, &EditorRoutes::solo_changed_so_update_mute)
update_mute_display ();
}
void handle_gui_changes (std::string const &, void *);
void update_rec_display ();
void update_mute_display ();
- void update_solo_display ();
+ void update_solo_display (bool);
void update_solo_isolate_display ();
void update_solo_safe_display ();
void set_all_tracks_visibility (bool);
/* map the current state */
mute_changed (0);
- solo_changed (0);
+ update_solo_display ();
delete input_selector;
input_selector = 0;
/* now force an update of all the various elements */
mute_changed (0);
- solo_changed (0);
+ update_solo_display ();
name_changed ();
comment_changed (0);
route_group_changed ();
}
mute_changed (0);
- solo_changed (0);
+ update_solo_display ();
timestretch_rect = 0;
no_redraw = false;
_route->active_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_active_changed, this), gui_context());
_route->mute_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::mute_changed, this, _1), gui_context());
- _route->solo_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::solo_changed, this, _1), gui_context());
- _route->solo_safe_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::solo_changed, this, _1), gui_context());
- _route->listen_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::listen_changed, this, _1), gui_context());
- _route->solo_isolated_changed.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::solo_changed, this, _1), gui_context());
+
+ _route->solo_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
+ _route->solo_safe_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
+ _route->listen_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
+ _route->solo_isolated_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
+
_route->phase_invert_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::polarity_changed, this), gui_context());
_route->PropertyChanged.connect (route_connections, invalidator (*this), ui_bind (&RouteUI::property_changed, this, _1), gui_context());
}
}
-void
-RouteUI::solo_changed(void* /*src*/)
-{
- Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteUI::update_solo_display, this));
-}
-
-
-void
-RouteUI::listen_changed(void* /*src*/)
-{
- Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteUI::update_solo_display, this));
-}
-
int
RouteUI::solo_visual_state (boost::shared_ptr<Route> r)
{
if (r->listening()) {
return 1;
} else {
- return 0;
+ return 0;
}
}
void create_sends (ARDOUR::Placement);
void create_selected_sends (ARDOUR::Placement);
- void solo_changed(void*);
+ void solo_changed(bool, void*);
void solo_changed_so_update_mute ();
void mute_changed(void*);
void listen_changed(void*);
PBD::Signal0<void> phase_invert_changed;
PBD::Signal0<void> denormal_protection_changed;
PBD::Signal1<void,void*> listen_changed;
- PBD::Signal1<void,void*> solo_changed;
+ PBD::Signal2<void,bool,void*> solo_changed;
PBD::Signal1<void,void*> solo_safe_changed;
PBD::Signal1<void,void*> solo_isolated_changed;
PBD::Signal1<void,void*> comment_changed;
int listen_via (boost::shared_ptr<Route>, Placement p, bool active, bool aux);
void drop_listen (boost::shared_ptr<Route>);
- bool feeds (boost::shared_ptr<Route>, bool* via_send_only = 0);
- std::set<boost::weak_ptr<Route> > fed_by;
+ /**
+ * return true if this route feeds the first argument via at least one
+ * (arbitrarily long) signal pathway.
+ */
+ bool feeds (boost::shared_ptr<Route>, bool* via_send_only = 0);
+
+ /**
+ * return true if this route feeds the first argument directly, via
+ * either its main outs or a send.
+ */
+ bool direct_feeds (boost::shared_ptr<Route>, bool* via_send_only = 0);
+
+ struct FeedRecord {
+ boost::weak_ptr<Route> r;
+ bool sends_only;
+
+ FeedRecord (boost::shared_ptr<Route> rp, bool sendsonly)
+ : r (rp)
+ , sends_only (sendsonly) {}
+ };
+
+ struct FeedRecordCompare {
+ bool operator() (const FeedRecord& a, const FeedRecord& b) const {
+ return a.r < b.r;
+ }
+ };
+
+ typedef std::set<FeedRecord,FeedRecordCompare> FedBy;
+
+ const FedBy& fed_by() const { return _fed_by; }
+ void clear_fed_by ();
+ bool add_fed_by (boost::shared_ptr<Route>, bool sends_only);
+ bool not_fed() const { return _fed_by.empty(); }
/* Controls (not all directly owned by the Route */
bool _have_internal_generator;
bool _solo_safe;
DataType _default_type;
+ FedBy _fed_by;
virtual ChanCount input_streams () const;
void route_listen_changed (void *src, boost::weak_ptr<Route>);
void route_mute_changed (void *src);
- void route_solo_changed (void *src, boost::weak_ptr<Route>);
+ void route_solo_changed (bool self_solo_change, void *src, boost::weak_ptr<Route>);
void update_route_solo_state (boost::shared_ptr<RouteList> r = boost::shared_ptr<RouteList>());
void listen_position_changed ();
if (self_soloed() != yn) {
set_self_solo (yn);
set_delivery_solo ();
- solo_changed (src); /* EMIT SIGNAL */
+ solo_changed (true, src); /* EMIT SIGNAL */
_solo_control->Changed (); /* EMIT SIGNAL */
}
}
}
set_delivery_solo ();
- solo_changed (this);
+ solo_changed (false, this);
}
void
boost::shared_ptr<RouteList> routes = _session.get_routes ();
for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
bool sends_only;
- bool does_feed = feeds (*i, &sends_only);
+ bool does_feed = direct_feeds (*i, &sends_only);
if (does_feed && !sends_only) {
(*i)->set_solo_isolated (yn, (*i)->route_group());
}
bool
-Route::feeds (boost::shared_ptr<Route> other, bool* only_send)
+Route::add_fed_by (boost::shared_ptr<Route> other, bool via_sends_only)
+{
+ FeedRecord fr (other, via_sends_only);
+
+ pair<FedBy::iterator,bool> result = _fed_by.insert (fr);
+
+ if (!result.second) {
+
+ /* already a record for "other" - make sure sends-only information is correct */
+ if (!via_sends_only && result.first->sends_only) {
+ FeedRecord* frp = const_cast<FeedRecord*>(&(*result.first));
+ frp->sends_only = false;
+ }
+ }
+
+ return result.second;
+}
+
+void
+Route::clear_fed_by ()
+{
+ _fed_by.clear ();
+}
+
+bool
+Route::feeds (boost::shared_ptr<Route> other, bool* via_sends_only)
+{
+ const FedBy& fed_by (other->fed_by());
+
+ for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) {
+ boost::shared_ptr<Route> sr = f->r.lock();
+
+ if (sr && (sr.get() == this)) {
+
+ if (via_sends_only) {
+ *via_sends_only = f->sends_only;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
{
DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
struct RouteSorter {
bool operator() (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> r2) {
- if (r1->fed_by.find (r2) != r1->fed_by.end()) {
+ if (r2->feeds (r1)) {
return false;
- } else if (r2->fed_by.find (r1) != r2->fed_by.end()) {
+ } else if (r1->feeds (r2)) {
return true;
} else {
- if (r1->fed_by.empty()) {
- if (r2->fed_by.empty()) {
+ if (r1->not_fed ()) {
+ if (r2->not_fed ()) {
/* no ardour-based connections inbound to either route. just use signal order */
return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
} else {
{
shared_ptr<Route> r2;
- if ((r1->fed_by.find (rbase) != r1->fed_by.end()) && (rbase->fed_by.find (r1) != rbase->fed_by.end())) {
+ if (r1->feeds (rbase) && rbase->feeds (r1)) {
info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg;
return;
}
/* make a copy of the existing list of routes that feed r1 */
- set<weak_ptr<Route> > existing = r1->fed_by;
-
+ Route::FedBy existing (r1->fed_by());
+
/* for each route that feeds r1, recurse, marking it as feeding
rbase as well.
*/
- for (set<weak_ptr<Route> >::iterator i = existing.begin(); i != existing.end(); ++i) {
- if (!(r2 = (*i).lock ())) {
+ for (Route::FedBy::iterator i = existing.begin(); i != existing.end(); ++i) {
+ if (!(r2 = i->r.lock ())) {
/* (*i) went away, ignore it */
continue;
}
base as being fed by r2
*/
- rbase->fed_by.insert (r2);
+ rbase->add_fed_by (r2, i->sends_only);
if (r2 != rbase) {
stop here.
*/
- if ((r1->fed_by.find (r2) != r1->fed_by.end()) && (r2->fed_by.find (r1) != r2->fed_by.end())) {
+ if (r1->feeds (r2) && r2->feeds (r1)) {
continue;
}
/* writer goes out of scope and forces update */
}
+#ifndef NDEBUG
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
+
+ const Route::FedBy& fb ((*i)->fed_by());
+
+ for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
+ boost::shared_ptr<Route> sf = f->r.lock();
+ if (sf) {
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+ }
+ }
+ }
+#endif
+
}
void
Session::resort_routes_using (shared_ptr<RouteList> r)
for (i = r->begin(); i != r->end(); ++i) {
- (*i)->fed_by.clear ();
+ (*i)->clear_fed_by ();
for (j = r->begin(); j != r->end(); ++j) {
continue;
}
- if ((*j)->feeds (*i)) {
- (*i)->fed_by.insert (*j);
+ bool via_sends_only;
+
+ if ((*j)->direct_feeds (*i, &via_sends_only)) {
+ (*i)->add_fed_by (*j, via_sends_only);
}
}
}
RouteSorter cmp;
r->sort (cmp);
-#if 0
- cerr << "finished route resort\n";
-
+#ifndef NDEBUG
+ DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl;
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n", (*i)->name(), (*i)->order_key ("signal")));
}
- cerr << endl;
#endif
}
boost::shared_ptr<Route> r (*x);
r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr));
- r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, wpr));
+ r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1));
r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1));
}
void
-Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_ptr<Route> wpr)
{
+ if (!self_solo_change) {
+ // session doesn't care about changes to soloed-by-others
+ return;
+ }
+
if (solo_update_disabled) {
// We know already
return;
delta = -1;
}
- /* now mod the solo level of all other routes except master/control outs/auditioner
- so that they will be silent if appropriate.
- */
-
solo_update_disabled = true;
+ /* from IRC:
+
+ <las> oofus_lt: solo a route, do NOT mute anything in the feed-forward chain for the route
+ <las> oofus_lt: and do solo-by-other everything in the feed-backward chain
+
+ */
+
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
bool via_sends_only;
if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
continue;
- } else if ((*i)->feeds (route, &via_sends_only)) {
+ }
+
+ /* feed-backwards (other route to solo change route):
+
+ if (*i) feeds the one whose solo status changed
+ it should be soloed by other if the change was -> solo OR de-soloed by other if change was -> !solo
+ else
+ do nothing
+
+ */
+
+ if ((*i)->feeds (route, &via_sends_only)) {
if (!via_sends_only) {
(*i)->mod_solo_by_others (delta);
}
}
+
+ /* feed-forward (solo change route to other routes):
+
+ if the route whose solo status changed feeds (*i)
+ do nothing
+ else
+ mute if the change was -> solo OR demute if change was -> !solo
+ */
+
+ if (route->feeds (*i, &via_sends_only)) {
+ (*i)->mod_solo_by_others (delta);
+ }
}
solo_update_disabled = false;
}
set_widget_name (name);
-
visual_state = n;
}