+ } else {
+ _controllable->stop_touch (false, _controllable->session().transport_frame());
+ }
+}
+
+void
+AutomationController::toggled ()
+{
+ ArdourButton* but = dynamic_cast<ArdourButton*>(_widget);
+ const AutoState as = _controllable->automation_state ();
+ const double where = _controllable->session ().audible_frame ();
+ const bool to_list = _controllable->list () && _controllable->session().transport_rolling () && (as == Touch || as == Write);
+
+ if (but) {
+ if (to_list) {
+ if (as == Touch && _controllable->list ()->in_new_write_pass ()) {
+ _controllable->alist ()->start_write_pass (where);
+ }
+ _controllable->list ()->set_in_write_pass (true, false, where);
+ }
+ /* set to opposite value.*/
+ _controllable->set_double (but->get_active () ? 0.0 : 1.0, where, to_list);
+
+ const bool active = _controllable->get_double (to_list, where) >= 0.5;
+ if (active && !but->get_active ()) {
+ _adjustment->set_value (1.0);
+ but->set_active (true);
+ } else if (!active && but->get_active ()) {
+ _adjustment->set_value (0.0);
+ but->set_active (false);
+ }
+ }
+}
+
+static double
+midi_note_to_hz(int note)
+{
+ const double tuning = 440.0;
+ return tuning * pow(2, (note - 69.0) / 12.0);
+}
+
+static double
+clamp(double val, double min, double max)
+{
+ if (val < min) {
+ return min;
+ } else if (val > max) {
+ return max;
+ }
+ return val;
+}
+
+void
+AutomationController::run_note_select_dialog()
+{
+ const ARDOUR::ParameterDescriptor& desc = _controllable->desc();
+ NoteSelectDialog* dialog = new NoteSelectDialog();
+ if (dialog->run() == Gtk::RESPONSE_ACCEPT) {
+ const double value = ((_controllable->desc().unit == ARDOUR::ParameterDescriptor::HZ)
+ ? midi_note_to_hz(dialog->note_number())
+ : dialog->note_number());
+ _controllable->set_value(clamp(value, desc.lower, desc.upper), Controllable::NoGroup);
+ }
+ delete dialog;
+}
+
+void
+AutomationController::set_freq_beats(double beats)
+{
+ const ARDOUR::ParameterDescriptor& desc = _controllable->desc();
+ const ARDOUR::Session& session = _controllable->session();
+ const framepos_t pos = session.transport_frame();
+ const ARDOUR::Tempo& tempo = session.tempo_map().tempo_at_frame (pos);
+ const double bpm = tempo.beats_per_minute();
+ const double bps = bpm / 60.0;
+ const double freq = bps / beats;
+ _controllable->set_value(clamp(freq, desc.lower, desc.upper), Controllable::NoGroup);
+}
+
+void
+AutomationController::set_ratio(double ratio)
+{
+ const ARDOUR::ParameterDescriptor& desc = _controllable->desc();
+ const double value = _controllable->get_value() * ratio;
+ _controllable->set_value(clamp(value, desc.lower, desc.upper), Controllable::NoGroup);
+}
+
+bool
+AutomationController::on_button_release(GdkEventButton* ev)
+{
+ using namespace Gtk::Menu_Helpers;
+
+ if (ev->button != 3) {
+ return false;