2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <sigc++/bind.h>
24 #include <pbd/failed_constructor.h>
25 #include <pbd/xml++.h>
26 #include <pbd/stacktrace.h>
28 #include <ardour/insert.h>
29 #include <ardour/mtdm.h>
30 #include <ardour/plugin.h>
31 #include <ardour/port.h>
32 #include <ardour/route.h>
33 #include <ardour/ladspa_plugin.h>
36 #include <ardour/lv2_plugin.h>
40 #include <ardour/vst_plugin.h>
43 #ifdef HAVE_AUDIOUNITS
44 #include <ardour/audio_unit.h>
47 #include <ardour/audioengine.h>
48 #include <ardour/session.h>
49 #include <ardour/types.h>
54 using namespace ARDOUR;
57 Insert::Insert(Session& s, string name, Placement p)
58 : Redirect (s, name, p)
62 Insert::Insert(Session& s, string name, Placement p, int imin, int imax, int omin, int omax)
63 : Redirect (s, name, p, imin, imax, omin, omax)
67 /***************************************************************
68 Plugin inserts: send data through a plugin
69 ***************************************************************/
71 const string PluginInsert::port_automation_node_name = "PortAutomation";
73 PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug, Placement placement)
74 : Insert (s, plug->name(), placement)
76 /* the first is the master */
78 _plugins.push_back (plug);
80 _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed));
84 RedirectCreated (this); /* EMIT SIGNAL */
87 PluginInsert::PluginInsert (Session& s, const XMLNode& node)
88 : Insert (s, "will change", PreFader)
90 if (set_state (node)) {
91 throw failed_constructor();
94 _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed));
97 PluginInsert::PluginInsert (const PluginInsert& other)
98 : Insert (other._session, other.plugin()->name(), other.placement())
100 uint32_t count = other._plugins.size();
102 /* make as many copies as requested */
103 for (uint32_t n = 0; n < count; ++n) {
104 _plugins.push_back (plugin_factory (other.plugin (n)));
108 _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed));
112 RedirectCreated (this); /* EMIT SIGNAL */
116 PluginInsert::set_count (uint32_t num)
118 bool require_state = !_plugins.empty();
120 /* this is a bad idea.... we shouldn't do this while active.
121 only a route holding their redirect_lock should be calling this
126 } else if (num > _plugins.size()) {
127 uint32_t diff = num - _plugins.size();
129 for (uint32_t n = 0; n < diff; ++n) {
130 _plugins.push_back (plugin_factory (_plugins[0]));
133 /* XXX do something */
137 } else if (num < _plugins.size()) {
138 uint32_t diff = _plugins.size() - num;
139 for (uint32_t n= 0; n < diff; ++n) {
148 PluginInsert::init ()
153 PluginInsert::~PluginInsert ()
155 GoingAway (); /* EMIT SIGNAL */
159 PluginInsert::automation_list_creation_callback (uint32_t which, AutomationList& alist)
161 alist.automation_state_changed.connect (sigc::bind (mem_fun (*this, &PluginInsert::auto_state_changed), (which)));
165 PluginInsert::auto_state_changed (uint32_t which)
167 AutomationList& alist (automation_list (which));
169 /* don't reset automation if we're moving to Off or Write mode;
170 if we're moving to Write, the user may have manually set up automation
171 that they don't want to lose */
172 if (alist.automation_state() != Off && alist.automation_state() != Write) {
173 _plugins[0]->set_parameter (which, alist.eval (_session.transport_frame()));
178 PluginInsert::output_streams() const
180 int32_t out = _plugins[0]->get_info()->n_outputs;
183 return _plugins[0]->output_streams ();
185 return out * _plugins.size();
190 PluginInsert::input_streams() const
192 int32_t in = _plugins[0]->get_info()->n_inputs;
195 return _plugins[0]->input_streams ();
197 return in * _plugins.size();
202 PluginInsert::natural_output_streams() const
204 return _plugins[0]->get_info()->n_outputs;
208 PluginInsert::natural_input_streams() const
210 return _plugins[0]->get_info()->n_inputs;
214 PluginInsert::is_generator() const
216 /* XXX more finesse is possible here. VST plugins have a
217 a specific "instrument" flag, for example.
220 return _plugins[0]->get_info()->n_inputs == 0;
224 PluginInsert::set_automatable ()
226 /* fill the parameter automation list with null AutomationLists */
228 parameter_automation.assign (_plugins.front()->parameter_count(), (AutomationList*) 0);
232 a = _plugins.front()->automatable ();
234 for (set<uint32_t>::iterator i = a.begin(); i != a.end(); ++i) {
240 PluginInsert::parameter_changed (uint32_t which, float val)
242 vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin();
244 /* don't set the first plugin, just all the slaves */
246 if (i != _plugins.end()) {
248 for (; i != _plugins.end(); ++i) {
249 (*i)->set_parameter (which, val);
255 PluginInsert::set_block_size (nframes_t nframes)
257 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
258 (*i)->set_block_size (nframes);
263 PluginInsert::activate ()
265 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
271 PluginInsert::deactivate ()
273 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
279 PluginInsert::connect_and_run (vector<Sample*>& bufs, uint32_t nbufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now)
281 int32_t in_index = 0;
282 int32_t out_index = 0;
284 /* Note that we've already required that plugins
285 be able to handle in-place processing.
288 // cerr << "Connect and run for " << _plugins[0]->name() << " auto ? " << with_auto << " nf = " << nframes << " off = " << offset << endl;
292 vector<AutomationList*>::iterator li;
295 for (n = 0, li = parameter_automation.begin(); li != parameter_automation.end(); ++li, ++n) {
297 AutomationList* alist = *li;
299 if (alist && alist->automation_playback()) {
302 float val = alist->rt_safe_eval (now, valid);
305 /* set the first plugin, the others will be set via signals */
306 // cerr << "\t@ " << now << " param[" << n << "] = " << val << endl;
307 _plugins[0]->set_parameter (n, val);
314 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
315 (*i)->connect_and_run (bufs, nbufs, in_index, out_index, nframes, offset);
320 PluginInsert::automation_snapshot (nframes_t now, bool force)
322 vector<AutomationList*>::iterator li;
325 for (n = 0, li = parameter_automation.begin(); li != parameter_automation.end(); ++li, ++n) {
327 AutomationList *alist = *li;
329 if (alist && alist->automation_write ()) {
331 float val = _plugins[0]->get_parameter (n);
332 alist->rt_add (now, val);
333 last_automation_snapshot = now;
339 PluginInsert::transport_stopped (nframes_t now)
341 vector<AutomationList*>::iterator li;
344 for (n = 0, li = parameter_automation.begin(); li != parameter_automation.end(); ++li, ++n) {
346 AutomationList* alist = *li;
349 alist->reposition_for_rt_add (now);
350 if (alist->automation_state() != Off) {
351 _plugins[0]->set_parameter (n, alist->eval (now));
358 PluginInsert::silence (nframes_t nframes)
360 int32_t in_index = 0;
361 int32_t out_index = 0;
365 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
367 (*i)->connect_and_run (_session.get_silent_buffers (n), n, in_index, out_index, nframes, 0);
373 PluginInsert::run (vector<Sample *>& bufs, uint32_t nbufs, nframes_t nframes)
377 if (_session.transport_rolling()) {
378 automation_run (bufs, nbufs, nframes);
380 connect_and_run (bufs, nbufs, nframes, 0, false);
385 uint32_t in = input_streams ();
386 uint32_t out = output_streams ();
390 /* not active, but something has make up for any channel count increase,
391 so copy the last buffer to the extras.
394 for (uint32_t n = out - in; n < out && n < nbufs; ++n) {
395 memcpy (bufs[n], bufs[in - 1], sizeof (Sample) * nframes);
402 PluginInsert::set_parameter (uint32_t port, float val)
404 /* the others will be set from the event triggered by this */
406 float last_val = _plugins[0]->get_parameter (port);
407 Plugin::ParameterDescriptor desc;
408 _plugins[0]->get_parameter_descriptor(port, desc);
410 _plugins[0]->set_parameter (port, val);
412 if (automation_list (port).automation_write()) {
413 if ( desc.toggled ) //store the previous value just before this so any interpolation works right
414 automation_list (port).add (_session.audible_frame()-1, last_val);
415 automation_list (port).add (_session.audible_frame(), val);
418 _session.set_dirty();
422 PluginInsert::automation_run (vector<Sample *>& bufs, uint32_t nbufs, nframes_t nframes)
424 ControlEvent next_event (0, 0.0f);
425 nframes_t now = _session.transport_frame ();
426 nframes_t end = now + nframes;
427 nframes_t offset = 0;
429 Glib::Mutex::Lock lm (_automation_lock, Glib::TRY_LOCK);
432 connect_and_run (bufs, nbufs, nframes, 0, false, now);
436 if (!find_next_event (now, end, next_event)) {
437 /* no events have a time within the relevant range */
438 connect_and_run (bufs, nbufs, nframes, 0, true, now);
443 nframes_t cnt = min (((nframes_t) ceil (next_event.when) - now), nframes);
445 connect_and_run (bufs, nbufs, cnt, offset, true, now);
451 if (!find_next_event (now, end, next_event)) {
456 /* cleanup anything that is left to do */
459 connect_and_run (bufs, nbufs, nframes, offset, true, now);
465 PluginInsert::default_parameter_value (uint32_t port)
467 if (_plugins.empty()) {
468 fatal << _("programming error: ") << X_("PluginInsert::default_parameter_value() called with no plugin")
473 return _plugins[0]->default_value (port);
477 PluginInsert::set_port_automation_state (uint32_t port, AutoState s)
479 if (port < _plugins[0]->parameter_count()) {
481 AutomationList& al = automation_list (port);
483 if (s != al.automation_state()) {
484 al.set_automation_state (s);
485 _session.set_dirty ();
491 PluginInsert::get_port_automation_state (uint32_t port)
493 if (port < _plugins[0]->parameter_count()) {
494 return automation_list (port).automation_state();
501 PluginInsert::protect_automation ()
503 set<uint32_t> automated_params;
505 what_has_automation (automated_params);
507 for (set<uint32_t>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
509 AutomationList& al = automation_list (*i);
511 switch (al.automation_state()) {
513 al.set_automation_state (Off);
516 al.set_automation_state (Play);
524 boost::shared_ptr<Plugin>
525 PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
527 boost::shared_ptr<LadspaPlugin> lp;
529 boost::shared_ptr<LV2Plugin> lv2p;
532 boost::shared_ptr<VSTPlugin> vp;
534 #ifdef HAVE_AUDIOUNITS
535 boost::shared_ptr<AUPlugin> ap;
538 if ((lp = boost::dynamic_pointer_cast<LadspaPlugin> (other)) != 0) {
539 return boost::shared_ptr<Plugin> (new LadspaPlugin (*lp));
541 } else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin> (other)) != 0) {
542 return boost::shared_ptr<Plugin> (new LV2Plugin (*lv2p));
545 } else if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (other)) != 0) {
546 return boost::shared_ptr<Plugin> (new VSTPlugin (*vp));
548 #ifdef HAVE_AUDIOUNITS
549 } else if ((ap = boost::dynamic_pointer_cast<AUPlugin> (other)) != 0) {
550 return boost::shared_ptr<Plugin> (new AUPlugin (*ap));
554 fatal << string_compose (_("programming error: %1"),
555 X_("unknown plugin type in PluginInsert::plugin_factory"))
558 return boost::shared_ptr<Plugin> ((Plugin*) 0);
562 PluginInsert::configure_io (int32_t magic, int32_t in, int32_t out)
566 if ((ret = set_count (magic)) < 0) {
570 /* if we're running replicated plugins, each plugin has
571 the same i/o configuration and we may need to announce how many
572 output streams there are.
574 if we running a single plugin, we need to configure it.
577 return _plugins[0]->configure_io (in, out);
581 PluginInsert::can_do (int32_t in, int32_t& out)
583 return _plugins[0]->can_do (in, out);
587 PluginInsert::get_state(void)
593 PluginInsert::state (bool full)
596 XMLNode *node = new XMLNode("Insert");
598 node->add_child_nocopy (Redirect::state (full));
600 node->add_property ("type", _plugins[0]->state_node_name());
601 node->add_property("unique-id", _plugins[0]->unique_id());
602 node->add_property("count", string_compose("%1", _plugins.size()));
603 node->add_child_nocopy (_plugins[0]->get_state());
605 /* add controllables */
607 XMLNode* control_node = new XMLNode (X_("controls"));
609 for (uint32_t x = 0; x < _plugins[0]->parameter_count(); ++x) {
610 Controllable* c = _plugins[0]->get_nth_control (x, true);
612 XMLNode& controllable_state (c->get_state());
613 controllable_state.add_property ("parameter", to_string (x, std::dec));
614 control_node->add_child_nocopy (controllable_state);
617 node->add_child_nocopy (*control_node);
619 /* add port automation state */
620 XMLNode *autonode = new XMLNode(port_automation_node_name);
621 set<uint32_t> automatable = _plugins[0]->automatable();
623 for (set<uint32_t>::iterator x = automatable.begin(); x != automatable.end(); ++x) {
625 XMLNode* child = new XMLNode("port");
626 snprintf(buf, sizeof(buf), "%" PRIu32, *x);
627 child->add_property("number", string(buf));
630 LV2Plugin* lv2p = dynamic_cast<LV2Plugin*>(_plugins[0].get());
632 child->add_property("symbol", string(lv2p->port_symbol(*x)));
636 child->add_child_nocopy (automation_list (*x).state (full));
637 autonode->add_child_nocopy (*child);
640 node->add_child_nocopy (*autonode);
646 PluginInsert::set_state(const XMLNode& node)
648 XMLNodeList nlist = node.children();
649 XMLNodeIterator niter;
650 XMLPropertyList plist;
651 const XMLProperty *prop;
652 ARDOUR::PluginType type;
654 if ((prop = node.property ("type")) == 0) {
655 error << _("XML node describing insert is missing the `type' field") << endmsg;
659 if (prop->value() == X_("ladspa") || prop->value() == X_("Ladspa")) { /* handle old school sessions */
660 type = ARDOUR::LADSPA;
661 } else if (prop->value() == X_("lv2")) {
663 } else if (prop->value() == X_("vst")) {
665 } else if (prop->value() == X_("audiounit")) {
666 type = ARDOUR::AudioUnit;
668 error << string_compose (_("unknown plugin type %1 in plugin insert state"),
674 prop = node.property ("unique-id");
677 /* older sessions contain VST plugins with only an "id" field.
680 if (type == ARDOUR::VST) {
681 prop = node.property ("id");
687 error << _("Plugin has no unique ID field") << endmsg;
692 boost::shared_ptr<Plugin> plugin;
694 plugin = find_plugin (_session, prop->value(), type);
697 error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n"
698 "Perhaps it was removed or moved since it was last used."), prop->value())
705 if ((prop = node.property ("count")) != 0) {
706 sscanf (prop->value().c_str(), "%u", &count);
709 if (_plugins.size() != count) {
711 _plugins.push_back (plugin);
713 for (uint32_t n=1; n < count; ++n) {
714 _plugins.push_back (plugin_factory (plugin));
718 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
719 if ((*niter)->name() == plugin->state_node_name()) {
720 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
721 (*i)->set_state (**niter);
727 if (niter == nlist.end()) {
728 error << string_compose(_("XML node describing a plugin insert is missing the `%1' information"), plugin->state_node_name()) << endmsg;
732 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
733 if ((*niter)->name() == Redirect::state_node_name) {
734 Redirect::set_state (**niter);
739 if (niter == nlist.end()) {
740 error << _("XML node describing insert is missing a Redirect node") << endmsg;
744 /* look for controllables node */
746 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
748 if ((*niter)->name() != X_("controls")) {
752 XMLNodeList grandchildren ((*niter)->children());
754 XMLNodeIterator gciter;
757 for (gciter = grandchildren.begin(); gciter != grandchildren.end(); ++gciter) {
758 if ((prop = (*gciter)->property (X_("parameter"))) != 0) {
759 param = atoi (prop->value());
760 /* force creation of controllable for this parameter */
761 _plugins[0]->make_nth_control (param, **gciter);
770 /* look for port automation node */
772 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
774 if ((*niter)->name() != port_automation_node_name) {
780 XMLNodeConstIterator iter;
785 cnodes = (*niter)->children ("port");
787 for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){
791 if ((cprop = child->property("number")) != 0) {
792 port = cprop->value().c_str();
794 warning << _("PluginInsert: Auto: no ladspa port number") << endmsg;
798 sscanf (port, "%" PRIu32, &port_id);
800 if (port_id >= _plugins[0]->parameter_count()) {
801 warning << _("PluginInsert: Auto: port id out of range") << endmsg;
805 if (!child->children().empty()) {
806 automation_list (port_id).set_state (*child->children().front());
808 if ((cprop = child->property("auto")) != 0) {
813 sscanf (cprop->value().c_str(), "0x%x", &x);
814 automation_list (port_id).set_automation_state (AutoState (x));
820 automation_list (port_id).set_automation_state (Off);
831 if (niter == nlist.end()) {
832 warning << string_compose(_("XML node describing a port automation is missing the `%1' information"), port_automation_node_name) << endmsg;
835 // The name of the PluginInsert comes from the plugin, nothing else
836 set_name(plugin->get_info()->name,this);
842 PluginInsert::describe_parameter (uint32_t what)
844 return _plugins[0]->describe_parameter (what);
848 PluginInsert::latency()
850 return _plugins[0]->latency ();
854 PluginInsert::type ()
856 return plugin()->get_info()->type;
859 /***************************************************************
860 Port inserts: send output to a port, pick up input at a port
861 ***************************************************************/
863 PortInsert::PortInsert (Session& s, Placement p)
864 : Insert (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1)
867 RedirectCreated (this); /* EMIT SIGNAL */
871 PortInsert::PortInsert (const PortInsert& other)
872 : Insert (other._session, string_compose (_("insert %1"), (bitslot = other._session.next_insert_id()) + 1), other.placement(), 1, -1, 1, -1)
875 RedirectCreated (this); /* EMIT SIGNAL */
882 _latency_detect = false;
883 _latency_flush_frames = false;
884 _measured_latency = 0;
887 PortInsert::PortInsert (Session& s, const XMLNode& node)
888 : Insert (s, "will change", PreFader)
892 bitslot = 0xffffffff;
894 if (set_state (node)) {
895 throw failed_constructor();
898 RedirectCreated (this); /* EMIT SIGNAL */
901 PortInsert::~PortInsert ()
908 PortInsert::start_latency_detection ()
915 _latency_flush_frames = false;
916 _latency_detect = true;
917 _measured_latency = 0;
921 PortInsert::stop_latency_detection ()
923 _latency_flush_frames = latency() + _session.engine().frames_per_cycle();
924 _latency_detect = false;
928 PortInsert::set_measured_latency (nframes_t n)
930 _measured_latency = n;
934 PortInsert::run (vector<Sample *>& bufs, uint32_t nbufs, nframes_t nframes)
936 if (n_outputs() == 0) {
940 vector<Port*>::iterator o;
942 if (_latency_detect) {
944 if (n_inputs() != 0) {
945 Sample* in = get_input_buffer (0, nframes);
946 Sample* out = get_output_buffer (0, nframes);
948 _mtdm->process (nframes, in, out);
950 for (o = _outputs.begin(); o != _outputs.end(); ++o) {
951 (*o)->mark_silence (false);
957 } else if (_latency_flush_frames) {
959 /* wait for the entire input buffer to drain before picking up input again so that we can't
960 hear the remnants of whatever MTDM pumped into the pipeline.
965 if (_latency_flush_frames > nframes) {
966 _latency_flush_frames -= nframes;
968 _latency_flush_frames = 0;
975 /* deliver silence */
984 for (o = _outputs.begin(), n = 0; o != _outputs.end(); ++o, ++n) {
985 memcpy (get_output_buffer (n, nframes), bufs[min(nbufs,n)], sizeof (Sample) * nframes);
986 (*o)->mark_silence (false);
989 vector<Port*>::iterator i;
993 for (i = _inputs.begin(), n = 0; i != _inputs.end(); ++i, ++n) {
994 memcpy (bufs[min(nbufs,n)], get_input_buffer (n, nframes), sizeof (Sample) * nframes);
999 PortInsert::get_state(void)
1001 return state (true);
1005 PortInsert::state (bool full)
1007 XMLNode *node = new XMLNode("Insert");
1009 node->add_child_nocopy (Redirect::state(full));
1010 node->add_property ("type", "port");
1011 snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
1012 node->add_property ("bitslot", buf);
1018 PortInsert::set_state(const XMLNode& node)
1020 XMLNodeList nlist = node.children();
1021 XMLNodeIterator niter;
1022 XMLPropertyList plist;
1023 const XMLProperty *prop;
1025 if ((prop = node.property ("type")) == 0) {
1026 error << _("XML node describing insert is missing the `type' field") << endmsg;
1030 if (prop->value() != "port") {
1031 error << _("non-port insert XML used for port plugin insert") << endmsg;
1035 if ((prop = node.property ("bitslot")) == 0) {
1036 bitslot = _session.next_insert_id();
1038 uint32_t old_bitslot = bitslot;
1039 sscanf (prop->value().c_str(), "%" PRIu32, &bitslot);
1041 if (old_bitslot != bitslot) {
1042 _session.mark_insert_id (bitslot);
1046 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1047 if ((*niter)->name() == Redirect::state_node_name) {
1048 Redirect::set_state (**niter);
1053 if (niter == nlist.end()) {
1054 error << _("XML node describing insert is missing a Redirect node") << endmsg;
1062 PortInsert::latency()
1064 /* because we deliver and collect within the same cycle,
1065 all I/O is necessarily delayed by at least frames_per_cycle().
1067 if the return port for insert has its own latency, we
1068 need to take that into account too.
1071 if (_measured_latency == 0) {
1072 return _session.engine().frames_per_cycle() + input_latency();
1074 return _measured_latency;
1079 PortInsert::can_do (int32_t in, int32_t& out)
1081 if (input_maximum() == -1 && output_maximum() == -1) {
1083 /* not configured yet */
1090 /* the "input" config for a port insert corresponds to how
1091 many output ports it will have.
1094 if (output_maximum() == in) {
1104 PortInsert::configure_io (int32_t ignored_magic, int32_t in, int32_t out)
1106 /* do not allow configuration to be changed outside the range of
1107 the last request config. or something like that.
1110 set_output_maximum (in);
1111 set_output_minimum (in);
1112 set_input_maximum (out);
1113 set_input_minimum (out);
1115 /* this can be momentarily confusing:
1117 the number of inputs we are required to handle corresponds
1118 to the number of output ports we need.
1120 the number of outputs we are required to have corresponds
1121 to the number of input ports we need.
1132 return ensure_io (out, in, false, this);
1136 PortInsert::output_streams() const
1142 PortInsert::input_streams() const
1144 return n_outputs ();