{
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;
}
+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)
{
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 ()
{
}
}
+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)
{
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) {
}
}
+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)
{
}
} 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;
}
}
+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
{
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;
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 ());
}
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));
_configured = false;
return false;
}
+ if (!_custom_cfg) {
+ _custom_sinks = din;
+ }
}
break;
default:
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 () ? "==" : "!="
));
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
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 */
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);
}
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);
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);
}
/* 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")));
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
XMLNodeList nlist = node.children();
XMLNodeIterator niter;
XMLPropertyList plist;
- const XMLProperty *prop;
+ XMLProperty const * prop;
ARDOUR::PluginType type;
if ((prop = node.property ("type")) == 0) {
}
#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"
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;
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;
}
}
XMLNodeList cnodes;
- XMLProperty *cprop;
+ XMLProperty const * cprop;
XMLNodeConstIterator iter;
XMLNode *child;
const char *port;
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);
_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 ()
{