prepare plugin configuration and replacing instruments
[ardour.git] / libs / ardour / plugin_insert.cc
index de67acc0b8a0fc47dd365f00c4ac44c66c173d76..a72412a86875fbc99d99520249287265802b2da1 100644 (file)
@@ -106,9 +106,14 @@ PluginInsert::set_count (uint32_t num)
 {
        bool require_state = !_plugins.empty();
 
+       if (require_state && num > 1 && plugin (0)->get_info ()->type == ARDOUR::AudioUnit) {
+               // we don't allow to replicate AUs
+               return false;
+       }
+
        /* this is a bad idea.... we shouldn't do this while active.
-          only a route holding their redirect_lock should be calling this
-       */
+        * only a route holding their redirect_lock should be calling this
+        */
 
        if (num == 0) {
                return false;
@@ -142,6 +147,13 @@ PluginInsert::set_count (uint32_t num)
 }
 
 
+void
+PluginInsert::set_sinks (const ChanCount& c)
+{
+       _custom_sinks = c;
+       /* no signal, change will only be visible after re-config */
+}
+
 void
 PluginInsert::set_outputs (const ChanCount& c)
 {
@@ -363,6 +375,35 @@ PluginInsert::needs_midi_input() const
        return pip->n_inputs.n_midi() != 0 && pip->n_outputs.n_audio() != 0;
 }
 
+bool
+PluginInsert::has_output_presets (ChanCount in, ChanCount out)
+{
+       if (!_configured && _plugins[0]->get_info ()->reconfigurable_io ()) {
+               // collect possible configurations, prefer given in/out
+               _plugins[0]->can_support_io_configuration (in, out);
+       }
+
+       PluginOutputConfiguration ppc (_plugins[0]->possible_output ());
+
+       if (ppc.size () == 0) {
+               return false;
+       }
+       if (!strict_io () && ppc.size () == 1) {
+               return false;
+       }
+
+       if (strict_io () && ppc.size () == 1) {
+               // "stereo" is currently preferred default for instruments
+               if (ppc.find (2) != ppc.end ()) {
+                       return false;
+               }
+       }
+       if (!needs_midi_input ()) {
+                       return false;
+       }
+       return true;
+}
+
 void
 PluginInsert::create_automatable_parameters ()
 {
@@ -485,6 +526,39 @@ PluginInsert::flush ()
        }
 }
 
+void
+PluginInsert::inplace_silence_unconnected (BufferSet& bufs, const PinMappings& out_map, framecnt_t nframes, framecnt_t offset) const
+{
+       // TODO optimize: store "unconnected" in a fixed set.
+       // it only changes on reconfiguration.
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               for (uint32_t out = 0; out < bufs.count().get (*t); ++out) {
+                       bool mapped = false;
+                       if (*t == DataType::MIDI && out == 0 && has_midi_bypass ()) {
+                               mapped = true; // in-place Midi bypass
+                       }
+                       for (uint32_t pc = 0; pc < get_count() && !mapped; ++pc) {
+                               PinMappings::const_iterator i = out_map.find (pc);
+                               if (i == out_map.end ()) {
+                                       continue;
+                               }
+                               const ChanMapping& outmap (i->second);
+                               for (uint32_t o = 0; o < natural_output_streams().get (*t); ++o) {
+                                       bool valid;
+                                       uint32_t idx = outmap.get (*t, o, &valid);
+                                       if (valid && idx == out) {
+                                               mapped = true;
+                                               break;
+                                       }
+                               }
+                       }
+                       if (!mapped) {
+                               bufs.get (*t, out).silence (nframes, offset);
+                       }
+               }
+       }
+}
+
 void
 PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now)
 {
@@ -712,30 +786,8 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                                deactivate ();
                        }
                }
-
-               // TODO optimize: store "unconnected" in a fixed set.
-               // it only changes on reconfiguration.
-               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-                       for (uint32_t out = 0; out < bufs.count().get (*t); ++out) {
-                               bool mapped = false;
-                               if (*t == DataType::MIDI && out == 0 && has_midi_bypass ()) {
-                                       mapped = true; // in-place Midi bypass
-                               }
-                               for (uint32_t pc = 0; pc < get_count() && !mapped; ++pc) {
-                                       for (uint32_t o = 0; o < natural_output_streams().get (*t); ++o) {
-                                               bool valid;
-                                               uint32_t idx = out_map[pc].get (*t, o, &valid);
-                                               if (valid && idx == out) {
-                                                       mapped = true;
-                                                       break;
-                                               }
-                                       }
-                               }
-                               if (!mapped) {
-                                       bufs.get (*t, out).silence (nframes, offset);
-                               }
-                       }
-               }
+               // now silence unconnected outputs
+               inplace_silence_unconnected (bufs, _out_map, nframes, offset);
        }
 
        if (collect_signal_nframes > 0) {
@@ -765,6 +817,117 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
        }
 }
 
+void
+PluginInsert::bypass (BufferSet& bufs, pframes_t nframes)
+{
+       /* bypass the plugin(s) not the whole processor.
+        * -> use mappings just like connect_and_run
+        */
+
+       // TODO: atomically copy maps & _no_inplace
+       ChanMapping in_map (input_map ());
+       ChanMapping out_map (output_map ());
+       if (_mapping_changed) {
+               _no_inplace = check_inplace ();
+               _mapping_changed = false;
+       }
+
+       bufs.set_count(ChanCount::max(bufs.count(), _configured_internal));
+       bufs.set_count(ChanCount::max(bufs.count(), _configured_out));
+
+       if (_no_inplace) {
+               ChanMapping thru_map (_thru_map);
+
+               BufferSet& inplace_bufs  = _session.get_noinplace_buffers();
+               // copy all inputs
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       for (uint32_t in = 0; in < _configured_internal.get (*t); ++in) {
+                               inplace_bufs.get (*t, in).read_from (bufs.get (*t, in), nframes, 0, 0);
+                       }
+               }
+               ARDOUR::ChanMapping used_outputs;
+               // copy thru
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       for (uint32_t out = 0; out < _configured_out.get (*t); ++out) {
+                               bool valid;
+                               uint32_t in_idx = thru_map.get (*t, out, &valid);
+                               if (valid) {
+                                       bufs.get (*t, out).read_from (inplace_bufs.get (*t, in_idx), nframes, 0, 0);
+                                       used_outputs.set (*t, out, 1); // mark as used
+                               }
+                       }
+               }
+               // plugin no-op: assume every plugin has an internal identity map
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       for (uint32_t out = 0; out < _configured_out.get (*t); ++out) {
+                               bool valid;
+                               uint32_t src_idx = out_map.get_src (*t, out, &valid);
+                               if (!valid) {
+                                       continue;
+                               }
+                               uint32_t in_idx = in_map.get (*t, src_idx, &valid);
+                               if (!valid) {
+                                       continue;
+                               }
+                               bufs.get (*t, out).read_from (inplace_bufs.get (*t, in_idx), nframes, 0, 0);
+                               used_outputs.set (*t, out, 1); // mark as used
+                       }
+               }
+               // now silence all unused outputs
+               if (has_midi_bypass ()) {
+                       used_outputs.set (DataType::MIDI, 0, 1); // Midi bypass.
+               }
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       for (uint32_t out = 0; out < _configured_out.get (*t); ++out) {
+                               bool valid;
+                               used_outputs.get (*t, out, &valid);
+                               if (!valid) {
+                                               bufs.get (*t, out).silence (nframes, 0);
+                               }
+                       }
+               }
+       } else {
+               if (_match.method == Split) {
+                       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                               if (_configured_internal.get (*t) == 0) {
+                                       continue;
+                               }
+                               // copy/feeds _all_ *connected* inputs, copy the first buffer
+                               bool valid;
+                               uint32_t first_idx = in_map.get (*t, 0, &valid);
+                               assert (valid && first_idx == 0); // check_inplace ensures this
+                               for (uint32_t i = 1; i < natural_input_streams ().get (*t); ++i) {
+                                       uint32_t idx = in_map.get (*t, i, &valid);
+                                       if (valid) {
+                                               assert (idx == 0);
+                                               bufs.get (*t, i).read_from (bufs.get (*t, first_idx), nframes, 0, 0);
+                                       }
+                               }
+                       }
+               }
+
+               // apply output map and/or monotonic but not identity i/o mappings
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       for (uint32_t out = 0; out < _configured_out.get (*t); ++out) {
+                               bool valid;
+                               uint32_t src_idx = out_map.get_src (*t, out, &valid);
+                               if (!valid) {
+                                       bufs.get (*t, out).silence (nframes, 0);
+                                       continue;
+                               }
+                               uint32_t in_idx = in_map.get (*t, src_idx, &valid);
+                               if (!valid) {
+                                       bufs.get (*t, out).silence (nframes, 0);
+                                       continue;
+                               }
+                               if (in_idx != src_idx) {
+                                       bufs.get (*t, out).read_from (bufs.get (*t, in_idx), nframes, 0, 0);
+                               }
+                       }
+               }
+       }
+}
+
 void
 PluginInsert::silence (framecnt_t nframes)
 {
@@ -808,35 +971,8 @@ PluginInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame
                }
 
        } else {
-               // TODO use mapping in bypassed mode ?!
-               // -> do we bypass the processor or the plugin
-
-               // TODO include sidechain??
-
-               uint32_t in = input_streams ().n_audio ();
-               uint32_t out = output_streams().n_audio ();
-
-               if (has_no_audio_inputs() || in == 0) {
-
-                       /* silence all (audio) outputs. Should really declick
-                        * at the transitions of "active"
-                        */
-
-                       for (uint32_t n = 0; n < out; ++n) {
-                               bufs.get_audio (n).silence (nframes);
-                       }
-
-               } else if (out > in) {
-
-                       /* not active, but something has make up for any channel count increase
-                        * for now , simply replicate last buffer
-                        */
-                       for (uint32_t n = in; n < out; ++n) {
-                               bufs.get_audio(n).read_from(bufs.get_audio(in - 1), nframes);
-                       }
-               }
-
-               bufs.count().set_audio (out);
+               bypass (bufs, nframes);
+               _delaybuffers.flush ();
        }
 
        _active = _pending_active;
@@ -1056,6 +1192,20 @@ PluginInsert::set_thru_map (ChanMapping m) {
        }
 }
 
+bool
+PluginInsert::pre_seed (const ChanCount& in, const ChanCount& out,
+               const ChanMapping& im, const ChanMapping& om, const ChanMapping& tm)
+{
+       if (_configured) { return false; }
+       _configured_in = in;
+       _configured_out = out;
+       _in_map[0] = im;
+       _out_map[0] = om;
+       _thru_map = tm;
+       _maps_from_state = in.n_total () > 0 && out.n_total () > 0;
+       return true;
+}
+
 ChanMapping
 PluginInsert::input_map () const
 {
@@ -1368,12 +1518,12 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
        ChanCount old_in;
        ChanCount old_internal;
        ChanCount old_out;
+       ChanCount old_pins;
 
-       if (_configured) {
-               old_in = _configured_in;
-               old_internal = _configured_internal;
-               old_out = _configured_out;
-       }
+       old_pins = natural_input_streams();
+       old_in = _configured_in;
+       old_out = _configured_out;
+       old_internal = _configured_internal;
 
        _configured_in = in;
        _configured_internal = in;
@@ -1424,8 +1574,12 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
                break;
        case Delegate:
                {
-                       ChanCount dout (in); // hint
+                       ChanCount din (_configured_internal);
+                       ChanCount dout (din); // hint
                        if (_custom_cfg) {
+                               if (_custom_sinks.n_total () > 0) {
+                                       din = _custom_sinks;
+                               }
                                dout = _custom_out;
                        } else if (_preset_out.n_audio () > 0) {
                                dout.set (DataType::AUDIO, _preset_out.n_audio ());
@@ -1434,10 +1588,11 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
                        }
                        if (out.n_audio () == 0) { out.set (DataType::AUDIO, 1); }
                        ChanCount useins;
-                       bool const r = _plugins.front()->can_support_io_configuration (in, dout, &useins);
+                       DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("%1: Delegate lookup : %2 %3\n", name(), din, dout));
+                       bool const r = _plugins.front()->can_support_io_configuration (din, dout, &useins);
                        assert (r);
                        if (useins.n_audio() == 0) {
-                               useins = in;
+                               useins = din;
                        }
                        DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("%1: Delegate configuration: %2 %3\n", name(), useins, dout));
 
@@ -1446,6 +1601,9 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
                                _configured = false;
                                return false;
                        }
+                       if (!_custom_cfg) {
+                               _custom_sinks = din;
+                       }
                }
                break;
        default:
@@ -1457,13 +1615,15 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
                break;
        }
 
-       DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("%1: cfg:%2 state:%3 chn-in:%4 chn-out:%5 match:%6 size-in:%7 size-out:%8\n",
+       DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("%1: cfg:%2 state:%3 chn-in:%4 chn-out:%5 inpin:%6 match:%7 cust:%8 size-in:%9 size-out:%10\n",
                                name (),
                                _configured ? "Y" : "N",
                                _maps_from_state ? "Y" : "N",
                                old_in == in ? "==" : "!=",
                                old_out == out ? "==" : "!=",
+                               old_pins == natural_input_streams () ? "==" : "!=",
                                old_match.method == _match.method ? "==" : "!=",
+                               old_match.custom_cfg == _match.custom_cfg ? "==" : "!=",
                                _in_map.size() == get_count () ? "==" : "!=",
                                _out_map.size() == get_count () ? "==" : "!="
                                ));
@@ -1471,17 +1631,18 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
        bool mapping_changed = false;
        if (old_in == in && old_out == out
                        && _configured
+                       && old_pins == natural_input_streams ()
                        && old_match.method == _match.method
+                       && old_match.custom_cfg == _match.custom_cfg
                        && _in_map.size() == _out_map.size()
                        && _in_map.size() == get_count ()
                 ) {
                assert (_maps_from_state == false);
                /* If the configuration has not changed, keep the mapping */
-               if (old_internal != _configured_internal) {
-                       mapping_changed = sanitize_maps ();
-               }
+               mapping_changed = sanitize_maps ();
        } else if (_match.custom_cfg && _configured) {
                assert (_maps_from_state == false);
+               /* don't touch the map in manual mode */
                mapping_changed = sanitize_maps ();
        } else {
 #ifdef MIXBUS
@@ -1565,6 +1726,7 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
                        natural_input_streams () + ChanCount::max (_configured_out, natural_output_streams () * get_count ()));
 
        if (old_in != in || old_out != out || old_internal != _configured_internal
+                       || old_pins != natural_input_streams ()
                        || (old_match.method != _match.method && (old_match.method == Split || _match.method == Split))
                 ) {
                PluginIoReConfigure (); /* EMIT SIGNAL */
@@ -1646,7 +1808,7 @@ PluginInsert::internal_can_support_io_configuration (ChanCount const & inx, Chan
                PluginInfoPtr info = _plugins.front()->get_info();
                out = _custom_out;
                if (info->reconfigurable_io()) {
-                       return Match (Delegate, get_count(), _strict_io, true);
+                       return Match (Delegate, 1, _strict_io, true);
                } else {
                        return Match (ExactMatch, get_count(), _strict_io, true);
                }
@@ -1710,7 +1872,7 @@ PluginInsert::internal_can_support_io_configuration (ChanCount const & inx, Chan
                out = inx; // hint
                if (out.n_midi () > 0 && out.n_audio () == 0) { out.set (DataType::AUDIO, 2); }
                if (out.n_audio () == 0) { out.set (DataType::AUDIO, 1); }
-               bool const r = _plugins.front()->can_support_io_configuration (inx, out, &useins);
+               bool const r = _plugins.front()->can_support_io_configuration (inx + sidechain_input_pins (), out, &useins);
                if (!r) {
                        // houston, we have a problem.
                        return Match (Impossible, 0);
@@ -1779,7 +1941,7 @@ PluginInsert::automatic_can_support_io_configuration (ChanCount const & inx, Cha
                out = in; // hint
                if (out.n_midi () > 0 && out.n_audio () == 0) { out.set (DataType::AUDIO, 2); }
                if (out.n_audio () == 0) { out.set (DataType::AUDIO, 1); }
-               bool const r = _plugins.front()->can_support_io_configuration (in, out);
+               bool const r = _plugins.front()->can_support_io_configuration (in + sidechain_input_pins (), out);
                if (!r) {
                        return Match (Impossible, 0);
                }
@@ -1941,6 +2103,7 @@ PluginInsert::state (bool full)
        /* remember actual i/o configuration (for later placeholder
         * in case the plugin goes missing) */
        node.add_child_nocopy (* _configured_in.state (X_("ConfiguredInput")));
+       node.add_child_nocopy (* _custom_sinks.state (X_("CustomSinks")));
        node.add_child_nocopy (* _configured_out.state (X_("ConfiguredOutput")));
        node.add_child_nocopy (* _preset_out.state (X_("PresetOutput")));
 
@@ -1981,7 +2144,7 @@ PluginInsert::set_control_ids (const XMLNode& node, int version)
 
        for (iter = nlist.begin(); iter != nlist.end(); ++iter) {
                if ((*iter)->name() == Controllable::xml_node_name) {
-                       const XMLProperty* prop;
+                       XMLProperty const * prop;
 
                        uint32_t p = (uint32_t)-1;
 #ifdef LV2_SUPPORT
@@ -2022,7 +2185,7 @@ PluginInsert::set_state(const XMLNode& node, int version)
        XMLNodeList nlist = node.children();
        XMLNodeIterator niter;
        XMLPropertyList plist;
-       const XMLProperty *prop;
+       XMLProperty const * prop;
        ARDOUR::PluginType type;
 
        if ((prop = node.property ("type")) == 0) {
@@ -2094,6 +2257,19 @@ PluginInsert::set_state(const XMLNode& node, int version)
        }
 #endif
 
+       if (plugin == 0 && type == ARDOUR::Lua) {
+               /* unique ID (sha1 of script) was not found,
+                * load the plugin from the serialized version in the
+                * session-file instead.
+                */
+               boost::shared_ptr<LuaProc> lp (new LuaProc (_session.engine(), _session, ""));
+               XMLNode *ls = node.child (lp->state_node_name().c_str());
+               if (ls && lp) {
+                       lp->set_script_from_state (*ls);
+                       plugin = lp;
+               }
+       }
+
        if (plugin == 0) {
                error << string_compose(
                        _("Found a reference to a plugin (\"%1\") that is unknown.\n"
@@ -2103,15 +2279,6 @@ PluginInsert::set_state(const XMLNode& node, int version)
                return -1;
        }
 
-       if (type == ARDOUR::Lua) {
-               XMLNode *ls = node.child (plugin->state_node_name().c_str());
-               // we need to load the script to set the name and parameters.
-               boost::shared_ptr<LuaProc> lp = boost::dynamic_pointer_cast<LuaProc>(plugin);
-               if (ls && lp) {
-                       lp->set_script_from_state (*ls);
-               }
-       }
-
        // The name of the PluginInsert comes from the plugin, nothing else
        _name = plugin->get_info()->name;
 
@@ -2213,22 +2380,29 @@ PluginInsert::set_state(const XMLNode& node, int version)
        uint32_t out_maps = 0;
        XMLNodeList kids = node.children ();
        for (XMLNodeIterator i = kids.begin(); i != kids.end(); ++i) {
+               if ((*i)->name() == X_("ConfiguredInput")) {
+                       _configured_in = ChanCount(**i);
+               }
+               if ((*i)->name() == X_("CustomSinks")) {
+                       _custom_sinks = ChanCount(**i);
+               }
                if ((*i)->name() == X_("ConfiguredOutput")) {
                        _custom_out = ChanCount(**i);
+                       _configured_out = ChanCount(**i);
                }
                if ((*i)->name() == X_("PresetOutput")) {
                        _preset_out = ChanCount(**i);
                }
                if (strncmp ((*i)->name ().c_str(), X_("InputMap-"), 9) == 0) {
                        long pc = atol (&((*i)->name().c_str()[9]));
-                       if (pc >=0 && pc <= get_count()) {
+                       if (pc >= 0 && pc <= (long) get_count()) {
                                _in_map[pc] = ChanMapping (**i);
                                ++in_maps;
                        }
                }
                if (strncmp ((*i)->name ().c_str(), X_("OutputMap-"), 10) == 0) {
                        long pc = atol (&((*i)->name().c_str()[10]));
-                       if (pc >=0 && pc <= get_count()) {
+                       if (pc >= 0 && pc <= (long) get_count()) {
                                _out_map[pc] = ChanMapping (**i);
                                ++out_maps;
                        }
@@ -2293,7 +2467,7 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version)
                }
 
                XMLNodeList cnodes;
-               XMLProperty *cprop;
+               XMLProperty const * cprop;
                XMLNodeConstIterator iter;
                XMLNode *child;
                const char *port;
@@ -2587,6 +2761,7 @@ PluginInsert::add_plugin (boost::shared_ptr<Plugin> plugin)
                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->LatencyChanged.connect_same_thread (*this, boost::bind (&PluginInsert::latency_changed, this, _1, _2));
+               _custom_sinks = plugin->get_info()->n_inputs;
                // cache sidechain port count
                _cached_sidechain_pins.reset ();
                const ChanCount& nis (plugin->get_info()->n_inputs);
@@ -2608,6 +2783,18 @@ PluginInsert::add_plugin (boost::shared_ptr<Plugin> plugin)
        _plugins.push_back (plugin);
 }
 
+bool
+PluginInsert::load_preset (ARDOUR::Plugin::PresetRecord pr)
+{
+       bool ok = true;
+       for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+               if (! (*i)->load_preset (pr)) {
+                       ok = false;
+               }
+       }
+       return ok;
+}
+
 void
 PluginInsert::realtime_handle_transport_stopped ()
 {