#include "pbd/stacktrace.h"
#include "pbd/convert.h"
#include "pbd/boost_debug.h"
+#include "pbd/unwind.h"
#include "ardour/amp.h"
#include "ardour/audio_buffer.h"
}
void
-Route::set_listen (bool yn, void* src)
+Route::set_listen (bool yn, void* src, bool group_override)
{
if (_solo_safe) {
return;
}
- if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
- _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group));
+ bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
+ if (group_override && _route_group) {
+ group_active = !group_active;
+ }
+
+ if (_route_group && src != _route_group && group_active) {
+ _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group, group_override));
return;
}
}
_mute_master->set_soloed_by_others (false);
- listen_changed (src); /* EMIT SIGNAL */
+ listen_changed (src, group_override); /* EMIT SIGNAL */
}
}
}
}
void
-Route::set_solo (bool yn, void *src)
+Route::clear_all_solo_state ()
+{
+ // ideally this function will never do anything, it only exists to forestall Murphy
+ bool emit_changed = false;
+
+#ifndef NDEBUG
+ // these are really debug messages, but of possible interest.
+ if (_self_solo) {
+ PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
+ }
+ if (_soloed_by_others_upstream || _soloed_by_others_downstream) {
+ PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"),
+ name(), _soloed_by_others_upstream, _soloed_by_others_downstream);
+ }
+#endif
+
+ if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) {
+ // if self-soled, set_solo() will do signal emission
+ emit_changed = true;
+ }
+
+ _soloed_by_others_upstream = 0;
+ _soloed_by_others_downstream = 0;
+
+ {
+ PBD::Unwinder<bool> uw (_solo_safe, false);
+ set_solo (false, this);
+ }
+
+ if (emit_changed) {
+ set_mute_master_solo ();
+ solo_changed (false, this, false); /* EMIT SIGNAL */
+ }
+}
+
+void
+Route::set_solo (bool yn, void *src, bool group_override)
{
if (_solo_safe) {
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
return;
}
- if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
- _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
+ bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
+ if (group_override && _route_group) {
+ group_active = !group_active;
+ }
+ if (_route_group && src != _route_group && group_active) {
+ _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group, group_override));
return;
}
if (self_soloed() != yn) {
set_self_solo (yn);
set_mute_master_solo ();
- solo_changed (true, src); /* EMIT SIGNAL */
+ solo_changed (true, src, group_override); /* EMIT SIGNAL */
_solo_control->Changed (); /* EMIT SIGNAL */
}
+ assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active());
+
/* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF
Config->get_solo_mute_overrride().
*/
(old_sbu > 0 && _soloed_by_others_upstream == 0))) {
if (delta > 0 || !Config->get_exclusive_solo()) {
- DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+ DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+ if (i->sends_only) {
+ continue;
+ }
boost::shared_ptr<Route> sr = i->r.lock();
if (sr) {
sr->mod_solo_by_others_downstream (-delta);
}
set_mute_master_solo ();
- solo_changed (false, this);
+ solo_changed (false, this, false); /* EMIT SIGNAL */
}
void
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
set_mute_master_solo ();
- solo_changed (false, this);
+ solo_changed (false, this, false); /* EMIT SIGNAL */
}
void
Route::mod_solo_isolated_by_upstream (bool yn, void* src)
{
bool old = solo_isolated ();
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
+ name(), _solo_isolated_by_upstream, yn ? "+1" : "-1"));
if (!yn) {
if (_solo_isolated_by_upstream >= 1) {
if (solo_isolated() != old) {
/* solo isolated status changed */
- _mute_master->set_solo_ignore (yn);
- solo_isolated_changed (src);
+ _mute_master->set_solo_ignore (solo_isolated());
+ solo_isolated_changed (src); /* EMIT SIGNAL */
}
}
} else {
if (_solo_isolated == true) {
_solo_isolated = false;
- _mute_master->set_solo_ignore (false);
+ _mute_master->set_solo_ignore (false);
changed = true;
}
}
/* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
- solo_isolated_changed (src);
+ solo_isolated_changed (src); /* EMIT SIGNAL */
}
bool
void
Route::input_change_handler (IOChange change, void * /*src*/)
{
- bool need_to_queue_solo_change = true;
-
if ((change.type & IOChange::ConfigurationChanged)) {
/* This is called with the process lock held if change
contains ConfigurationChanged
*/
- need_to_queue_solo_change = false;
configure_processors (0);
_phase_invert.resize (_input->n_ports().n_audio ());
io_changed (); /* EMIT SIGNAL */
}
- if (!_input->connected() && _soloed_by_others_upstream) {
- if (need_to_queue_solo_change) {
- _session.cancel_solo_after_disconnect (shared_from_this(), true);
- } else {
- cancel_solo_after_disconnect (true);
- }
-#if 1
- } else if (_soloed_by_others_upstream) {
- bool cancel_solo = true;
+ if (_soloed_by_others_upstream || _solo_isolated_by_upstream) {
+ int sbou = 0;
+ int ibou = 0;
boost::shared_ptr<RouteList> routes = _session.get_routes ();
+ if (_input->connected()) {
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+ if (does_feed && !sends_only) {
+ if ((*i)->soloed()) {
+ ++sbou;
+ }
+ if ((*i)->solo_isolated()) {
+ ++ibou;
+ }
+ }
+ }
+ }
+
+ int delta = sbou - _soloed_by_others_upstream;
+ int idelta = ibou - _solo_isolated_by_upstream;
+
+ if (idelta < -1) {
+ PBD::warning << string_compose (
+ _("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"),
+ _name, ibou, _solo_isolated_by_upstream, idelta)
+ << endmsg;
+
+ }
+
+ if (_soloed_by_others_upstream) {
+ // ignore new connections (they're not propagated)
+ if (delta <= 0) {
+ mod_solo_by_others_upstream (delta);
+ }
+ }
+
+ if (_solo_isolated_by_upstream) {
+ // solo-isolate currently only propagates downstream
+ if (idelta < 0) {
+ mod_solo_isolated_by_upstream (false, this);
+ }
+ // TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
+ // but idelta can't be smaller than -1, can it?
+ //_solo_isolated_by_upstream = ibou;
+ }
+
+ // Session::route_solo_changed does not propagate indirect solo-changes
+ // propagate downstream to tracks
for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
continue;
}
bool sends_only;
- bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
- if (does_feed && !sends_only) {
- if ((*i)->soloed()) {
- cancel_solo = false;
- break;
- }
+ bool does_feed = feeds (*i, &sends_only);
+ if (delta <= 0 && does_feed && !sends_only) {
+ (*i)->mod_solo_by_others_upstream (delta);
+ }
+
+ if (idelta < 0 && does_feed && !sends_only) {
+ (*i)->mod_solo_isolated_by_upstream (false, this);
}
}
- if (cancel_solo) {
- cancel_solo_after_disconnect (true);
- }
-#else
- } else if (self_soloed()) {
-#endif
- // TODO propagate upstream
- // see commment in output_change_handler() below
}
}
void
Route::output_change_handler (IOChange change, void * /*src*/)
{
- bool need_to_queue_solo_change = true;
if (_initial_io_setup) {
return;
}
/* This is called with the process lock held if change
contains ConfigurationChanged
*/
- need_to_queue_solo_change = false;
configure_processors (0);
if (is_master()) {
io_changed (); /* EMIT SIGNAL */
}
- if (!_output->connected() && _soloed_by_others_downstream) {
- if (need_to_queue_solo_change) {
- _session.cancel_solo_after_disconnect (shared_from_this(), false);
- } else {
- cancel_solo_after_disconnect (false);
- }
-#if 1
- } else if (_soloed_by_others_downstream) {
- bool cancel_solo = true;
+ if (_soloed_by_others_downstream) {
+ int sbod = 0;
/* checking all all downstream routes for
* explicit of implict solo is a rather drastic measure,
* ideally the input_change_handler() of the other route
* would propagate the change to us.
*/
boost::shared_ptr<RouteList> routes = _session.get_routes ();
- for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
- if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
- continue;
- }
- bool sends_only;
- bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
- if (does_feed && !sends_only) {
- if ((*i)->soloed()) {
- cancel_solo = false;
- break;
+ if (_output->connected()) {
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
+ if (does_feed && !sends_only) {
+ if ((*i)->soloed()) {
+ ++sbod;
+ break;
+ }
}
}
}
- if (cancel_solo) {
- cancel_solo_after_disconnect (false);
- }
-#else
- } else if (self_soloed()) {
- // TODO propagate change downstream to the disconnected routes
- // Q: how to get the routes that were just disconnected. ?
- // A: /maybe/ by diff feeds() aka fed_by() vs direct_feeds_according_to_reality() ?!?
-#endif
- }
-}
+ int delta = sbod - _soloed_by_others_downstream;
+ if (delta <= 0) {
+ // do not allow new connections to change implicit solo (no propagation)
+ mod_solo_by_others_downstream (delta);
+ // Session::route_solo_changed() does not propagate indirect solo-changes
+ // propagate upstream to tracks
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
+ if (delta != 0 && does_feed && !sends_only) {
+ (*i)->mod_solo_by_others_downstream (delta);
+ }
+ }
-void
-Route::cancel_solo_after_disconnect (bool upstream)
-{
- if (upstream) {
- _soloed_by_others_upstream = 0;
- } else {
- _soloed_by_others_downstream = 0;
+ }
}
- set_mute_master_solo ();
- solo_changed (false, this);
}
uint32_t
ProcessorState pstate (this);
if (configure_processors_unlocked (0)) {
+ DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
pstate.restore ();
configure_processors_unlocked (0); // it worked before we tried to add it ...
return;
return _amp->gain_control();
}
+boost::shared_ptr<AutomationControl>
+Route::trim_control() const
+{
+ return _trim->gain_control();
+}
+
boost::shared_ptr<AutomationControl>
Route::get_control (const Evoral::Parameter& param)
{
if (_monitor_send && !is_monitor ()) {
assert (!_monitor_send->display_to_user ());
- if (Config->get_solo_control_is_listen_control()) {
- switch (Config->get_listen_position ()) {
- case PreFaderListen:
- switch (Config->get_pfl_position ()) {
- case PFLFromBeforeProcessors:
- new_processors.push_front (_monitor_send);
- break;
- case PFLFromAfterProcessors:
- new_processors.insert (amp, _monitor_send);
- break;
- }
- _monitor_send->set_can_pan (false);
+ switch (Config->get_listen_position ()) {
+ case PreFaderListen:
+ switch (Config->get_pfl_position ()) {
+ case PFLFromBeforeProcessors:
+ new_processors.push_front (_monitor_send);
break;
- case AfterFaderListen:
- switch (Config->get_afl_position ()) {
- case AFLFromBeforeProcessors:
- new_processors.insert (after_amp, _monitor_send);
- break;
- case AFLFromAfterProcessors:
- new_processors.insert (new_processors.end(), _monitor_send);
- break;
- }
- _monitor_send->set_can_pan (true);
+ case PFLFromAfterProcessors:
+ new_processors.insert (amp, _monitor_send);
break;
}
- } else {
- new_processors.insert (new_processors.end(), _monitor_send);
_monitor_send->set_can_pan (false);
+ break;
+ case AfterFaderListen:
+ switch (Config->get_afl_position ()) {
+ case AFLFromBeforeProcessors:
+ new_processors.insert (after_amp, _monitor_send);
+ break;
+ case AFLFromAfterProcessors:
+ new_processors.insert (new_processors.end(), _monitor_send);
+ break;
+ }
+ _monitor_send->set_can_pan (true);
+ break;
}
}