promote Playlist::RegionList to ARDOUR::RegionList; fix timefx on multiple regions...
[ardour.git] / libs / ardour / route.cc
index 00507627b5ee353cb2c9d951af8e72c1a2e63a6e..acc353f7dbb4e2526af14e872921c73e2c2b56de 100644 (file)
@@ -108,6 +108,13 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
 {
        processor_max_streams.reset();
        order_keys[N_("signal")] = order_key_cnt++;
+
+       if (is_master()) {
+               set_remote_control_id (MasterBusRemoteControlID);
+       } else if (is_monitor()) {
+               set_remote_control_id (MonitorBusRemoteControlID);
+       }
+               
 }
 
 int
@@ -138,6 +145,8 @@ Route::init ()
        _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
        _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
 
+       _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
+
        /* add amp processor  */
 
        _amp.reset (new Amp (_session));
@@ -207,6 +216,26 @@ Route::~Route ()
 void
 Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 {
+       /* force IDs for master/monitor busses and prevent 
+          any other route from accidentally getting these IDs
+          (i.e. legacy sessions)
+       */
+
+       if (is_master() && id != MasterBusRemoteControlID) {
+               id = MasterBusRemoteControlID;
+       }
+
+       if (is_monitor() && id != MonitorBusRemoteControlID) {
+               id = MonitorBusRemoteControlID;
+       }
+
+       /* don't allow it to collide */
+
+       if (!is_master () && !is_monitor() && 
+           (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
+               id += MonitorBusRemoteControlID;
+       }
+
        if (id != _remote_control_id) {
                _remote_control_id = id;
                RemoteControlIDChanged ();
@@ -484,18 +513,19 @@ Route::process_output_buffers (BufferSet& bufs,
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
-                       break;
+               if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
+                       /* don't ::run() the meter, otherwise it will have its previous peak corrupted */
+                       continue;
                }
 
-               if (boost::dynamic_pointer_cast<PeakMeter> (*i) && meter_already_run) {
-                       /* don't ::run() the meter, otherwise it will have its previous peak corrupted */
+               if (Config->get_plugins_stop_with_transport() && _session.transport_speed() == 0 && boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+                       /* don't run plugins with the transport stopped, if configured this way */
                        continue;
                }
                
 #ifndef NDEBUG
                /* if it has any inputs, make sure they match */
-               if ((*i)->input_streams() != ChanCount::ZERO) {
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i) == 0 && (*i)->input_streams() != ChanCount::ZERO) {
                        if (bufs.count() != (*i)->input_streams()) {
                                cerr << _name << " bufs = " << bufs.count()
                                     << " input for " << (*i)->name() << " = " << (*i)->input_streams()
@@ -1330,7 +1360,7 @@ Route::clear_processors (Placement p)
 }
 
 int
-Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err)
+Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, bool need_process_lock)
 {
        /* these can never be removed */
 
@@ -1388,11 +1418,18 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                if (!removed) {
                        /* what? */
                        return 1;
-               }
+               } 
 
-               {
+               if (need_process_lock) {
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
+                       if (configure_processors_unlocked (err)) {
+                               pstate.restore ();
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
+               } else {
                        if (configure_processors_unlocked (err)) {
                                pstate.restore ();
                                /* we know this will work, because it worked before :) */
@@ -1839,6 +1876,24 @@ 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 
+                          aux sends because the chance of the target ID
+                          in the session where this template is used
+                          is not very likely.
+
+                          similarly, do not save listen sends which connect to
+                          the monitor section, because these will always be
+                          added if necessary.
+                       */
+                       boost::shared_ptr<InternalSend> is;
+
+                       if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
+                               if (is->role() == Delivery::Aux || is->role() == Delivery::Listen) {
+                                       continue;
+                               }
+                       }
+               }
                node->add_child_nocopy((*i)->state (full_state));
        }
 
@@ -2531,6 +2586,7 @@ Route::add_send_to_internal_return (InternalSend* send)
 void
 Route::remove_send_from_internal_return (InternalSend* send)
 {
+       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
        Glib::RWLock::ReaderLock rm (_processor_lock);
 
        for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
@@ -2542,11 +2598,13 @@ Route::remove_send_from_internal_return (InternalSend* send)
        }
 }
 
-/** Add a monitor send (if we don't already have one) but don't activate it */
-int
-Route::listen_via_monitor ()
+void
+Route::enable_monitor_send ()
 {
-       /* master never sends to control outs */
+       /* Caller must hold process lock */
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
+       /* master never sends to monitor section via the normal mechanism */
        assert (!is_master ());
 
        /* make sure we have one */
@@ -2556,18 +2614,15 @@ Route::listen_via_monitor ()
        }
 
        /* set it up */
-       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
        configure_processors (0);
-
-       return 0;
 }
 
-/** Add an internal send to a route.
+/** Add an aux send to a route.
  *  @param route route to send to.
  *  @param placement placement for the send.
  */
 int
-Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
+Route::add_aux_send (boost::shared_ptr<Route> route, Placement placement)
 {
        assert (route != _session.monitor_out ());
 
@@ -2586,7 +2641,14 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
        }
 
        try {
-               boost::shared_ptr<InternalSend> listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
+
+               boost::shared_ptr<InternalSend> listener;
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
+               }
+
                add_processor (listener, placement);
 
        } catch (failed_constructor& err) {
@@ -2597,36 +2659,39 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
 }
 
 void
-Route::drop_listen (boost::shared_ptr<Route> route)
+Route::remove_aux_or_listen (boost::shared_ptr<Route> route)
 {
        ProcessorStreams err;
        ProcessorList::iterator tmp;
 
-       Glib::RWLock::ReaderLock rl(_processor_lock);
-       rl.acquire ();
-
-  again:
-       for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
-
-               boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-
-               if (d && d->target_route() == route) {
-                       rl.release ();
-                       remove_processor (*x, &err);
-                       rl.acquire ();
+       {
+               Glib::RWLock::ReaderLock rl(_processor_lock);
 
-                       /* list could have been demolished while we dropped the lock
-                          so start over.
-                       */
+               /* have to do this early because otherwise processor reconfig
+                * will put _monitor_send back in the list
+                */
 
-                       goto again;
+               if (route == _session.monitor_out()) {
+                       _monitor_send.reset ();
                }
-       }
 
-       rl.release ();
+         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 ();
+                               remove_processor (*x, &err, false);
+                               rl.acquire ();
 
-       if (route == _session.monitor_out()) {
-               _monitor_send.reset ();
+                               /* list could have been demolished while we dropped the lock
+                                  so start over.
+                               */
+                               
+                               goto again;
+                       }
+               }
        }
 }
 
@@ -2757,15 +2822,61 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_lo
        _roll_delay = _initial_delay;
 }
 
-/** Called with the process lock held if change contains ConfigurationChanged */
 void
 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 
+               */
+               need_to_queue_solo_change = false;
                configure_processors (0);
                _phase_invert.resize (_input->n_ports().n_audio ());
                io_changed (); /* EMIT SIGNAL */
        }
+
+       if (!_input->connected() && _soloed_by_others_upstream) {
+               if (need_to_queue_solo_change) {
+                       _session.cancel_solo_after_disconnect (shared_from_this(), true);
+               } else {
+                       cancel_solo_after_disconnect (true);
+               }
+       }
+}
+
+void
+Route::output_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 
+               */
+               need_to_queue_solo_change = false;
+       }
+
+       if (!_output->connected() && _soloed_by_others_downstream) {
+               if (need_to_queue_solo_change) {
+                       _session.cancel_solo_after_disconnect (shared_from_this(), false);
+               } else {
+                       cancel_solo_after_disconnect (false);
+               }
+       }
+}
+
+void
+Route::cancel_solo_after_disconnect (bool upstream)
+{
+       if (upstream) {
+               _soloed_by_others_upstream = 0;
+       } else {
+               _soloed_by_others_downstream = 0;
+       }
+       set_mute_master_solo ();
+       solo_changed (false, this);
 }
 
 uint32_t