2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2011 Paul Davis
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <boost/algorithm/string.hpp>
22 #include <gtkmm/table.h>
23 #include <gtkmm/frame.h>
24 #include <gtkmm/box.h>
25 #include <gtkmm/label.h>
27 #include "pbd/replace_all.h"
29 #include "gtkmm2ext/utils.h"
30 #include "gtkmm2ext/rgb_macros.h"
32 #include "ardour/audioengine.h"
33 #include "ardour/plugin.h"
34 #include "ardour/port.h"
35 #include "ardour/session.h"
37 #include "plugin_pin_dialog.h"
38 #include "gui_thread.h"
40 #include "ui_config.h"
44 using namespace ARDOUR;
48 using namespace Gtkmm2ext;
50 PluginPinDialog::PluginPinDialog (boost::shared_ptr<ARDOUR::PluginInsert> pi)
51 : ArdourWindow (string_compose (_("Pin Configuration: %1"), pi->name ()))
52 , _set_config (_("Configure"), ArdourButton::led_default_elements)
53 , _tgl_sidechain (_("Side Chain"), ArdourButton::led_default_elements)
54 , _add_plugin (_("+"))
55 , _del_plugin (_("-"))
56 , _add_output_audio (_("+"))
57 , _del_output_audio (_("-"))
58 , _add_output_midi (_("+"))
59 , _del_output_midi (_("-"))
60 , _add_sc_audio (_("Audio"))
61 , _add_sc_midi (_("MIDI"))
73 , _position_valid (false)
74 , _ignore_updates (false)
75 , _sidechain_selector (0)
78 assert (pi->owner ()); // Route
80 _pi->PluginIoReConfigure.connect (
81 _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
84 _pi->PluginMapChanged.connect (
85 _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
88 _pi->PluginConfigChanged.connect (
89 _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
92 _pin_box_size = 2 * ceil (max (8., 10. * UIConfiguration::instance ().get_ui_scale ()) * .5);
93 _margin_x = 2 * ceil (max (24., 28. * UIConfiguration::instance ().get_ui_scale ()) * .5);
94 _margin_y = 2 * ceil (max (36., 40. * UIConfiguration::instance ().get_ui_scale ()) * .5);
96 _tgl_sidechain.set_name ("pinrouting sidechain");
97 _set_config.set_name ("pinrouting custom");
99 Menu_Helpers::MenuList& citems = reset_menu.items();
100 reset_menu.set_name ("ArdourContextMenu");
102 citems.push_back (Menu_Helpers::MenuElem (_("Reset"), sigc::mem_fun (*this, &PluginPinDialog::reset_mapping)));
104 _pm_size_group = SizeGroup::create (SIZE_GROUP_BOTH);
105 _add_plugin.set_tweaks (ArdourButton::Square);
106 _del_plugin.set_tweaks (ArdourButton::Square);
107 _pm_size_group->add_widget (_add_plugin);
108 _pm_size_group->add_widget (_del_plugin);
109 _pm_size_group->add_widget (_add_output_audio);
110 _pm_size_group->add_widget (_del_output_audio);
111 _pm_size_group->add_widget (_add_output_midi);
112 _pm_size_group->add_widget (_del_output_midi);
117 VBox* tl = manage (new VBox ());
118 tl->set_border_width (2);
121 VBox* tr = manage (new VBox ());
122 tr->set_border_width (2);
126 tl->pack_start (_set_config, false, false);
127 tl->pack_start (*manage (new Label ("")), true, true); // invisible separator
129 box = manage (new HBox ());
130 box->set_border_width (2);
131 box->pack_start (_add_plugin, true, false);
132 box->pack_start (_del_plugin, true, false);
133 f = manage (new Frame());
134 f->set_label (_("Instances"));
136 tl->pack_start (*f, false, false);
138 box = manage (new HBox ());
139 box->set_border_width (2);
140 box->pack_start (_add_output_audio, true, false);
141 box->pack_start (_del_output_audio, true, false);
142 f = manage (new Frame());
143 f->set_label (_("Audio Out"));
145 tl->pack_start (*f, false, false);
147 box = manage (new HBox ());
148 box->set_border_width (2);
149 box->pack_start (_add_output_midi, true, false);
150 box->pack_start (_del_output_midi, true, false);
151 f = manage (new Frame());
152 f->set_label (_("MIDI Out"));
154 tl->pack_start (*f, false, false);
158 _sidechain_tbl = manage (new Gtk::Table ());
159 _sidechain_tbl->set_spacings (2);
161 tr->pack_start (_tgl_sidechain, false, false);
162 tr->pack_start (*_sidechain_tbl, true, true);
164 box = manage (new VBox ());
165 box->set_border_width (2);
166 box->set_spacing (2);
167 box->pack_start (_add_sc_audio, false, false);
168 box->pack_start (_add_sc_midi , false, false);
169 f = manage (new Frame());
170 f->set_label (_("Add Sidechain Input"));
173 tr->pack_start (*f, false, false);
176 HBox* hbox = manage (new HBox ());
177 hbox->set_spacing (4);
178 hbox->pack_start (*tl, false, false);
179 hbox->pack_start (darea, true, true);
180 hbox->pack_start (*tr, false, false);
182 VBox* vbox = manage (new VBox ());
183 vbox->pack_start (*hbox, true, true);
184 set_border_width (4);
188 plugin_reconfigured ();
190 darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
191 darea.signal_size_request ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_request));
192 darea.signal_size_allocate ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_allocate));
193 darea.signal_expose_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_expose_event));
194 darea.signal_button_press_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_press_event));
195 darea.signal_button_release_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_release_event));
196 darea.signal_motion_notify_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_motion_notify_event));
198 _tgl_sidechain.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::toggle_sidechain));
200 _set_config.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::reset_configuration));
201 _add_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), true));
202 _del_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), false));
204 _add_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::AUDIO));
205 _del_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::AUDIO));
206 _add_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::MIDI));
207 _del_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::MIDI));
208 _add_sc_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_sidechain_port), DataType::AUDIO));
209 _add_sc_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_sidechain_port), DataType::MIDI));
211 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
212 _io_connection, invalidator (*this), boost::bind (&PluginPinDialog::port_connected_or_disconnected, this, _1, _3), gui_context ()
216 PluginPinDialog::~PluginPinDialog ()
218 delete _sidechain_selector;
222 PluginPinDialog::plugin_reconfigured ()
224 ENSURE_GUI_THREAD (*this, &PluginPinDialog::plugin_reconfigured);
225 if (_ignore_updates) {
228 _n_plugins = _pi->get_count ();
229 _pi->configured_io (_in, _out);
230 _ins = _pi->internal_streams (); // with sidechain
231 _sinks = _pi->natural_input_streams ();
232 _sources = _pi->natural_output_streams ();
234 _tgl_sidechain.set_active (_pi->has_sidechain ());
235 _add_sc_audio.set_sensitive (_pi->has_sidechain ());
236 _add_sc_midi.set_sensitive (_pi->has_sidechain ());
238 if (_pi->custom_cfg ()) {
239 _set_config.set_active (true);
240 _add_plugin.set_sensitive (true);
241 _add_output_audio.set_sensitive (true);
242 _add_output_midi.set_sensitive (true);
243 _del_plugin.set_sensitive (_n_plugins > 1);
244 _del_output_audio.set_sensitive (_out.n_audio () > 0 && _out.n_total () > 1);
245 _del_output_midi.set_sensitive (_out.n_midi () > 0 && _out.n_total () > 1);
247 _set_config.set_active (false);
248 _add_plugin.set_sensitive (false);
249 _add_output_audio.set_sensitive (false);
250 _add_output_midi.set_sensitive (false);
251 _del_plugin.set_sensitive (false);
252 _del_output_audio.set_sensitive (false);
253 _del_output_midi.set_sensitive (false);
256 if (!_pi->has_sidechain () && _sidechain_selector) {
257 delete _sidechain_selector;
258 _sidechain_selector = 0;
261 refill_sidechain_table ();
263 /* update elements */
272 _n_inputs = _n_sidechains = 0;
274 for (uint32_t i = 0; i < _ins.n_total (); ++i) {
275 DataType dt = i < _ins.n_midi () ? DataType::MIDI : DataType::AUDIO;
276 uint32_t id = dt == DataType::MIDI ? i : i - _ins.n_midi ();
277 bool sidechain = id >= _in.get (dt) ? true : false;
284 CtrlWidget cw (CtrlWidget ("", Input, dt, id, 0, sidechain));
285 _elements.push_back (cw);
288 for (uint32_t i = 0; i < _out.n_total (); ++i) {
289 int id = (i < _out.n_midi ()) ? i : i - _out.n_midi ();
290 _elements.push_back (CtrlWidget ("", Output, (i < _out.n_midi () ? DataType::MIDI : DataType::AUDIO), id));
293 for (uint32_t n = 0; n < _n_plugins; ++n) {
294 boost::shared_ptr<Plugin> plugin = _pi->plugin (n);
295 for (uint32_t i = 0; i < _sinks.n_total (); ++i) {
296 DataType dt (i < _sinks.n_midi () ? DataType::MIDI : DataType::AUDIO);
297 int idx = (dt == DataType::MIDI) ? i : i - _sinks.n_midi ();
298 const Plugin::IOPortDescription& iod (plugin->describe_io_port (dt, true, idx));
299 CtrlWidget cw (CtrlWidget (iod.name, Sink, dt, idx, n, iod.is_sidechain));
300 _elements.push_back (cw);
302 for (uint32_t i = 0; i < _sources.n_total (); ++i) {
303 DataType dt (i < _sources.n_midi () ? DataType::MIDI : DataType::AUDIO);
304 int idx = (dt == DataType::MIDI) ? i : i - _sources.n_midi ();
305 const Plugin::IOPortDescription& iod (plugin->describe_io_port (dt, false, idx));
306 _elements.push_back (CtrlWidget (iod.name, Source, dt, idx, n));
310 /* calc minimum size */
311 const uint32_t max_ports = std::max (_ins.n_total (), _out.n_total ());
312 const uint32_t max_pins = std::max ((_sinks * _n_plugins).n_total (), (_sources * _n_plugins).n_total ());
313 uint32_t min_width = std::max (25 * max_ports, (uint32_t)(20 + _pin_box_size) * max_pins);
314 min_width = std::max (min_width, (uint32_t)ceilf (_margin_y * .45 * _n_plugins * 16. / 9.)); // 16 : 9 aspect
315 min_width = std::max ((uint32_t)300, min_width);
317 min_width = 50 + 10 * ceilf (min_width / 10.f);
319 uint32_t min_height = 3.5 * _margin_y + 2 * (_n_sidechains + 1) * _pin_box_size;
320 min_height = std::max ((uint32_t)200, min_height);
321 min_height = 4 * ceilf (min_height / 4.f);
323 if (min_width != _min_width || min_height != _min_height) {
324 _min_width = min_width;
325 _min_height = min_height;
326 darea.queue_resize ();
329 _position_valid = false;
334 PluginPinDialog::refill_sidechain_table ()
336 Table_Helpers::TableList& kids = _sidechain_tbl->children ();
337 for (Table_Helpers::TableList::iterator i = kids.begin (); i != kids.end ();) {
340 _sidechain_tbl->resize (1, 1);
341 if (!_pi->has_sidechain () && _sidechain_selector) {
344 boost::shared_ptr<IO> io = _pi->sidechain_input ();
350 PortSet& p (io->ports ());
351 bool can_remove = p.num_ports () > 1;
352 for (PortSet::iterator i = p.begin (DataType::MIDI); i != p.end (DataType::MIDI); ++i, ++r) {
353 add_port_to_table (*i, r, can_remove);
355 for (PortSet::iterator i = p.begin (DataType::AUDIO); i != p.end (DataType::AUDIO); ++i, ++r) {
356 add_port_to_table (*i, r, can_remove);
358 _sidechain_tbl->show_all ();
362 PluginPinDialog::add_port_to_table (boost::shared_ptr<Port> p, uint32_t r, bool can_remove)
365 std::string tip = p->name ();
366 std::vector<std::string> cns;
367 p->get_connections (cns);
369 // TODO proper labels, see MixerStrip::update_io_button()
370 if (cns.size () == 0) {
372 } else if (cns.size () > 1) {
376 string lpn (PROGRAM_NAME);
377 boost::to_lower (lpn);
378 std::string program_port_prefix = lpn + ":"; // e.g. "ardour:"
382 if (lbl.find ("system:capture_") == 0) {
383 lbl = AudioEngine::instance ()->get_pretty_name_by_name (lbl);
385 lbl = cns[0].substr (15);
387 } else if (lbl.find("system:midi_capture_") == 0) {
388 lbl = AudioEngine::instance ()->get_pretty_name_by_name (lbl);
390 // "system:midi_capture_123" -> "123"
391 lbl = "M " + cns[0].substr (20);
393 } else if (lbl.find (program_port_prefix) == 0) {
394 lbl = lbl.substr (program_port_prefix.size());
397 for (std::vector<std::string>::const_iterator i = cns.begin(); i != cns.end(); ++i) {
401 replace_all (lbl, "_", " ");
403 ArdourButton *pb = manage (new ArdourButton (lbl));
404 pb->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
405 pb->set_layout_ellipsize_width (108 * PANGO_SCALE);
406 ARDOUR_UI_UTILS::set_tooltip (*pb, tip);
407 _sidechain_tbl->attach (*pb, 0, 1, r, r +1 , EXPAND|FILL, SHRINK);
409 pb->signal_button_press_event ().connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::sc_input_press), boost::weak_ptr<Port> (p)), false);
410 pb->signal_button_release_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::sc_input_release), false);
412 pb = manage (new ArdourButton ("-"));
413 _sidechain_tbl->attach (*pb, 1, 2, r, r +1 , FILL, SHRINK);
415 pb->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::remove_port), boost::weak_ptr<Port> (p)));
417 pb->set_sensitive (false);
422 PluginPinDialog::update_element_pos ()
425 _innerwidth = _width - 2. * _margin_x;
427 const double yc = rint (_height * .5);
428 const double bxh2 = rint (_margin_y * .45); // TODO grow?
429 const double bxw = rint ((_innerwidth * .95) / ((_n_plugins) + .2 * (_n_plugins - 1)));
430 const double bxw2 = rint (bxw * .5);
431 const double y_in = _margin_y;
432 const double y_out = _height - _margin_y;
437 const double dx = _pin_box_size * .5;
440 for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
444 i->x = _innerwidth + _margin_x - dx;
445 i->y = y_in + (sc_cnt + .5) * _pin_box_size;
446 i->h = _pin_box_size;
447 i->w = 1.5 * _pin_box_size;
450 uint32_t idx = i->e->id;
451 if (i->e->dt == DataType::AUDIO) { idx += _in.n_midi (); }
452 i->x = rint ((idx + 1) * _width / (1. + _n_inputs)) - 0.5 - dx;
453 i->w = _pin_box_size;
454 i->h = 1.5 * _pin_box_size;
460 uint32_t idx = i->e->id;
461 if (i->e->dt == DataType::AUDIO) { idx += _out.n_midi (); }
462 i->x = rint ((idx + 1) * _width / (1. + _out.n_total ())) - 0.5 - dx;
464 i->w = _pin_box_size;
465 i->h = 1.5 * _pin_box_size;
470 uint32_t idx = i->e->id;
471 if (i->e->dt == DataType::AUDIO) { idx += _sinks.n_midi (); }
472 const double x0 = rint ((i->e->ip + .5) * _innerwidth / (double)(_n_plugins)) - .5 - bxw2;
473 i->x = _margin_x + rint (x0 + (idx + 1) * bxw / (1. + _sinks.n_total ())) - .5 - dx;
474 i->y = yc - bxh2 - dx;
475 i->w = _pin_box_size;
476 i->h = _pin_box_size;
481 uint32_t idx = i->e->id;
482 if (i->e->dt == DataType::AUDIO) { idx += _sources.n_midi (); }
483 const double x0 = rint ((i->e->ip + .5) * _innerwidth / (double)(_n_plugins)) - .5 - bxw2;
484 i->x = _margin_x + rint (x0 + (idx + 1) * bxw / (1. + _sources.n_total ())) - .5 - dx;
485 i->y = yc + bxh2 - dx;
486 i->w = _pin_box_size;
487 i->h = _pin_box_size;
495 PluginPinDialog::set_color (cairo_t* cr, bool midi)
497 // see also gtk2_ardour/processor_box.cc
498 static const uint32_t audio_port_color = 0x4A8A0EFF; // Green
499 static const uint32_t midi_port_color = 0x960909FF; //Red
502 cairo_set_source_rgb (cr,
503 UINT_RGBA_R_FLT (midi_port_color),
504 UINT_RGBA_G_FLT (midi_port_color),
505 UINT_RGBA_B_FLT (midi_port_color));
507 cairo_set_source_rgb (cr,
508 UINT_RGBA_R_FLT (audio_port_color),
509 UINT_RGBA_G_FLT (audio_port_color),
510 UINT_RGBA_B_FLT (audio_port_color));
515 PluginPinDialog::draw_io_pin (cairo_t* cr, const CtrlWidget& w)
518 const double dy = w.h * .5;
519 const double dx = w.w - dy;
520 cairo_move_to (cr, w.x, w.y + dy);
521 cairo_rel_line_to (cr, dy, -dy);
522 cairo_rel_line_to (cr, dx, 0);
523 cairo_rel_line_to (cr, 0, w.h);
524 cairo_rel_line_to (cr, -dx, 0);
526 const double dir = (w.e->ct == Input) ? 1 : -1;
527 const double dx = w.w * .5;
528 const double dy = w.h - dx;
530 cairo_move_to (cr, w.x + dx, w.y + ((w.e->ct == Input) ? w.h : 0));
531 cairo_rel_line_to (cr, -dx, -dx * dir);
532 cairo_rel_line_to (cr, 0., -dy * dir);
533 cairo_rel_line_to (cr, 2. * dx, 0.);
534 cairo_rel_line_to (cr, 0., dy * dir);
536 cairo_close_path (cr);
538 cairo_set_line_width (cr, 1.0);
539 cairo_set_source_rgb (cr, 0, 0, 0);
540 cairo_stroke_preserve (cr);
542 set_color (cr, w.e->dt == DataType::MIDI);
545 assert (w.e->ct == Input);
546 cairo_fill_preserve (cr);
547 cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.4);
550 if (w.e == _selection || w.e == _actor) {
551 cairo_fill_preserve (cr);
552 cairo_set_source_rgba (cr, 0.9, 0.9, 1.0, 0.6);
553 } else if (w.prelight) {
554 cairo_fill_preserve (cr);
555 cairo_set_source_rgba (cr, 0.9, 0.9, 0.9, 0.3);
561 PluginPinDialog::draw_plugin_pin (cairo_t* cr, const CtrlWidget& w)
563 const double dx = w.w * .5;
564 const double dy = w.h * .5;
566 cairo_move_to (cr, w.x + dx, w.y);
567 cairo_rel_line_to (cr, -dx, dy);
568 cairo_rel_line_to (cr, dx, dy);
569 cairo_rel_line_to (cr, dx, -dy);
570 cairo_close_path (cr);
572 cairo_set_line_width (cr, 1.0);
573 cairo_set_source_rgb (cr, 0, 0, 0);
574 cairo_stroke_preserve (cr);
576 set_color (cr, w.e->dt == DataType::MIDI);
579 assert (w.e->ct == Sink);
580 cairo_fill_preserve (cr);
581 cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.4);
584 if (w.e == _selection || w.e == _actor) {
585 cairo_fill_preserve (cr);
586 cairo_set_source_rgba (cr, 0.9, 0.9, 1.0, 0.6);
587 } else if (w.prelight) {
588 cairo_fill_preserve (cr);
589 cairo_set_source_rgba (cr, 0.9, 0.9, 0.9, 0.3);
593 if ((w.prelight || w.e == _selection) && !w.name.empty()) {
596 Glib::RefPtr<Pango::Layout> layout;
597 layout = Pango::Layout::create (get_pango_context ());
598 layout->set_text (w.name);
599 layout->get_pixel_size (text_width, text_height);
601 rounded_rectangle (cr, w.x + dx - .5 * text_width - 2, w.y - text_height - 2, text_width + 4, text_height + 2, 7);
602 cairo_set_source_rgba (cr, 0, 0, 0, .5);
605 cairo_move_to (cr, w.x + dx - .5 * text_width, w.y - text_height - 1);
606 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
607 pango_cairo_show_layout (cr, layout->gobj ());
612 PluginPinDialog::pin_x_pos (uint32_t i, double x0, double width, uint32_t n_total, uint32_t n_midi, bool midi)
614 if (!midi) { i += n_midi; }
615 return rint (x0 + (i + 1) * width / (1. + n_total)) - .5;
618 const PluginPinDialog::CtrlWidget&
619 PluginPinDialog::get_io_ctrl (CtrlType ct, DataType dt, uint32_t id, uint32_t ip) const
621 for (CtrlElemList::const_iterator i = _elements.begin (); i != _elements.end (); ++i) {
622 if (i->e->ct == ct && i->e->dt == dt && i->e->id == id && i->e->ip == ip) {
627 fatal << string_compose (_("programming error: %1"),
628 X_("Invalid Plugin I/O Port."))
630 abort (); /*NOTREACHED*/
631 static CtrlWidget screw_old_compilers ("", Input, DataType::NIL, 0);
632 return screw_old_compilers;
636 PluginPinDialog::edge_coordinates (const CtrlWidget& w, double &x, double &y)
664 PluginPinDialog::draw_connection (cairo_t* cr, double x0, double x1, double y0, double y1, bool midi, bool horiz, bool dashed)
666 const double bz = 2 * _pin_box_size;
667 const double bc = (dashed && x0 == x1) ? 1.25 * _pin_box_size : 0;
669 cairo_move_to (cr, x0, y0);
671 cairo_curve_to (cr, x0 - bz, y0 + bc, x1 - bc, y1 - bz, x1, y1);
673 cairo_curve_to (cr, x0 - bc, y0 + bz, x1 - bc, y1 - bz, x1, y1);
675 cairo_set_line_width (cr, 3.0);
676 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
677 cairo_set_source_rgb (cr, 1, 0, 0);
679 const double dashes[] = { 5, 7 };
680 cairo_set_dash (cr, dashes, 2, 0);
682 set_color (cr, midi);
685 cairo_set_dash (cr, 0, 0, 0);
690 PluginPinDialog::draw_connection (cairo_t* cr, const CtrlWidget& w0, const CtrlWidget& w1, bool dashed)
692 double x0, x1, y0, y1;
693 edge_coordinates (w0, x0, y0);
694 edge_coordinates (w1, x1, y1);
695 assert (w0.e->dt == w1.e->dt);
696 draw_connection (cr, x0, x1, y0, y1, w0.e->dt == DataType::MIDI, w0.e->sc, dashed);
701 PluginPinDialog::darea_expose_event (GdkEventExpose* ev)
703 Gtk::Allocation a = darea.get_allocation ();
704 double const width = a.get_width ();
705 double const height = a.get_height ();
707 if (!_position_valid) {
710 update_element_pos ();
711 _position_valid = true;
714 cairo_t* cr = gdk_cairo_create (darea.get_window ()->gobj ());
715 cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
718 Gdk::Color const bg = get_style ()->get_bg (STATE_NORMAL);
719 cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
720 cairo_rectangle (cr, 0, 0, width, height);
723 const double yc = rint (_height * .5);
726 rounded_rectangle (cr, _margin_x, _margin_y - _pin_box_size * .5, _innerwidth, _height - 2 * _margin_y + _pin_box_size, 7);
727 cairo_set_line_width (cr, 1.0);
728 cairo_set_source_rgb (cr, .1, .1, .3);
729 cairo_stroke_preserve (cr);
730 cairo_set_source_rgb (cr, .3, .3, .3);
733 /* draw midi-bypass (behind) */
734 if (_pi->has_midi_bypass ()) {
735 const CtrlWidget& cw0 = get_io_ctrl (Input, DataType::MIDI, 0);
736 const CtrlWidget& cw1 = get_io_ctrl (Output, DataType::MIDI, 0);
737 draw_connection (cr, cw0, cw1, true);
740 /* thru connections */
741 const ChanMapping::Mappings thru_map = _pi->thru_map ().mappings ();
742 for (ChanMapping::Mappings::const_iterator t = thru_map.begin (); t != thru_map.end (); ++t) {
743 for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
744 const CtrlWidget& cw0 = get_io_ctrl (Output, t->first, c->first);
745 const CtrlWidget& cw1 = get_io_ctrl (Input, t->first, c->second);
746 if (!(_dragging && cw1.e == _selection && cw0.e == _drag_dst)) {
747 draw_connection (cr, cw1, cw0, true);
753 Glib::RefPtr<Pango::Layout> layout;
754 layout = Pango::Layout::create (get_pango_context ());
756 layout->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
757 layout->set_width (_height * PANGO_SCALE);
762 layout->set_text (_route ()->name ());
763 layout->get_pixel_size (text_width, text_height);
765 cairo_move_to (cr, .5 * (_margin_x - text_height), .5 * (_height + text_width));
766 cairo_rotate (cr, M_PI * -.5);
767 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
768 pango_cairo_show_layout (cr, layout->gobj ());
772 layout->set_width ((_innerwidth - 2 * _pin_box_size) * PANGO_SCALE);
773 layout->set_text (_pi->name ());
774 layout->get_pixel_size (text_width, text_height);
775 cairo_move_to (cr, _margin_x + _innerwidth - text_width - _pin_box_size * .5, _height - _margin_y - text_height);
776 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
777 pango_cairo_show_layout (cr, layout->gobj ());
779 if (_pi->strict_io ()) {
780 layout->set_text (_("Strict I/O"));
781 layout->get_pixel_size (text_width, text_height);
782 const double sx0 = _margin_x + .5 * (_innerwidth - text_width);
783 const double sy0 = _height - 3 - text_height;
785 rounded_rectangle (cr, sx0 - 2, sy0 - 1, text_width + 4, text_height + 2, 7);
786 cairo_set_source_rgba (cr, .4, .3, .1, 1.);
789 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
790 cairo_move_to (cr, sx0, sy0);
791 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
792 pango_cairo_show_layout (cr, layout->gobj ());
796 /* plugins & connection wires */
797 for (uint32_t i = 0; i < _n_plugins; ++i) {
798 double x0 = _margin_x + rint ((i + .5) * _innerwidth / (double)(_n_plugins)) - .5;
801 cairo_set_source_rgb (cr, .5, .5, .5);
802 rounded_rectangle (cr, x0 - _bxw2, yc - _bxh2, 2 * _bxw2, 2 * _bxh2, 7);
805 const ChanMapping::Mappings in_map = _pi->input_map (i).mappings ();
806 const ChanMapping::Mappings out_map = _pi->output_map (i).mappings ();
808 for (ChanMapping::Mappings::const_iterator t = in_map.begin (); t != in_map.end (); ++t) {
809 for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
810 const CtrlWidget& cw0 = get_io_ctrl (Input, t->first, c->second);
811 const CtrlWidget& cw1 = get_io_ctrl (Sink, t->first, c->first, i);
812 if (!(_dragging && cw0.e == _selection && cw1.e == _drag_dst)) {
813 draw_connection (cr, cw0, cw1);
818 for (ChanMapping::Mappings::const_iterator t = out_map.begin (); t != out_map.end (); ++t) {
819 for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
820 const CtrlWidget& cw0 = get_io_ctrl (Source, t->first, c->first, i);
821 const CtrlWidget& cw1 = get_io_ctrl (Output, t->first, c->second);
822 if (!(_dragging && cw0.e == _selection && cw1.e == _drag_dst)) {
823 draw_connection (cr, cw0, cw1);
830 for (CtrlElemList::const_iterator i = _elements.begin (); i != _elements.end (); ++i) {
834 draw_io_pin (cr, *i);
838 draw_plugin_pin (cr, *i);
844 CtrlWidget *drag_src = NULL;
846 for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
847 if (i->e == _selection ) {
855 if (_selection->ct == Input || _selection->ct == Source) {
856 edge_coordinates (*drag_src, x0, y0);
857 draw_connection (cr, x0, _drag_x, y0, _drag_y,
858 _selection->dt == DataType::MIDI, _selection->sc);
860 edge_coordinates (*drag_src, x0, y0);
861 draw_connection (cr, _drag_x, x0, _drag_y, y0,
862 _selection->dt == DataType::MIDI, _selection->sc);
871 PluginPinDialog::darea_size_request (Gtk::Requisition* req)
873 req->width = _min_width;
874 req->height = _min_height;
878 PluginPinDialog::darea_size_allocate (Gtk::Allocation&)
880 _position_valid = false;
884 PluginPinDialog::drag_type_matches (const CtrlElem& e)
886 if (!_dragging || !_selection) {
889 if (_selection->dt != e->dt) {
892 if (_selection->ct == Input && e->ct == Sink) { return true; }
893 if (_selection->ct == Sink && e->ct == Input) { return true; }
894 if (_selection->ct == Output && e->ct == Source) { return true; }
895 if (_selection->ct == Source && e->ct == Output) { return true; }
896 if (_selection->ct == Input && e->ct == Output) { return true; }
897 if (_selection->ct == Output && e->ct == Input) { return true; }
902 PluginPinDialog::start_drag (const CtrlElem& e, double x, double y)
904 assert (_selection == e);
908 const ChanMapping& map (_pi->input_map (e->ip));
909 uint32_t idx = map.get (e->dt, e->id, &valid);
911 const CtrlWidget& cw = get_io_ctrl (Input, e->dt, idx, 0);
916 else if (e->ct == Output) {
917 for (uint32_t i = 0; i < _n_plugins; ++i) {
919 const ChanMapping& map (_pi->output_map (i));
920 uint32_t idx = map.get_src (e->dt, e->id, &valid);
922 const CtrlWidget& cw = get_io_ctrl (Source, e->dt, idx, i);
930 const ChanMapping& map (_pi->thru_map ());
931 uint32_t idx = map.get (e->dt, e->id, &valid);
933 const CtrlWidget& cw = get_io_ctrl (Input, e->dt, idx, 0);
945 PluginPinDialog::darea_motion_notify_event (GdkEventMotion* ev)
947 bool changed = false;
949 for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
950 if (ev->x >= i->x && ev->x <= i->x + i->w
951 && ev->y >= i->y && ev->y <= i->y + i->h
952 && drag_type_matches (i->e))
954 if (!i->prelight) changed = true;
958 if (i->prelight) changed = true;
966 if (changed || _dragging) {
973 PluginPinDialog::darea_button_press_event (GdkEventButton* ev)
975 if (ev->type != GDK_BUTTON_PRESS) {
979 switch (ev->button) {
982 if (!_selection || (_selection && !_hover)) {
986 start_drag (_selection, ev->x, ev->y);
989 } else if (_selection && _hover && _selection != _hover) {
990 if (_selection->dt != _hover->dt) { _actor.reset (); }
991 else if (_selection->ct == Input && _hover->ct == Sink) { _actor = _hover; }
992 else if (_selection->ct == Sink && _hover->ct == Input) { _actor = _hover; }
993 else if (_selection->ct == Output && _hover->ct == Source) { _actor = _hover; }
994 else if (_selection->ct == Source && _hover->ct == Output) { _actor = _hover; }
995 else if (_selection->ct == Input && _hover->ct == Output) { _actor = _hover; }
996 else if (_selection->ct == Output && _hover->ct == Input) { _actor = _hover; }
999 start_drag (_selection, ev->x, ev->y);
1001 darea.queue_draw ();
1002 } else if (_hover) {
1003 _selection = _hover;
1005 start_drag (_selection, ev->x, ev->y);
1010 if (_selection != _hover) {
1011 _selection = _hover;
1012 darea.queue_draw ();
1024 PluginPinDialog::darea_button_release_event (GdkEventButton* ev)
1026 if (_dragging && _selection && _drag_dst && _drag_dst == _hover) {
1027 // select click. (or re-connect same)
1028 assert (_selection != _hover);
1033 darea.queue_draw ();
1037 if (_dragging && _hover && _hover != _selection) {
1041 if (_hover == _actor && _actor && ev->button == 1) {
1042 assert (_selection);
1043 assert (_selection->dt == _actor->dt);
1045 assert (_dragging && _selection != _drag_dst);
1046 handle_disconnect (_drag_dst, true);
1048 if (_selection->ct == Input && _actor->ct == Sink) {
1049 handle_input_action (_actor, _selection);
1051 else if (_selection->ct == Sink && _actor->ct == Input) {
1052 handle_input_action (_selection, _actor);
1054 else if (_selection->ct == Output && _actor->ct == Source) {
1055 handle_output_action (_actor, _selection);
1057 else if (_selection->ct == Source && _actor->ct == Output) {
1058 handle_output_action (_selection, _actor);
1060 else if (_selection->ct == Input && _actor->ct == Output) {
1061 handle_thru_action (_actor, _selection);
1063 else if (_selection->ct == Output && _actor->ct == Input) {
1064 handle_thru_action (_selection, _actor);
1066 _selection.reset ();
1067 } else if (_hover == _selection && _selection && ev->button == 3) {
1068 handle_disconnect (_selection);
1069 } else if (!_hover && ev->button == 3) {
1070 reset_menu.popup (1, ev->time);
1073 if (_dragging && _hover != _selection) {
1074 _selection.reset ();
1079 darea.queue_draw ();
1084 PluginPinDialog::handle_input_action (const CtrlElem &s, const CtrlElem &i)
1086 const int pc = s->ip;
1088 ChanMapping in_map (_pi->input_map (pc));
1089 uint32_t idx = in_map.get (s->dt, s->id, &valid);
1091 if (valid && idx == i->id) {
1094 in_map.unset (s->dt, s->id);
1095 _pi->set_input_map (pc, in_map);
1097 plugin_reconfigured ();
1102 in_map.set (s->dt, s->id, i->id);
1103 _pi->set_input_map (pc, in_map);
1107 in_map.unset (s->dt, s->id);
1108 in_map.set (s->dt, s->id, i->id);
1109 _pi->set_input_map (pc, in_map);
1114 PluginPinDialog::disconnect_other_outputs (uint32_t skip_pc, DataType dt, uint32_t id)
1116 _ignore_updates = true;
1117 for (uint32_t n = 0; n < _n_plugins; ++n) {
1122 ChanMapping n_out_map (_pi->output_map (n));
1123 uint32_t idx = n_out_map.get_src (dt, id, &valid);
1125 n_out_map.unset (dt, idx);
1126 _pi->set_output_map (n, n_out_map);
1129 _ignore_updates = false;
1133 PluginPinDialog::disconnect_other_thru (DataType dt, uint32_t id)
1135 _ignore_updates = true;
1137 ChanMapping n_thru_map (_pi->thru_map ());
1138 n_thru_map.get (dt, id, &valid);
1140 n_thru_map.unset (dt, id);
1141 _pi->set_thru_map (n_thru_map);
1143 _ignore_updates = false;
1147 PluginPinDialog::handle_output_action (const CtrlElem &s, const CtrlElem &o)
1149 const uint32_t pc = s->ip;
1151 ChanMapping out_map (_pi->output_map (pc));
1152 uint32_t idx = out_map.get (s->dt, s->id, &valid);
1154 if (valid && idx == o->id) {
1157 out_map.unset (s->dt, s->id);
1158 _pi->set_output_map (pc, out_map);
1160 plugin_reconfigured ();
1164 // disconnect source
1165 disconnect_other_outputs (pc, s->dt, o->id);
1166 disconnect_other_thru (s->dt, o->id);
1167 out_map = _pi->output_map (pc); // re-read map
1169 out_map.unset (s->dt, s->id);
1171 idx = out_map.get_src (s->dt, o->id, &valid);
1173 out_map.unset (s->dt, idx);
1176 out_map.set (s->dt, s->id, o->id);
1177 _pi->set_output_map (pc, out_map);
1182 PluginPinDialog::handle_thru_action (const CtrlElem &o, const CtrlElem &i)
1185 ChanMapping thru_map (_pi->thru_map ());
1186 uint32_t idx = thru_map.get (o->dt, o->id, &valid);
1188 if (valid && idx == i->id) {
1190 thru_map.unset (o->dt, o->id);
1193 // disconnect other outputs first
1194 disconnect_other_outputs (UINT32_MAX, o->dt, o->id);
1195 disconnect_other_thru (o->dt, o->id);
1196 thru_map = _pi->thru_map (); // re-read map
1198 thru_map.set (o->dt, o->id, i->id);
1200 _pi->set_thru_map (thru_map);
1204 PluginPinDialog::handle_disconnect (const CtrlElem &e, bool no_signal)
1206 _ignore_updates = true;
1207 bool changed = false;
1213 ChanMapping n_thru_map (_pi->thru_map ());
1214 for (uint32_t i = 0; i < _sources.n_total (); ++i) {
1215 uint32_t idx = n_thru_map.get (e->dt, i, &valid);
1216 if (valid && idx == e->id) {
1217 n_thru_map.unset (e->dt, i);
1222 _pi->set_thru_map (n_thru_map);
1225 for (uint32_t n = 0; n < _n_plugins; ++n) {
1226 ChanMapping map (_pi->input_map (n));
1227 for (uint32_t i = 0; i < _sinks.n_total (); ++i) {
1228 uint32_t idx = map.get (e->dt, i, &valid);
1229 if (valid && idx == e->id) {
1230 map.unset (e->dt, i);
1234 _pi->set_input_map (n, map);
1239 ChanMapping map (_pi->input_map (e->ip));
1240 map.get (e->dt, e->id, &valid);
1242 map.unset (e->dt, e->id);
1243 _pi->set_input_map (e->ip, map);
1250 ChanMapping map (_pi->output_map (e->ip));
1251 map.get (e->dt, e->id, &valid);
1253 map.unset (e->dt, e->id);
1254 _pi->set_output_map (e->ip, map);
1260 for (uint32_t n = 0; n < _n_plugins; ++n) {
1261 ChanMapping map (_pi->output_map (n));
1262 for (uint32_t i = 0; i < _sources.n_total (); ++i) {
1263 uint32_t idx = map.get (e->dt, i, &valid);
1264 if (valid && idx == e->id) {
1265 map.unset (e->dt, i);
1270 _pi->set_output_map (n, map);
1274 ChanMapping n_thru_map (_pi->thru_map ());
1275 n_thru_map.get (e->dt, e->id, &valid);
1277 n_thru_map.unset (e->dt, e->id);
1279 _pi->set_thru_map (n_thru_map);
1284 _ignore_updates = false;
1285 if (changed && !no_signal) {
1286 plugin_reconfigured ();
1292 PluginPinDialog::toggle_sidechain ()
1294 if (_session && _session->actively_recording()) { return; }
1295 _route ()->add_remove_sidechain (_pi, !_pi->has_sidechain ());
1299 PluginPinDialog::connect_sidechain ()
1301 if (!_session) { return; }
1303 if (_sidechain_selector == 0) {
1304 _sidechain_selector = new IOSelectorWindow (_session, _pi->sidechain_input ());
1307 if (_sidechain_selector->is_visible ()) {
1308 _sidechain_selector->get_toplevel ()->get_window ()->raise ();
1310 _sidechain_selector->present ();
1315 PluginPinDialog::reset_configuration ()
1317 if (_set_config.get_active ()) {
1318 _route ()->reset_plugin_insert (_pi);
1320 _route ()->customize_plugin_insert (_pi, _n_plugins, _out);
1325 PluginPinDialog::reset_mapping ()
1331 PluginPinDialog::add_remove_plugin_clicked (bool add)
1333 if (_session && _session->actively_recording()) { return; }
1334 ChanCount out = _out;
1335 assert (add || _n_plugins > 0);
1336 _route ()->customize_plugin_insert (_pi, _n_plugins + (add ? 1 : -1), out);
1340 PluginPinDialog::add_remove_port_clicked (bool add, ARDOUR::DataType dt)
1342 if (_session && _session->actively_recording()) { return; }
1343 ChanCount out = _out;
1344 assert (add || out.get (dt) > 0);
1345 out.set (dt, out.get (dt) + (add ? 1 : -1));
1346 _route ()->customize_plugin_insert (_pi, _n_plugins, out);
1350 PluginPinDialog::add_sidechain_port (DataType dt)
1352 if (_session && _session->actively_recording()) { return; }
1353 boost::shared_ptr<IO> io = _pi->sidechain_input ();
1357 io->add_port ("", this, dt);
1361 PluginPinDialog::remove_port (boost::weak_ptr<ARDOUR::Port> wp)
1363 if (_session && _session->actively_recording()) { return; }
1364 boost::shared_ptr<ARDOUR::Port> p = wp.lock ();
1365 boost::shared_ptr<IO> io = _pi->sidechain_input ();
1369 io->remove_port (p, this);
1373 PluginPinDialog::disconnect_port (boost::weak_ptr<ARDOUR::Port> wp)
1375 if (_session && _session->actively_recording()) { return; }
1376 boost::shared_ptr<ARDOUR::Port> p = wp.lock ();
1377 boost::shared_ptr<IO> io = _pi->sidechain_input ();
1381 p->disconnect_all ();
1385 PluginPinDialog::connect_port (boost::weak_ptr<ARDOUR::Port> wp0, boost::weak_ptr<ARDOUR::Port> wp1)
1387 if (_session && _session->actively_recording()) { return; }
1388 boost::shared_ptr<ARDOUR::Port> p0 = wp0.lock ();
1389 boost::shared_ptr<ARDOUR::Port> p1 = wp1.lock ();
1390 boost::shared_ptr<IO> io = _pi->sidechain_input ();
1391 if (!io || !p0 || !p1) {
1394 _ignore_updates = true;
1395 p0->disconnect_all ();
1396 _ignore_updates = false;
1397 p0->connect (p1->name ());
1401 PluginPinDialog::sc_input_release (GdkEventButton *ev)
1403 if (_session && _session->actively_recording()) { return false; }
1404 if (ev->button == 3) {
1405 connect_sidechain ();
1410 struct RouteCompareByName {
1411 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1412 return a->name().compare (b->name()) < 0;
1417 PluginPinDialog::sc_input_press (GdkEventButton *ev, boost::weak_ptr<ARDOUR::Port> wp)
1419 using namespace Menu_Helpers;
1420 if (!_session || _session->actively_recording()) { return false; }
1421 if (!_session->engine().connected()) { return false; }
1423 if (ev->button == 1) {
1424 MenuList& citems = input_menu.items();
1425 input_menu.set_name ("ArdourContextMenu");
1428 boost::shared_ptr<Port> p = wp.lock ();
1429 if (p && p->connected ()) {
1430 citems.push_back (MenuElem (_("Disconnect"), sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::disconnect_port), wp)));
1431 citems.push_back (SeparatorElem());
1435 // TODO add system inputs, too ?!
1436 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1437 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1438 for (uint32_t j = 0; j < i->nchannels ().n_total (); ++j) {
1440 //maybe_add_bundle_to_input_menu (*i, current);
1444 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1445 RouteList copy = *routes;
1446 copy.sort (RouteCompareByName ());
1448 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1449 added += maybe_add_route_to_input_menu (*i, p->type (), wp);
1453 citems.push_back (SeparatorElem());
1455 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*this, &PluginPinDialog::connect_sidechain)));
1456 input_menu.popup (1, ev->time);
1462 PluginPinDialog::maybe_add_route_to_input_menu (boost::shared_ptr<Route> r, DataType dt, boost::weak_ptr<Port> wp)
1465 using namespace Menu_Helpers;
1466 if (r->output () == _route()->output()) {
1470 if (_route ()->feeds_according_to_graph (r)) {
1474 MenuList& citems = input_menu.items();
1475 const IOVector& iov (r->all_outputs());
1477 for (IOVector::const_iterator o = iov.begin(); o != iov.end(); ++o) {
1478 boost::shared_ptr<IO> op = o->lock();
1482 PortSet& p (op->ports ());
1483 for (PortSet::iterator i = p.begin (dt); i != p.end (dt); ++i) {
1484 std::string n = i->name ();
1485 replace_all (n, "_", " ");
1486 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::connect_port), wp, boost::weak_ptr<Port> (*i))));
1494 PluginPinDialog::port_connected_or_disconnected (boost::weak_ptr<ARDOUR::Port> w0, boost::weak_ptr<ARDOUR::Port> w1)
1496 boost::shared_ptr<Port> p0 = w0.lock ();
1497 boost::shared_ptr<Port> p1 = w1.lock ();
1499 boost::shared_ptr<IO> io = _pi->sidechain_input ();
1500 if (!io) { return; }
1502 if (p0 && io->has_port (p0)) {
1503 plugin_reconfigured ();
1505 else if (p1 && io->has_port (p1)) {
1506 plugin_reconfigured ();