#include "ardour/luaproc.h"
#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
+#include "ardour/port.h"
#ifdef LV2_SUPPORT
#include "ardour/lv2_plugin.h"
for (uint32_t n = 0; n < diff; ++n) {
boost::shared_ptr<Plugin> p = plugin_factory (_plugins[0]);
add_plugin (p);
- if (active ()) {
- p->activate ();
- }
if (require_state) {
- /* XXX do something */
+ XMLNode& state = _plugins[0]->get_state ();
+ p->set_state (state, Stateful::loading_state_version);
+ }
+
+ if (active ()) {
+ p->activate ();
}
}
PluginConfigChanged (); /* EMIT SIGNAL */
}
}
+bool
+PluginInsert::set_preset_out (const ChanCount& c)
+{
+ bool changed = _preset_out != c;
+ _preset_out = c;
+ if (changed && !_custom_cfg) {
+ PluginConfigChanged (); /* EMIT SIGNAL */
+ }
+ return changed;
+}
+
bool
PluginInsert::add_sidechain (uint32_t n_audio)
{
PluginInfoPtr info = _plugins.front()->get_info();
if (info->reconfigurable_io()) {
- assert (_plugins.size() == 1);
in = _plugins.front()->input_streams();
} else {
in = info->n_inputs;
}
bool
-PluginInsert::is_midi_instrument() const
+PluginInsert::needs_midi_input() const
{
- /* XXX more finesse is possible here. VST plugins have a
- a specific "instrument" flag, for example.
- */
- PluginInfoPtr pi = _plugins[0]->get_info();
-
- return pi->n_inputs.n_midi() != 0 &&
- pi->n_outputs.n_audio() > 0;
+ PluginInfoPtr pip = _plugins[0]->get_info();
+ if (pip->needs_midi_input ()) {
+ return true;
+ }
+ return pip->n_inputs.n_midi() != 0 && pip->n_outputs.n_audio() != 0;
}
void
/* strip dead wood */
PinMappings new_ins;
PinMappings new_outs;
- ChanMapping new_thru (_thru_map);
+ ChanMapping new_thru;
for (uint32_t pc = 0; pc < get_count(); ++pc) {
ChanMapping new_in;
}
}
+ /* remove excess thru */
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ for (uint32_t o = 0; o < _configured_out.get (*t); ++o) {
+ bool valid;
+ uint32_t idx = _thru_map.get (*t, o, &valid);
+ if (valid && idx < _configured_internal.get (*t)) {
+ new_thru.set (*t, o, idx);
+ }
+ }
+ }
+
/* prevent out + thru, existing plugin outputs override thru */
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
for (uint32_t o = 0; o < _configured_out.get (*t); ++o) {
bool
PluginInsert::reset_map (bool emit)
{
- uint32_t pc = 0;
const PinMappings old_in (_in_map);
const PinMappings old_out (_out_map);
_in_map.clear ();
_out_map.clear ();
_thru_map = ChanMapping ();
- 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 no sidechain sinks in round-robin fashion */
- for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
- const uint32_t cend = _configured_in.get (*t);
- if (cend == 0) { continue; }
- uint32_t c = 0;
- for (uint32_t in = 0; in < ns_inputs.get (*t); ++in) {
- _in_map[pc].set (*t, in, c);
- c = (c + 1) % cend;
- }
- }
- } else {
- _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) {
+ /* build input map */
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ uint32_t sc = 0; // side-chain round-robin (all instances)
+ uint32_t pc = 0;
+ for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
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
+ /* SC inputs are last in the plugin-insert.. */
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;
+ /* ...but may not be at the end of the plugin ports.
+ * in case the side-chain is not the last port, shift connections back.
+ * and connect to side-chain
+ */
+ uint32_t shift = 0;
+ uint32_t ic = 0; // split inputs
+ const uint32_t cend = _configured_in.get (*t);
+
+ for (uint32_t in = 0; in < nis; ++in) {
+ const Plugin::IOPortDescription& iod (_plugins[pc]->describe_io_port (*t, true, in));
+ if (iod.is_sidechain) {
+ /* connect sidechain sinks to sidechain inputs in round-robin fashion */
+ if (sc_len > 0) {// side-chain may be hidden
+ _in_map[pc].set (*t, in, sc_start + sc);
+ sc = (sc + 1) % sc_len;
+ }
+ ++shift;
+ } else {
+ if (_match.method == Split) {
+ if (cend == 0) { continue; }
+ if (_strict_io && ic + stride * pc >= cend) {
+ break;
+ }
+ /* connect *no* sidechain sinks in round-robin fashion */
+ _in_map[pc].set (*t, in, ic + stride * pc);
+ if (_strict_io && (ic + 1) == cend) {
+ break;
+ }
+ ic = (ic + 1) % cend;
+ } else {
+ uint32_t s = in - shift;
+ if (stride * pc + s < cend) {
+ _in_map[pc].set (*t, in, s + stride * pc);
+ }
+ }
+ }
}
}
}
+
+ /* build output map */
+ uint32_t pc = 0;
+ for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
+ _out_map[pc] = ChanMapping (ChanCount::min (natural_output_streams(), _configured_out));
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ _out_map[pc].offset_to(*t, pc * natural_output_streams().get(*t));
+ }
+ }
+
sanitize_maps ();
if (old_in == _in_map && old_out == _out_map) {
return false;
return false;
}
_configured_internal += _sidechain->input()->n_ports();
+
+ // include (static_cast<Route*>owner())->name() ??
+ _sidechain->input ()-> set_pretty_name (string_compose (_("SC %1"), name ()));
}
/* get plugin configuration */
break;
case Delegate:
{
- ChanCount dout;
+ ChanCount dout (in); // hint
+ if (_custom_cfg) {
+ dout = _custom_out;
+ } else if (_preset_out.n_audio () > 0) {
+ dout.set (DataType::AUDIO, _preset_out.n_audio ());
+ } else if (dout.n_midi () > 0 && dout.n_audio () == 0) {
+ dout.set (DataType::AUDIO, 2);
+ }
+ if (out.n_audio () == 0) { out.set (DataType::AUDIO, 1); }
ChanCount useins;
bool const r = _plugins.front()->can_support_io_configuration (in, dout, &useins);
assert (r);
- assert (_match.strict_io || dout.n_audio() == out.n_audio()); // sans midi bypass
if (useins.n_audio() == 0) {
useins = in;
}
+ DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("Delegate configuration: %1 %2 %3\n", name(), useins, dout));
+
if (_plugins.front()->configure_io (useins, dout) == false) {
PluginIoReConfigure (); /* EMIT SIGNAL */
_configured = false;
&& _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 ();
}
} else if (_match.custom_cfg && _configured) {
+ assert (_maps_from_state == false);
mapping_changed = sanitize_maps ();
} else {
#ifdef MIXBUS
_no_inplace = check_inplace ();
_mapping_changed = false;
+ /* only the "noinplace_buffers" thread buffers need to be this large,
+ * this can be optimized. other buffers are fine with
+ * ChanCount::max (natural_input_streams (), natural_output_streams())
+ * and route.cc's max (configured_in, configured_out)
+ *
+ * no-inplace copies "thru" outputs (to emulate in-place) for
+ * all outputs (to prevent overwrite) into a temporary space
+ * which also holds input buffers (in case the plugin does process
+ * in-place and overwrites those).
+ *
+ * this buffers need to be at least as
+ * natural_input_streams () + possible outputs.
+ *
+ * sidechain inputs add a constraint on the input:
+ * configured input + sidechain (=_configured_internal)
+ */
+ _required_buffers =ChanCount::max (_configured_internal,
+ natural_input_streams () + ChanCount::max (_configured_out, natural_output_streams () * get_count ()));
+
if (old_in != in || old_out != out || old_internal != _configured_internal
|| (old_match.method != _match.method && (old_match.method == Split || _match.method == Split))
) {
return private_can_support_io_configuration (in, out).method != Impossible;
}
+PluginInsert::Match
+PluginInsert::private_can_support_io_configuration (ChanCount const& in, ChanCount& out) const
+{
+ if (!_custom_cfg && _preset_out.n_audio () > 0) {
+ // preseed hint (for variable i/o)
+ out.set (DataType::AUDIO, _preset_out.n_audio ());
+ }
+
+ Match rv = internal_can_support_io_configuration (in, out);
+
+ if (!_custom_cfg && _preset_out.n_audio () > 0) {
+ DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("using output preset: %1 %2\n", name(), _preset_out));
+ out.set (DataType::AUDIO, _preset_out.n_audio ());
+ }
+ return rv;
+}
+
/** A private version of can_support_io_configuration which returns the method
* by which the configuration can be matched, rather than just whether or not
* it can be.
*/
PluginInsert::Match
-PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanCount& out) const
+PluginInsert::internal_can_support_io_configuration (ChanCount const & inx, ChanCount& out) const
{
if (_plugins.empty()) {
return Match();
/* if a user specified a custom cfg, so be it. */
if (_custom_cfg) {
+ PluginInfoPtr info = _plugins.front()->get_info();
out = _custom_out;
- return Match (ExactMatch, get_count(), _strict_io, true); // XXX
+ if (info->reconfigurable_io()) {
+ return Match (Delegate, get_count(), _strict_io, true);
+ } else {
+ return Match (ExactMatch, get_count(), _strict_io, true);
+ }
}
/* try automatic configuration */
m.strict_io = true;
/* special case MIDI instruments */
- if (is_midi_instrument()) {
+ if (needs_midi_input ()) {
// output = midi-bypass + at most master-out channels.
ChanCount max_out (DataType::AUDIO, 2); // TODO use master-out
max_out.set (DataType::MIDI, out.get(DataType::MIDI));
if (info->reconfigurable_io()) {
ChanCount useins;
- // TODO add sidechains here
+ 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);
if (!r) {
// houston, we have a problem.
ChanCount midi_bypass;
if (info->reconfigurable_io()) {
- /* Plugin has flexible I/O, so delegate to it */
+ /* Plugin has flexible I/O, so delegate to it
+ * pre-seed outputs, plugin tries closest match
+ */
+ 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);
if (!r) {
return Match (Impossible, 0);
* in case the plugin goes missing) */
node.add_child_nocopy (* _configured_in.state (X_("ConfiguredInput")));
node.add_child_nocopy (* _configured_out.state (X_("ConfiguredOutput")));
+ node.add_child_nocopy (* _preset_out.state (X_("PresetOutput")));
/* save custom i/o config */
node.add_property("custom", _custom_cfg ? "yes" : "no");
_custom_cfg = string_is_affirmative (prop->value());
}
- // TODO load/add sidechain
-
uint32_t in_maps = 0;
uint32_t out_maps = 0;
XMLNodeList kids = node.children ();
if ((*i)->name() == X_("ConfiguredOutput")) {
_custom_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 ((*i)->name () == "ThruMap") {
_thru_map = ChanMapping (**i);
}
+
+ // sidechain is a Processor (IO)
if ((*i)->name () == Processor::state_node_name) {
if (!_sidechain) {
add_sidechain (0);
}
}
+ PluginConfigChanged (); /* EMIT SIGNAL */
return 0;
}
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));
- // cache sidechain ports
+ // cache sidechain port count
_cached_sidechain_pins.reset ();
const ChanCount& nis (plugin->get_info()->n_inputs);
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
}
}
}
+#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT)
+ boost::shared_ptr<VSTPlugin> vst = boost::dynamic_pointer_cast<VSTPlugin> (plugin);
+ if (vst) {
+ vst->set_insert (this, _plugins.size ());
+ }
+#endif
_plugins.push_back (plugin);
}