allow to customize variable i/o plugin inputs
[ardour.git] / libs / ardour / route.cc
index a53fdee416cf2dde77ab5e2cd814e0392adb8809..5bbb9d3562e29ab93bcb317ca90b806a94066e11 100644 (file)
@@ -1339,9 +1339,26 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 
+       boost::shared_ptr<Send> send;
+       if ((send = boost::dynamic_pointer_cast<Send> (processor))) {
+               send->SelfDestruct.connect_same_thread (*this,
+                               boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
+       }
+
        return 0;
 }
 
+void
+Route::processor_selfdestruct (boost::weak_ptr<Processor> wp)
+{
+       /* We cannot destruct the processor here (usually RT-thread
+        * with various locks held - in case of sends also io_locks).
+        * Queue for deletion in low-priority thread.
+        */
+       Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+       selfdestruct_sequence.push_back (wp);
+}
+
 bool
 Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
@@ -2083,7 +2100,8 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
 
                        if (boost::dynamic_pointer_cast<Delivery> (*p)
                                        && boost::dynamic_pointer_cast<Delivery> (*p)->role() == Delivery::Main
-                                       && _strict_io) {
+                                       && !(is_monitor() || is_auditioner())
+                                       && ( _strict_io || Profile->get_mixbus ())) {
                                /* with strict I/O the panner + output are forced to
                                 * follow the last processor's output.
                                 *
@@ -2505,15 +2523,54 @@ Route::add_remove_sidechain (boost::shared_ptr<Processor> proc, bool add)
        return true;
 }
 
+bool
+Route::plugin_preset_output (boost::shared_ptr<Processor> proc, ChanCount outs)
+{
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+               const ChanCount& old (pi->preset_out ());
+               if (!pi->set_preset_out (outs)) {
+                       return true; // no change, OK
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               if (c.empty()) {
+                       /* not possible */
+                       pi->set_preset_out (old);
+                       return false;
+               }
+               configure_processors_unlocked (0);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
 bool
 Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
 {
        ChanCount unused;
-       return customize_plugin_insert (proc, 0, unused);
+       return customize_plugin_insert (proc, 0, unused, unused);
 }
 
 bool
-Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs, ChanCount sinks)
 {
        boost::shared_ptr<PluginInsert> pi;
        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
@@ -2532,9 +2589,10 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
 
-               bool      old_cust = pi->custom_cfg ();
-               uint32_t  old_cnt  = pi->get_count ();
-               ChanCount old_chan = pi->output_streams ();
+               bool      old_cust  = pi->custom_cfg ();
+               uint32_t  old_cnt   = pi->get_count ();
+               ChanCount old_chan  = pi->output_streams ();
+               ChanCount old_sinks = pi->natural_input_streams ();
 
                if (count == 0) {
                        pi->set_custom_cfg (false);
@@ -2542,6 +2600,7 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                        pi->set_custom_cfg (true);
                        pi->set_count (count);
                        pi->set_outputs (outs);
+                       pi->set_sinks (sinks);
                }
 
                list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
@@ -2549,6 +2608,7 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                        /* not possible */
 
                        pi->set_count (old_cnt);
+                       pi->set_sinks (old_sinks);
                        pi->set_outputs (old_chan);
                        pi->set_custom_cfg (old_cust);
 
@@ -2565,6 +2625,8 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
 bool
 Route::set_strict_io (const bool enable)
 {
+       Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+
        if (_strict_io != enable) {
                _strict_io = enable;
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
@@ -2590,10 +2652,9 @@ Route::set_strict_io (const bool enable)
                }
                lm.release ();
 
-               {
-                       Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
-                       configure_processors (0);
-               }
+               configure_processors (0);
+               lx.release ();
+
                processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
                _session.set_dirty ();
        }
@@ -3321,6 +3382,9 @@ Route::set_processor_state (const XMLNode& node)
                                } else if (prop->value() == "send") {
 
                                        processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
+                                       boost::shared_ptr<Send> send = boost::dynamic_pointer_cast<Send> (processor);
+                                       send->SelfDestruct.connect_same_thread (*this,
+                                                       boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
 
                                } else {
                                        error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
@@ -3896,18 +3960,13 @@ Route::output_change_handler (IOChange change, void * /*src*/)
 }
 
 void
-Route::sidechain_change_handler (IOChange change, void * /*src*/)
+Route::sidechain_change_handler (IOChange change, void* src)
 {
        if (_initial_io_setup || _in_sidechain_setup) {
                return;
        }
 
-       if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change
-                  contains ConfigurationChanged
-               */
-               configure_processors (0);
-       }
+       input_change_handler (change, src);
 }
 
 uint32_t
@@ -4067,13 +4126,12 @@ Route::apply_processor_changes_rt ()
                g_atomic_int_set (&_pending_signals, emissions);
                return true;
        }
-       return false;
+       return (!selfdestruct_sequence.empty ());
 }
 
 void
 Route::emit_pending_signals ()
 {
-
        int sig = g_atomic_int_and (&_pending_signals, 0);
        if (sig & EmitMeterChanged) {
                _meter->emit_configuration_changed();
@@ -4087,6 +4145,24 @@ Route::emit_pending_signals ()
        if (sig & EmitRtProcessorChange) {
                processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */
        }
+
+       /* this would be a job for the butler.
+        * Conceptually we should not take processe/processor locks here.
+        * OTOH its more efficient (less overhead for summoning the butler and
+        * telling her what do do) and signal emission is called
+        * directly after the process callback, which decreases the chance
+        * of x-runs when taking the locks.
+        */
+       while (!selfdestruct_sequence.empty ()) {
+               Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+               if (selfdestruct_sequence.empty ()) { break; } // re-check with lock
+               boost::shared_ptr<Processor> proc = selfdestruct_sequence.back ().lock ();
+               selfdestruct_sequence.pop_back ();
+               lx.release ();
+               if (proc) {
+                       remove_processor (proc);
+               }
+       }
 }
 
 void