more solo propagation fixes.
[ardour.git] / libs / ardour / route.cc
index 550901f8fbc8311d8c52b648193dc7b1214e1801..5393bcc6820dbf5c03e583421338e51b0aaf4887 100644 (file)
@@ -22,7 +22,6 @@
 #endif
 
 #include <cmath>
-#include <fstream>
 #include <cassert>
 #include <algorithm>
 
@@ -501,11 +500,11 @@ Route::process_output_buffers (BufferSet& bufs,
           on a transition between monitoring states we get a de-clicking gain
           change in the _main_outs delivery, if config.get_use_monitor_fades()
           is true.
-       
+
           We override this in the case where we have an internal generator.
        */
        bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence);
-       
+
        _main_outs->no_outs_cuz_we_no_monitor (silence);
 
        /* -------------------------------------------------------------------------------------------
@@ -779,11 +778,12 @@ Route::set_listen (bool yn, void* src)
                if (yn != _monitor_send->active()) {
                        if (yn) {
                                _monitor_send->activate ();
-                               _mute_master->set_soloed (true);
+                               _mute_master->set_soloed_by_self (true);
                        } else {
                                _monitor_send->deactivate ();
-                               _mute_master->set_soloed (false);
+                               _mute_master->set_soloed_by_self (false);
                        }
+                       _mute_master->set_soloed_by_others (false);
 
                        listen_changed (src); /* EMIT SIGNAL */
                }
@@ -862,11 +862,6 @@ Route::set_self_solo (bool yn)
 void
 Route::mod_solo_by_others_upstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name()));
-               return;
-       }
-
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
@@ -905,8 +900,11 @@ Route::mod_solo_by_others_upstream (int32_t delta)
             (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);
@@ -922,11 +920,6 @@ Route::mod_solo_by_others_upstream (int32_t delta)
 void
 Route::mod_solo_by_others_downstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name()));
-               return;
-       }
-
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
@@ -949,7 +942,8 @@ Route::mod_solo_by_others_downstream (int32_t delta)
 void
 Route::set_mute_master_solo ()
 {
-       _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
+       _mute_master->set_soloed_by_self (self_soloed());
+       _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
 }
 
 void
@@ -1002,28 +996,28 @@ Route::set_solo_isolated (bool yn, void *src)
                }
        }
 
-       
+
        if (!changed) {
                return;
        }
-       
+
        /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
 
        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 = feeds (*i, &sends_only);
-               
+
                if (does_feed && !sends_only) {
                        (*i)->mod_solo_isolated_by_upstream (yn, src);
                }
        }
-       
+
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
        solo_isolated_changed (src);
@@ -1076,12 +1070,16 @@ Route::muted () const
 bool
 Route::muted_by_others () const
 {
+       // This method is only used by route_ui for display state.
+       // The real thing is MuteMaster::muted_by_others_at()
+
        //master is never muted by others
        if (is_master())
                return false;
-               
+
        //now check to see if something is soloed (and I am not)
-       return (_session.soloing() && !self_soloed() && !solo_isolated());
+       //see also MuteMaster::mute_gain_at()
+       return (_session.soloing() && !soloed() && !solo_isolated());
 }
 
 #if 0
@@ -1106,7 +1104,7 @@ Route::before_processor_for_placement (Placement p)
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        ProcessorList::iterator loc;
-       
+
        if (p == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
                loc = find (_processors.begin(), _processors.end(), _amp);
@@ -1129,14 +1127,14 @@ Route::before_processor_for_index (int index)
        }
 
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator i = _processors.begin ();
        int j = 0;
        while (i != _processors.end() && j < index) {
                if ((*i)->display_to_user()) {
                        ++j;
                }
-               
+
                ++i;
        }
 
@@ -1319,7 +1317,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                if (processor->set_state (node, version)) {
                        return false;
                }
-               
+
                //A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO
                if (i != children.end()) {
                        if ((prop = (*i)->property (X_("active"))) != 0) {
@@ -1993,12 +1991,12 @@ Route::all_visible_processors_active (bool state)
        if (_processors.empty()) {
                return;
        }
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
                        continue;
                }
-               
+
                if (state) {
                        (*i)->activate ();
                } else {
@@ -3103,9 +3101,9 @@ Route::remove_aux_or_listen (boost::shared_ptr<Route> route)
 
          again:
                for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
-                       
+
                        boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-                       
+
                        if (d && d->target_route() == route) {
                                rl.release ();
                                if (remove_processor (*x, &err, false) > 0) {
@@ -3272,6 +3270,31 @@ Route::input_change_handler (IOChange change, void * /*src*/)
                } else {
                        cancel_solo_after_disconnect (true);
                }
+#if 1
+       } else if (_soloed_by_others_upstream) {
+               bool cancel_solo = true;
+               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 = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+                       if (does_feed && !sends_only) {
+                               if ((*i)->soloed()) {
+                                       cancel_solo = false;
+                                       break;
+                               }
+                       }
+               }
+               if (cancel_solo) {
+                       cancel_solo_after_disconnect (true);
+               }
+#else
+       } else if (self_soloed()) {
+#endif
+               // TODO propagate upstream
+               // see commment in output_change_handler() below
        }
 }
 
@@ -3303,6 +3326,37 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                } else {
                        cancel_solo_after_disconnect (false);
                }
+#if 1
+       } else if (_soloed_by_others_downstream) {
+               bool cancel_solo = true;
+               /* 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 (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
        }
 }
 
@@ -3569,9 +3623,9 @@ Route::set_meter_point_unlocked ()
        /* Set up the meter for its new position */
 
        ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
-       
+
        ChanCount m_in;
-       
+
        if (loc == _processors.begin()) {
                m_in = _input->n_ports();
        } else {
@@ -3974,7 +4028,7 @@ Route::set_name_in_state (XMLNode& node, string const & name)
 
        XMLNodeList children = node.children();
        for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
-               
+
                if ((*i)->name() == X_("IO")) {
 
                        IO::set_name_in_state (**i, name);
@@ -3985,12 +4039,12 @@ Route::set_name_in_state (XMLNode& node, string const & name)
                        if (role && role->value() == X_("Main")) {
                                (*i)->add_property (X_("name"), name);
                        }
-                       
+
                } else if ((*i)->name() == X_("Diskstream")) {
 
                        (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
                        (*i)->add_property (X_("name"), name);
-                       
+
                }
        }
 }
@@ -4276,17 +4330,17 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn
        } else {
                all_connections.min = ~((pframes_t) 0);
                all_connections.max = 0;
-               
+
                /* iterate over all "from" ports and determine the latency range for all of their
                   connections to the "outside" (outside of this Route).
                */
-               
+
                for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
-                       
+
                        LatencyRange range;
-                       
+
                        p->get_connected_latency_range (range, playback);
-                       
+
                        all_connections.min = min (all_connections.min, range.min);
                        all_connections.max = max (all_connections.max, range.max);
                }
@@ -4539,7 +4593,7 @@ Route::setup_invisible_processors ()
        _processors = new_processors;
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if (!(*i)->display_to_user () && !(*i)->active ()) {
+               if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) {
                        (*i)->activate ();
                }
        }
@@ -4579,7 +4633,7 @@ Route::maybe_note_meter_position ()
        if (_meter_point != MeterCustom) {
                return;
        }
-       
+
        _custom_meter_position_noted = true;
        /* custom meter points range from after trim to before panner/main_outs
         * this is a limitation by the current processor UI
@@ -4648,7 +4702,7 @@ Route::has_external_redirects () const
                /* ignore inactive processors and obviously ignore the main
                 * outs since everything has them and we don't care.
                 */
-               
+
                if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
                        return true;;
                }
@@ -4694,7 +4748,7 @@ Route::non_realtime_locate (framepos_t pos)
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-               
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        (*i)->transport_located (pos);
                }
@@ -4707,7 +4761,7 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 {
        size_t n_buffers;
        size_t i;
-       
+
        /* MIDI
         *
         * We don't currently mix MIDI input together, so we don't need the
@@ -4720,7 +4774,7 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 
                boost::shared_ptr<MidiPort> source_port = io->midi (i);
                MidiBuffer& buf (bufs.get_midi (i));
-               
+
                if (source_port) {
                        buf.copy (source_port->get_midi_buffer(nframes));
                } else {
@@ -4738,19 +4792,19 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
        if (n_ports > n_buffers) {
                scaling = ((float) n_buffers) / n_ports;
        }
-       
+
        for (i = 0; i < n_ports; ++i) {
-               
+
                /* if there are more ports than buffers, map them onto buffers
                 * in a round-robin fashion
                 */
 
                boost::shared_ptr<AudioPort> source_port = io->audio (i);
                AudioBuffer& buf (bufs.get_audio (i%n_buffers));
-                       
+
 
                if (i < n_buffers) {
-                       
+
                        /* first time through just copy a channel into
                           the output buffer.
                        */
@@ -4760,9 +4814,9 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                        if (scaling != 1.0f) {
                                buf.apply_gain (scaling, nframes);
                        }
-                       
+
                } else {
-                       
+
                        /* on subsequent times around, merge data from
                         * the port with what is already there
                         */