get "solo safe" back in action again
[ardour.git] / libs / ardour / route.cc
index e129a22d2c9bb8b6e292e9317c51ca0f0e8f051a..6941b537f399c4d01f87292f67ac79cd923090de 100644 (file)
@@ -25,7 +25,6 @@
 #include <sigc++/bind.h>
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
-#include "pbd/stacktrace.h"
 #include "pbd/memento_command.h"
 
 #include "evoral/Curve.hpp"
@@ -37,6 +36,7 @@
 #include "ardour/buffer_set.h"
 #include "ardour/configuration.h"
 #include "ardour/cycle_timer.h"
+#include "ardour/debug.h"
 #include "ardour/delivery.h"
 #include "ardour/dB.h"
 #include "ardour/internal_send.h"
@@ -80,6 +80,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        /* add standard processors other than amp (added by ::init()) */
 
        _meter.reset (new PeakMeter (_session));
+       _meter->set_display_to_user (_meter_point == MeterCustom);
        add_processor (_meter, PreFader);
 
        if (_flags & ControlOut) {
@@ -115,11 +116,12 @@ Route::Route (Session& sess, const XMLNode& node, DataType default_type)
 void
 Route::init ()
 {
-       _solo_level = 0;
+       _self_solo = false;
+       _soloed_by_others = 0;
        _solo_isolated = false;
+       _solo_safe = false;
        _active = true;
        processor_max_streams.reset();
-       _solo_safe = false;
        _recordable = true;
        order_keys[N_("signal")] = order_key_cnt++;
        _silent = false;
@@ -131,6 +133,7 @@ Route::init ()
        _pending_declick = true;
        _remote_control_id = 0;
        _in_configure_processors = false;
+       _mute_points = MuteMaster::AllPoints;
 
        _route_group = 0;
 
@@ -518,10 +521,25 @@ Route::listening () const
        }
 }
 
+void
+Route::set_solo_safe (bool yn, void *src)
+{
+       if (_solo_safe != yn) {
+               _solo_safe = yn;
+               solo_safe_changed (src);
+       } 
+}
+
+bool
+Route::solo_safe() const
+{
+       return _solo_safe;
+}
+
 void
 Route::set_solo (bool yn, void *src)
 {
-       if (_solo_safe || _solo_isolated) {
+       if (_solo_safe) {
                return;
        }
 
@@ -530,31 +548,53 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
-       if (soloed() != yn) {
-               mod_solo_level (yn ? 1 : -1);
+       if (self_soloed() != yn) {
+               set_self_solo (yn);
+               set_delivery_solo ();
                solo_changed (src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
 }
 
 void
-Route::mod_solo_level (int32_t delta)
+Route::set_self_solo (bool yn)
+{
+       _self_solo = yn;
+}
+
+void
+Route::mod_solo_by_others (int32_t delta)
 {
        if (delta < 0) {
-               if (_solo_level >= (uint32_t) delta) {
-                       _solo_level += delta;
+               if (_soloed_by_others >= (uint32_t) delta) {
+                       _soloed_by_others += delta;
                } else {
-                       _solo_level = 0;
+                       _soloed_by_others = 0;
                }
        } else {
-               _solo_level += delta;
+               _soloed_by_others += delta;
        }
 
-       /* tell main outs what the solo situation is
-        */
+       set_delivery_solo ();
+}
+
+void
+Route::set_delivery_solo ()
+{
+       /* tell all delivery processors what the solo situation is, so that they keep
+          delivering even though Session::soloing() is true and they were not
+          explicitly soloed.
+       */
 
-       _main_outs->set_solo_level (_solo_level);
-       _main_outs->set_solo_isolated (_solo_isolated);
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<Delivery> d;
+               
+               if ((d = boost::dynamic_pointer_cast<Delivery> (*i)) != 0) {
+                       d->set_solo_level (soloed ());
+                       d->set_solo_isolated (solo_isolated());
+               }
+       }
 }
 
 void
@@ -567,13 +607,7 @@ Route::set_solo_isolated (bool yn, void *src)
 
        if (yn != _solo_isolated) {
                _solo_isolated = yn;
-
-               /* tell main outs what the solo situation is
-                */
-
-               _main_outs->set_solo_level (_solo_level);
-               _main_outs->set_solo_isolated (_solo_isolated);
-
+               set_delivery_solo ();
                solo_isolated_changed (src);
        }
 }
@@ -584,6 +618,18 @@ Route::solo_isolated () const
        return _solo_isolated;
 }
 
+void
+Route::set_mute_points (MuteMaster::MutePoint mp)
+{
+       _mute_points = mp;
+       mute_points_changed (); /* EMIT SIGNAL */
+
+       if (_mute_master->muted()) {
+               _mute_master->mute_at (_mute_points);
+               mute_changed (this); /* EMIT SIGNAL */
+       }
+}
+
 void
 Route::set_mute (bool yn, void *src)
 {
@@ -593,8 +639,13 @@ Route::set_mute (bool yn, void *src)
        }
 
        if (muted() != yn) {
-               _mute_master->mute (yn);
-               mute_changed (src);
+               if (yn) {
+                       _mute_master->mute_at (_mute_points);
+               } else {
+                       _mute_master->clear_mute ();
+               }
+
+               mute_changed (src); /* EMIT SIGNAL */
        }
 }
 
@@ -756,6 +807,7 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                                }
 
                                _meter.reset (new PeakMeter (_session, node));
+                               _meter->set_display_to_user (_meter_point == MeterCustom);
                                processor = _meter;
 
                        } else if (prop->value() == "amp") {
@@ -804,12 +856,12 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                                return false;
                        }
 
-                       if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
+                       if (iter == _processors.end() && processor->display_to_user() && !_processors.empty()) {
                                /* check for invisible processors stacked at the end and leave them there */
                                ProcessorList::iterator p;
                                p = _processors.end();
                                --p;
-                               while (!(*p)->visible() && p != _processors.begin()) {
+                               while (!(*p)->display_to_user() && p != _processors.begin()) {
                                        --p;
                                }
                                ++p;
@@ -867,12 +919,12 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
                        return false;
                }
 
-               if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
+               if (iter == _processors.end() && processor->display_to_user() && !_processors.empty()) {
                        /* check for invisible processors stacked at the end and leave them there */
                        ProcessorList::iterator p;
                        p = _processors.end();
                        --p;
-                       while (!(*p)->visible() && p != _processors.begin()) {
+                       while (!(*p)->display_to_user() && p != _processors.begin()) {
                                --p;
                        }
                        ++p;
@@ -1376,16 +1428,25 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
 
        _in_configure_processors = true;
 
-
        // Check each processor in order to see if we can configure as requested
        ChanCount in = _input->n_ports ();
        ChanCount out;
        list< pair<ChanCount,ChanCount> > configuration;
        uint32_t index = 0;
 
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
+#ifndef NDEBUG
+       DEBUG_TRACE (DEBUG::Processors, "{\n");
+       for (list<boost::shared_ptr<Processor> >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) {
+               DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id()));
+       }
+       DEBUG_TRACE (DEBUG::Processors, "}\n");
+#endif
+
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
                if ((*p)->can_support_io_configuration(in, out)) {
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1in = %2 out = %3\n",(*p)->name(), in, out));
                        configuration.push_back(make_pair(in, out));
                        in = out;
                } else {
@@ -1398,6 +1459,15 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                }
        }
 
+       /* Take the process lock so that if we add a processor which increases the required
+          number of scratch buffers, we create those scratch buffers before the process
+          thread has a chance to ask for them.
+          XXX: in an ideal world we'd perhaps use some RCU magic to avoid having to take
+          the lock here.
+       */
+       
+       Glib::Mutex::Lock pl (_session.engine().process_lock ());
+       
        // We can, so configure everything
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
@@ -1407,6 +1477,10 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                out = c->second;
        }
 
+       /* make sure we have sufficient scratch buffers to cope with the new processor
+          configuration */
+       _session.ensure_buffers (n_process_buffers ());
+
        _in_configure_processors = false;
        return 0;
 }
@@ -1495,7 +1569,7 @@ int
 Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
        /* "new_order" is an ordered list of processors to be positioned according to "placement".
-          NOTE: all processors in "new_order" MUST be marked as visible. There maybe additional
+          NOTE: all processors in "new_order" MUST be marked as display_to_user(). There maybe additional
           processors in the current actual processor list that are hidden. Any visible processors
           in the current list but not in "new_order" will be assumed to be deleted.
        */
@@ -1534,7 +1608,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                        } else {
 
-                               if (!(*oiter)->visible()) {
+                               if (!(*oiter)->display_to_user()) {
 
                                        as_it_will_be.push_back (*oiter);
 
@@ -1626,6 +1700,9 @@ Route::state(bool full_state)
                order_string += ':';
        }
        node->add_property ("order-keys", order_string);
+       node->add_property ("self-solo", (_self_solo ? "yes" : "no"));
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others);
+       node->add_property ("soloed-by-others", buf);
 
        node->add_child_nocopy (_input->state (full_state));
        node->add_child_nocopy (_output->state (full_state));
@@ -1719,9 +1796,13 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 
        set_processor_state (processor_state);
 
-       if ((prop = node.property ("solo_level")) != 0) {
-               _solo_level = 0; // needed for mod_solo_level() to work
-               mod_solo_level (atoi (prop->value()));
+       if ((prop = node.property ("self-solo")) != 0) {
+               set_self_solo (string_is_affirmative (prop->value()));
+       }
+
+       if ((prop = node.property ("soloed-by-others")) != 0) {
+               _soloed_by_others = 0; // needed for mod_solo_by_others () to work
+               mod_solo_by_others (atoi (prop->value()));
        }
 
        if ((prop = node.property ("solo-isolated")) != 0) {
@@ -1742,16 +1823,11 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                set_active (yn);
        }
 
-       if ((prop = node.property (X_("soloed"))) != 0) {
-               bool yn = string_is_affirmative (prop->value());
-
-               /* XXX force reset of solo status */
-
-               set_solo (yn, this);
-       }
-
        if ((prop = node.property (X_("meter-point"))) != 0) {
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
+               if (_meter) {
+                       _meter->set_display_to_user (_meter_point == MeterCustom);
+               }
        }
 
        if ((prop = node.property (X_("route-group"))) != 0) {
@@ -2352,30 +2428,41 @@ Route::set_comment (string cmt, void *src)
 }
 
 bool
-Route::feeds (boost::shared_ptr<Route> other)
+Route::feeds (boost::shared_ptr<Route> other, bool* only_send)
 {
-       // cerr << _name << endl;
+       DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
 
        if (_output->connected_to (other->input())) {
-               // cerr << "\tdirect FEEDS " << other->name() << endl;
+               DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name()));
+               if (only_send) {
+                       *only_send = false;
+               }
+
                return true;
        }
 
+       
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
 
                boost::shared_ptr<IOProcessor> iop;
 
                if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
                        if (iop->feeds (other)) {
-                               // cerr << "\tIOP " << iop->name() << " feeds " << other->name() << endl;
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
+                               if (only_send) {
+                                       *only_send = true;
+                               }
                                return true;
                        } else {
-                               // cerr << "\tIOP " << iop->name() << " does NOT feeds " << other->name() << endl;
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does NOT feed %2\n", iop->name(), other->name()));
                        }
+               } else {
+                       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tPROC %1 is not an IOP\n", (*r)->name()));
                }
+                       
        }
 
-       // cerr << "\tdoes NOT FEED " << other->name() << endl;
+       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tdoes NOT feed %1\n", other->name()));
        return false;
 }
 
@@ -2578,30 +2665,57 @@ Route::flush_processors ()
 void
 Route::set_meter_point (MeterPoint p, void *src)
 {
-       if (_meter_point != p) {
-               _meter_point = p;
+       if (_meter_point == p) {
+               return;
+       }
 
-               // Move meter in the processors list
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter);
-               _processors.erase(loc);
-               switch (p) {
-               case MeterInput:
-                       loc = _processors.begin();
-                       break;
-               case MeterPreFader:
-                       loc = find(_processors.begin(), _processors.end(), _amp);
-                       break;
-               case MeterPostFader:
-                       loc = _processors.end();
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList as_it_was (_processors);
+
+               if (p != MeterCustom) {
+                       // Move meter in the processors list to reflect the new position
+                       ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter);
+                       _processors.erase(loc);
+                       switch (p) {
+                       case MeterInput:
+                               loc = _processors.begin();
+                               break;
+                       case MeterPreFader:
+                               loc = find(_processors.begin(), _processors.end(), _amp);
+                               break;
+                       case MeterPostFader:
+                               loc = _processors.end();
+                               break;
+                       default:
                        break;
-               }
-               _processors.insert(loc, _meter);
+                       }
+
+                       _processors.insert(loc, _meter);
+                       
+                       if (configure_processors_unlocked (0)) {
+                               _processors = as_it_was;
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return;
+                       }
 
-                meter_change (src); /* EMIT SIGNAL */
-               processors_changed (); /* EMIT SIGNAL */
-               _session.set_dirty ();
+                       _meter->set_display_to_user (false);
+
+               } else {
+
+                       // just make it visible and let the user move it
+
+                       _meter->set_display_to_user (true);
+               }
+               
        }
+       
+       _meter_point = p;
+       meter_change (src); /* EMIT SIGNAL */
+       processors_changed (); /* EMIT SIGNAL */
+       _session.set_dirty ();
 }
+
 void
 Route::put_control_outs_at (Placement p)
 {
@@ -2609,26 +2723,36 @@ Route::put_control_outs_at (Placement p)
                return;
        }
 
-       // Move meter in the processors list
-       ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs);
-       _processors.erase(loc);
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList as_it_was (_processors);
+               // Move meter in the processors list
+               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs);
+               _processors.erase(loc);
+               
+               switch (p) {
+               case PreFader:
+                       loc = find(_processors.begin(), _processors.end(), _amp);
+                       if (loc != _processors.begin()) {
+                               --loc;
+                       }
+                       break;
+               case PostFader:
+                       loc = find(_processors.begin(), _processors.end(), _amp);
+                       assert (loc != _processors.end());
+                       loc++;
+                       break;
+               }
+               
+               _processors.insert(loc, _control_outs);
 
-       switch (p) {
-       case PreFader:
-               loc = find(_processors.begin(), _processors.end(), _amp);
-               if (loc != _processors.begin()) {
-                       --loc;
+               if (configure_processors_unlocked (0)) {
+                       _processors = as_it_was;
+                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                       return;
                }
-               break;
-       case PostFader:
-               loc = find(_processors.begin(), _processors.end(), _amp);
-               assert (loc != _processors.end());
-               loc++;
-               break;
        }
 
-       _processors.insert(loc, _control_outs);
-
        processors_changed (); /* EMIT SIGNAL */
        _session.set_dirty ();
 }
@@ -2645,10 +2769,7 @@ Route::update_total_latency ()
                }
        }
 
-#undef DEBUG_LATENCY
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": internal redirect latency = " << own_latency << endl;
-#endif
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal redirect latency = %2\n", _name, own_latency));
 
        _output->set_port_latency (own_latency);
 
@@ -2669,10 +2790,7 @@ Route::update_total_latency ()
                signal_latency_changed (); /* EMIT SIGNAL */
        }
 
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": input latency = " << _input->signal_latency() << " total = "
-            << own_latency << endl;
-#endif
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency));
 
        return _output->effective_latency ();
 }
@@ -2732,7 +2850,7 @@ Route::SoloControllable::set_value (float val)
 float
 Route::SoloControllable::get_value (void) const
 {
-       return route.soloed() ? 1.0f : 0.0f;
+       return route.self_soloed() ? 1.0f : 0.0f;
 }
 
 void
@@ -2741,7 +2859,8 @@ Route::set_block_size (nframes_t nframes)
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
        }
-       _session.ensure_buffers(processor_max_streams);
+       
+       _session.ensure_buffers (n_process_buffers ());
 }
 
 void