forward port automation handling changes from 2.x, upto and including about rev 6981...
[ardour.git] / gtk2_ardour / automation_controller.cc
1 /*
2     Copyright (C) 2007 Paul Davis
3     Author: Dave 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
23 #include "pbd/error.h"
24
25 #include "ardour/automation_list.h"
26 #include "ardour/automation_control.h"
27 #include "ardour/event_type_map.h"
28 #include "ardour/automatable.h"
29 #include "ardour/panner.h"
30 #include "ardour/session.h"
31
32 #include "ardour_ui.h"
33 #include "utils.h"
34 #include "automation_controller.h"
35 #include "gui_thread.h"
36
37 #include "i18n.h"
38
39 using namespace ARDOUR;
40 using namespace Gtk;
41
42
43 AutomationController::AutomationController(boost::shared_ptr<AutomationControl> ac, Adjustment* adj)
44         : BarController (*adj, ac)
45         , _ignore_change(false)
46         , _controllable(ac)
47         , _adjustment(adj)
48 {
49         set_name (X_("PluginSlider")); // FIXME: get yer own name!
50         set_style (BarController::LeftToRight);
51         set_use_parent (true);
52
53         StartGesture.connect (sigc::mem_fun(*this, &AutomationController::start_touch));
54         StopGesture.connect (sigc::mem_fun(*this, &AutomationController::end_touch));
55
56         _adjustment->signal_value_changed().connect (
57                         sigc::mem_fun(*this, &AutomationController::value_adjusted));
58
59         _screen_update_connection = ARDOUR_UI::RapidScreenUpdate.connect (
60                         sigc::mem_fun (*this, &AutomationController::display_effective_value));
61
62         ac->Changed.connect (_changed_connection, invalidator (*this), boost::bind (&AutomationController::value_changed, this), gui_context());
63 }
64
65 AutomationController::~AutomationController()
66 {
67 }
68
69 boost::shared_ptr<AutomationController>
70 AutomationController::create(
71                 boost::shared_ptr<Automatable> parent,
72                 const Evoral::Parameter& param,
73                 boost::shared_ptr<AutomationControl> ac)
74 {
75         Gtk::Adjustment* adjustment = manage(new Gtk::Adjustment(param.normal(), param.min(), param.max()));
76         if (!ac) {
77                 PBD::warning << "Creating AutomationController for " << EventTypeMap::instance().to_symbol(param) << endmsg;
78                 ac = boost::dynamic_pointer_cast<AutomationControl>(parent->control_factory(param));
79         } else {
80                 assert(ac->parameter() == param);
81         }
82         return boost::shared_ptr<AutomationController>(new AutomationController(ac, adjustment));
83 }
84
85 std::string
86 AutomationController::get_label (int&)
87 {
88         std::stringstream s;
89
90         // Hack to display CC rounded to int
91         if (_controllable->parameter().type() == MidiCCAutomation) {
92                 s << (int)_controllable->get_value();
93         } else if (_controllable->parameter().type() == PanAutomation) {
94                 s << Panner::value_as_string (_controllable->get_value ());
95         } else {
96                 s << std::fixed << std::setprecision(3) << _controllable->get_value();
97         }
98
99         return s.str ();
100 }
101
102 void
103 AutomationController::display_effective_value()
104 {
105         //if ( ! _controllable->list()->automation_playback())
106         //      return;
107
108         float value = _controllable->get_value();
109
110         if (_adjustment->get_value() != value) {
111                 _ignore_change = true;
112                 _adjustment->set_value (value);
113                 _ignore_change = false;
114         }
115 }
116
117 void
118 AutomationController::value_adjusted()
119 {
120         if (!_ignore_change) {
121                 _controllable->set_value(_adjustment->get_value());
122         }
123 }
124
125 void
126 AutomationController::start_touch()
127 {
128         _controllable->start_touch (_controllable->session().transport_frame());
129 }
130
131 void
132 AutomationController::end_touch ()
133 {
134         if (_controllable->automation_state() == Touch) {
135
136                 bool mark = false;
137                 double when = 0;
138                 
139                 if (_controllable->session().transport_rolling()) {
140                         mark = true;
141                         when = _controllable->session().transport_frame();
142                 }
143
144                 _controllable->stop_touch (mark, when);
145         }
146 }
147
148 void
149 AutomationController::automation_state_changed ()
150 {
151         ENSURE_GUI_THREAD (*this, &AutomationController::automation_state_changed)
152
153         bool x = (_controllable->automation_state() != Off);
154
155         /* start watching automation so that things move */
156
157         _screen_update_connection.disconnect();
158
159         if (x) {
160                 _screen_update_connection = ARDOUR_UI::RapidScreenUpdate.connect (
161                                 sigc::mem_fun (*this, &AutomationController::display_effective_value));
162         }
163 }
164
165 void
166 AutomationController::value_changed ()
167 {
168         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&AutomationController::display_effective_value, this));
169 }
170
171 /** Stop updating our value from our controllable */
172 void
173 AutomationController::stop_updating ()
174 {
175         _screen_update_connection.disconnect ();
176 }