13
0
livetrax/gtk2_ardour/plugin_pin_dialog.cc

742 lines
22 KiB
C++

/*
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
* Copyright (C) 2011 Paul Davis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <gtkmm/table.h>
#include <gtkmm/box.h>
#include <gtkmm/label.h>
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/rgb_macros.h"
#include "ardour/plugin.h"
#include "io_selector.h"
#include "plugin_pin_dialog.h"
#include "gui_thread.h"
#include "ui_config.h"
#include "i18n.h"
using namespace ARDOUR;
using namespace PBD;
using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
PluginPinDialog::PluginPinDialog (boost::shared_ptr<ARDOUR::PluginInsert> pi)
: ArdourWindow (string_compose (_("Pin Configuration: %1"), pi->name ()))
, _ind_strict_io (_("Strict I/O"))
, _ind_customized (_("Customized"))
, _rst_config (_("Configuration"))
, _rst_mapping (_("Connections"))
, _tgl_sidechain (_("Enable"))
, _edt_sidechain (_("Connect"))
, _add_plugin (_("+"))
, _del_plugin (_("-"))
, _add_output_audio (_("+"))
, _del_output_audio (_("-"))
, _add_output_midi (_("+"))
, _del_output_midi (_("-"))
, _pi (pi)
, _pin_box_size (8)
, _min_width (300)
, _position_valid (false)
, _ignore_updates (false)
{
assert (pi->owner ()); // Route
_pi->PluginIoReConfigure.connect (
_plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
);
_pi->PluginMapChanged.connect (
_plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
);
_pi->PluginConfigChanged.connect (
_plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
);
// needs a better way: insensitive indicators
_ind_strict_io.set_sensitive (false); // track status
_ind_strict_io.set_name ("rude solo");
_ind_customized.set_sensitive (false); // plugin status
_ind_customized.set_name ("rude solo");
Label* l;
int r = 0;
Table* t = manage (new Table (4, 3));
t->set_border_width (0);
t->set_spacings (4);
l = manage (new Label (_("Track/Bus:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
l = manage (new Label ());
l->set_alignment (ALIGN_START);
l->set_padding (2, 1);
l->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
l->set_width_chars (24);
l->set_max_width_chars (24);
l->set_text (_route ()->name ());
t->attach (*l, 1, 3, r, r + 1);
++r;
l = manage (new Label (_("Plugin:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
l = manage (new Label ());
l->set_alignment (ALIGN_START);
l->set_padding (2, 1);
l->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
l->set_width_chars (24);
l->set_max_width_chars (24);
l->set_text (pi->name ());
t->attach (*l, 1, 3, r, r + 1);
++r;
l = manage (new Label (_("Status:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
t->attach (_ind_strict_io, 1, 2, r, r + 1, FILL, SHRINK);
t->attach (_ind_customized, 2, 3, r, r + 1, FILL, SHRINK);
++r;
l = manage (new Label (_("Reset:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
t->attach (_rst_mapping, 1, 2, r, r + 1, FILL, SHRINK);
t->attach (_rst_config, 2, 3, r, r + 1, FILL, SHRINK);
++r;
l = manage (new Label (_("Sidechain:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
t->attach (_tgl_sidechain, 1, 2, r, r + 1, FILL, SHRINK);
t->attach (_edt_sidechain, 2, 3, r, r + 1, FILL, SHRINK);
++r;
l = manage (new Label (_("Instances:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
t->attach (_add_plugin, 1, 2, r, r + 1, SHRINK, SHRINK);
t->attach (_del_plugin, 2, 3, r, r + 1, SHRINK, SHRINK);
++r;
l = manage (new Label (_("Audio Out:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
t->attach (_add_output_audio, 1, 2, r, r + 1, SHRINK, SHRINK);
t->attach (_del_output_audio, 2, 3, r, r + 1, SHRINK, SHRINK);
++r;
l = manage (new Label (_("Midi Out:"), ALIGN_END));
t->attach (*l, 0, 1, r, r + 1);
t->attach (_add_output_midi, 1, 2, r, r + 1, SHRINK, SHRINK);
t->attach (_del_output_midi, 2, 3, r, r + 1, SHRINK, SHRINK);
++r;
HBox* hbox = manage (new HBox);
hbox->pack_start (darea, true, true);
hbox->pack_start (*t, false, true);
VBox* vbox = manage (new VBox);
vbox->pack_start (*hbox, true, true);
add (*vbox);
vbox->show_all ();
plugin_reconfigured ();
darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
darea.signal_size_request ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_request));
darea.signal_size_allocate ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_allocate));
darea.signal_expose_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_expose_event));
darea.signal_button_press_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_press_event));
darea.signal_button_release_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_release_event));
darea.signal_motion_notify_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_motion_notify_event));
_tgl_sidechain.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::toggle_sidechain));
_edt_sidechain.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::connect_sidechain));
_rst_mapping.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::reset_mapping));
_rst_config.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::reset_configuration));
_add_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), true));
_del_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), false));
_add_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::AUDIO));
_del_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::AUDIO));
_add_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::MIDI));
_del_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::MIDI));
}
PluginPinDialog::~PluginPinDialog ()
{
}
void
PluginPinDialog::plugin_reconfigured ()
{
if (_ignore_updates) {
return;
}
_n_plugins = _pi->get_count ();
_pi->configured_io (_in, _out);
_ins = _pi->internal_streams (); // with sidechain
_sinks = _pi->natural_input_streams ();
_sources = _pi->natural_output_streams ();
_del_plugin.set_sensitive (_n_plugins > 1);
_del_output_audio.set_sensitive (_out.n_audio () > 0 && _out.n_total () > 1);
_del_output_midi.set_sensitive (_out.n_midi () > 0 && _out.n_total () > 1);
_ind_strict_io.set_active (_pi->strict_io ());
_ind_customized.set_active (_pi->custom_cfg ());
_tgl_sidechain.set_active (_pi->has_sidechain ());
_edt_sidechain.set_sensitive (_pi->has_sidechain ()); // && _session
// calc minimum width
const uint32_t max_ports = std::max (_ins.n_total (), _out.n_total ());
const uint32_t max_pins = std::max ((_sinks * _n_plugins).n_total (), (_sources * _n_plugins).n_total ());
uint32_t min_width = std::max (25 * max_ports, (uint32_t)(20 + _pin_box_size) * max_pins);
min_width = std::max (min_width, 64 * _n_plugins); // bxh2 = 18 ; aspect 16:9 (incl. 10% space)
min_width = std::max ((uint32_t)300, min_width);
min_width = 10 * ceilf (min_width / 10.f);
if (min_width != _min_width) {
_min_width = min_width;
darea.queue_resize ();
}
update_elements ();
}
void
PluginPinDialog::update_elements ()
{
_elements.clear ();
_hover.reset ();
_actor.reset ();
_selection.reset ();
for (uint32_t i = 0; i < _ins.n_total (); ++i) {
int id = (i < _ins.n_midi ()) ? i : i - _ins.n_midi ();
bool sidechain = i >= _in.n_total () ? true : false;
CtrlWidget cw (CtrlWidget (Input, (i < _ins.n_midi () ? DataType::MIDI : DataType::AUDIO), id, 0, sidechain));
_elements.push_back (cw);
}
for (uint32_t i = 0; i < _out.n_total (); ++i) {
int id = (i < _out.n_midi ()) ? i : i - _out.n_midi ();
_elements.push_back (CtrlWidget (Output, (i < _out.n_midi () ? DataType::MIDI : DataType::AUDIO), id));
}
for (uint32_t n = 0; n < _n_plugins; ++n) {
boost::shared_ptr<Plugin> plugin = _pi->plugin (n);
for (uint32_t i = 0; i < _sinks.n_total (); ++i) {
DataType dt (_sinks.n_midi () ? DataType::MIDI : DataType::AUDIO);
int idx = (i < _sinks.n_midi ()) ? i : i - _sinks.n_midi ();
const Plugin::IOPortDescription& iod (plugin->describe_io_port (dt, true, idx));
CtrlWidget cw (CtrlWidget (Sink, dt, i, n, iod.is_sidechain));
_elements.push_back (cw);
}
for (uint32_t i = 0; i < _sources.n_total (); ++i) {
_elements.push_back (CtrlWidget (Source, (i < _sources.n_midi () ? DataType::MIDI : DataType::AUDIO), i, n));
}
}
_position_valid = false;
darea.queue_draw ();
}
void
PluginPinDialog::update_element_pos ()
{
/* layout sizes */
const double yc = rint (_height * .5);
const double bxh2 = 18;
const double bxw = rint ((_width * .9) / ((_n_plugins) + .2 * (_n_plugins - 1)));
const double bxw2 = rint (bxw * .5);
const double y_in = 40;
const double y_out = _height - 40;
_pin_box_size = rint (max (6., 8. * UIConfiguration::instance ().get_ui_scale ()));
const double dx = ceil (_pin_box_size * .5);
for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
switch (i->e->ct) {
case Input:
{
uint32_t idx = i->e->id;
if (i->e->dt == DataType::AUDIO) { idx += _ins.n_midi (); }
i->x = rint ((idx + 1) * _width / (1. + _ins.n_total ())) - 0.5 - dx;
i->y = y_in - 25;
i->w = 10;
i->h = 25;
}
break;
case Output:
{
uint32_t idx = i->e->id;
if (i->e->dt == DataType::AUDIO) { idx += _out.n_midi (); }
i->x = rint ((idx + 1) * _width / (1. + _out.n_total ())) - 0.5 - dx;
i->y = y_out;
i->w = 10;
i->h = 25;
}
break;
case Sink:
{
const double x0 = rint ((i->e->ip + .5) * _width / (double)(_n_plugins)) - .5 - bxw2;
i->x = rint (x0 + (i->e->id + 1) * bxw / (1. + _sinks.n_total ())) - .5 - _pin_box_size * .5;
i->y = yc - bxh2 - _pin_box_size;
i->w = _pin_box_size + 1;
i->h = _pin_box_size;
}
break;
case Source:
{
const double x0 = rint ((i->e->ip + .5) * _width / (double)(_n_plugins)) - .5 - bxw2;
i->x = rint (x0 + (i->e->id + 1) * bxw / (1. + _sources.n_total ())) - .5 - _pin_box_size * .5;
i->y = yc + bxh2;
i->w = _pin_box_size + 1;
i->h = _pin_box_size;
}
break;
}
}
}
void
PluginPinDialog::set_color (cairo_t* cr, bool midi)
{
// see also gtk2_ardour/processor_box.cc
static const uint32_t audio_port_color = 0x4A8A0EFF; // Green
static const uint32_t midi_port_color = 0x960909FF; //Red
if (midi) {
cairo_set_source_rgb (cr,
UINT_RGBA_R_FLT (midi_port_color),
UINT_RGBA_G_FLT (midi_port_color),
UINT_RGBA_B_FLT (midi_port_color));
} else {
cairo_set_source_rgb (cr,
UINT_RGBA_R_FLT (audio_port_color),
UINT_RGBA_G_FLT (audio_port_color),
UINT_RGBA_B_FLT (audio_port_color));
}
}
void
PluginPinDialog::draw_io_pin (cairo_t* cr, const CtrlWidget& w)
{
const double dir = (w.e->ct == Input) ? 1 : -1;
const double dx = ceil (_pin_box_size * .5);
const double dy = min (36.0, 6. * dx);
cairo_move_to (cr, w.x + dx, w.y + ((w.e->ct == Input) ? 25 : 0));
cairo_rel_line_to (cr, -dx, -dx * dir);
cairo_rel_line_to (cr, 0., -dy * dir);
cairo_rel_line_to (cr, 2. * dx, 0.);
cairo_rel_line_to (cr, 0., dy * dir);
cairo_close_path (cr);
if (w.e->sc) {
assert (w.e->ct == Input);
// side-chain
cairo_set_source_rgb (cr, .1, .6, .7);
} else {
set_color (cr, w.e->dt == DataType::MIDI);
}
if (w.e == _selection || w.e == _actor) {
cairo_fill_preserve (cr);
cairo_set_source_rgba (cr, 0.9, 0.9, 1.0, 0.6);
} else if (w.prelight) {
cairo_fill_preserve (cr);
cairo_set_source_rgba (cr, 0.9, 0.9, 0.9, 0.3);
}
cairo_fill_preserve (cr);
cairo_set_line_width (cr, 1.0);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_stroke (cr);
}
void
PluginPinDialog::draw_plugin_pin (cairo_t* cr, const CtrlWidget& w)
{
cairo_rectangle (cr, w.x, w.y, w.w, w.h);
if (w.e->sc) {
assert (w.e->ct == Sink);
// side-chain
cairo_set_source_rgb (cr, .1, .6, .7);
} else {
set_color (cr, w.e->dt == DataType::MIDI);
}
if (w.e == _selection || w.e == _actor) {
cairo_fill_preserve (cr);
cairo_set_source_rgba (cr, 0.9, 0.9, 1.0, 0.6);
} else if (w.prelight) {
cairo_fill_preserve (cr);
cairo_set_source_rgba (cr, 0.9, 0.9, 0.9, 0.3);
}
cairo_fill (cr);
}
double
PluginPinDialog::pin_x_pos (uint32_t i, double x0, double width, uint32_t n_total, uint32_t n_midi, bool midi)
{
if (!midi) { i += n_midi; }
return rint (x0 + (i + 1) * width / (1. + n_total)) - .5;
}
void
PluginPinDialog::draw_connection (cairo_t* cr, double x0, double x1, double y0, double y1, bool midi, bool dashed)
{
const double bz = 2 * _pin_box_size;
const double bc = (dashed && x0 == x1) ? 1.25 * _pin_box_size : 0;
cairo_move_to (cr, x0, y0);
cairo_curve_to (cr, x0 - bc, y0 + bz, x1 - bc, y1 - bz, x1, y1);
cairo_set_line_width (cr, 3.0);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_set_source_rgb (cr, 1, 0, 0);
if (dashed) {
const double dashes[] = { 5, 7 };
cairo_set_dash (cr, dashes, 2, 0);
}
set_color (cr, midi);
cairo_stroke (cr);
if (dashed) {
cairo_set_dash (cr, 0, 0, 0);
}
}
bool
PluginPinDialog::darea_expose_event (GdkEventExpose* ev)
{
Gtk::Allocation a = darea.get_allocation ();
double const width = a.get_width ();
double const height = a.get_height ();
if (!_position_valid) {
_width = width;
_height = height;
update_element_pos ();
_position_valid = true;
}
cairo_t* cr = gdk_cairo_create (darea.get_window ()->gobj ());
cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
cairo_clip (cr);
Gdk::Color const bg = get_style ()->get_bg (STATE_NORMAL);
cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
cairo_rectangle (cr, 0, 0, width, height);
cairo_fill (cr);
/* layout sizes -- TODO consolidate w/ update_element_pos() */
// i/o pins
const double y_in = 40;
const double y_out = height - 40;
// plugin box(es)
const double yc = rint (height * .5);
const double bxh2 = 18;
const double bxw = rint ((width * .9) / ((_n_plugins) + .2 * (_n_plugins - 1)));
const double bxw2 = rint (bxw * .5);
const uint32_t pc_in = _ins.n_total ();
const uint32_t pc_in_midi = _ins.n_midi ();
const uint32_t pc_out = _out.n_total ();
const uint32_t pc_out_midi = _out.n_midi ();
/* draw midi-bypass (behind) */
if (_pi->has_midi_bypass ()) {
double x0 = rint (width / (1. + pc_in)) - .5;
double x1 = rint (width / (1. + pc_out)) - .5;
draw_connection (cr, x0, x1, y_in, y_out, true, true);
}
/* plugins & connection wires */
for (uint32_t i = 0; i < _n_plugins; ++i) {
double x0 = rint ((i + .5) * width / (double)(_n_plugins)) - .5;
/* plugin box */
cairo_set_source_rgb (cr, .3, .3, .3);
rounded_rectangle (cr, x0 - bxw2, yc - bxh2, bxw, 2 * bxh2, 7);
cairo_fill (cr);
const ChanMapping::Mappings in_map = _pi->input_map (i).mappings ();
const ChanMapping::Mappings out_map = _pi->output_map (i).mappings ();
for (ChanMapping::Mappings::const_iterator t = in_map.begin (); t != in_map.end (); ++t) {
bool is_midi = t->first == DataType::MIDI;
for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
uint32_t pn = (*c).first; // pin
uint32_t pb = (*c).second;
double c_x0 = pin_x_pos (pb, 0, width, pc_in, pc_in_midi, is_midi);
double c_x1 = pin_x_pos (pn, x0 - bxw2, bxw, _sinks.n_total (), _sinks.n_midi (), is_midi);
draw_connection (cr, c_x0, c_x1, y_in, yc - bxh2 - _pin_box_size, is_midi);
}
}
for (ChanMapping::Mappings::const_iterator t = out_map.begin (); t != out_map.end (); ++t) {
bool is_midi = t->first == DataType::MIDI;
for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
uint32_t pn = (*c).first; // pin
uint32_t pb = (*c).second;
double c_x0 = pin_x_pos (pn, x0 - bxw2, bxw, _sources.n_total (), _sources.n_midi (), is_midi);
double c_x1 = pin_x_pos (pb, 0, width, pc_out, pc_out_midi, is_midi);
draw_connection (cr, c_x0, c_x1, yc + bxh2 + _pin_box_size, y_out, is_midi);
}
}
}
/* pins and ports */
for (CtrlElemList::const_iterator i = _elements.begin (); i != _elements.end (); ++i) {
switch (i->e->ct) {
case Input:
case Output:
draw_io_pin (cr, *i);
break;
case Sink:
case Source:
draw_plugin_pin (cr, *i);
break;
}
}
cairo_destroy (cr);
return true;
}
void
PluginPinDialog::darea_size_request (Gtk::Requisition* req)
{
req->width = _min_width;
req->height = 200;
}
void
PluginPinDialog::darea_size_allocate (Gtk::Allocation&)
{
_position_valid = false;
}
bool
PluginPinDialog::darea_motion_notify_event (GdkEventMotion* ev)
{
bool changed = false;
_hover.reset ();
for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
if (ev->x >= i->x && ev->x <= i->x + i->w
&& ev->y >= i->y && ev->y <= i->y + i->h)
{
if (!i->prelight) changed = true;
i->prelight = true;
_hover = i->e;
} else {
if (i->prelight) changed = true;
i->prelight = false;
}
}
if (changed) {
darea.queue_draw ();
}
return true;
}
bool
PluginPinDialog::darea_button_press_event (GdkEventButton* ev)
{
if (ev->type != GDK_BUTTON_PRESS) {
return false;
}
switch (ev->button) {
case 1:
if (!_selection || (_selection && !_hover)) {
_selection = _hover;
_actor.reset ();
darea.queue_draw ();
} else if (_selection && _hover && _selection != _hover) {
if (_selection->dt != _hover->dt) { _actor.reset (); }
else if (_selection->ct == Input && _hover->ct == Sink) { _actor = _hover; }
else if (_selection->ct == Sink && _hover->ct == Input) { _actor = _hover; }
else if (_selection->ct == Output && _hover->ct == Source) { _actor = _hover; }
else if (_selection->ct == Source && _hover->ct == Output) { _actor = _hover; }
if (!_actor) {
_selection = _hover;
}
darea.queue_draw ();
}
case 3:
if (_hover) {
}
break;
default:
break;
}
return true;
}
bool
PluginPinDialog::darea_button_release_event (GdkEventButton* ev)
{
if (_hover == _actor && _actor) {
assert (_selection);
assert (_selection->dt == _actor->dt);
if (_selection->ct == Input && _actor->ct == Sink) {
handle_input_action (_actor, _selection);
}
else if (_selection->ct == Sink && _actor->ct == Input) {
handle_input_action (_selection, _actor);
}
else if (_selection->ct == Output && _actor->ct == Source) {
handle_output_action (_actor, _selection);
}
else if (_selection->ct == Source && _actor->ct == Output) {
handle_output_action (_selection, _actor);
}
_selection.reset ();
}
_actor.reset ();
darea.queue_draw ();
return true;
}
void
PluginPinDialog::handle_input_action (const CtrlElem &s, const CtrlElem &i)
{
const int pc = s->ip;
bool valid;
ChanMapping in_map (_pi->input_map (pc));
uint32_t idx = in_map.get (s->dt, s->id, &valid);
if (valid && idx == i->id) {
// disconnect
in_map.unset (s->dt, s->id);
_pi->set_input_map (pc, in_map);
}
else if (!valid) {
// connect
in_map.set (s->dt, s->id, i->id);
_pi->set_input_map (pc, in_map);
}
else {
// reconnect
in_map.unset (s->dt, s->id);
in_map.set (s->dt, s->id, i->id);
_pi->set_input_map (pc, in_map);
}
}
void
PluginPinDialog::handle_output_action (const CtrlElem &s, const CtrlElem &o)
{
const uint32_t pc = s->ip;
bool valid;
ChanMapping out_map (_pi->output_map (pc));
uint32_t idx = out_map.get (s->dt, s->id, &valid);
if (valid && idx == o->id) {
// disconnect
out_map.unset (s->dt, s->id);
_pi->set_output_map (pc, out_map);
}
else {
// disconnect source
if (valid) {
out_map.unset (s->dt, s->id);
}
// disconnect other outputs
_ignore_updates = true;
for (uint32_t n = 0; n < _n_plugins; ++n) {
if (n == pc) {
continue;
}
ChanMapping n_out_map (_pi->output_map (n));
idx = n_out_map.get_src (s->dt, o->id, &valid);
if (valid) {
n_out_map.unset (s->dt, idx);
_pi->set_output_map (n, n_out_map);
}
}
_ignore_updates = false;
idx = out_map.get_src (s->dt, o->id, &valid);
if (valid) {
out_map.unset (s->dt, idx);
}
// connect
out_map.set (s->dt, s->id, o->id);
_pi->set_output_map (pc, out_map);
}
}
void
PluginPinDialog::toggle_sidechain ()
{
_route ()->add_remove_sidechain (_pi, !_pi->has_sidechain ());
}
void
PluginPinDialog::connect_sidechain ()
{
if (!_session) { return; }
// TODO non-modal would be cooler ... :)
SideChainUI sc (*this, _session, _pi->sidechain_input ());
sc.run ();
}
void
PluginPinDialog::reset_configuration ()
{
_route ()->reset_plugin_insert (_pi);
}
void
PluginPinDialog::reset_mapping ()
{
_pi->reset_map ();
}
void
PluginPinDialog::add_remove_plugin_clicked (bool add)
{
ChanCount out = _out;
assert (add || _n_plugins > 0);
_route ()->customize_plugin_insert (_pi, _n_plugins + (add ? 1 : -1), out);
}
void
PluginPinDialog::add_remove_port_clicked (bool add, ARDOUR::DataType dt)
{
ChanCount out = _out;
assert (add || out.get (dt) > 0);
out.set (dt, out.get (dt) + (add ? 1 : -1));
_route ()->customize_plugin_insert (_pi, _n_plugins, out);
}
SideChainUI::SideChainUI (Gtk::Window& p, Session* session, boost::shared_ptr<IO> sc)
: ArdourDialog (p, string (_("Sidechain ")) + sc->name (), true)
{
HBox* hbox = manage (new HBox);
IOSelector * io = Gtk::manage (new IOSelector (this, session, sc));
hbox->pack_start (*io, true, true);
get_vbox ()->pack_start (*hbox, true, true);
io->show ();
hbox->show ();
}