diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 8346e09951..996bfb414e 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -126,15 +126,12 @@ RouteUI::init () main_mute_check = 0; solo_safe_check = 0; solo_isolated_check = 0; - _mute_release = 0; _momentary_solo = false; - _was_muted_before_momentary_soloed = false; denormal_menu_item = 0; step_edit_item = 0; - multiple_mute_change = false; - multiple_solo_change = false; _i_am_the_modifier = 0; - + _momentary_solo = 0; + setup_invert_buttons (); _session->SoloChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::solo_changed_so_update_mute, this), gui_context()); @@ -482,8 +479,6 @@ RouteUI::solo_press(GdkEventButton* ev) return true; } - multiple_solo_change = false; - /* Goal here is to use Ctrl as the modifier on all platforms. This is questionable design, but here's how we do it. */ @@ -499,61 +494,102 @@ RouteUI::solo_press(GdkEventButton* ev) #endif int momentary_mask = alt_modifier | Keyboard::TertiaryModifier; /* Alt+Shift */ - if (Keyboard::is_context_menu_event (ev)) - { + if (Keyboard::is_context_menu_event (ev)) { if (solo_menu == 0) { build_solo_menu (); } solo_menu->popup (1, ev->time); - } else - { + } else { if ( ev->button == 1 ) { - if (Keyboard::modifier_state_equals (ev->state, control_modifier)) - { + if (Keyboard::modifier_state_equals (ev->state, control_modifier)) { + // control-click: toggle solo isolated status - - _route->set_solo_isolated (!_route->solo_isolated(), this); + if ( is_selected () ) { + boost::shared_ptr selected_route_list = get_selected_route_list (); + _session->set_solo_isolated_force (selected_route_list, !_route->solo_isolated(), Session::rt_cleanup, true); + } else { + _route->set_solo_isolated_force (!_route->solo_isolated(), _session); + } } else - if (Keyboard::modifier_state_equals (ev->state, alt_modifier)) - { + if (Keyboard::modifier_state_equals (ev->state, alt_modifier)) { + // alt-click: toogle Solo X-Or - DisplaySuspender ds; - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (_session->get_routes(), false, Session::rt_cleanup, true); - } else { + // Tracks Live doesn't use monitor bus so far + //if (Config->get_solo_control_is_listen_control()) { + // _session->set_listen (_session->get_routes(), false, Session::rt_cleanup, true); + //} else { _session->set_solo (_session->get_routes(), false, Session::rt_cleanup, true); - } + //} boost::shared_ptr rl (new RouteList); - rl->push_back (route()); - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (rl, true); - } else { + if ( is_selected () ) { + rl = get_selected_route_list (); + } else { + rl->push_back (_route); + } + + // Tracks Live doesn't use monitor bus so far + //if (Config->get_solo_control_is_listen_control()) { + // _session->set_listen (rl, true); + //} else { _session->set_solo (rl, true); - } - } else - { - if (Keyboard::modifier_state_equals (ev->state, momentary_mask)) - { + //} + } else { + if (Keyboard::modifier_state_equals (ev->state, momentary_mask)) { + // alt+shift-click: toogle momentary solo - _momentary_solo = true; - _was_muted_before_momentary_soloed = _route->muted (); + + _momentary_solo = new SoloMuteRelease(true); + // is used for muting tracks back after momentary solo release + _momentary_solo->routes_on = boost::shared_ptr(new ARDOUR::RouteList); + // is used for unsoloing tracks after momentary solo release + _momentary_solo->routes_off = boost::shared_ptr(new ARDOUR::RouteList); + + if ( is_selected () ) { + + // is used for unsoloing tracks after momentary solo release + _momentary_solo->routes_off = get_selected_route_list (); + + for (RouteList::iterator it = _momentary_solo->routes_off->begin(); it != _momentary_solo->routes_off->end(); ++it) { + if ( (*it)->muted() ) + _momentary_solo->routes_on->push_back ( *it ); + } + } else { + // is used for unsoloing tracks after momentary solo release + _momentary_solo->routes_off->push_back (_route); + if (_route->muted ()) + _momentary_solo->routes_on->push_back (_route); + } } /* click: solo this route */ - boost::shared_ptr rl (new RouteList); - rl->push_back (route()); + if ( is_selected () ) { + + boost::shared_ptr selected_route_list = get_selected_route_list (); +// DisplaySuspender ds; - DisplaySuspender ds; - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (rl, !_route->listening_via_monitor()); - } else { - _session->set_solo (rl, !_route->self_soloed()); - } + // Tracks Live doesn't use monitor bus so far + //if (Config->get_solo_control_is_listen_control()) { + // _session->set_listen (selected_route_list, !_route->listening_via_monitor(), Session::rt_cleanup, true); + //} else { + + _session->set_solo (selected_route_list, !_route->self_soloed(), Session::rt_cleanup, true); + //} + } else { + boost::shared_ptr rl (new RouteList); + rl->push_back (_route); + + // Tracks Live doesn't use monitor bus so far + //if (Config->get_solo_control_is_listen_control()) { + // _session->set_listen (rl, !_route->listening_via_monitor(), Session::rt_cleanup, true); + //} else { + _session->set_solo (rl, !_route->self_soloed(), Session::rt_cleanup, true); + //} + } } } // if ( ev->button == 1 ) } @@ -566,25 +602,20 @@ RouteUI::solo_release (GdkEventButton*) { if ( _momentary_solo ) { - boost::shared_ptr rl (new RouteList); - rl->push_back (route()); + // Tracks Live doesn't use monitor bus so far + //if (Config->get_solo_control_is_listen_control()) { + // _session->set_listen (rl, false); + //} else { - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (rl, false); - } else { - _session->set_solo (rl, false); - } + // unsolo tracks after momentary solo release + _session->set_solo (_momentary_solo->routes_off, false); + //} - _momentary_solo = false; + // mute tracks back after momentary solo release + _session->set_mute (_momentary_solo->routes_on, true); - if ( _was_muted_before_momentary_soloed ) - { - boost::shared_ptr rl (new RouteList); - rl->push_back (_route); - - _session->set_mute (rl, true); - _was_muted_before_momentary_soloed = false; - } + delete _momentary_solo; + _momentary_solo = 0; } return true; diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h index 8beb589b6f..5aab672de6 100644 --- a/gtk2_ardour/route_ui.h +++ b/gtk2_ardour/route_ui.h @@ -129,8 +129,6 @@ class RouteUI : public Gtk::EventBox, public WavesUI, public virtual AxisView bool ignore_toggle; bool wait_for_release; - bool multiple_mute_change; - bool multiple_solo_change; WavesButton& master_mute_button; WavesButton& mute_button; WavesButton& solo_button; @@ -265,7 +263,7 @@ class RouteUI : public Gtk::EventBox, public WavesUI, public virtual AxisView static PBD::Signal1 > BusSendDisplayChanged; bool dnd_in_progress() {return _dnd_operation_in_progress; } - + protected: PBD::ScopedConnectionList route_connections; bool self_destruct; @@ -305,12 +303,9 @@ class RouteUI : public Gtk::EventBox, public WavesUI, public virtual AxisView bool exclusive; }; - SoloMuteRelease* _mute_release; + SoloMuteRelease* _momentary_solo; bool is_selected (); boost::shared_ptr get_selected_route_list (); - - bool _momentary_solo; - bool _was_muted_before_momentary_soloed; void setup_invert_buttons (); void set_invert_button_state (); diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 6dbdda496c..6f27074e85 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -161,6 +161,7 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou bool self_soloed () const { return _self_solo; } void set_solo_isolated (bool yn, void *src); + void set_solo_isolated_force (bool yn, void *src); bool solo_isolated() const; void set_solo_safe (bool yn, void *src); @@ -521,6 +522,7 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou uint32_t _soloed_by_others_upstream; uint32_t _soloed_by_others_downstream; uint32_t _solo_isolated; + bool _forced_solo_isolated; bool _denormal_protection; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 823c41169a..c007c01360 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -697,6 +697,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop void set_listen (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false); void set_record_enabled (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false); void set_solo_isolated (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false); + void set_solo_isolated_force (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false); void set_monitoring (boost::shared_ptr, MonitorChoice, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false); void set_exclusive_input_active (boost::shared_ptr rt, bool onoff, bool flip_others=false); @@ -1464,6 +1465,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop void route_listen_changed (void *src, boost::weak_ptr); void route_mute_changed (void *src); void route_solo_changed (bool self_solo_change, void *src, boost::weak_ptr); + void routes_solo_changed (boost::shared_ptr solo_change_routes); void route_solo_isolated_changed (void *src, boost::weak_ptr); void update_route_solo_state (boost::shared_ptr r = boost::shared_ptr()); @@ -1702,6 +1704,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop void rt_set_mute (boost::shared_ptr, bool yn, bool group_override); void rt_set_listen (boost::shared_ptr, bool yn, bool group_override); void rt_set_solo_isolated (boost::shared_ptr, bool yn, bool group_override); + void rt_set_solo_isolated_force (boost::shared_ptr, bool yn, bool group_override); void rt_set_record_enabled (boost::shared_ptr, bool yn, bool group_override); void rt_set_monitoring (boost::shared_ptr, MonitorChoice, bool group_override); diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 8a2404b74e..cc457330f3 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -921,6 +921,12 @@ Route::set_solo_isolated (bool yn, void *src) _solo_isolated++; } else { if (_solo_isolated > 0) { + + // do not drop to 0 if we are forced to be in solo isolated mode + if (_forced_solo_isolated && _solo_isolated == 1) { + return; + } + _solo_isolated--; if (_solo_isolated == 0) { _mute_master->set_solo_ignore (false); @@ -934,6 +940,43 @@ Route::set_solo_isolated (bool yn, void *src) } } +void +Route::set_solo_isolated_force (bool yn, void *src) +{ + if (is_master() || is_monitor() || is_auditioner()) { + return; + } + + bool changed = false; + + if (yn) + { + _forced_solo_isolated = true; + + if (_solo_isolated == 0) { + _solo_isolated++; + _mute_master->set_solo_ignore (true); + changed = true; + } else { + return; + } + } else { + _forced_solo_isolated = false; + + if (_solo_isolated > 0) { + _solo_isolated--; + if (_solo_isolated == 0) { + _mute_master->set_solo_ignore (false); + changed = true; + } + } + } + + if (changed) { + solo_isolated_changed (src); + } +} + bool Route::solo_isolated () const { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index b00d098326..3fd47b6d11 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -3397,6 +3397,7 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) update_route_solo_state (); } + void Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr wpr) { @@ -3558,6 +3559,120 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p set_dirty(); } +void +Session::routes_solo_changed (boost::shared_ptr solo_change_routes) +{ + if (solo_update_disabled) { + // We know already + DEBUG_TRACE (DEBUG::Solo, "solo update disabled - changed ignored\n"); + return; + } + + if (solo_change_routes->empty() ) { + return; + } + + boost::shared_ptr non_solo_change_routes (new RouteList); + boost::shared_ptr r = routes.reader (); + int32_t delta; + + std::set_difference (r->begin(), r->end(), + solo_change_routes->begin(), solo_change_routes->end(), + std::back_inserter(*non_solo_change_routes) ); + + DEBUG_TRACE (DEBUG::Solo, string_compose ("propagate solo change, delta = %1\n", delta)); + + solo_update_disabled = true; + RouteList uninvolved; + + for (RouteList::iterator route = solo_change_routes->begin(); route != solo_change_routes->begin(); ++route) { + + if ((*route)->self_soloed() ) { + delta = 1; + } else { + delta = -1; + } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1\n", (*route)->name())); + + for (RouteList::iterator i = non_solo_change_routes->begin(); i != non_solo_change_routes->end(); ++i) { + bool via_sends_only; + bool in_signal_flow; + + if ((*i) == *route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ) { + continue; + } + + in_signal_flow = false; + + DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed from %1\n", (*i)->name())); + + if ((*i)->feeds (*route, &via_sends_only)) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name())); + if (!via_sends_only) { + if (!(*route)->soloed_by_others_upstream()) { + (*i)->mod_solo_by_others_downstream (delta); + } + } else { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a send-only feed from %1\n", (*i)->name())); + } + in_signal_flow = true; + } else { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tno feed from %1\n", (*i)->name())); + } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed to %1\n", (*i)->name())); + + if ((*route)->feeds (*i, &via_sends_only)) { + /* propagate solo upstream only if routing other than + sends is involved, but do consider the other route + (*i) to be part of the signal flow even if only + sends are involved. + */ + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 feeds %2 via sends only %3 sboD %4 sboU %5\n", + (*route)->name(), + (*i)->name(), + via_sends_only, + (*route)->soloed_by_others_downstream(), + (*route)->soloed_by_others_upstream())); + if (!via_sends_only) { + if (!(*route)->soloed_by_others_downstream()) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); + (*i)->mod_solo_by_others_upstream (delta); + } else { + DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others downstream\n"); + } + } else { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name())); + } + in_signal_flow = true; + } else { + DEBUG_TRACE (DEBUG::Solo, "\tno feed to\n"); + } + + if (!in_signal_flow) { + uninvolved.push_back (*i); + } + } + } + solo_update_disabled = false; + DEBUG_TRACE (DEBUG::Solo, "propagation complete\n"); + + update_route_solo_state (); + + /* now notify that the mute state of the routes not involved in the signal + pathway of the just-solo-changed route may have altered. + */ + + for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1\n", (*i)->name() )); + (*i)->mute_changed (this); + } + + SoloChanged (); /* EMIT SIGNAL */ + set_dirty(); +} + void Session::update_route_solo_state (boost::shared_ptr r) { diff --git a/libs/ardour/session_rtevents.cc b/libs/ardour/session_rtevents.cc index 039e28e3d8..d04105e73d 100644 --- a/libs/ardour/session_rtevents.cc +++ b/libs/ardour/session_rtevents.cc @@ -62,12 +62,15 @@ Session::set_solo (boost::shared_ptr rl, bool yn, SessionEvent::RTeve void Session::rt_set_solo (boost::shared_ptr rl, bool yn, bool /* group_override */) { + solo_update_disabled = true; for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { if (!(*i)->is_auditioner()) { (*i)->set_solo (yn, this); } } - + solo_update_disabled = false; + + routes_solo_changed (rl); set_dirty(); } @@ -176,6 +179,24 @@ Session::rt_set_solo_isolated (boost::shared_ptr rl, bool yn, bool /* set_dirty(); } +void +Session::set_solo_isolated_force (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, bool group_override) +{ + queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo_isolated_force)); +} + +void +Session::rt_set_solo_isolated_force (boost::shared_ptr rl, bool yn, bool /*group_override*/) +{ + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) { + (*i)->set_solo_isolated_force (yn, this); + } + } + + set_dirty(); +} + void Session::set_record_enabled (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, bool group_override) {