update processor in-place mode when pin-mapping changes
[ardour.git] / libs / ardour / plugin_insert.cc
index 3f1146c861c79ff7d5014b9379502b01bb0cefac..cdf1e600a8451bfbf6f6e93e4b0a19b2347a29f1 100644 (file)
@@ -79,6 +79,10 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug)
        if (plug) {
                add_plugin (plug);
                create_automatable_parameters ();
+               const ChanCount& sc (sidechain_input_pins ());
+               if (sc.n_total () > 0) {
+                       add_sidechain (sc.n_audio ());
+               }
        }
 }
 
@@ -290,15 +294,31 @@ PluginInsert::internal_input_streams() const
 ChanCount
 PluginInsert::natural_output_streams() const
 {
+#ifdef MIXBUS
+       if (is_channelstrip ()) {
+               return _configured_out;
+       }
+#endif
        return _plugins[0]->get_info()->n_outputs;
 }
 
 ChanCount
 PluginInsert::natural_input_streams() const
 {
+#ifdef MIXBUS
+       if (is_channelstrip ()) {
+               return _configured_in;
+       }
+#endif
        return _plugins[0]->get_info()->n_inputs;
 }
 
+ChanCount
+PluginInsert::sidechain_input_pins() const
+{
+       return _cached_sidechain_pins;
+}
+
 bool
 PluginInsert::has_no_inputs() const
 {
@@ -448,8 +468,13 @@ PluginInsert::flush ()
 void
 PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now)
 {
+       // TODO: atomically copy maps & _no_inplace
        PinMappings in_map (_in_map);
        PinMappings out_map (_out_map);
+       if (_mapping_changed) {
+               _no_inplace = check_inplace ();
+               _mapping_changed = false;
+       }
 
 #if 1
        // TODO optimize special case.
@@ -530,7 +555,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
 
        }
 #ifdef MIXBUS
-       if (_plugins.front()->is_channelstrip() ) {
+       if (is_channelstrip ()) {
                if (_configured_in.n_audio() > 0) {
                        ChanMapping mb_in_map (ChanCount::min (_configured_in, ChanCount (DataType::AUDIO, 2)));
                        ChanMapping mb_out_map (ChanCount::min (_configured_out, ChanCount (DataType::AUDIO, 2)));
@@ -928,6 +953,7 @@ PluginInsert::set_input_map (uint32_t num, ChanMapping m) {
                sanitize_maps ();
                if (changed) {
                        PluginMapChanged (); /* EMIT SIGNAL */
+                       _mapping_changed = true;
                }
        }
 }
@@ -940,6 +966,7 @@ PluginInsert::set_output_map (uint32_t num, ChanMapping m) {
                sanitize_maps ();
                if (changed) {
                        PluginMapChanged (); /* EMIT SIGNAL */
+                       _mapping_changed = true;
                }
        }
 }
@@ -985,12 +1012,48 @@ PluginInsert::output_map () const
 bool
 PluginInsert::has_midi_bypass () const
 {
-       if (_configured_in.n_midi () == 1 && _configured_out.n_midi () == 1 && natural_output_streams ().n_midi () == 0) {
+       if (_configured_in.n_midi () == 1 && _configured_out.n_midi () == 1
+                       && natural_output_streams ().n_midi () == 0) {
+               return true;
+       }
+       return false;
+}
+
+bool
+PluginInsert::has_midi_thru () const
+{
+       if (_configured_in.n_midi () == 1 && _configured_out.n_midi () == 1
+                       && natural_input_streams ().n_midi () == 0 && natural_output_streams ().n_midi () == 0) {
                return true;
        }
        return false;
 }
 
+#ifdef MIXBUS
+bool
+PluginInsert::is_channelstrip () const {
+       return _plugins.front()->is_channelstrip();
+}
+#endif
+
+bool
+PluginInsert::check_inplace ()
+{
+       // auto-detect if inplace processing is possible
+       bool inplace_ok = true;
+       for (uint32_t pc = 0; pc < get_count() && inplace_ok ; ++pc) {
+               if (!_in_map[pc].is_monotonic ()) {
+                       inplace_ok = false;
+               }
+               if (!_out_map[pc].is_monotonic ()) {
+                       inplace_ok = false;
+               }
+       }
+       bool no_inplace = !inplace_ok || _plugins.front()->inplace_broken ();
+       DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("%1 %2\n", name(), no_inplace ? "No Inplace Processing" : "In-Place"));
+       return no_inplace;
+}
+
 bool
 PluginInsert::sanitize_maps ()
 {
@@ -1058,26 +1121,39 @@ PluginInsert::reset_map (bool emit)
        _in_map.clear ();
        _out_map.clear ();
        for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
+               ChanCount ns_inputs  = natural_input_streams() - sidechain_input_pins ();
                if (_match.method == Split) {
                        _in_map[pc] = ChanMapping ();
-                       /* connect inputs in round-robin fashion */
+                       /* connect no sidechain sinks in round-robin fashion */
                        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-                               const uint32_t cend = _configured_internal.get (*t);
+                               const uint32_t cend = _configured_in.get (*t);
                                if (cend == 0) { continue; }
                                uint32_t c = 0;
-                               for (uint32_t in = 0; in < natural_input_streams().get (*t); ++in) {
+                               for (uint32_t in = 0; in < ns_inputs.get (*t); ++in) {
                                        _in_map[pc].set (*t, in, c);
-                                       c = c + 1 % cend;
+                                       c = (c + 1) % cend;
                                }
                        }
                } else {
-                       _in_map[pc] = ChanMapping (ChanCount::min (natural_input_streams (), _configured_internal));
+                       _in_map[pc] = ChanMapping (ChanCount::min (ns_inputs, _configured_in));
                }
                _out_map[pc] = ChanMapping (ChanCount::min (natural_output_streams(), _configured_out));
 
                for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-                       _in_map[pc].offset_to(*t, pc * natural_input_streams().get(*t));
+                       const uint32_t nis = natural_input_streams ().get(*t);
+                       const uint32_t stride = nis - sidechain_input_pins().get (*t);
+                       _in_map[pc].offset_to(*t, stride * pc);
                        _out_map[pc].offset_to(*t, pc * natural_output_streams().get(*t));
+
+                       // connect side-chains
+                       const uint32_t sc_start = _configured_in.get (*t);
+                       const uint32_t sc_len = _configured_internal.get (*t) - sc_start;
+                       if (sc_len == 0) { continue; }
+                       uint32_t c = 0;
+                       for (uint32_t in = ns_inputs.get (*t); in < nis; ++in) {
+                               _in_map[pc].set (*t, in, sc_start + c);
+                               c = (c + 1) % sc_len;
+                       }
                }
        }
        sanitize_maps ();
@@ -1086,6 +1162,7 @@ PluginInsert::reset_map (bool emit)
        }
        if (emit) {
                PluginMapChanged (); /* EMIT SIGNAL */
+               _mapping_changed = true;
        }
        return true;
 }
@@ -1188,6 +1265,9 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
        } else if (_match.custom_cfg && _configured) {
                mapping_changed = sanitize_maps ();
        } else {
+#ifdef MIXBUS
+               if (is_channelstrip ()) { _maps_from_state = false; }
+#endif
                if (_maps_from_state) {
                        _maps_from_state = false;
                        mapping_changed = true;
@@ -1222,17 +1302,9 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
 #endif
        }
 
-       // auto-detect if inplace processing is possible
-       bool inplace_ok = true;
-       for (uint32_t pc = 0; pc < get_count() && inplace_ok ; ++pc) {
-               if (!_in_map[pc].is_monotonic ()) {
-                       inplace_ok = false;
-               }
-               if (!_out_map[pc].is_monotonic ()) {
-                       inplace_ok = false;
-               }
-       }
-       _no_inplace = !inplace_ok || _plugins.front()->inplace_broken ();
+       _no_inplace = check_inplace ();
+       _mapping_changed = false;
+       DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("%1 %2\n", name(), _no_inplace ? "No Inplace Processing" : "In-Place"));
 
        if (old_in != in || old_out != out || old_internal != _configured_internal
                        || (old_match.method != _match.method && (old_match.method == Split || _match.method == Split))
@@ -1284,6 +1356,13 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
                return Match();
        }
 
+#ifdef MIXBUS
+       if (is_channelstrip ()) {
+               out = inx;
+               return Match (ExactMatch, 1);
+       }
+#endif
+
        /* if a user specified a custom cfg, so be it. */
        if (_custom_cfg) {
                out = _custom_out;
@@ -1318,17 +1397,15 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
                                         * at least enough to feed every output port. */
                                        uint32_t f = 1; // at least one. e.g. control data filters, no in, no out.
                                        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-                                               uint32_t nin = inputs.get (*t);
-                                               if (nin == 0 || inx.get(*t) == 0) { continue; }
-                                               f = max (f, (uint32_t) ceil (inx.get(*t) / (float)nin));
+                                               uint32_t nout = outputs.get (*t);
+                                               if (nout == 0 || inx.get(*t) == 0) { continue; }
+                                               f = max (f, (uint32_t) ceil (inx.get(*t) / (float)nout));
                                        }
                                        out = inx;
                                        DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("special case strict-i/o generator: %1\n", name()));
                                        return Match (Replicate, f, _strict_io);
                                }
                                break;
-                       case Split:
-                               break;
                        default:
                                break;
                }
@@ -1341,16 +1418,19 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
                return m;
        }
 
+       ChanCount ns_inputs  = inputs - sidechain_input_pins ();
+
        DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("resolving 'Impossible' match for %1\n", name()));
 
        if (info->reconfigurable_io()) {
                ChanCount useins;
+               // TODO add sidechains here
                bool const r = _plugins.front()->can_support_io_configuration (inx, out, &useins);
                if (!r) {
                        // houston, we have a problem.
                        return Match (Impossible, 0);
                }
-               return Match (Delegate, 1);
+               return Match (Delegate, 1, _strict_io);
        }
 
        ChanCount midi_bypass;
@@ -1358,10 +1438,10 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
                midi_bypass.set (DataType::MIDI, 1);
        }
 
-       // add at least as many plugins so that output count matches input count
+       // add at least as many plugins so that output count matches input count (w/o sidechain pins)
        uint32_t f = 0;
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-               uint32_t nin = inputs.get (*t);
+               uint32_t nin = ns_inputs.get (*t);
                uint32_t nout = outputs.get (*t);
                if (nin == 0 || inx.get(*t) == 0) { continue; }
                // prefer floor() so the count won't overly increase IFF (nin < nout)
@@ -1369,7 +1449,19 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
        }
        if (f > 0 && outputs * f >= _configured_out) {
                out = outputs * f + midi_bypass;
-               return Match (Replicate, f);
+               return Match (Replicate, f, _strict_io);
+       }
+
+       // add at least as many plugins needed to connect all inputs (w/o sidechain pins)
+       f = 0;
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               uint32_t nin = ns_inputs.get (*t);
+               if (nin == 0 || inx.get(*t) == 0) { continue; }
+               f = max (f, (uint32_t) ceil (inx.get(*t) / (float)nin));
+       }
+       if (f > 0) {
+               out = outputs * f + midi_bypass;
+               return Match (Replicate, f, _strict_io);
        }
 
        // add at least as many plugins needed to connect all inputs
@@ -1380,7 +1472,7 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
                f = max (f, (uint32_t) ceil (inx.get(*t) / (float)nin));
        }
        out = outputs * f + midi_bypass;
-       return Match (Replicate, f);
+       return Match (Replicate, f, _strict_io);
 }
 
 /* this is the original Ardour 3/4 behavior, mainly for backwards compatibility */
@@ -1406,6 +1498,7 @@ PluginInsert::automatic_can_support_io_configuration (ChanCount const & inx, Cha
 
        ChanCount inputs  = info->n_inputs;
        ChanCount outputs = info->n_outputs;
+       ChanCount ns_inputs  = inputs - sidechain_input_pins ();
 
        if (in.get(DataType::MIDI) == 1 && outputs.get(DataType::MIDI) == 0) {
                DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("bypassing midi-data around %1\n", name()));
@@ -1416,6 +1509,9 @@ PluginInsert::automatic_can_support_io_configuration (ChanCount const & inx, Cha
                in.set(DataType::MIDI, 0);
        }
 
+       // add internally provided sidechain ports
+       ChanCount insc = in + sidechain_input_ports ();
+
        bool no_inputs = true;
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                if (inputs.get (*t) != 0) {
@@ -1430,8 +1526,14 @@ PluginInsert::automatic_can_support_io_configuration (ChanCount const & inx, Cha
                return Match (NoInputs, 1);
        }
 
-       /* Plugin inputs match requested inputs exactly */
-       if (inputs == in) {
+       /* Plugin inputs match requested inputs + side-chain-ports exactly */
+       if (inputs == insc) {
+               out = outputs + midi_bypass;
+               return Match (ExactMatch, 1);
+       }
+
+       /* Plugin inputs matches without side-chain-pins */
+       if (ns_inputs == in) {
                out = outputs + midi_bypass;
                return Match (ExactMatch, 1);
        }
@@ -1444,9 +1546,10 @@ PluginInsert::automatic_can_support_io_configuration (ChanCount const & inx, Cha
 
        uint32_t f             = 0;
        bool     can_replicate = true;
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+       for (DataType::iterator t = DataType::begin(); t != DataType::end() && can_replicate; ++t) {
 
-               uint32_t nin = inputs.get (*t);
+               // ignore side-chains
+               uint32_t nin = ns_inputs.get (*t);
 
                // No inputs of this type
                if (nin == 0 && in.get(*t) == 0) {
@@ -1489,7 +1592,7 @@ PluginInsert::automatic_can_support_io_configuration (ChanCount const & inx, Cha
        bool can_split = true;
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
 
-               bool const can_split_type = (in.get (*t) == 1 && inputs.get (*t) > 1);
+               bool const can_split_type = (in.get (*t) == 1 && ns_inputs.get (*t) > 1);
                bool const nothing_to_do_for_type = (in.get (*t) == 0 && inputs.get (*t) == 0);
 
                if (!can_split_type && !nothing_to_do_for_type) {
@@ -2176,14 +2279,23 @@ PluginInsert::add_plugin (boost::shared_ptr<Plugin> plugin)
        plugin->set_insert_id (this->id());
 
        if (_plugins.empty()) {
-                /* first (and probably only) plugin instance - connect to relevant signals
-                 */
+               /* first (and probably only) plugin instance - connect to relevant signals */
 
                plugin->ParameterChangedExternally.connect_same_thread (*this, boost::bind (&PluginInsert::parameter_changed_externally, this, _1, _2));
-                plugin->StartTouch.connect_same_thread (*this, boost::bind (&PluginInsert::start_touch, this, _1));
-                plugin->EndTouch.connect_same_thread (*this, boost::bind (&PluginInsert::end_touch, this, _1));
+               plugin->StartTouch.connect_same_thread (*this, boost::bind (&PluginInsert::start_touch, this, _1));
+               plugin->EndTouch.connect_same_thread (*this, boost::bind (&PluginInsert::end_touch, this, _1));
+               // cache sidechain ports
+               _cached_sidechain_pins.reset ();
+               const ChanCount& nis (plugin->get_info()->n_inputs);
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       for (uint32_t in = 0; in < nis.get (*t); ++in) {
+                               const Plugin::IOPortDescription& iod (plugin->describe_io_port (*t, true, in));
+                               if (iod.is_sidechain) {
+                                       _cached_sidechain_pins.set (*t, 1 + _cached_sidechain_pins.n(*t));
+                               }
+                       }
+               }
        }
-
        _plugins.push_back (plugin);
 }