ardour
automation_controller.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2007 Paul Davis
3  Author: David Robillard
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
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.
14 
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 
19 */
20 
21 #include <iomanip>
22 #include <cmath>
23 
24 #include "pbd/compose.h"
25 #include "pbd/error.h"
26 
27 #include "ardour/automatable.h"
29 #include "ardour/session.h"
30 #include "ardour/tempo.h"
31 
32 #include "ardour_button.h"
33 #include "ardour_ui.h"
34 #include "automation_controller.h"
35 #include "gui_thread.h"
36 #include "note_select_dialog.h"
37 #include "timers.h"
38 
39 #include "i18n.h"
40 
41 using namespace ARDOUR;
42 using namespace Gtk;
43 
47  Adjustment* adj)
48  : Gtkmm2ext::BarController(*adj, ac)
49  , _printer(printer)
50  , _controllable(ac)
51 {
52 }
53 
54 std::string
56 {
57  xpos = 0.5;
59 }
60 
62 {
63 }
64 
67  Adjustment* adj)
68  : _widget(NULL)
69  , _printer (printer)
70  , _controllable(ac)
71  , _adjustment(adj)
72  , _ignore_change(false)
73 {
74  assert (_printer);
75 
76  if (ac->toggled()) {
77  ArdourButton* but = manage(new ArdourButton());
78 
79  // Apply styles for special types
80  if (ac->parameter().type() == MuteAutomation) {
81  but->set_name("mute button");
82  } else if (ac->parameter().type() == SoloAutomation) {
83  but->set_name("solo button");
84  } else {
85  but->set_name("generic button");
86  }
87  but->set_controllable(ac);
88  but->signal_clicked.connect(
89  sigc::mem_fun(*this, &AutomationController::toggled));
90 
91  _widget = but;
92  } else {
93  AutomationBarController* bar = manage(new AutomationBarController(_printer, ac, adj));
94 
95  bar->set_name(X_("ProcessorControlSlider"));
96  bar->StartGesture.connect(
97  sigc::mem_fun(*this, &AutomationController::start_touch));
98  bar->StopGesture.connect(
99  sigc::mem_fun(*this, &AutomationController::end_touch));
100  bar->signal_button_release_event().connect(
101  sigc::mem_fun(*this, &AutomationController::on_button_release));
102 
103  _widget = bar;
104  }
105 
106  _adjustment->signal_value_changed().connect(
107  sigc::mem_fun(*this, &AutomationController::value_adjusted));
108 
110  sigc::mem_fun (*this, &AutomationController::display_effective_value));
111 
112  ac->Changed.connect (_changed_connection, invalidator (*this), boost::bind (&AutomationController::value_changed, this), gui_context());
113 
114  add(*_widget);
115  show_all();
116 }
117 
119 {
120 }
121 
124  const Evoral::Parameter& param,
125  const ParameterDescriptor& desc,
127 {
128  const double lo = ac->internal_to_interface(desc.lower);
129  const double up = ac->internal_to_interface(desc.upper);
130  const double normal = ac->internal_to_interface(desc.normal);
131  const double smallstep = ac->internal_to_interface(desc.lower + desc.smallstep);
132  const double largestep = ac->internal_to_interface(desc.lower + desc.largestep);
133 
134  Gtk::Adjustment* adjustment = manage (
135  new Gtk::Adjustment (normal, lo, up, smallstep, largestep));
136 
137  assert (ac);
138  assert(ac->parameter() == param);
139  return boost::shared_ptr<AutomationController>(new AutomationController(printer, ac, adjustment));
140 }
141 
142 void
144 {
145  double const interface_value = _controllable->internal_to_interface(_controllable->get_value());
146 
147  if (_adjustment->get_value () != interface_value) {
148  _ignore_change = true;
149  _adjustment->set_value (interface_value);
150  _ignore_change = false;
151  }
152 }
153 
154 void
156 {
157  if (!_ignore_change) {
159  }
160 
161  /* A bar controller will automatically follow the adjustment, but for a
162  button we have to do it manually. */
163  ArdourButton* but = dynamic_cast<ArdourButton*>(_widget);
164  if (but) {
165  const bool active = _adjustment->get_value() >= 0.5;
166  if (but->get_active() != active) {
167  but->set_active(active);
168  }
169  }
170 }
171 
172 void
174 {
176  StartGesture.emit(); /* EMIT SIGNAL */
177 }
178 
179 void
181 {
183 
184  bool mark = false;
185  double when = 0;
186 
188  mark = true;
190  }
191 
192  _controllable->stop_touch (mark, when);
193  }
194  StopGesture.emit(); /* EMIT SIGNAL */
195 }
196 
197 void
199 {
200  ArdourButton* but = dynamic_cast<ArdourButton*>(_widget);
201  if (but) {
205  }
206  if (_controllable->list()) {
207  _controllable->list()->set_in_write_pass(true, false, _controllable->session().audible_frame());
208  }
209  }
210  const bool was_active = _controllable->get_value() >= 0.5;
211  if (was_active && but->get_active()) {
212  _adjustment->set_value(0.0);
213  but->set_active(false);
214  } else if (!was_active && !but->get_active()) {
215  _adjustment->set_value(1.0);
216  but->set_active(true);
217  }
218  }
219 }
220 
221 static double
223 {
224  const double tuning = 440.0;
225  return tuning * pow(2, (note - 69.0) / 12.0);
226 }
227 
228 static double
229 clamp(double val, double min, double max)
230 {
231  if (val < min) {
232  return min;
233  } else if (val > max) {
234  return max;
235  }
236  return val;
237 }
238 
239 void
241 {
243  NoteSelectDialog* dialog = new NoteSelectDialog();
244  if (dialog->run() == Gtk::RESPONSE_ACCEPT) {
245  const double value = ((_controllable->desc().unit == ARDOUR::ParameterDescriptor::HZ)
246  ? midi_note_to_hz(dialog->note_number())
247  : dialog->note_number());
248  _controllable->set_value(clamp(value, desc.lower, desc.upper));
249  }
250  delete dialog;
251 }
252 
253 void
255 {
257  const ARDOUR::Session& session = _controllable->session();
258  const framepos_t pos = session.transport_frame();
259  const ARDOUR::Tempo& tempo = session.tempo_map().tempo_at(pos);
260  const double bpm = tempo.beats_per_minute();
261  const double bps = bpm / 60.0;
262  const double freq = bps / beats;
263  _controllable->set_value(clamp(freq, desc.lower, desc.upper));
264 }
265 
266 void
268 {
270  const double value = _controllable->get_value() * ratio;
271  _controllable->set_value(clamp(value, desc.lower, desc.upper));
272 }
273 
274 bool
276 {
277  using namespace Gtk::Menu_Helpers;
278 
279  if (ev->button != 3) {
280  return false;
281  }
282 
285  Gtk::Menu* menu = manage(new Menu());
286  MenuList& items = menu->items();
287  items.push_back(MenuElem(_("Select Note..."),
288  sigc::mem_fun(*this, &AutomationController::run_note_select_dialog)));
289  menu->popup(1, ev->time);
290  return true;
291  } else if (desc.unit == ARDOUR::ParameterDescriptor::HZ) {
292  Gtk::Menu* menu = manage(new Menu());
293  MenuList& items = menu->items();
294  items.push_back(MenuElem(_("Halve"),
295  sigc::bind(sigc::mem_fun(*this, &AutomationController::set_ratio),
296  0.5)));
297  items.push_back(MenuElem(_("Double"),
298  sigc::bind(sigc::mem_fun(*this, &AutomationController::set_ratio),
299  2.0)));
300  const bool is_audible = desc.upper > 40.0;
301  const bool is_low = desc.lower < 1.0;
302  if (is_audible) {
303  items.push_back(MenuElem(_("Select Note..."),
304  sigc::mem_fun(*this, &AutomationController::run_note_select_dialog)));
305  }
306  if (is_low) {
307  for (int beats = 1; beats <= 16; ++beats) {
308  items.push_back(MenuElem (string_compose(P_("Set to %1 beat", "Set to %1 beats", beats), beats),
309  sigc::bind(sigc::mem_fun(*this, &AutomationController::set_freq_beats),
310  (double)beats)));
311  }
312  }
313  menu->popup(1, ev->time);
314  return true;
315  }
316 
317  return false;
318 }
319 
320 void
322 {
324 }
325 
327 void
329 {
330  _screen_update_connection.disconnect ();
331 }
332 
333 void
335 {
337  if (bar) {
338  bar->set_tweaks (
341  }
342 }
framepos_t audible_frame() const
Definition: session.cc:1760
bool transport_rolling() const
Definition: session.h:592
std::string get_label(double &)
sigc::connection _screen_update_connection
AutoState automation_state() const
void set_tweaks(PixFader::Tweaks t)
Definition: barcontroller.h:41
Gtk::Adjustment * adjustment()
float lower
Minimum value (in Hz, for frequencies)
sigc::signal< void > signal_clicked
bool on_button_release(GdkEventButton *ev)
PBD::ScopedConnection _changed_connection
Definition: ardour_ui.h:130
boost::shared_ptr< ARDOUR::Automatable > _printer
sigc::signal< void > StartGesture
TempoMap & tempo_map()
Definition: session.h:596
static double midi_note_to_hz(int note)
uint8_t note_number() const
sigc::signal< void > StartGesture
Definition: barcontroller.h:43
#define P_(Singular, Plural, HowMany)
Definition: i18n.h:41
void set_freq_beats(double beats)
#define invalidator(x)
Definition: gui_thread.h:40
static UI * instance()
Definition: gtk_ui.h:119
void set_ratio(double ratio)
#define _(Text)
Definition: i18n.h:11
#define X_(Text)
Definition: i18n.h:13
virtual std::string value_as_string(boost::shared_ptr< AutomationControl >) const
Definition: automatable.cc:494
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)
const ParameterDescriptor & desc() const
sigc::signal< void > StopGesture
virtual double internal_to_interface(double i) const
Definition: controllable.h:71
const ARDOUR::Session & session() const
framepos_t transport_frame() const
Definition: session.h:551
Definition: amp.h:29
void set_controllable(boost::shared_ptr< PBD::Controllable > c)
#define gui_context()
Definition: gui_thread.h:36
boost::shared_ptr< ARDOUR::AutomationControl > _controllable
int64_t framepos_t
Definition: types.h:66
AutomationBarController(boost::shared_ptr< ARDOUR::Automatable > printer, boost::shared_ptr< ARDOUR::AutomationControl > ac, Gtk::Adjustment *adj)
LIBARDOUR_API PBD::PropertyDescriptor< bool > active
Definition: route_group.cc:43
double beats_per_minute() const
Definition: tempo.h:53
boost::shared_ptr< ARDOUR::Automatable > _printer
const Parameter & parameter() const
Definition: Control.hpp:69
void call_slot(EventLoop::InvalidationRecord *, const boost::function< void()> &)
Definition: abstract_ui.cc:368
bool get_active()
Definition: cairo_widget.h:57
void set_active(bool)
boost::shared_ptr< ControlList > list()
Definition: Control.hpp:66
PBD::Signal0< void > Changed
Definition: controllable.h:94
virtual double interface_to_internal(double i) const
Definition: controllable.h:72
uint32_t type() const
Definition: Parameter.hpp:47
AutomationController(boost::shared_ptr< ARDOUR::Automatable > printer, boost::shared_ptr< ARDOUR::AutomationControl > ac, Gtk::Adjustment *adj)
static double clamp(double val, double min, double max)
void set_automation_state(AutoState as)
boost::shared_ptr< ARDOUR::AutomationControl > _controllable
PixFader::Tweaks tweaks() const
Definition: barcontroller.h:40
sigc::connection rapid_connect(const sigc::slot< void > &slot)
Definition: timers.cc:183
Gtk::Adjustment * _adjustment
const Tempo & tempo_at(framepos_t) const
Definition: tempo.cc:1617
sigc::signal< void > StopGesture
Definition: barcontroller.h:44
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
void stop_touch(bool mark, double when)