From 62d2d22b4425dabe22dc2a97f2b375907dc0f65e Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 27 Mar 2016 21:35:42 +0200 Subject: [PATCH] get started with Pin Manager - currently display-only. --- gtk2_ardour/ardour.menus.in | 2 + gtk2_ardour/plugin_pin_dialog.cc | 227 +++++++++++++++++++++++++++++++ gtk2_ardour/plugin_pin_dialog.h | 55 ++++++++ gtk2_ardour/processor_box.cc | 104 ++++++++++++++ gtk2_ardour/processor_box.h | 25 +++- gtk2_ardour/wscript | 1 + 6 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 gtk2_ardour/plugin_pin_dialog.cc create mode 100644 gtk2_ardour/plugin_pin_dialog.h diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index aeb5de249f..0f296bce15 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -654,6 +654,8 @@ + + diff --git a/gtk2_ardour/plugin_pin_dialog.cc b/gtk2_ardour/plugin_pin_dialog.cc new file mode 100644 index 0000000000..1e7259ba5c --- /dev/null +++ b/gtk2_ardour/plugin_pin_dialog.cc @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2016 Robin Gareus + * 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 "gtkmm2ext/utils.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 pi) + : ArdourWindow (string_compose (_("Pin Configuration: %1"), pi->name ())) + , _pi (pi) +{ + 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() + ); + + darea.signal_expose_event().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_expose_event)); + + // TODO min. width depending on # of pins. + darea.set_size_request(600, 200); + + VBox* vbox = manage (new VBox); + vbox->pack_start (darea, true, true); + add (*vbox); +} + +PluginPinDialog::~PluginPinDialog() +{ +} + +void +PluginPinDialog::plugin_reconfigured () +{ + darea.queue_draw (); +} + +void +PluginPinDialog::draw_io_pins (cairo_t* cr, double y0, double width, uint32_t n_total, uint32_t n_midi, bool input) +{ + double dir = input ? 1. : -1.; + + for (uint32_t i = 0; i < n_total; ++i) { + double x0 = rint ((i + 1) * width / (1. + n_total)) - .5; + cairo_move_to (cr, x0, y0); + cairo_rel_line_to (cr, -5., -5. * dir); + cairo_rel_line_to (cr, 0., -25. * dir); + cairo_rel_line_to (cr, 10., 0.); + cairo_rel_line_to (cr, 0., 25. * dir); + cairo_close_path (cr); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_stroke_preserve (cr); + + if (i < n_midi) { + cairo_set_source_rgb (cr, 1, 0, 0); + } else { + cairo_set_source_rgb (cr, 0, 1, 0); + } + 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_plugin_pins (cairo_t* cr, double x0, double y0, double width, uint32_t n_total, uint32_t n_midi, bool input) +{ + // see also ProcessorEntry::PortIcon::on_expose_event + const double dxy = rint (max (6., 8. * UIConfiguration::instance().get_ui_scale())); + + for (uint32_t i = 0; i < n_total; ++i) { + double x = rint (x0 + (i + 1) * width / (1. + n_total)) - .5; + cairo_rectangle (cr, x - dxy * .5, input ? y0 - dxy : y0, 1 + dxy, dxy); + + if (i < n_midi) { + cairo_set_source_rgb (cr, 1, 0, 0); + } else { + cairo_set_source_rgb (cr, 0, 1, 0); + } + + cairo_fill(cr); + } +} + +void +PluginPinDialog::draw_connection (cairo_t* cr, double x0, double x1, double y0, double y1, bool midi) +{ + cairo_move_to (cr, x0, y0); + cairo_curve_to (cr, x0, y0 + 10, x1, y1 - 10, x1, y1); + cairo_set_line_width (cr, 3.0); + cairo_set_source_rgb (cr, 1, 0, 0); + if (midi) { + cairo_set_source_rgb (cr, 1, 0, 0); + } else { + cairo_set_source_rgb (cr, 0, 1, 0); + } + cairo_stroke (cr); +} + +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(); + + 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); + + ChanCount in, out; // actual configured i/o + _pi->configured_io (in, out); + + ChanCount sinks = _pi->natural_input_streams (); + ChanCount sources = _pi->natural_output_streams (); + uint32_t plugins = _pi->get_count (); + + /* layout sizes */ + // i/o pins + double y_in = 40; + double y_out = height - 40; + + // plugin box(es) + double yc = rint (height * .5); + double bxh2 = 18; + double bxw = rint ((width * .9) / ((plugins) + .2 * (plugins - 1))); + double bxw2 = rint (bxw * .5); + + // i/o pins + const uint32_t pc_in = in.n_total(); + const uint32_t pc_in_midi = in.n_midi(); + const uint32_t pc_out = out.n_total(); + const uint32_t pc_out_midi = out.n_midi(); + + cairo_set_line_width (cr, 1.0); + draw_io_pins (cr, y_in, width, pc_in, pc_in_midi, true); + draw_io_pins (cr, y_out, width, pc_out, pc_out_midi, false); + + // draw midi-bypass (behind) + if (sources.n_midi() == 0 && pc_in_midi > 0 && pc_out_midi > 0) { + 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); + } + + for (uint32_t i = 0; i < plugins; ++i) { + double x0 = rint ((i + .5) * width / (double)(plugins)) - .5; + + draw_plugin_pins (cr, x0 - bxw2, yc - bxh2, bxw, sinks.n_total (), sinks.n_midi (), true); + draw_plugin_pins (cr, x0 - bxw2, yc + bxh2, bxw, sources.n_total (), sources.n_midi (), false); + + 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, 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, y_out, is_midi); + } + } + } + + cairo_destroy (cr); + return true; +} diff --git a/gtk2_ardour/plugin_pin_dialog.h b/gtk2_ardour/plugin_pin_dialog.h new file mode 100644 index 0000000000..cb196a5bd0 --- /dev/null +++ b/gtk2_ardour/plugin_pin_dialog.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Robin Gareus + * + * 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. + */ + +#ifndef __gtkardour_plugin_pin_dialog_h__ +#define __gtkardour_plugin_pin_dialog_h__ + +#include +#include + +#include "pbd/stateful.h" +#include "pbd/signals.h" + +#include "ardour/plugin_insert.h" +#include "ardour/route.h" + +#include "ardour_window.h" + +class PluginPinDialog : public ArdourWindow +{ +public: + PluginPinDialog (boost::shared_ptr); + ~PluginPinDialog (); + +private: + Gtk::DrawingArea darea; + bool darea_expose_event (GdkEventExpose* event); + + void plugin_reconfigured (); + + void draw_io_pins (cairo_t*, double, double, uint32_t, uint32_t, bool); + void draw_plugin_pins (cairo_t*, double, double, double, uint32_t, uint32_t, bool); + void draw_connection (cairo_t*, double, double, double, double, bool); + + double pin_x_pos (uint32_t, double, double, uint32_t, uint32_t, bool); + + PBD::ScopedConnectionList _plugin_connections; + boost::shared_ptr _pi; +}; + +#endif diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 6f01633ce4..3b49b61aae 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -70,6 +70,7 @@ #include "luainstance.h" #include "mixer_ui.h" #include "mixer_strip.h" +#include "plugin_pin_dialog.h" #include "plugin_selector.h" #include "plugin_ui.h" #include "port_insert_ui.h" @@ -106,6 +107,7 @@ RefPtr ProcessorBox::cut_action; RefPtr ProcessorBox::copy_action; RefPtr ProcessorBox::rename_action; RefPtr ProcessorBox::delete_action; +RefPtr ProcessorBox::manage_pins_action; RefPtr ProcessorBox::edit_action; RefPtr ProcessorBox::edit_generic_action; RefPtr ProcessorBox::processor_box_actions; @@ -1822,6 +1824,8 @@ ProcessorBox::show_processor_menu (int arg) pi = boost::dynamic_pointer_cast (single_selection->processor ()); } + manage_pins_action->set_sensitive (pi != 0); + /* allow editing with an Ardour-generated UI for plugin inserts with editors */ edit_action->set_sensitive (pi && pi->plugin()->has_editor ()); @@ -2330,6 +2334,7 @@ ProcessorBox::redisplay_processors () _route->foreach_processor (sigc::mem_fun (*this, &ProcessorBox::add_processor_to_display)); _route->foreach_processor (sigc::mem_fun (*this, &ProcessorBox::maybe_add_processor_to_ui_list)); + _route->foreach_processor (sigc::mem_fun (*this, &ProcessorBox::maybe_add_processor_pin_mgr)); setup_entry_positions (); } @@ -2385,6 +2390,31 @@ ProcessorBox::maybe_add_processor_to_ui_list (boost::weak_ptr w) WM::Manager::instance().register_window (wp); } +void +ProcessorBox::maybe_add_processor_pin_mgr (boost::weak_ptr w) +{ + boost::shared_ptr p = w.lock (); + if (!p || p->pinmgr_proxy ()) { + return; + } + + PluginPinWindowProxy* wp = new PluginPinWindowProxy ( + string_compose ("PM-%2-%3", _route->id(), p->id()), w); + + const XMLNode* ui_xml = _session->extra_xml (X_("UI")); + if (ui_xml) { + wp->set_state (*ui_xml, 0); + } + + void* existing_ui = p->get_ui (); + + if (existing_ui) { + wp->use_window (*(reinterpret_cast(existing_ui))); + } + + p->set_pingmgr_proxy (wp); + WM::Manager::instance().register_window (wp); +} void ProcessorBox::help_count_visible_prefader_processors (boost::weak_ptr p, uint32_t* cnt, bool* amp_seen) { @@ -3229,6 +3259,10 @@ ProcessorBox::register_actions () myactions.register_action (processor_box_actions, X_("ab_plugins"), _("A/B Plugins"), sigc::ptr_fun (ProcessorBox::rb_ab_plugins)); + manage_pins_action = myactions.register_action ( + processor_box_actions, X_("manage-pins"), _("Pin Management..."), + sigc::ptr_fun (ProcessorBox::rb_manage_pins)); + /* show editors */ edit_action = myactions.register_action ( processor_box_actions, X_("edit"), _("Edit..."), @@ -3261,6 +3295,15 @@ ProcessorBox::rb_ab_plugins () _current_processor_box->ab_plugins (); } +void +ProcessorBox::rb_manage_pins () +{ + if (_current_processor_box == 0) { + return; + } + + _current_processor_box->for_selected_processors (&ProcessorBox::manage_pins); +} void ProcessorBox::rb_choose_plugin () { @@ -3490,6 +3533,20 @@ ProcessorBox::generic_edit_processor (boost::shared_ptr processor) } } +void +ProcessorBox::manage_pins (boost::shared_ptr processor) +{ + if (!processor) { + return; + } + PluginPinWindowProxy* proxy = processor->pinmgr_proxy (); + if (proxy) { + proxy->get (true); + proxy->present(); + } +} + + void ProcessorBox::route_property_changed (const PropertyChange& what_changed) { @@ -3770,6 +3827,53 @@ ProcessorWindowProxy::show_the_right_window () toggle (); } + +PluginPinWindowProxy::PluginPinWindowProxy(std::string const &name, boost::weak_ptr processor) + : WM::ProxyBase (name, string()) + , _processor (processor) +{ + boost::shared_ptr p = _processor.lock (); + if (!p) { + return; + } + p->DropReferences.connect (going_away_connection, MISSING_INVALIDATOR, boost::bind (&PluginPinWindowProxy::processor_going_away, this), gui_context()); +} + +PluginPinWindowProxy::~PluginPinWindowProxy() +{ + _window = 0; +} + + +Gtk::Window* +PluginPinWindowProxy::get (bool create) +{ + boost::shared_ptr p = _processor.lock (); + boost::shared_ptr pi = boost::dynamic_pointer_cast (p); + if (!p || !pi) { + return 0; + } + + if (!_window) { + if (!create) { + return 0; + } + _window = new PluginPinDialog (pi); + } + + _window->show_all (); + return _window; +} + +void +PluginPinWindowProxy::processor_going_away () +{ + delete _window; + _window = 0; + WM::Manager::instance().remove (this); + going_away_connection.disconnect(); +} + void ProcessorBox::load_bindings () { diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 2df431139c..ef45b4c8b8 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -102,12 +102,31 @@ class ProcessorWindowProxy : public WM::ProxyBase boost::weak_ptr _processor; bool is_custom; bool want_custom; - bool _valid; void processor_going_away (); PBD::ScopedConnection going_away_connection; }; + +class PluginPinWindowProxy : public WM::ProxyBase +{ + public: + PluginPinWindowProxy (std::string const &, boost::weak_ptr); + ~PluginPinWindowProxy(); + + Gtk::Window* get (bool create = false); + ARDOUR::SessionHandlePtr* session_handle() { return 0; } + + private: + ProcessorBox* _processor_box; + boost::weak_ptr _processor; + + void processor_going_away (); + PBD::ScopedConnection going_away_connection; +}; + + + class ProcessorEntry : public Gtkmm2ext::DnDVBoxChild, public sigc::trackable { public: @@ -344,6 +363,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD Gtk::Window* get_editor_window (boost::shared_ptr, bool); Gtk::Window* get_generic_editor_window (boost::shared_ptr); + void manage_pins (boost::shared_ptr); void edit_processor (boost::shared_ptr); void generic_edit_processor (boost::shared_ptr); @@ -462,6 +482,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD static Glib::RefPtr paste_action; static Glib::RefPtr rename_action; static Glib::RefPtr delete_action; + static Glib::RefPtr manage_pins_action; static Glib::RefPtr edit_action; static Glib::RefPtr edit_generic_action; void paste_processor_state (const XMLNodeList&, boost::shared_ptr); @@ -495,6 +516,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD static void rb_activate_all (); static void rb_deactivate_all (); static void rb_ab_plugins (); + static void rb_manage_pins (); static void rb_edit (); static void rb_edit_generic (); @@ -508,6 +530,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD void set_processor_ui (boost::shared_ptr, Gtk::Window *); void maybe_add_processor_to_ui_list (boost::weak_ptr); + void maybe_add_processor_pin_mgr (boost::weak_ptr); bool one_processor_can_be_edited (); bool processor_can_be_edited (boost::shared_ptr); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 72e8120585..ac8f09c27e 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -176,6 +176,7 @@ gtk2_ardour_sources = [ 'pingback.cc', 'playlist_selector.cc', 'plugin_eq_gui.cc', + 'plugin_pin_dialog.cc', 'plugin_selector.cc', 'plugin_ui.cc', 'port_group.cc',