Allow multiple simulataneous Drags to be active, and hence set up and drag time range...
[ardour.git] / libs / ardour / route.cc
index eeb8ae83c17b28b77134f2033756591cf6c87898..44b0acb50ab3b710c8d25cde480b66ed2d9cf19e 100644 (file)
@@ -25,6 +25,7 @@
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
 
 #include "evoral/Curve.hpp"
 
@@ -64,12 +65,14 @@ using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
 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)
 
@@ -93,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 */
 
-       Metering::Meter.connect (*this, (boost::bind (&Route::meter, this)));
+       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)
 {
@@ -109,7 +113,7 @@ Route::Route (Session& sess, const XMLNode& node, DataType default_type)
 
        /* now that we have _meter, its safe to connect to this */
 
-       Metering::Meter.connect (*this, (boost::bind (&Route::meter, this)));
+       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
 }
 
 void
@@ -139,16 +143,19 @@ Route::init ()
 
        /* 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 (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
-       _output->changed.connect (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
+       _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  */
 
@@ -160,8 +167,14 @@ Route::~Route ()
 {
        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 
+        */
+
+       drop_connections ();
+
        /* don't use clear_processors here, as it depends on the session which may
-          be half-destroyed by now */
+          be half-destroyed by now 
+       */
 
        Glib::RWLock::WriterLock lm (_processor_lock);
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -172,11 +185,14 @@ Route::~Route ()
 }
 
 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 ();
+               }
        }
 }
 
@@ -792,7 +808,8 @@ 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 (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
+
+               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
 
                _output->set_user_latency (0);
        }
@@ -1047,7 +1064,7 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                                return -1;
                        }
 
-                       (*i)->ActiveChanged.connect (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
+                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
                }
 
                _output->set_user_latency (0);
@@ -2837,14 +2854,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
@@ -3099,3 +3162,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> ();
+}