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 if (alist.automation_state() != Off) {
170 _plugins[0]->set_parameter (which, alist.eval (_session.transport_frame()));
175 PluginInsert::output_streams() const
177 int32_t out = _plugins[0]->get_info()->n_outputs;
180 return _plugins[0]->output_streams ();
182 return out * _plugins.size();
187 PluginInsert::input_streams() const
189 int32_t in = _plugins[0]->get_info()->n_inputs;
192 return _plugins[0]->input_streams ();
194 return in * _plugins.size();
199 PluginInsert::natural_output_streams() const
201 return _plugins[0]->get_info()->n_outputs;
205 PluginInsert::natural_input_streams() const
207 return _plugins[0]->get_info()->n_inputs;
211 PluginInsert::is_generator() const
213 /* XXX more finesse is possible here. VST plugins have a
214 a specific "instrument" flag, for example.
217 return _plugins[0]->get_info()->n_inputs == 0;
221 PluginInsert::set_automatable ()
223 /* fill the parameter automation list with null AutomationLists */
225 parameter_automation.assign (_plugins.front()->parameter_count(), (AutomationList*) 0);
229 a = _plugins.front()->automatable ();
231 for (set<uint32_t>::iterator i = a.begin(); i != a.end(); ++i) {
237 PluginInsert::parameter_changed (uint32_t which, float val)
239 vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin();
241 /* don't set the first plugin, just all the slaves */
243 if (i != _plugins.end()) {
245 for (; i != _plugins.end(); ++i) {
246 (*i)->set_parameter (which, val);
252 PluginInsert::set_block_size (nframes_t nframes)
254 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
255 (*i)->set_block_size (nframes);
260 PluginInsert::activate ()
262 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
268 PluginInsert::deactivate ()
270 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
276 PluginInsert::connect_and_run (vector<Sample*>& bufs, uint32_t nbufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now)
278 int32_t in_index = 0;
279 int32_t out_index = 0;
281 /* Note that we've already required that plugins
282 be able to handle in-place processing.
285 // cerr << "Connect and run for " << _plugins[0]->name() << " auto ? " << with_auto << " nf = " << nframes << " off = " << offset << endl;
289 vector<AutomationList*>::iterator li;
292 for (n = 0, li = parameter_automation.begin(); li != parameter_automation.end(); ++li, ++n) {
294 AutomationList* alist = *li;
296 if (alist && alist->automation_playback()) {
299 float val = alist->rt_safe_eval (now, valid);
302 /* set the first plugin, the others will be set via signals */
303 // cerr << "\t@ " << now << " param[" << n << "] = " << val << endl;
304 _plugins[0]->set_parameter (n, val);
311 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
312 (*i)->connect_and_run (bufs, nbufs, in_index, out_index, nframes, offset);
317 PluginInsert::automation_snapshot (nframes_t now, bool force)
319 vector<AutomationList*>::iterator li;
322 for (n = 0, li = parameter_automation.begin(); li != parameter_automation.end(); ++li, ++n) {
324 AutomationList *alist = *li;
326 if (alist && alist->automation_write ()) {
328 float val = _plugins[0]->get_parameter (n);
329 alist->rt_add (now, val);
330 last_automation_snapshot = now;
336 PluginInsert::transport_stopped (nframes_t now)
338 vector<AutomationList*>::iterator li;
341 for (n = 0, li = parameter_automation.begin(); li != parameter_automation.end(); ++li, ++n) {
343 AutomationList* alist = *li;
346 alist->reposition_for_rt_add (now);
347 if (alist->automation_state() != Off) {
348 _plugins[0]->set_parameter (n, alist->eval (now));
355 PluginInsert::silence (nframes_t nframes)
357 int32_t in_index = 0;
358 int32_t out_index = 0;
362 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
364 (*i)->connect_and_run (_session.get_silent_buffers (n), n, in_index, out_index, nframes, 0);
370 PluginInsert::run (vector<Sample *>& bufs, uint32_t nbufs, nframes_t nframes)
374 if (_session.transport_rolling()) {
375 automation_run (bufs, nbufs, nframes);
377 connect_and_run (bufs, nbufs, nframes, 0, false);
382 uint32_t in = input_streams ();
383 uint32_t out = output_streams ();
387 /* not active, but something has make up for any channel count increase,
388 so copy the last buffer to the extras.
391 for (uint32_t n = out - in; n < out && n < nbufs; ++n) {
392 memcpy (bufs[n], bufs[in - 1], sizeof (Sample) * nframes);
399 PluginInsert::set_parameter (uint32_t port, float val)
401 /* the others will be set from the event triggered by this */
403 float last_val = _plugins[0]->get_parameter (port);
404 Plugin::ParameterDescriptor desc;
405 _plugins[0]->get_parameter_descriptor(port, desc);
407 _plugins[0]->set_parameter (port, val);
409 if (automation_list (port).automation_write()) {
410 if ( desc.toggled ) //store the previous value just before this so any interpolation works right
411 automation_list (port).add (_session.audible_frame()-1, last_val);
412 automation_list (port).add (_session.audible_frame(), val);
415 _session.set_dirty();
419 PluginInsert::automation_run (vector<Sample *>& bufs, uint32_t nbufs, nframes_t nframes)
421 ControlEvent next_event (0, 0.0f);
422 nframes_t now = _session.transport_frame ();
423 nframes_t end = now + nframes;
424 nframes_t offset = 0;
426 Glib::Mutex::Lock lm (_automation_lock, Glib::TRY_LOCK);
429 connect_and_run (bufs, nbufs, nframes, 0, false, now);
433 if (!find_next_event (now, end, next_event)) {
434 /* no events have a time within the relevant range */
435 connect_and_run (bufs, nbufs, nframes, 0, true, now);
440 nframes_t cnt = min (((nframes_t) ceil (next_event.when) - now), nframes);
442 connect_and_run (bufs, nbufs, cnt, offset, true, now);
448 if (!find_next_event (now, end, next_event)) {
453 /* cleanup anything that is left to do */
456 connect_and_run (bufs, nbufs, nframes, offset, true, now);
462 PluginInsert::default_parameter_value (uint32_t port)
464 if (_plugins.empty()) {
465 fatal << _("programming error: ") << X_("PluginInsert::default_parameter_value() called with no plugin")
470 return _plugins[0]->default_value (port);
474 PluginInsert::set_port_automation_state (uint32_t port, AutoState s)
476 if (port < _plugins[0]->parameter_count()) {
478 AutomationList& al = automation_list (port);
480 if (s != al.automation_state()) {
481 al.set_automation_state (s);
482 _session.set_dirty ();
488 PluginInsert::get_port_automation_state (uint32_t port)
490 if (port < _plugins[0]->parameter_count()) {
491 return automation_list (port).automation_state();
498 PluginInsert::protect_automation ()
500 set<uint32_t> automated_params;
502 what_has_automation (automated_params);
504 for (set<uint32_t>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
506 AutomationList& al = automation_list (*i);
508 switch (al.automation_state()) {
510 al.set_automation_state (Off);
513 al.set_automation_state (Play);
521 boost::shared_ptr<Plugin>
522 PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
524 boost::shared_ptr<LadspaPlugin> lp;
526 boost::shared_ptr<LV2Plugin> lv2p;
529 boost::shared_ptr<VSTPlugin> vp;
531 #ifdef HAVE_AUDIOUNITS
532 boost::shared_ptr<AUPlugin> ap;
535 if ((lp = boost::dynamic_pointer_cast<LadspaPlugin> (other)) != 0) {
536 return boost::shared_ptr<Plugin> (new LadspaPlugin (*lp));
538 } else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin> (other)) != 0) {
539 return boost::shared_ptr<Plugin> (new LV2Plugin (*lv2p));
542 } else if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (other)) != 0) {
543 return boost::shared_ptr<Plugin> (new VSTPlugin (*vp));
545 #ifdef HAVE_AUDIOUNITS
546 } else if ((ap = boost::dynamic_pointer_cast<AUPlugin> (other)) != 0) {
547 return boost::shared_ptr<Plugin> (new AUPlugin (*ap));
551 fatal << string_compose (_("programming error: %1"),
552 X_("unknown plugin type in PluginInsert::plugin_factory"))
555 return boost::shared_ptr<Plugin> ((Plugin*) 0);
559 PluginInsert::configure_io (int32_t magic, int32_t in, int32_t out)
563 if ((ret = set_count (magic)) < 0) {
567 /* if we're running replicated plugins, each plugin has
568 the same i/o configuration and we may need to announce how many
569 output streams there are.
571 if we running a single plugin, we need to configure it.
574 return _plugins[0]->configure_io (in, out);
578 PluginInsert::can_do (int32_t in, int32_t& out)
580 return _plugins[0]->can_do (in, out);
584 PluginInsert::get_state(void)
590 PluginInsert::state (bool full)
593 XMLNode *node = new XMLNode("Insert");
595 node->add_child_nocopy (Redirect::state (full));
597 node->add_property ("type", _plugins[0]->state_node_name());
598 node->add_property("unique-id", _plugins[0]->unique_id());
599 node->add_property("count", string_compose("%1", _plugins.size()));
600 node->add_child_nocopy (_plugins[0]->get_state());
602 /* add controllables */
604 XMLNode* control_node = new XMLNode (X_("controls"));
606 for (uint32_t x = 0; x < _plugins[0]->parameter_count(); ++x) {
607 Controllable* c = _plugins[0]->get_nth_control (x, true);
609 XMLNode& controllable_state (c->get_state());
610 controllable_state.add_property ("parameter", to_string (x, std::dec));
611 control_node->add_child_nocopy (controllable_state);
614 node->add_child_nocopy (*control_node);
616 /* add port automation state */
617 XMLNode *autonode = new XMLNode(port_automation_node_name);
618 set<uint32_t> automatable = _plugins[0]->automatable();
620 for (set<uint32_t>::iterator x = automatable.begin(); x != automatable.end(); ++x) {
622 XMLNode* child = new XMLNode("port");
623 snprintf(buf, sizeof(buf), "%" PRIu32, *x);
624 child->add_property("number", string(buf));
627 LV2Plugin* lv2p = dynamic_cast<LV2Plugin*>(_plugins[0].get());
629 child->add_property("symbol", string(lv2p->port_symbol(*x)));
633 child->add_child_nocopy (automation_list (*x).state (full));
634 autonode->add_child_nocopy (*child);
637 node->add_child_nocopy (*autonode);
643 PluginInsert::set_state(const XMLNode& node)
645 XMLNodeList nlist = node.children();
646 XMLNodeIterator niter;
647 XMLPropertyList plist;
648 const XMLProperty *prop;
649 ARDOUR::PluginType type;
651 if ((prop = node.property ("type")) == 0) {
652 error << _("XML node describing insert is missing the `type' field") << endmsg;
656 if (prop->value() == X_("ladspa") || prop->value() == X_("Ladspa")) { /* handle old school sessions */
657 type = ARDOUR::LADSPA;
658 } else if (prop->value() == X_("lv2")) {
660 } else if (prop->value() == X_("vst")) {
662 } else if (prop->value() == X_("audiounit")) {
663 type = ARDOUR::AudioUnit;
665 error << string_compose (_("unknown plugin type %1 in plugin insert state"),
671 prop = node.property ("unique-id");
674 /* older sessions contain VST plugins with only an "id" field.
677 if (type == ARDOUR::VST) {
678 prop = node.property ("id");
684 error << _("Plugin has no unique ID field") << endmsg;
689 boost::shared_ptr<Plugin> plugin;
691 plugin = find_plugin (_session, prop->value(), type);
694 error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n"
695 "Perhaps it was removed or moved since it was last used."), prop->value())
702 if ((prop = node.property ("count")) != 0) {
703 sscanf (prop->value().c_str(), "%u", &count);
706 if (_plugins.size() != count) {
708 _plugins.push_back (plugin);
710 for (uint32_t n=1; n < count; ++n) {
711 _plugins.push_back (plugin_factory (plugin));
715 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
716 if ((*niter)->name() == plugin->state_node_name()) {
717 for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
718 (*i)->set_state (**niter);
724 if (niter == nlist.end()) {
725 error << string_compose(_("XML node describing a plugin insert is missing the `%1' information"), plugin->state_node_name()) << endmsg;
729 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
730 if ((*niter)->name() == Redirect::state_node_name) {
731 Redirect::set_state (**niter);
736 if (niter == nlist.end()) {
737 error << _("XML node describing insert is missing a Redirect node") << endmsg;
741 /* look for controllables node */
743 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
745 if ((*niter)->name() != X_("controls")) {
749 XMLNodeList grandchildren ((*niter)->children());
751 XMLNodeIterator gciter;
754 for (gciter = grandchildren.begin(); gciter != grandchildren.end(); ++gciter) {
755 if ((prop = (*gciter)->property (X_("parameter"))) != 0) {
756 param = atoi (prop->value());
757 /* force creation of controllable for this parameter */
758 _plugins[0]->make_nth_control (param, **gciter);
767 /* look for port automation node */
769 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
771 if ((*niter)->name() != port_automation_node_name) {
777 XMLNodeConstIterator iter;
782 cnodes = (*niter)->children ("port");
784 for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){
788 if ((cprop = child->property("number")) != 0) {
789 port = cprop->value().c_str();
791 warning << _("PluginInsert: Auto: no ladspa port number") << endmsg;
795 sscanf (port, "%" PRIu32, &port_id);
797 if (port_id >= _plugins[0]->parameter_count()) {
798 warning << _("PluginInsert: Auto: port id out of range") << endmsg;
802 if (!child->children().empty()) {
803 automation_list (port_id).set_state (*child->children().front());
805 if ((cprop = child->property("auto")) != 0) {
810 sscanf (cprop->value().c_str(), "0x%x", &x);
811 automation_list (port_id).set_automation_state (AutoState (x));
817 automation_list (port_id).set_automation_state (Off);
828 if (niter == nlist.end()) {
829 warning << string_compose(_("XML node describing a port automation is missing the `%1' information"), port_automation_node_name) << endmsg;
832 // The name of the PluginInsert comes from the plugin, nothing else
833 set_name(plugin->get_info()->name,this);
839 PluginInsert::describe_parameter (uint32_t what)
841 return _plugins[0]->describe_parameter (what);
845 PluginInsert::latency()
847 return _plugins[0]->latency ();
851 PluginInsert::type ()
853 return plugin()->get_info()->type;
856 /***************************************************************
857 Port inserts: send output to a port, pick up input at a port
858 ***************************************************************/
860 PortInsert::PortInsert (Session& s, Placement p)
861 : Insert (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1)
864 RedirectCreated (this); /* EMIT SIGNAL */
868 PortInsert::PortInsert (const PortInsert& other)
869 : Insert (other._session, string_compose (_("insert %1"), (bitslot = other._session.next_insert_id()) + 1), other.placement(), 1, -1, 1, -1)
872 RedirectCreated (this); /* EMIT SIGNAL */
879 _latency_detect = false;
880 _latency_flush_frames = false;
881 _measured_latency = 0;
884 PortInsert::PortInsert (Session& s, const XMLNode& node)
885 : Insert (s, "will change", PreFader)
889 bitslot = 0xffffffff;
891 if (set_state (node)) {
892 throw failed_constructor();
895 RedirectCreated (this); /* EMIT SIGNAL */
898 PortInsert::~PortInsert ()
905 PortInsert::start_latency_detection ()
912 _latency_flush_frames = false;
913 _latency_detect = true;
914 _measured_latency = 0;
918 PortInsert::stop_latency_detection ()
920 _latency_flush_frames = latency() + _session.engine().frames_per_cycle();
921 _latency_detect = false;
925 PortInsert::set_measured_latency (nframes_t n)
927 _measured_latency = n;
931 PortInsert::run (vector<Sample *>& bufs, uint32_t nbufs, nframes_t nframes)
933 if (n_outputs() == 0) {
937 vector<Port*>::iterator o;
939 if (_latency_detect) {
941 if (n_inputs() != 0) {
942 Sample* in = get_input_buffer (0, nframes);
943 Sample* out = get_output_buffer (0, nframes);
945 _mtdm->process (nframes, in, out);
947 for (o = _outputs.begin(); o != _outputs.end(); ++o) {
948 (*o)->mark_silence (false);
954 } else if (_latency_flush_frames) {
956 /* wait for the entire input buffer to drain before picking up input again so that we can't
957 hear the remnants of whatever MTDM pumped into the pipeline.
962 if (_latency_flush_frames > nframes) {
963 _latency_flush_frames -= nframes;
965 _latency_flush_frames = 0;
972 /* deliver silence */
981 for (o = _outputs.begin(), n = 0; o != _outputs.end(); ++o, ++n) {
982 memcpy (get_output_buffer (n, nframes), bufs[min(nbufs,n)], sizeof (Sample) * nframes);
983 (*o)->mark_silence (false);
986 vector<Port*>::iterator i;
990 for (i = _inputs.begin(), n = 0; i != _inputs.end(); ++i, ++n) {
991 memcpy (bufs[min(nbufs,n)], get_input_buffer (n, nframes), sizeof (Sample) * nframes);
996 PortInsert::get_state(void)
1002 PortInsert::state (bool full)
1004 XMLNode *node = new XMLNode("Insert");
1006 node->add_child_nocopy (Redirect::state(full));
1007 node->add_property ("type", "port");
1008 snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
1009 node->add_property ("bitslot", buf);
1015 PortInsert::set_state(const XMLNode& node)
1017 XMLNodeList nlist = node.children();
1018 XMLNodeIterator niter;
1019 XMLPropertyList plist;
1020 const XMLProperty *prop;
1022 if ((prop = node.property ("type")) == 0) {
1023 error << _("XML node describing insert is missing the `type' field") << endmsg;
1027 if (prop->value() != "port") {
1028 error << _("non-port insert XML used for port plugin insert") << endmsg;
1032 if ((prop = node.property ("bitslot")) == 0) {
1033 bitslot = _session.next_insert_id();
1035 uint32_t old_bitslot = bitslot;
1036 sscanf (prop->value().c_str(), "%" PRIu32, &bitslot);
1038 if (old_bitslot != bitslot) {
1039 _session.mark_insert_id (bitslot);
1043 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1044 if ((*niter)->name() == Redirect::state_node_name) {
1045 Redirect::set_state (**niter);
1050 if (niter == nlist.end()) {
1051 error << _("XML node describing insert is missing a Redirect node") << endmsg;
1059 PortInsert::latency()
1061 /* because we deliver and collect within the same cycle,
1062 all I/O is necessarily delayed by at least frames_per_cycle().
1064 if the return port for insert has its own latency, we
1065 need to take that into account too.
1068 if (_measured_latency == 0) {
1069 return _session.engine().frames_per_cycle() + input_latency();
1071 return _measured_latency;
1076 PortInsert::can_do (int32_t in, int32_t& out)
1078 if (input_maximum() == -1 && output_maximum() == -1) {
1080 /* not configured yet */
1087 /* the "input" config for a port insert corresponds to how
1088 many output ports it will have.
1091 if (output_maximum() == in) {
1101 PortInsert::configure_io (int32_t ignored_magic, int32_t in, int32_t out)
1103 /* do not allow configuration to be changed outside the range of
1104 the last request config. or something like that.
1107 set_output_maximum (in);
1108 set_output_minimum (in);
1109 set_input_maximum (out);
1110 set_input_minimum (out);
1112 /* this can be momentarily confusing:
1114 the number of inputs we are required to handle corresponds
1115 to the number of output ports we need.
1117 the number of outputs we are required to have corresponds
1118 to the number of input ports we need.
1129 return ensure_io (out, in, false, this);
1133 PortInsert::output_streams() const
1139 PortInsert::input_streams() const
1141 return n_outputs ();