more solo propagation fixes.
[ardour.git] / libs / ardour / route.cc
index 29aa5b8a6ab9a5cd6dfbba55b8961e93d0cbc588..5393bcc6820dbf5c03e583421338e51b0aaf4887 100644 (file)
@@ -22,7 +22,6 @@
 #endif
 
 #include <cmath>
-#include <fstream>
 #include <cassert>
 #include <algorithm>
 
@@ -258,7 +257,7 @@ Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 void
 Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
 {
-       /* force IDs for master/monitor busses and prevent 
+       /* force IDs for master/monitor busses and prevent
           any other route from accidentally getting these IDs
           (i.e. legacy sessions)
        */
@@ -277,7 +276,7 @@ Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
 
        /* don't allow it to collide */
 
-       if (!is_master () && !is_monitor() && 
+       if (!is_master () && !is_monitor() &&
            (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
                id += MonitorBusRemoteControlID;
        }
@@ -297,7 +296,7 @@ Route::remote_control_id() const
 {
        if (is_master()) {
                return MasterBusRemoteControlID;
-       } 
+       }
 
        if (is_monitor()) {
                return MonitorBusRemoteControlID;
@@ -365,7 +364,7 @@ Route::ensure_track_or_route_name(string name, Session &session)
        string newname = name;
 
        while (!session.io_name_is_legal (newname)) {
-               newname = bump_name_once (newname, '.');
+               newname = bump_name_once (newname, ' ');
        }
 
        return newname;
@@ -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 */
                }
@@ -833,7 +833,7 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n", 
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n",
                                                  name(), yn, src, (src == _route_group), self_soloed()));
 
        if (self_soloed() != yn) {
@@ -862,12 +862,7 @@ 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", 
+       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));
 
        uint32_t old_sbu = _soloed_by_others_upstream;
@@ -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,12 +920,7 @@ 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", 
+       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));
 
        if (delta < 0) {
@@ -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) {
@@ -1702,7 +1700,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                if (!removed) {
                        /* what? */
                        return 1;
-               } 
+               }
 
                if (configure_processors_unlocked (err)) {
                        pstate.restore ();
@@ -1972,7 +1970,7 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        }
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
-          configuration 
+          configuration
        */
        _session.ensure_buffers (n_process_buffers ());
 
@@ -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 {
@@ -2259,7 +2257,7 @@ Route::state(bool full_state)
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (!full_state) {
-                       /* template save: do not include internal sends functioning as 
+                       /* template save: do not include internal sends functioning as
                           aux sends because the chance of the target ID
                           in the session where this template is used
                           is not very likely.
@@ -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) {
@@ -3257,8 +3255,8 @@ 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 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
                need_to_queue_solo_change = false;
                configure_processors (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
        }
 }
 
@@ -3284,8 +3307,8 @@ Route::output_change_handler (IOChange change, void * /*src*/)
        }
 
        if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change 
-                  contains ConfigurationChanged 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
                need_to_queue_solo_change = false;
                configure_processors (0);
@@ -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);
                }
@@ -4389,7 +4443,7 @@ Route::setup_invisible_processors ()
 
        /* we'll build this new list here and then use it
         *
-        * TODO put the ProcessorList is on the stack for RT-safety. 
+        * TODO put the ProcessorList is on the stack for RT-safety.
         */
 
        ProcessorList new_processors;
@@ -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,11 +4748,12 @@ 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);
                }
        }
+       _roll_delay = _initial_delay;
 }
 
 void
@@ -4706,9 +4761,9 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 {
        size_t n_buffers;
        size_t i;
-       
-       /* MIDI 
-        *  
+
+       /* MIDI
+        *
         * We don't currently mix MIDI input together, so we don't need the
         * complex logic of the audio case.
         */
@@ -4719,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 {
@@ -4737,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.
                        */
@@ -4759,11 +4814,11 @@ 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 
+                        * the port with what is already there
                         */
 
                        if (scaling != 1.0f) {