Fix crash due to incorrect buffer count.
[ardour.git] / libs / ardour / route.cc
index 6941b537f399c4d01f87292f67ac79cd923090de..fa4b3ce51d08a72f280bf2acad00e297d96b99bd 100644 (file)
 #include <cassert>
 #include <algorithm>
 
-#include <sigc++/bind.h>
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
 
 #include "evoral/Curve.hpp"
 
@@ -64,13 +64,15 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void, string const &> Route::SyncOrderKeys;
+PBD::Signal1<void,string const&> Route::SyncOrderKeys;
+PBD::Signal0<void> Route::RemoteControlIDChange;
 
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
        , AutomatableControls (sess)
        , _flags (flg)
        , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_control (new MuteControllable (X_("mute"), *this))
        , _mute_master (new MuteMaster (sess, name))
        , _default_type (default_type)
 
@@ -80,8 +82,8 @@ 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);
+       _meter->set_display_to_user (false);
+       add_processor (_meter, PostFader);
 
        if (_flags & ControlOut) {
                /* where we listen to tracks */
@@ -94,13 +96,14 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
 
        /* now that we have _meter, its safe to connect to this */
 
-       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
+       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
 }
 
 Route::Route (Session& sess, const XMLNode& node, DataType default_type)
        : SessionObject (sess, "toBeReset")
        , AutomatableControls (sess)
        , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_control (new MuteControllable (X_("mute"), *this))
        , _mute_master (new MuteMaster (sess, "toBeReset"))
        , _default_type (default_type)
 {
@@ -110,7 +113,7 @@ Route::Route (Session& sess, const XMLNode& node, DataType default_type)
 
        /* now that we have _meter, its safe to connect to this */
 
-       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
+       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
 }
 
 void
@@ -118,7 +121,7 @@ Route::init ()
 {
        _self_solo = false;
        _soloed_by_others = 0;
-       _solo_isolated = false;
+       _solo_isolated = 0;
        _solo_safe = false;
        _active = true;
        processor_max_streams.reset();
@@ -135,23 +138,24 @@ Route::init ()
        _in_configure_processors = false;
        _mute_points = MuteMaster::AllPoints;
 
-       _route_group = 0;
-
        _phase_invert = 0;
        _denormal_protection = false;
 
        /* add standard controls */
 
+       _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+       _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+       
        add_control (_solo_control);
-       add_control (_mute_master);
+       add_control (_mute_control);
 
        /* input and output objects */
 
        _input.reset (new IO (_session, _name, IO::Input, _default_type));
        _output.reset (new IO (_session, _name, IO::Output, _default_type));
 
-       _input->changed.connect (mem_fun (this, &Route::input_change_handler));
-       _output->changed.connect (mem_fun (this, &Route::output_change_handler));
+       _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
+       _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
 
        /* add amp processor  */
 
@@ -161,18 +165,34 @@ Route::init ()
 
 Route::~Route ()
 {
-       Metering::disconnect (_meter_connection);
+       DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name));
+
+       /* do this early so that we don't get incoming signals as we are going through destruction 
+        */
 
-       clear_processors (PreFader);
-       clear_processors (PostFader);
+       drop_connections ();
+
+       /* don't use clear_processors here, as it depends on the session which may
+          be half-destroyed by now 
+       */
+
+       Glib::RWLock::WriterLock lm (_processor_lock);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->drop_references ();
+       }
+
+       _processors.clear ();
 }
 
 void
-Route::set_remote_control_id (uint32_t id)
+Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 {
        if (id != _remote_control_id) {
                _remote_control_id = id;
                RemoteControlIDChanged ();
+               if (notify_class_listeners) {
+                       RemoteControlIDChange ();
+               }
        }
 }
 
@@ -423,18 +443,16 @@ Route::process_output_buffers (BufferSet& bufs,
 
        if (rm.locked()) {
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
                        if (bufs.count() != (*i)->input_streams()) {
                                cerr << _name << " bufs = " << bufs.count()
                                     << " input for " << (*i)->name() << " = " << (*i)->input_streams()
                                     << endl;
                        }
                        assert (bufs.count() == (*i)->input_streams());
-                       (*i)->run (bufs, start_frame, end_frame, nframes);
-                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
-               }
 
-               if (!_processors.empty()) {
-                       bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams()));
+                       (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+                       bufs.set_count ((*i)->output_streams());
                }
        }
 }
@@ -600,13 +618,42 @@ Route::set_delivery_solo ()
 void
 Route::set_solo_isolated (bool yn, void *src)
 {
+       if (is_master() || is_control() || is_hidden()) {
+               return;
+       }
+
        if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
                _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
                return;
        }
+       
+       /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
 
-       if (yn != _solo_isolated) {
-               _solo_isolated = yn;
+       boost::shared_ptr<RouteList> routes = _session.get_routes ();
+       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+               bool sends_only;
+               bool does_feed = feeds (*i, &sends_only);
+               
+               if (does_feed && !sends_only) {
+                       (*i)->set_solo_isolated (yn, (*i)->route_group());
+               }
+       }
+
+       bool changed = false;
+
+       if (yn) {
+               if (_solo_isolated == 0) {
+                       changed = true;
+               }
+               _solo_isolated++;
+       } else {
+               changed = (_solo_isolated == 1);
+               if (_solo_isolated > 0) {
+                       _solo_isolated--;
+               }
+       }
+
+       if (changed) {
                set_delivery_solo ();
                solo_isolated_changed (src);
        }
@@ -615,7 +662,7 @@ Route::set_solo_isolated (bool yn, void *src)
 bool
 Route::solo_isolated () const
 {
-       return _solo_isolated;
+       return _solo_isolated > 0;
 }
 
 void
@@ -757,12 +804,13 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        // XXX: do we want to emit the signal here ? change call order.
                        processor->activate ();
                }
-               processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+
+               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
 
                _output->set_user_latency (0);
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -975,8 +1023,6 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ProcessorList::iterator existing_end = _processors.end();
-               --existing_end;
 
                ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
 
@@ -1002,22 +1048,25 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                                }
                        }
 
-                       _processors.insert (iter, *i);
+                       ProcessorList::iterator inserted = _processors.insert (iter, *i);
+
+                       if ((*i)->active()) {
+                               (*i)->activate ();
+                       }
 
                        if (configure_processors_unlocked (err)) {
-                               ++existing_end;
-                               _processors.erase (existing_end, _processors.end());
+                               _processors.erase (inserted);
                                configure_processors_unlocked (0); // it worked before we tried to add it ...
                                return -1;
                        }
 
-                       (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
                }
 
                _output->set_user_latency (0);
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1217,7 +1266,7 @@ Route::clear_processors (Placement p)
 
        processor_max_streams.reset();
        _have_internal_generator = false;
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        if (!already_deleting) {
                _session.clear_deletion_in_progress();
@@ -1308,7 +1357,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        }
 
        processor->drop_references ();
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1400,7 +1449,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
                (*i)->drop_references ();
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1459,15 +1508,6 @@ 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) {
@@ -1477,6 +1517,10 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                out = c->second;
        }
 
+       if (_meter) {
+               _meter->reset_max_channels (processor_max_streams);
+       }
+
        /* make sure we have sufficient scratch buffers to cope with the new processor
           configuration */
        _session.ensure_buffers (n_process_buffers ());
@@ -1640,7 +1684,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
                }
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1830,15 +1874,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                }
        }
 
-       if ((prop = node.property (X_("route-group"))) != 0) {
-               RouteGroup* route_group = _session.route_group_by_name(prop->value());
-               if (route_group == 0) {
-                       error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               } else {
-                       set_route_group (route_group, this);
-               }
-       }
-
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
                long n;
@@ -2005,26 +2040,9 @@ Route::_set_state_2X (const XMLNode& node, int version)
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
 
-       /* XXX: if the route was in both a mix group and an edit group, it'll end up
-          just in the edit group. */
-
-       if ((prop = node.property (X_("mix-group"))) != 0) {
-               RouteGroup* route_group = _session.route_group_by_name(prop->value());
-               if (route_group == 0) {
-                       error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               } else {
-                       set_route_group (route_group, this);
-               }
-       }
-
-       if ((prop = node.property (X_("edit-group"))) != 0) {
-               RouteGroup* route_group = _session.route_group_by_name(prop->value());
-               if (route_group == 0) {
-                       error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               } else {
-                       set_route_group (route_group, this);
-               }
-       }
+       /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they
+          don't mean the same thing.
+       */
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
@@ -2229,7 +2247,7 @@ Route::set_processor_state (const XMLNode& node)
           the XML state represents a working signal route.
        */
 
-       processors_changed ();
+       processors_changed (RouteProcessorChange ());
 }
 
 void
@@ -2392,33 +2410,6 @@ Route::drop_listen (boost::shared_ptr<Route> route)
        }
 }
 
-void
-Route::set_route_group (RouteGroup *rg, void *src)
-{
-       if (rg == _route_group) {
-               return;
-       }
-
-       if (_route_group) {
-               _route_group->remove (this);
-       }
-
-       if ((_route_group = rg) != 0) {
-               _route_group->add (this);
-       }
-
-       _session.set_dirty ();
-       route_group_changed (src); /* EMIT SIGNAL */
-}
-
-void
-Route::drop_route_group (void *src)
-{
-       _route_group = 0;
-       _session.set_dirty ();
-       route_group_changed (src); /* EMIT SIGNAL */
-}
-
 void
 Route::set_comment (string cmt, void *src)
 {
@@ -2665,55 +2656,71 @@ Route::flush_processors ()
 void
 Route::set_meter_point (MeterPoint p, void *src)
 {
+       /* CAN BE CALLED FROM PROCESS CONTEXT */
+
        if (_meter_point == p) {
                return;
        }
 
+       bool meter_was_visible_to_user = _meter->display_to_user ();
+
        {
                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);
+                       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);
+                               loc = find (_processors.begin(), _processors.end(), _amp);
                                break;
                        case MeterPostFader:
                                loc = _processors.end();
                                break;
                        default:
-                       break;
+                               break;
                        }
-
-                       _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;
+                       ChanCount m_in;
+                       
+                       if (loc == _processors.begin()) {
+                               m_in = _input->n_ports();
+                       } else {
+                               ProcessorList::iterator before = loc;
+                               --before;
+                               m_in = (*before)->output_streams ();
                        }
-
+                       
+                       _meter->reflect_inputs (m_in);
+                       
+                       _processors.insert (loc, _meter);
+                       
+                       /* we do not need to reconfigure the processors, because the meter
+                          (a) is always ready to handle processor_max_streams
+                          (b) is always an N-in/N-out processor, and thus moving
+                          it doesn't require any changes to the other processors.
+                       */
+                       
                        _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 ();
+
+       bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
+       
+       processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */
 }
 
 void
@@ -2753,7 +2760,7 @@ Route::put_control_outs_at (Placement p)
                }
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        _session.set_dirty ();
 }
 
@@ -2843,14 +2850,60 @@ void
 Route::SoloControllable::set_value (float val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
 
+       if (Config->get_solo_control_is_listen_control()) {
+               _session.set_listen (rl, bval);
+       } else {
+               _session.set_solo (rl, bval);
+       }
+#else
        route.set_solo (bval, this);
+#endif
 }
 
 float
 Route::SoloControllable::get_value (void) const
 {
-       return route.self_soloed() ? 1.0f : 0.0f;
+       if (Config->get_solo_control_is_listen_control()) {
+               return route.listening() ? 1.0f : 0.0f;
+       } else {
+               return route.self_soloed() ? 1.0f : 0.0f;
+       }
+}
+
+Route::MuteControllable::MuteControllable (std::string name, Route& r)
+       : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+                            boost::shared_ptr<AutomationList>(), name)
+       , route (r)
+{
+       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
+       set_list (gl);
+}
+
+void
+Route::MuteControllable::set_value (float val)
+{
+       bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+       _session.set_mute (rl, bval);
+#else
+       route.set_mute (bval, this);
+#endif
+}
+
+float
+Route::MuteControllable::get_value (void) const
+{
+       return route.muted() ? 1.0f : 0.0f;
 }
 
 void
@@ -3105,3 +3158,40 @@ Route::get_control (const Evoral::Parameter& param)
 
        return c;
 }
+
+boost::shared_ptr<Processor>
+Route::nth_plugin (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
+boost::shared_ptr<Processor>
+Route::nth_send (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               cerr << "check " << (*i)->name() << endl;
+               if (boost::dynamic_pointer_cast<Send> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               } else {
+                       cerr << "\tnot a send\n";
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}