Inrease the export "chunk size" to speed it up over 10% at least in some situations
[ardour.git] / libs / ardour / route.cc
index 6a7ddde48b9e6324798515d486a865cc10260cb7..83b1efce767a804016cb860883fc3932143064c2 100644 (file)
@@ -26,6 +26,8 @@
 #include <cassert>
 #include <algorithm>
 
+#include <boost/algorithm/string.hpp>
+
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
 #include "pbd/convert.h"
 #include "pbd/boost_debug.h"
 
-#include "evoral/Curve.hpp"
-
 #include "ardour/amp.h"
-#include "ardour/audio_port.h"
+#include "ardour/audio_buffer.h"
 #include "ardour/audioengine.h"
 #include "ardour/buffer.h"
 #include "ardour/buffer_set.h"
-#include "ardour/configuration.h"
-#include "ardour/cycle_timer.h"
+#include "ardour/capturing_processor.h"
 #include "ardour/debug.h"
 #include "ardour/delivery.h"
-#include "ardour/dB.h"
-#include "ardour/internal_send.h"
 #include "ardour/internal_return.h"
-#include "ardour/ladspa_plugin.h"
+#include "ardour/internal_send.h"
 #include "ardour/meter.h"
-#include "ardour/mix.h"
 #include "ardour/monitor_processor.h"
 #include "ardour/pannable.h"
-#include "ardour/panner.h"
 #include "ardour/panner_shell.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
 #include "ardour/processor.h"
-#include "ardour/profile.h"
 #include "ardour/route.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
 #include "ardour/session.h"
-#include "ardour/timestamps.h"
-#include "ardour/utils.h"
 #include "ardour/unknown_processor.h"
-#include "ardour/capturing_processor.h"
+#include "ardour/utils.h"
 
 #include "i18n.h"
 
@@ -145,6 +137,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));
@@ -431,6 +425,7 @@ Route::process_output_buffers (BufferSet& bufs,
 
        /* figure out if we're going to use gain automation */
        if (gain_automation_ok) {
+               _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
                _amp->setup_gain_automation (start_frame, end_frame, nframes);
        } else {
                _amp->apply_gain_automation (false);
@@ -511,23 +506,22 @@ 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 (boost::dynamic_pointer_cast<PeakMeter> (*i) && meter_already_run) {
+               if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
                        /* don't ::run() the meter, otherwise it will have its previous peak corrupted */
                        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()
-                                    << endl;
-                               abort ();
+                               DEBUG_TRACE (
+                                       DEBUG::Processors, string_compose (
+                                               "%1 bufs = %2 input for %3 = %4\n",
+                                               _name, bufs.count(), (*i)->name(), (*i)->input_streams()
+                                               )
+                                       );
+                               continue;
                        }
                }
 #endif
@@ -871,16 +865,17 @@ dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& p
 }
 #endif
 
-int
-Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
+/** Supposing that we want to insert a Processor at a given Placement, return
+ *  the processor to add the new one before (or 0 to add at the end).
+ */
+boost::shared_ptr<Processor>
+Route::before_processor_for_placement (Placement p)
 {
-       ProcessorList::iterator loc;
-
-       /* XXX this is not thread safe - we don't hold the lock across determining the iter
-          to add before and actually doing the insertion. dammit.
-       */
+       Glib::RWLock::ReaderLock lm (_processor_lock);
 
-       if (placement == PreFader) {
+       ProcessorList::iterator loc;
+       
+       if (p == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
                loc = find (_processors.begin(), _processors.end(), _amp);
        } else {
@@ -888,24 +883,20 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
                loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
-       return add_processor (processor, loc, err, activation_allowed);
+       return loc != _processors.end() ? *loc : boost::shared_ptr<Processor> ();
 }
 
-
-/** Add a processor to a route such that it ends up with a given index into the visible processors.
- *  @param index Index to add the processor at, or -1 to add at the end of the list.
+/** Supposing that we want to insert a Processor at a given index, return
+ *  the processor to add the new one before (or 0 to add at the end).
  */
-
-int
-Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index, ProcessorStreams* err, bool activation_allowed)
+boost::shared_ptr<Processor>
+Route::before_processor_for_index (int index)
 {
-       /* XXX this is not thread safe - we don't hold the lock across determining the iter
-          to add before and actually doing the insertion. dammit.
-       */
-
        if (index == -1) {
-               return add_processor (processor, _processors.end(), err, activation_allowed);
+               return boost::shared_ptr<Processor> ();
        }
+
+       Glib::RWLock::ReaderLock lm (_processor_lock);
        
        ProcessorList::iterator i = _processors.begin ();
        int j = 0;
@@ -916,15 +907,36 @@ Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index
                
                ++i;
        }
-       
-       return add_processor (processor, i, err, activation_allowed);
+
+       return (i != _processors.end() ? *i : boost::shared_ptr<Processor> ());
+}
+
+/** Add a processor either pre- or post-fader
+ *  @return 0 on success, non-0 on failure.
+ */
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
+{
+       return add_processor (processor, before_processor_for_placement (placement), err, activation_allowed);
+}
+
+
+/** Add a processor to a route such that it ends up with a given index into the visible processors.
+ *  @param index Index to add the processor at, or -1 to add at the end of the list.
+ *  @return 0 on success, non-0 on failure.
+ */
+int
+Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index, ProcessorStreams* err, bool activation_allowed)
+{
+       return add_processor (processor, before_processor_for_index (index), err, activation_allowed);
 }
 
 /** Add a processor to the route.
- *  @param iter an iterator in _processors; the new processor will be inserted immediately before this location.
+ *  @param before An existing processor in the list, or 0; the new processor will be inserted immediately before it (or at the end).
+ *  @return 0 on success, non-0 on failure.
  */
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
+Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<Processor> before, ProcessorStreams* err, bool activation_allowed)
 {
        assert (processor != _meter);
        assert (processor != _main_outs);
@@ -943,25 +955,32 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                boost::shared_ptr<PluginInsert> pi;
                boost::shared_ptr<PortInsert> porti;
 
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
-
                if (processor == _amp) {
-                       // Ensure only one amp is in the list at any time
-                       if (loc != _processors.end()) {
-                               if (iter == loc) { // Already in place, do nothing
+                       /* Ensure that only one amp is in the list at any time */
+                       ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor);
+                       if (check != _processors.end()) {
+                               if (before == _amp) {
+                                       /* Already in position; all is well */
                                        return 0;
-                               } else { // New position given, relocate
-                                       _processors.erase (loc);
+                               } else {
+                                       _processors.erase (check);
                                }
                        }
+               }
 
-               } else {
-                       if (loc != _processors.end()) {
-                               cerr << "ERROR: Processor added to route twice!" << endl;
+               assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
+
+               ProcessorList::iterator loc;
+               if (before) {
+                       /* inserting before a processor; find it */
+                       loc = find (_processors.begin(), _processors.end(), before);
+                       if (loc == _processors.end ()) {
+                               /* Not found */
                                return 1;
                        }
-
-                       loc = iter;
+               } else {
+                       /* inserting at end */
+                       loc = _processors.end ();
                }
 
                _processors.insert (loc, processor);
@@ -997,6 +1016,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                _output->set_user_latency (0);
        }
 
+       reset_instrument_info ();
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 
@@ -1145,6 +1165,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                _output->set_user_latency (0);
        }
 
+       reset_instrument_info ();
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 
@@ -1351,6 +1372,8 @@ Route::clear_processors (Placement p)
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 
+       reset_instrument_info ();
+
        if (!already_deleting) {
                _session.clear_deletion_in_progress();
        }
@@ -1359,6 +1382,11 @@ Route::clear_processors (Placement p)
 int
 Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, bool need_process_lock)
 {
+       // TODO once the export point can be configured properly, do something smarter here
+       if (processor == _capturing_processor) {
+               _capturing_processor.reset();
+       }
+
        /* these can never be removed */
 
        if (processor == _amp || processor == _meter || processor == _main_outs) {
@@ -1449,6 +1477,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                }
        }
 
+       reset_instrument_info ();
        processor->drop_references ();
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
@@ -1545,12 +1574,20 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
                (*i)->drop_references ();
        }
 
+       reset_instrument_info ();
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 
        return 0;
 }
 
+void
+Route::reset_instrument_info ()
+{
+       boost::shared_ptr<Processor> instr = the_instrument();
+       _instrument_info.set_internal_instrument (instr);
+}
+
 /** Caller must hold process lock */
 int
 Route::configure_processors (ProcessorStreams* err)
@@ -1873,6 +1910,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));
        }
 
@@ -2079,6 +2134,8 @@ Route::set_state (const XMLNode& node, int version)
                } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
                        if (prop->value() == "solo") {
                                _solo_control->set_state (*child, version);
+                       } else if (prop->value() == "mute") {
+                               _mute_control->set_state (*child, version);
                        }
 
                } else if (child->name() == X_("RemoteControl")) {
@@ -2116,7 +2173,9 @@ Route::set_state_2X (const XMLNode& node, int version)
        }
 
        if ((prop = node.property (X_("flags"))) != 0) {
-               _flags = Flag (string_2_enum (prop->value(), _flags));
+               string f = prop->value ();
+               boost::replace_all (f, "ControlOut", "MonitorOut");
+               _flags = Flag (string_2_enum (f, _flags));
        } else {
                _flags = Flag (0);
        }
@@ -2400,7 +2459,8 @@ Route::set_processor_state (const XMLNode& node)
                        }
                        _monitor_control->set_state (**niter, Stateful::current_state_version);
                } else if (prop->value() == "capture") {
-                       _capturing_processor.reset (new CapturingProcessor (_session));
+                       /* CapturingProcessor should never be restored, it's always
+                          added explicitly when needed */
                } else {
                        ProcessorList::iterator o;
 
@@ -2491,6 +2551,7 @@ Route::set_processor_state (const XMLNode& node)
                }
        }
 
+       reset_instrument_info ();
        processors_changed (RouteProcessorChange ());
        set_processor_positions ();
 }
@@ -2565,7 +2626,6 @@ 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) {
@@ -2598,10 +2658,10 @@ Route::enable_monitor_send ()
 
 /** Add an aux send to a route.
  *  @param route route to send to.
- *  @param placement placement for the send.
+ *  @param before Processor to insert before, or 0 to insert at the end.
  */
 int
-Route::add_aux_send (boost::shared_ptr<Route> route, Placement placement)
+Route::add_aux_send (boost::shared_ptr<Route> route, boost::shared_ptr<Processor> before)
 {
        assert (route != _session.monitor_out ());
 
@@ -2628,7 +2688,7 @@ Route::add_aux_send (boost::shared_ptr<Route> route, Placement placement)
                        listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
                }
 
-               add_processor (listener, placement);
+               add_processor (listener, before);
 
        } catch (failed_constructor& err) {
                return -1;
@@ -2801,15 +2861,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
@@ -2878,7 +2984,7 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in
                return 0;
        }
 
-       framecnt_t unused = 0;
+       framepos_t unused = 0;
 
        if ((nframes = check_initial_delay (nframes, unused)) == 0) {
                return 0;
@@ -2898,34 +3004,6 @@ Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*
        return 0;
 }
 
-bool
-Route::has_external_redirects () const
-{
-       // FIXME: what about sends? - they don't return a signal back to ardour?
-
-       boost::shared_ptr<const PortInsert> pi;
-
-       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-               if ((pi = boost::dynamic_pointer_cast<const PortInsert>(*i)) != 0) {
-
-                       for (PortSet::const_iterator port = pi->output()->ports().begin(); port != pi->output()->ports().end(); ++port) {
-
-                               string port_name = port->name();
-                               string client_name = port_name.substr (0, port_name.find(':'));
-
-                               /* only say "yes" if the redirect is actually in use */
-
-                               if (client_name != "ardour" && pi->active()) {
-                                       return true;
-                               }
-                       }
-               }
-       }
-
-       return false;
-}
-
 void
 Route::flush_processors ()
 {
@@ -3218,19 +3296,20 @@ Route::protect_automation ()
                (*i)->protect_automation();
 }
 
+/** @param declick 1 to set a pending declick fade-in,
+ *                -1 to set a pending declick fade-out
+ */
 void
 Route::set_pending_declick (int declick)
 {
        if (_declickable) {
-               /* this call is not allowed to turn off a pending declick unless "force" is true */
+               /* this call is not allowed to turn off a pending declick */
                if (declick) {
                        _pending_declick = declick;
                }
-               // cerr << _name << ": after setting to " << declick << " pending declick = " << _pending_declick << endl;
        } else {
                _pending_declick = 0;
        }
-
 }
 
 /** Shift automation forwards from a particular place, thereby inserting time.
@@ -3975,3 +4054,35 @@ Route::metering_state () const
 {
        return MeteringRoute;
 }
+
+bool
+Route::has_external_redirects () const
+{
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               /* 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;;
+               }
+       }
+
+       return false;
+}
+
+boost::shared_ptr<Processor>
+Route::the_instrument () const
+{
+       Glib::RWLock::WriterLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<PluginInsert>(*i)) {
+                       if ((*i)->input_streams().n_midi() > 0 &&
+                           (*i)->output_streams().n_audio() > 0) {
+                               return (*i);
+                       }
+               }
+       }
+       return boost::shared_ptr<Processor>();
+}