ardour
generic_pluginui.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000 Paul Davis
3 
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.
8 
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.
13 
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.
17 
18 */
19 
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23 
24 #include <climits>
25 #include <cerrno>
26 #include <cmath>
27 #include <string>
28 #include <vector>
29 
30 #include "pbd/stl_delete.h"
31 #include "pbd/xml++.h"
32 #include "pbd/failed_constructor.h"
33 
34 #include <gtkmm2ext/click_box.h>
35 #include <gtkmm2ext/fastmeter.h>
37 #include <gtkmm2ext/utils.h>
38 #include <gtkmm2ext/doi.h>
40 
41 #include "ardour/plugin.h"
42 #include "ardour/plugin_insert.h"
43 #include "ardour/session.h"
44 #include "ardour/value_as_string.h"
45 
46 #include "ardour_ui.h"
47 #include "prompter.h"
48 #include "plugin_ui.h"
49 #include "gui_thread.h"
50 #include "automation_controller.h"
51 #include "timers.h"
52 
53 #include "i18n.h"
54 
55 using namespace std;
56 using namespace ARDOUR;
57 using namespace PBD;
58 using namespace Gtkmm2ext;
59 using namespace Gtk;
60 
62  : PlugUIBase (pi)
63  , button_table (initial_button_rows, initial_button_cols)
64  , output_table (initial_output_rows, initial_output_cols)
65  , hAdjustment(0.0, 0.0, 0.0)
66  , vAdjustment(0.0, 0.0, 0.0)
67  , scroller_view(hAdjustment, vAdjustment)
68  , automation_menu (0)
69  , is_scrollable(scrollable)
70 {
71  set_name ("PluginEditor");
72  set_border_width (10);
73  //set_homogeneous (false);
74 
75  pack_start (main_contents, true, true);
76  settings_box.set_homogeneous (false);
77 
78  HBox* constraint_hbox = manage (new HBox);
79  HBox* smaller_hbox = manage (new HBox);
80  HBox* automation_hbox = manage (new HBox);
81  smaller_hbox->set_spacing (4);
82  automation_hbox->set_spacing (6);
83  Label* combo_label = manage (new Label (_("<span size=\"large\">Presets</span>")));
84  combo_label->set_use_markup (true);
85 
86  latency_button.signal_clicked.connect (sigc::mem_fun (*this, &PlugUIBase::latency_button_clicked));
88 
89  smaller_hbox->pack_start (latency_button, false, false, 4);
90  smaller_hbox->pack_start (_preset_combo, false, false);
91  smaller_hbox->pack_start (_preset_modified, false, false);
92  smaller_hbox->pack_start (add_button, false, false);
93  smaller_hbox->pack_start (save_button, false, false);
94  smaller_hbox->pack_start (delete_button, false, false);
95  smaller_hbox->pack_start (reset_button, false, false, 4);
96  smaller_hbox->pack_start (bypass_button, false, true, 4);
97 
99  automation_manual_all_button.set_name (X_("generic button"));
101  automation_play_all_button.set_name (X_("generic button"));
103  automation_write_all_button.set_name (X_("generic button"));
105  automation_touch_all_button.set_name (X_("generic button"));
106 
107  Label* l = manage (new Label (_("All Automation")));
108  l->set_alignment (1.0, 0.5);
109  automation_hbox->pack_start (*l, true, true);
110  automation_hbox->pack_start (automation_manual_all_button, false, false);
111  automation_hbox->pack_start (automation_play_all_button, false, false);
112  automation_hbox->pack_start (automation_write_all_button, false, false);
113  automation_hbox->pack_start (automation_touch_all_button, false, false);
114 
115  constraint_hbox->set_spacing (5);
116  constraint_hbox->set_homogeneous (false);
117 
118  VBox* v1_box = manage (new VBox);
119  VBox* v2_box = manage (new VBox);
120  pack_end (plugin_analysis_expander, false, false);
121  if (!plugin->get_docs().empty()) {
122  pack_end (description_expander, false, false);
123  }
124 
125  v1_box->set_spacing (6);
126  v1_box->pack_start (*smaller_hbox, false, true);
127  v1_box->pack_start (*automation_hbox, false, true);
128  v2_box->pack_start (focus_button, false, true);
129 
130  main_contents.pack_start (settings_box, false, false);
131 
132  constraint_hbox->pack_end (*v2_box, false, false);
133  constraint_hbox->pack_end (*v1_box, false, false);
134 
135  main_contents.pack_start (*constraint_hbox, false, false);
136 
137  if (is_scrollable ) {
138  scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
139  scroller.set_name ("PluginEditor");
140  scroller_view.set_name("PluginEditor");
141  scroller_view.add (hpacker);
142  scroller.add (scroller_view);
143 
144  main_contents.pack_start (scroller, true, true);
145 
146  } else {
147  main_contents.pack_start (hpacker, false, false);
148  }
149 
151 
153 
154  prefheight = 0;
155  build ();
156 }
157 
159 {
160  if (output_controls.size() > 0) {
161  screen_update_connection.disconnect();
162  }
163 }
164 
165 // Some functions for calculating the 'similarity' of two plugin
166 // control labels.
167 
168 static int get_number(string label) {
169 static const char *digits = "0123456789";
170 int value = -1;
171 
172  std::size_t first_digit_pos = label.find_first_of(digits);
173  if (first_digit_pos != string::npos) {
174  // found some digits: there's a number in there somewhere
175  stringstream s;
176  s << label.substr(first_digit_pos);
177  s >> value;
178  }
179  return value;
180 }
181 
182 static int match_or_digit(char c1, char c2) {
183  return c1 == c2 || (isdigit(c1) && isdigit(c2));
184 }
185 
186 static std::size_t matching_chars_at_head(const string s1, const string s2) {
187 std::size_t length, n = 0;
188 
189  length = min(s1.length(), s2.length());
190  while (n < length) {
191  if (!match_or_digit(s1[n], s2[n]))
192  break;
193  n++;
194  }
195  return n;
196 }
197 
198 static std::size_t matching_chars_at_tail(const string s1, const string s2) {
199 std::size_t s1pos, s2pos, n = 0;
200 
201  s1pos = s1.length();
202  s2pos = s2.length();
203  while (s1pos-- > 0 && s2pos-- > 0) {
204  if (!match_or_digit(s1[s1pos], s2[s2pos]) )
205  break;
206  n++;
207  }
208  return n;
209 }
210 
211 static const guint32 min_controls_per_column = 17, max_controls_per_column = 24;
212 static const float default_similarity_threshold = 0.3;
213 
214 void
216 {
217  guint32 i = 0;
218  guint32 x = 0;
219  Frame* frame;
220  Frame* bt_frame;
221  VBox* box;
222  int output_row, output_col;
223  int button_row, button_col;
224  int output_rows, output_cols;
225  int button_rows, button_cols;
226 
227  hpacker.set_spacing (10);
228 
229  output_rows = initial_output_rows;
230  output_cols = initial_output_cols;
231  button_rows = initial_button_rows;
232  button_cols = initial_button_cols;
233  output_row = 0;
234  button_row = 0;
235  output_col = 0;
236  button_col = 0;
237 
238  button_table.set_homogeneous (false);
239  button_table.set_row_spacings (2);
240  button_table.set_col_spacings (2);
241  output_table.set_homogeneous (true);
242  output_table.set_row_spacings (2);
243  output_table.set_col_spacings (2);
244  button_table.set_border_width (5);
245  output_table.set_border_width (5);
246 
247  hpacker.set_border_width (10);
248 
249  bt_frame = manage (new Frame);
250  bt_frame->set_name ("BaseFrame");
251  bt_frame->set_label (_("Switches"));
252  bt_frame->add (button_table);
253  hpacker.pack_start(*bt_frame, true, true);
254 
255  box = manage (new VBox);
256  box->set_border_width (5);
257  box->set_spacing (1);
258 
259  frame = manage (new Frame);
260  frame->set_name ("BaseFrame");
261  frame->set_label (_("Controls"));
262  frame->add (*box);
263  hpacker.pack_start(*frame, true, true);
264 
265  std::vector<ControlUI *> control_uis;
266 
267  // Build a ControlUI for each control port
268  for (i = 0; i < plugin->parameter_count(); ++i) {
269 
270  if (plugin->parameter_is_control (i)) {
271 
272  /* Don't show latency control ports */
273 
274  const Evoral::Parameter param(PluginAutomation, 0, i);
275  if (plugin->describe_parameter (param) == X_("latency")) {
276  continue;
277  }
278 
279  if (plugin->describe_parameter (param) == X_("hidden")) {
280  continue;
281  }
282 
283  const float value = plugin->get_parameter(i);
284 
285  ControlUI* cui;
286 
289  insert->control(param));
290 
291  ParameterDescriptor desc;
293  if ((cui = build_control_ui (param, desc, c, value, plugin->parameter_is_input(i))) == 0) {
294  error << string_compose(_("Plugin Editor: could not build control element for port %1"), i) << endmsg;
295  continue;
296  }
297 
298  const std::string param_docs = plugin->get_parameter_docs(i);
299  if (!param_docs.empty()) {
300  ARDOUR_UI::instance()->set_tip(cui, param_docs.c_str());
301  }
302 
303  control_uis.push_back(cui);
304  input_controls_with_automation.push_back (cui);
305  }
306  }
307 
308  // Build a ControlUI for each property
310  for (Plugin::PropertyDescriptors::const_iterator d = descs.begin(); d != descs.end(); ++d) {
311  const ParameterDescriptor& desc = d->second;
312  const Evoral::Parameter param(PluginPropertyAutomation, 0, desc.key);
313 
316  insert->control(param));
317 
318  if (!c) {
319  error << string_compose(_("Plugin Editor: no control for property %1"), desc.key) << endmsg;
320  continue;
321  }
322 
323  ControlUI* cui = build_control_ui(param, desc, c, c->get_value(), true);
324  if (!cui) {
325  error << string_compose(_("Plugin Editor: could not build control element for property %1"),
326  desc.key) << endmsg;
327  continue;
328  }
329 
330  control_uis.push_back(cui);
331  }
332  if (!descs.empty()) {
334  }
335 
336  // Add special controls to UI, and build list of normal controls to be layed out later
337  std::vector<ControlUI *> cui_controls_list;
338  for (i = 0; i < control_uis.size(); ++i) {
339  ControlUI* cui = control_uis[i];
340 
341  if (cui->controller || cui->clickbox || cui->combo) {
342  // Get all of the controls into a list, so that
343  // we can lay them out a bit more nicely later.
344  cui_controls_list.push_back(cui);
345  } else if (cui->button || cui->file_button) {
346 
347  if (!is_scrollable && button_row == button_rows) {
348  button_row = 0;
349  if (++button_col == button_cols) {
350  button_cols += 2;
351  button_table.resize (button_rows, button_cols);
352  }
353  }
354 
355  button_table.attach (*cui, button_col, button_col + 1, button_row, button_row+1,
356  FILL|EXPAND, FILL);
357  button_row++;
358 
359  } else if (cui->display) {
360 
361  output_table.attach (*cui, output_col, output_col + 1, output_row, output_row+1,
362  FILL|EXPAND, FILL);
363 
364  // TODO: The meters should be divided into multiple rows
365 
366  if (++output_col == output_cols) {
367  output_cols ++;
368  output_table.resize (output_rows, output_cols);
369  }
370  }
371  }
372 
373  // Iterate over the list of controls to find which adjacent controls
374  // are similar enough to be grouped together.
375 
376  string label, previous_label = "";
377  std::vector<int> numbers_in_labels(cui_controls_list.size());
378 
379  std::vector<float> similarity_scores(cui_controls_list.size());
380  float most_similar = 0.0, least_similar = 1.0;
381 
382  i = 0;
383  for (vector<ControlUI*>::iterator cuip = cui_controls_list.begin(); cuip != cui_controls_list.end(); ++cuip, ++i) {
384  label = (*cuip)->label.get_text();
385  numbers_in_labels[i] = get_number(label);
386 
387  if (i > 0) {
388  // A hand-wavy calculation of how similar this control's
389  // label is to the previous.
390  similarity_scores[i] =
391  (float) (
392  ( matching_chars_at_head(label, previous_label) +
393  matching_chars_at_tail(label, previous_label) +
394  1
395  )
396  ) / (label.length() + previous_label.length());
397  if (numbers_in_labels[i] >= 0) {
398  similarity_scores[i] += (numbers_in_labels[i] == numbers_in_labels[i-1]);
399  }
400  least_similar = min(least_similar, similarity_scores[i]);
401  most_similar = max(most_similar, similarity_scores[i]);
402  } else {
403  similarity_scores[0] = 1.0;
404  }
405 
406  // cerr << "label: " << label << " sim: " << fixed << setprecision(3) << similarity_scores[i] << " num: " << numbers_in_labels[i] << endl;
407  previous_label = label;
408  }
409 
410 
411  // cerr << "most similar: " << most_similar << ", least similar: " << least_similar << endl;
412  float similarity_threshold;
413 
414  if (most_similar > 1.0) {
415  similarity_threshold = default_similarity_threshold;
416  } else {
417  similarity_threshold = most_similar - (1 - default_similarity_threshold);
418  }
419 
420  // Now iterate over the list of controls to display them, placing an
421  // HSeparator between controls of less than a certain similarity, and
422  // starting a new column when necessary.
423 
424  i = 0;
425  for (vector<ControlUI*>::iterator cuip = cui_controls_list.begin(); cuip != cui_controls_list.end(); ++cuip, ++i) {
426 
427  ControlUI* cui = *cuip;
428 
429  if (!is_scrollable) {
430  x++;
431  }
432 
433  if (x > max_controls_per_column || similarity_scores[i] <= similarity_threshold) {
434  if (x > min_controls_per_column) {
435  frame = manage (new Frame);
436  frame->set_name ("BaseFrame");
437  frame->set_label (_("Controls"));
438  box = manage (new VBox);
439  box->set_border_width (5);
440  box->set_spacing (1);
441  frame->add (*box);
442  hpacker.pack_start(*frame, true, true);
443  x = 0;
444  } else {
445  HSeparator *split = new HSeparator();
446  split->set_size_request(-1, 5);
447  box->pack_start(*split, false, false, 0);
448  }
449 
450  }
451  box->pack_start (*cui, false, false);
452  }
453 
454  if (is_scrollable) {
455  prefheight = 30 * i;
456  }
457 
458  if (box->children().empty()) {
459  hpacker.remove (*frame);
460  }
461 
462  if (button_table.children().empty()) {
463  hpacker.remove (*bt_frame);
464  }
465 
466  if (!output_table.children().empty()) {
467  frame = manage (new Frame);
468  frame->set_name ("BaseFrame");
469  frame->set_label(_("Meters"));
470  frame->add (output_table);
471  hpacker.pack_end (*frame, true, true);
472  }
473 
474  output_update ();
475 
476  output_table.show_all ();
477  button_table.show_all ();
478 
480  automation_play_all_button.signal_clicked.connect(sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::set_all_automation), ARDOUR::Play));
483 }
484 
486  : param(p)
487  , automate_button (X_("")) // force creation of a label
488  , file_button(NULL)
489 {
490  automate_button.set_name ("PluginAutomateButton");
491  ARDOUR_UI::instance()->set_tip (automate_button, _("Automation control"));
492 
493  /* XXX translators: use a string here that will be at least as long
494  as the longest automation label (see ::automation_state_changed()
495  below). be sure to include a descender.
496  */
497 
499 
500  ignore_change = 0;
501  display = 0;
502  button = 0;
503  clickbox = 0;
504  meterinfo = 0;
505 }
506 
508 {
509  if (meterinfo) {
510  delete meterinfo->meter;
511  delete meterinfo;
512  }
513 }
514 
515 void
517 {
518  /* update button label */
519 
520  // don't lock to avoid deadlock because we're triggered by
521  // AutomationControl::Changed() while the automation lock is taken
523  case ARDOUR::Off:
524  cui->automate_button.set_label (S_("Automation|Manual"));
525  break;
526  case Play:
527  cui->automate_button.set_label (_("Play"));
528  break;
529  case Write:
530  cui->automate_button.set_label (_("Write"));
531  break;
532  case Touch:
533  cui->automate_button.set_label (_("Touch"));
534  break;
535  default:
536  cui->automate_button.set_label (_("???"));
537  break;
538  }
539 }
540 
541 bool
542 GenericPluginUI::integer_printer (char buf[32], Adjustment &adj, ControlUI* cui)
543 {
544  float const v = cui->control->interface_to_internal(adj.get_value ());
545  const std::string& str = ARDOUR::value_as_string(cui->control->desc(), Variant(v));
546  const size_t len = str.copy(buf, 31);
547  buf[len] = '\0';
548  return true;
549 }
550 
551 bool
552 GenericPluginUI::midinote_printer (char buf[32], Adjustment &adj, ControlUI* cui)
553 {
554  float const v = cui->control->interface_to_internal(adj.get_value ());
555  const std::string& str = ARDOUR::value_as_string(cui->control->desc(), Variant(v));
556  const size_t len = str.copy(buf, 31);
557  buf[len] = '\0';
558  return true;
559 }
560 
561 void
562 GenericPluginUI::print_parameter (char *buf, uint32_t len, uint32_t param)
563 {
564  plugin->print_parameter (param, buf, len);
565 }
566 
572  const ParameterDescriptor& desc,
574  float value,
575  bool is_input)
576 {
577  ControlUI* control_ui = 0;
578 
579  control_ui = manage (new ControlUI (param));
580  control_ui->combo = 0;
581  control_ui->control = mcontrol;
582  control_ui->update_pending = false;
583  control_ui->label.set_text (desc.label);
584  control_ui->label.set_alignment (0.0, 0.5);
585  control_ui->label.set_name ("PluginParameterLabel");
586 
587  control_ui->set_spacing (5);
588 
589  Gtk::Requisition req (control_ui->automate_button.size_request());
590 
591  if (is_input) {
592 
593  /* See if there any named values for our input value */
594  control_ui->scale_points = desc.scale_points;
595 
596  /* If this parameter is an integer, work out the number of distinct values
597  it can take on (assuming that lower and upper values are allowed).
598  */
599  int const steps = desc.integer_step ? (desc.upper - desc.lower + 1) / desc.step : 0;
600 
601  if (control_ui->scale_points && ((steps && int (control_ui->scale_points->size()) == steps) || desc.enumeration)) {
602 
603  /* Either:
604  * a) There is a label for each possible value of this input, or
605  * b) This port is marked as being an enumeration.
606  */
607 
608  std::vector<std::string> labels;
609  for (
610  ARDOUR::ScalePoints::const_iterator i = control_ui->scale_points->begin();
611  i != control_ui->scale_points->end();
612  ++i) {
613 
614  labels.push_back(i->first);
615  }
616 
617  control_ui->combo = new Gtk::ComboBoxText();
618  set_popdown_strings(*control_ui->combo, labels);
619  control_ui->combo->signal_changed().connect(
620  sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::control_combo_changed),
621  control_ui));
622  mcontrol->Changed.connect(control_connections, invalidator(*this),
624  this, control_ui),
625  gui_context());
626  control_ui->pack_start(control_ui->label, true, true);
627  control_ui->pack_start(*control_ui->combo, false, true);
628 
629  update_control_display(control_ui);
630 
631  return control_ui;
632  }
633 
634  if (desc.toggled) {
635 
636  /* Build a button */
637 
638  control_ui->button = manage (new ToggleButton ());
639  control_ui->button->set_name ("PluginEditorButton");
640  control_ui->button->set_size_request (20, 20);
641 
642  control_ui->pack_start (control_ui->label, true, true);
643  control_ui->pack_start (*control_ui->button, false, true);
644  control_ui->pack_start (control_ui->automate_button, false, false);
645 
646  control_ui->button->signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::control_port_toggled), control_ui));
647  control_ui->automate_button.signal_clicked().connect (bind (mem_fun(*this, &GenericPluginUI::astate_clicked), control_ui));
648 
649  mcontrol->Changed.connect (control_connections, invalidator (*this), boost::bind (&GenericPluginUI::toggle_parameter_changed, this, control_ui), gui_context());
650  mcontrol->alist()->automation_state_changed.connect (control_connections, invalidator (*this), boost::bind (&GenericPluginUI::automation_state_changed, this, control_ui), gui_context());
651 
652  if (value > 0.5){
653  control_ui->button->set_active(true);
654  }
655 
656  automation_state_changed (control_ui);
657 
658  return control_ui;
659  }
660 
661  if (desc.datatype == Variant::PATH) {
662 
663  /* Build a file selector button */
664 
665  // Create/add controller
666  control_ui->file_button = manage(new Gtk::FileChooserButton(Gtk::FILE_CHOOSER_ACTION_OPEN));
667  control_ui->file_button->set_title(desc.label);
668 
669  control_ui->pack_start (control_ui->label, true, true);
670  control_ui->pack_start (*control_ui->file_button, true, true);
671 
672  // Connect signals (TODO: do this via the Control)
673  control_ui->file_button->signal_file_set().connect(
674  sigc::bind(sigc::mem_fun(*this, &GenericPluginUI::set_property),
675  desc, control_ui->file_button));
676  plugin->PropertyChanged.connect(*this, invalidator(*this),
677  boost::bind(&GenericPluginUI::property_changed, this, _1, _2),
678  gui_context());
679 
680  _property_controls.insert(std::make_pair(desc.key, control_ui->file_button));
681  control_ui->file_button = control_ui->file_button;
682 
683  return control_ui;
684  }
685 
686  /* create the controller */
687 
688  if (mcontrol) {
689  control_ui->controller = AutomationController::create(insert, mcontrol->parameter(), desc, mcontrol);
690  }
691 
692  /* XXX this code is not right yet, because it doesn't handle
693  the absence of bounds in any sensible fashion.
694  */
695 
696  Adjustment* adj = control_ui->controller->adjustment();
697 
698  if (desc.integer_step) {
699  control_ui->clickbox = new ClickBox (adj, "PluginUIClickBox", desc.enumeration);
700  Gtkmm2ext::set_size_request_to_display_given_text (*control_ui->clickbox, "g9999999", 2, 2);
701  if (desc.unit == ParameterDescriptor::MIDI_NOTE) {
702  control_ui->clickbox->set_printer (sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::midinote_printer), control_ui));
703  } else {
704  control_ui->clickbox->set_printer (sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::integer_printer), control_ui));
705  }
706  } else {
707  //sigc::slot<void,char*,uint32_t> pslot = sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::print_parameter), (uint32_t) port_index);
708 
709  control_ui->controller->set_size_request (200, req.height);
710  control_ui->controller->set_name (X_("ProcessorControlSlider"));
711 
712  }
713 
714  adj->set_value (mcontrol->internal_to_interface(value));
715 
716  /* XXX memory leak: SliderController not destroyed by ControlUI
717  destructor, and manage() reports object hierarchy
718  ambiguity.
719  */
720 
721  control_ui->pack_start (control_ui->label, true, true);
722  if (desc.integer_step) {
723  control_ui->pack_start (*control_ui->clickbox, false, false);
724  } else {
725  control_ui->pack_start (*control_ui->controller, false, false);
726  }
727 
728  control_ui->pack_start (control_ui->automate_button, false, false);
729  control_ui->automate_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::astate_clicked), control_ui));
730 
731  automation_state_changed (control_ui);
732 
733  mcontrol->alist()->automation_state_changed.connect (control_connections, invalidator (*this), boost::bind (&GenericPluginUI::automation_state_changed, this, control_ui), gui_context());
734 
735  input_controls.push_back (control_ui);
736  input_controls_with_automation.push_back (control_ui);
737 
738  } else if (!is_input) {
739 
740  control_ui->display = manage (new EventBox);
741  control_ui->display->set_name ("ParameterValueDisplay");
742 
743  control_ui->display_label = manage (new Label);
744 
745  control_ui->display_label->set_name ("ParameterValueDisplay");
746 
747  control_ui->display->add (*control_ui->display_label);
748  Gtkmm2ext::set_size_request_to_display_given_text (*control_ui->display, "-888.8g", 2, 6);
749 
750  control_ui->display->show_all ();
751 
752  /* set up a meter */
753  /* TODO: only make a meter if the port is Hinted for it */
754 
755  MeterInfo * info = new MeterInfo();
756  control_ui->meterinfo = info;
757 
758  info->meter = new FastMeter (
759  5, 5, FastMeter::Vertical, 0,
760  0x0000aaff,
761  0x008800ff, 0x008800ff,
762  0x00ff00ff, 0x00ff00ff,
763  0xcccc00ff, 0xcccc00ff,
764  0xffaa00ff, 0xffaa00ff,
765  0xff0000ff,
766  ARDOUR_UI::config()->color ("meter background bottom"),
767  ARDOUR_UI::config()->color ("meter background top")
768  );
769 
770  info->min_unbound = desc.min_unbound;
771  info->max_unbound = desc.max_unbound;
772 
773  info->min = desc.lower;
774  info->max = desc.upper;
775 
776  control_ui->vbox = manage (new VBox);
777  control_ui->hbox = manage (new HBox);
778 
779  control_ui->hbox->set_spacing(1);
780  control_ui->vbox->set_spacing(3);
781 
782  control_ui->label.set_angle(90);
783  control_ui->hbox->pack_start (control_ui->label, false, false);
784  control_ui->hbox->pack_start (*info->meter, false, false);
785 
786  control_ui->vbox->pack_start (*control_ui->hbox, false, false);
787 
788  control_ui->vbox->pack_start (*control_ui->display, false, false);
789 
790  control_ui->pack_start (*control_ui->vbox);
791 
792  control_ui->meterinfo->meter->show_all();
793  control_ui->meterinfo->packed = true;
794 
795  output_controls.push_back (control_ui);
796  }
797 
798  if (mcontrol) {
799  mcontrol->Changed.connect (control_connections, invalidator (*this), boost::bind (&GenericPluginUI::ui_parameter_changed, this, control_ui), gui_context());
800  }
801 
802  return control_ui;
803 }
804 
805 void
807 {
808  using namespace Menu_Helpers;
809 
810  if (automation_menu == 0) {
811  automation_menu = manage (new Menu);
812  automation_menu->set_name ("ArdourContextMenu");
813  }
814 
815  MenuList& items (automation_menu->items());
816 
817  items.clear ();
818  items.push_back (MenuElem (S_("Automation|Manual"),
819  sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) ARDOUR::Off, cui)));
820  items.push_back (MenuElem (_("Play"),
821  sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) Play, cui)));
822  items.push_back (MenuElem (_("Write"),
823  sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) Write, cui)));
824  items.push_back (MenuElem (_("Touch"),
825  sigc::bind (sigc::mem_fun(*this, &GenericPluginUI::set_automation_state), (AutoState) Touch, cui)));
826 
827  automation_menu->popup (1, gtk_get_current_event_time());
828 }
829 
830 void
832 {
833  for (vector<ControlUI*>::iterator i = input_controls_with_automation.begin(); i != input_controls_with_automation.end(); ++i) {
834  if ((*i)->controller || (*i)->button) {
835  set_automation_state (as, (*i));
836  }
837  }
838 }
839 
840 void
842 {
844 }
845 
846 void
848 {
849  float val = cui->control->get_value();
850 
851  if (!cui->ignore_change) {
852  if (val > 0.5) {
853  cui->button->set_active (true);
854  cui->button->set_name ("PluginEditorButton-active");
855  } else {
856  cui->button->set_active (false);
857  cui->button->set_name ("PluginEditorButton");
858  }
859  }
860 }
861 
862 void
864 {
865  if (!cui->update_pending) {
866  cui->update_pending = true;
868  }
869 }
870 
871 void
873 {
874  /* XXX how do we handle logarithmic stuff here ? */
875 
876  cui->update_pending = false;
877 
878  float val = cui->control->get_value();
879 
880  cui->ignore_change++;
881 
882  if (cui->combo && cui->scale_points) {
883  for (ARDOUR::ScalePoints::iterator it = cui->scale_points->begin(); it != cui->scale_points->end(); ++it) {
884  if (it->second == val) {
885  cui->combo->set_active_text(it->first);
886  break;
887  }
888  }
889  } else if (cui->button) {
890 
891  if (val > 0.5) {
892  cui->button->set_active (true);
893  } else {
894  cui->button->set_active (false);
895  }
896  }
897 
898  if( cui->controller ) {
900  }
901 
902 
903  /*} else {
904  if (cui->logarithmic) {
905  val = log(val);
906  }
907  if (val != cui->adjustment->get_value()) {
908  cui->adjustment->set_value (val);
909  }
910  }*/
911  cui->ignore_change--;
912 }
913 
914 void
916 {
917  cui->ignore_change++;
918  const bool active = cui->button->get_active();
919  if (active) {
920  cui->button->set_name ("PluginEditorButton-active");
921  } else {
922  cui->button->set_name ("PluginEditorButton");
923  }
924  insert->automation_control (cui->parameter())->set_value (active);
925  cui->ignore_change--;
926 }
927 
928 void
930 {
931  if (!cui->ignore_change && cui->scale_points) {
932  string value = cui->combo->get_active_text();
933  insert->automation_control (cui->parameter())->set_value ((*cui->scale_points)[value]);
934  }
935 }
936 
937 bool
939 {
940  if (output_controls.size() > 0 ) {
941  screen_update_connection.disconnect();
943  }
944  return false;
945 }
946 
947 bool
949 {
950  for (vector<ControlUI*>::iterator i = input_controls.begin(); i != input_controls.end(); ++i) {
951  (*i)->controller->stop_updating ();
952  }
953 
954  if (output_controls.size() > 0 ) {
955  screen_update_connection.disconnect();
956  }
957 
958  return false;
959 }
960 
961 void
963 {
964  for (vector<ControlUI*>::iterator i = output_controls.begin(); i != output_controls.end(); ++i) {
965  float val = plugin->get_parameter ((*i)->parameter().id());
966  char buf[32];
967  snprintf (buf, sizeof(buf), "%.2f", val);
968  (*i)->display_label->set_text (buf);
969 
970  /* autoscaling for the meter */
971  if ((*i)->meterinfo && (*i)->meterinfo->packed) {
972 
973  if (val < (*i)->meterinfo->min) {
974  if ((*i)->meterinfo->min_unbound)
975  (*i)->meterinfo->min = val;
976  else
977  val = (*i)->meterinfo->min;
978  }
979 
980  if (val > (*i)->meterinfo->max) {
981  if ((*i)->meterinfo->max_unbound)
982  (*i)->meterinfo->max = val;
983  else
984  val = (*i)->meterinfo->max;
985  }
986 
987  if ((*i)->meterinfo->max > (*i)->meterinfo->min ) {
988  float lval = (val - (*i)->meterinfo->min) / ((*i)->meterinfo->max - (*i)->meterinfo->min) ;
989  (*i)->meterinfo->meter->set (lval );
990  }
991  }
992  }
993 }
994 
995 void
997  Gtk::FileChooserButton* widget)
998 {
999  plugin->set_property(desc.key, Variant(Variant::PATH, widget->get_filename()));
1000 }
1001 
1002 void
1003 GenericPluginUI::property_changed (uint32_t key, const Variant& value)
1004 {
1005  PropertyControls::iterator c = _property_controls.find(key);
1006  if (c != _property_controls.end()) {
1007  c->second->set_filename(value.get_path());
1008  } else {
1009  std::cerr << "warning: property change for property with no control" << std::endl;
1010  }
1011 }
static std::size_t matching_chars_at_head(const string s1, const string s2)
void set_automation_state(ARDOUR::AutoState state, ControlUI *cui)
virtual std::string get_parameter_docs(uint32_t) const
Definition: plugin.h:112
Gtk::Adjustment * adjustment()
bool integer_printer(char *buf, Gtk::Adjustment &, ControlUI *)
float lower
Minimum value (in Hz, for frequencies)
virtual void announce_property_values()
Definition: plugin.h:264
const Evoral::Parameter parameter() const
Definition: plugin_ui.h:241
bool stop_updating(GdkEventAny *)
sigc::signal< void > signal_clicked
sigc::connection super_rapid_connect(const sigc::slot< void > &slot)
Definition: timers.cc:189
LIBGTKMM2EXT_API void set_size_request_to_display_given_text(Gtk::Widget &w, const gchar *text, gint hpadding, gint vpadding)
Definition: utils.cc:70
std::vector< ControlUI * > output_controls
Definition: plugin_ui.h:274
Gtk::Table button_table
Definition: plugin_ui.h:202
void update_control_display(ControlUI *cui)
void set_latency_label()
Definition: plugin_ui.cc:509
void ui_parameter_changed(ControlUI *cui)
void latency_button_clicked()
Definition: plugin_ui.cc:526
LIBPBD_API void split(std::string, std::vector< std::string > &, char)
Definition: ardour_ui.h:130
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
virtual bool parameter_is_control(uint32_t) const =0
ArdourDropdown _preset_combo
Definition: plugin_ui.h:119
std::string value_as_string(const ARDOUR::ParameterDescriptor &desc, double v)
Gtkmm2ext::FastMeter * meter
Definition: plugin_ui.h:215
virtual float get_parameter(uint32_t which) const =0
void print_parameter(char *buf, uint32_t len, uint32_t param)
void astate_clicked(ControlUI *)
boost::shared_ptr< Control > control(const Parameter &id, bool create_if_missing=false)
Definition: ControlSet.cpp:73
Definition: Beats.hpp:239
ArdourButton delete_button
Definition: plugin_ui.h:127
LIBPBD_API Transmitter error
void automation_state_changed()
Definition: plugin_ui.cc:650
static const int32_t initial_button_rows
Definition: plugin_ui.h:233
boost::shared_ptr< ARDOUR::PluginInsert > insert
Definition: plugin_ui.h:113
ArdourButton latency_button
Definition: plugin_ui.h:139
uint32_t key
for properties
static const int32_t initial_output_rows
Definition: plugin_ui.h:235
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
LIBGTKMM2EXT_API void set_popdown_strings(Gtk::ComboBoxText &, const std::vector< std::string > &)
Gtk::ComboBoxText * combo
Definition: plugin_ui.h:248
virtual void set_parameter_automation_state(Evoral::Parameter param, AutoState)
Definition: automatable.cc:272
Gtk::FileChooserButton * file_button
Definition: plugin_ui.h:257
#define invalidator(x)
Definition: gui_thread.h:40
ArdourButton bypass_button
Definition: plugin_ui.h:131
void processor_active_changed(boost::weak_ptr< ARDOUR::Processor > p)
Definition: plugin_ui.cc:547
Gtk::ScrolledWindow scroller
Definition: plugin_ui.h:205
PBD::ScopedConnectionList control_connections
Definition: plugin_ui.h:181
virtual const PropertyDescriptors & get_supported_properties() const
Definition: plugin.h:244
static UI * instance()
Definition: gtk_ui.h:119
static int match_or_digit(char c1, char c2)
void control_combo_changed(ControlUI *cui)
#define _(Text)
Definition: i18n.h:11
Gtk::Expander description_expander
Definition: plugin_ui.h:135
Gtk::EventBox * display
Definition: plugin_ui.h:261
std::vector< ControlUI * > input_controls
Definition: plugin_ui.h:272
#define X_(Text)
Definition: i18n.h:13
Gtk::Expander plugin_analysis_expander
Definition: plugin_ui.h:137
void control_port_toggled(ControlUI *cui)
float upper
Maximum value (in Hz, for frequencies)
static boost::shared_ptr< AutomationController > create(boost::shared_ptr< ARDOUR::Automatable > parent, const Evoral::Parameter &param, const ARDOUR::ParameterDescriptor &desc, boost::shared_ptr< ARDOUR::AutomationControl > ac)
Gtk::EventBox focus_button
Definition: plugin_ui.h:133
std::map< uint32_t, ParameterDescriptor > PropertyDescriptors
Definition: plugin.h:234
const ParameterDescriptor & desc() const
virtual double internal_to_interface(double i) const
Definition: controllable.h:71
void set_property(const ARDOUR::ParameterDescriptor &desc, Gtk::FileChooserButton *widget)
Gtkmm2ext::ClickBox * clickbox
Definition: plugin_ui.h:252
Gtk::ToggleButton * button
Definition: plugin_ui.h:250
Gtk::Viewport scroller_view
Definition: plugin_ui.h:208
Definition: amp.h:29
static const guint32 max_controls_per_column
ArdourButton automation_write_all_button
Definition: plugin_ui.h:145
Gtk::VBox main_contents
Definition: plugin_ui.h:197
virtual bool parameter_is_input(uint32_t) const =0
boost::shared_ptr< ARDOUR::AutomationControl > control
Definition: plugin_ui.h:244
#define gui_context()
Definition: gui_thread.h:36
virtual int get_parameter_descriptor(uint32_t which, ParameterDescriptor &) const =0
boost::shared_ptr< ARDOUR::ScalePoints > scale_points
Definition: plugin_ui.h:249
Gtk::Button automate_button
Definition: plugin_ui.h:256
virtual void print_parameter(uint32_t, char *, uint32_t len) const =0
ControlUI * build_control_ui(const Evoral::Parameter &param, const ARDOUR::ParameterDescriptor &desc, boost::shared_ptr< ARDOUR::AutomationControl > mcontrol, float value, bool is_input)
static const float default_similarity_threshold
static int get_number(string label)
void set_printer(sigc::slot< bool, char *, Gtk::Adjustment & >)
Definition: click_box.cc:143
const std::string & get_path() const
Definition: variant.h:136
LIBPBD_API Transmitter info
LIBARDOUR_API PBD::PropertyDescriptor< bool > active
Definition: route_group.cc:43
void toggle_parameter_changed(ControlUI *cui)
ArdourButton automation_manual_all_button
Definition: plugin_ui.h:141
const Parameter & parameter() const
Definition: Control.hpp:69
void set_tip(Gtk::Widget &w, const gchar *tip)
void call_slot(EventLoop::InvalidationRecord *, const boost::function< void()> &)
Definition: abstract_ui.cc:368
static const int32_t initial_output_cols
Definition: plugin_ui.h:236
boost::shared_ptr< AutomationController > controller
Definition: plugin_ui.h:251
Gtk::Table output_table
Definition: plugin_ui.h:203
PBD::ScopedConnection active_connection
Definition: plugin_ui.h:178
PBD::Signal2< void, uint32_t, Variant > PropertyChanged
Definition: plugin.h:267
Gtk::HBox settings_box
Definition: plugin_ui.h:199
void property_changed(uint32_t key, const ARDOUR::Variant &value)
ArdourButton automation_touch_all_button
Definition: plugin_ui.h:147
void set_active(bool)
bool toggled
True iff parameter is boolean.
ArdourButton add_button
Definition: plugin_ui.h:123
static UIConfiguration * config()
Definition: ardour_ui.h:188
void set_text(const std::string &)
PBD::Signal0< void > Changed
Definition: controllable.h:94
virtual std::string describe_parameter(Evoral::Parameter)=0
PBD::Signal0< void > ActiveChanged
Definition: processor.h:112
virtual double interface_to_internal(double i) const
Definition: controllable.h:72
Definition: debug.h:30
static const guint32 min_controls_per_column
static std::size_t matching_chars_at_tail(const string s1, const string s2)
boost::shared_ptr< ScalePoints > scale_points
boost::shared_ptr< AutomationControl > automation_control(const Evoral::Parameter &id, bool create_if_missing=false)
Definition: automatable.cc:475
Gtk::Menu * automation_menu
Definition: plugin_ui.h:209
GenericPluginUI(boost::shared_ptr< ARDOUR::PluginInsert > plug, bool scrollable=false)
#define S_(Text)
Definition: i18n.h:18
PropertyControls _property_controls
Definition: plugin_ui.h:306
boost::shared_ptr< AutomationList > alist() const
virtual void set_property(uint32_t key, const Variant &value)
Definition: plugin.h:261
bool is_scrollable
Definition: plugin_ui.h:212
AutoState get_parameter_automation_state(Evoral::Parameter param)
Definition: automatable.cc:286
boost::shared_ptr< ARDOUR::Plugin > plugin
Definition: plugin_ui.h:114
Gtk::Label _preset_modified
Definition: plugin_ui.h:121
Gtk::Label * display_label
Definition: plugin_ui.h:262
#define MISSING_INVALIDATOR
Definition: event_loop.h:86
bool start_updating(GdkEventAny *)
std::vector< ControlUI * > input_controls_with_automation
Definition: plugin_ui.h:273
virtual uint32_t parameter_count() const =0
virtual std::string get_docs() const
Definition: plugin.h:111
Variant::Type datatype
for properties
ArdourButton automation_play_all_button
Definition: plugin_ui.h:143
bool active() const
Definition: processor.h:61
ArdourButton reset_button
Definition: plugin_ui.h:129
sigc::connection screen_update_connection
Definition: plugin_ui.h:275
bool midinote_printer(char *buf, Gtk::Adjustment &, ControlUI *)
Gtk::HBox hpacker
Definition: plugin_ui.h:200
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
ControlUI(const Evoral::Parameter &param)
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
Definition: region.cc:64
PBD::Signal1< void, AutoState > automation_state_changed
void set_all_automation(ARDOUR::AutoState state)
ArdourButton save_button
Definition: plugin_ui.h:125
AutoState
Definition: types.h:145
LIBARDOUR_API PBD::PropertyDescriptor< bool > color
Definition: route_group.cc:50
static const int32_t initial_button_cols
Definition: plugin_ui.h:234