if (plug) {
add_plugin (plug);
create_automatable_parameters ();
+ const ChanCount& sc (sidechain_input_pins ());
+ if (sc.n_total () > 0) {
+ add_sidechain (sc.n_audio ());
+ }
}
}
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
{
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.
}
#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)));
sanitize_maps ();
if (changed) {
PluginMapChanged (); /* EMIT SIGNAL */
+ _mapping_changed = true;
}
}
}
sanitize_maps ();
if (changed) {
PluginMapChanged (); /* EMIT SIGNAL */
+ _mapping_changed = true;
}
}
}
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 ()
{
_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 ();
}
if (emit) {
PluginMapChanged (); /* EMIT SIGNAL */
+ _mapping_changed = true;
}
return true;
}
} 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;
#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))
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;
* 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;
}
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;
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)
}
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
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 */
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()));
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) {
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);
}
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) {
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) {
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);
}